git-svn-id: https://svn.code.sf.net/p/jode/code/branches/generics@1397 379699f6-c40d-0410-875b-85095c16579egenerics
commit
a5a48b3727
@ -0,0 +1,9 @@ |
|||||||
|
Makefile |
||||||
|
Makefile.in |
||||||
|
configure |
||||||
|
config.log |
||||||
|
config.cache |
||||||
|
config.status |
||||||
|
stamp-h |
||||||
|
libtool |
||||||
|
aclocal.m4 |
@ -0,0 +1 @@ |
|||||||
|
Jochen Hoenicke <Jochen.Hoenicke@Informatik.Uni-Oldenburg.DE> |
@ -0,0 +1,563 @@ |
|||||||
|
2005-10-14 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
* 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> |
||||||
|
|
||||||
|
Check for NullPointer in SyntheticAnalyzer. Based on |
||||||
|
patch suggessted by Peter Klauser (klp at users.sf.net). |
||||||
|
|
||||||
|
* src/net/sf/jode/jvm/SyntheticAnalyzer.java: |
||||||
|
(checkStaticAccess): Check refField for null pointer. |
||||||
|
(checkAccess): Likewise. |
||||||
|
|
||||||
|
2004-08-06 Jochen Hoenicke <hoenicke@marge.Informatik.Uni-Oldenburg.DE> |
||||||
|
|
||||||
|
* 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 a missing breakOp. |
||||||
|
|
||||||
|
2004-01-22 Jochen Hoenicke <hoenicke@informatik.uni-oldenburg.de> |
||||||
|
|
||||||
|
* net/sf/jode/jvm/CodeVerifier.java (modelEffect): Allow assigning |
||||||
|
fields in an uninitialized class as some synthetic code does this. |
||||||
|
|
||||||
|
2003-06-11 Mark Morschhäuser <mark.morschhaeuser@firemail.de> |
||||||
|
* net/sf/jode/decompiler/Main.java: New MenuItem to save a decompiled file. |
||||||
|
|
||||||
|
* net/sf/jode/decompiler/Main.java: Main-window will be centered on startup |
||||||
|
|
||||||
|
* build.xml: |
||||||
|
(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 |
||||||
|
|
||||||
|
2002-06-11 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
* net/sf/jode/decompiler/Main.java: New option keep-alive. With |
||||||
|
this option jode won't stop after an error but will continue with |
||||||
|
the next class. |
||||||
|
Patch suggested by Francis Devereux, francis at hc.eclipse.co.uk |
||||||
|
|
||||||
|
2002-02-25 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
* jode/bytecode/ClassInfo.java.in (read): Don't check for a |
||||||
|
maximum version anymore. Sun changes it with every release without |
||||||
|
changing the bytecode format. |
||||||
|
|
||||||
|
2002-02-15 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
* net/sf/jode/bytecode/BasicBlockReader.java: handle empty loops. |
||||||
|
(IS_NULL): new constant to tag empty blocks. |
||||||
|
(markReachableBlocks): check for empty loops. |
||||||
|
(convertBlock): Handle empty blocks. |
||||||
|
(convert): Handle IS_NULL. |
||||||
|
|
||||||
|
* net/sf/jode/decompiler/MethodAnalyzer.java: |
||||||
|
(analyzeCode): handle empty blocks. |
||||||
|
|
||||||
|
2001-08-14 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
* build.xml: test is default. |
||||||
|
(release-javadoc): New target. |
||||||
|
(release-src): Get from dir test only source files. |
||||||
|
(doc-javadoc): More parameters for nicer docu. |
||||||
|
|
||||||
|
2001-08-12 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
* net/sf/jode/bytecode/TypeSignature.java: |
||||||
|
(getArgumentSize): Renamed to ... |
||||||
|
(getParameterSize): ... this. Changed all callers. |
||||||
|
(skipType): Made private. |
||||||
|
|
||||||
|
* net/sf/jode/jvm/CodeVerifier.java: |
||||||
|
(initInfo): Use TypeSignature.getParameterTypes instead of skipType. |
||||||
|
|
||||||
|
* net/sf/jode/jvm/SyntheticAnalyzer.java: |
||||||
|
(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 |
||||||
|
(dumpExpression): Fixed the check for null outerExpr. |
||||||
|
|
||||||
|
* net/sf/jode/flow/FlowBlock.java: |
||||||
|
(checkConsistent): Allow lastModified in a finally block. |
||||||
|
* net/sf/jode/flow/TransformExceptionHandlers.java: Reworked exception |
||||||
|
handlers again. This time checked with javac 1.3, javac 1.1 and |
||||||
|
jikes. |
||||||
|
(checkTryCatchOrder): New method that was previously part of |
||||||
|
analyze. |
||||||
|
(analyze): Use checkTryCatchOrder. Don't merge try and catch flow |
||||||
|
blocks anymore, leave it to the analyzeXXX methods. |
||||||
|
(mergeTryCatch): New method. |
||||||
|
(analyzeCatchBlock): Get catchFlow as parameter. Call |
||||||
|
mergeTryCatch. |
||||||
|
(transformSubroutine): Handle POP-only subroutines. |
||||||
|
(removeJSR): Don't do special case for catchBlock any more. This |
||||||
|
is because catchFlow isn't yet merged when this method is called. |
||||||
|
(checkAndRemoveJSR): Likewise. |
||||||
|
(checkAndRemoveMonitorExit): Likewise. Merge subroutine only if |
||||||
|
we are the only predecessor. |
||||||
|
(analyzeSynchronized): Get catchFlow as parameter. Call |
||||||
|
mergeTryCatch. |
||||||
|
(mergeFinallyBlocks): New method, calls mergeTryCatch and does the |
||||||
|
common part of mergeFinally and mergeSpecialFinally. |
||||||
|
(analyzeFinally): Simplified, after checking and removing JSR, it |
||||||
|
does immediately analyze and transform subroutine to get the |
||||||
|
finallyBlock. Then it throws away the catchFlow and calls |
||||||
|
mergeFinallyBlocks. |
||||||
|
(analyzeSpecialFinally): Simplified, after checking it only handles |
||||||
|
the jumps in the try part and then call mergeFinallyBlocks. |
||||||
|
|
||||||
|
2001-08-08 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
More Documentation updates. |
||||||
|
* build.xml: Release rules. |
||||||
|
* scripts/jcpp.pl: Don't make backups of original. |
||||||
|
* net/sf/jode/bytecode/BasicBlocks.java (setBlocks): Check that |
||||||
|
successors are inside method. |
||||||
|
* net/sf/jode/bytecode/Block.java (getStackHeight): New Method. |
||||||
|
* net/sf/jode/bytecode/ClassPath.java (Location): public class to |
||||||
|
model a component of the class path. Previously it was Path. |
||||||
|
(ClassPath): New constructors added that take Location objects. |
||||||
|
* net/sf/jode/bytecode/ConstantPool.java (getClassName): Cache |
||||||
|
constants. |
||||||
|
* net/sf/jode/bytecode/GrowableConstantPool.java: Made public. |
||||||
|
(grow): Check that not too many constants are added. |
||||||
|
(reserveLongConstants): Removed (not used). |
||||||
|
(copyConstant): Removed (not used). |
||||||
|
* net/sf/jode/jvm/NewObject.java: Made package protected. |
||||||
|
* net/sf/jode/obfuscator/modules/RemovePopAnalyzer.java: |
||||||
|
Big updates (almost rewrote from scratch). Still doesn't compile. |
||||||
|
|
||||||
|
2001-08-05 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
Documentation updates (INSTALL, javadoc). |
||||||
|
Added JUnit Test cases. |
||||||
|
* build.xml: Big update. |
||||||
|
* net/sf/jode/bytecode/BasicBlock.java: |
||||||
|
(updateMaxStackLocals): new method to calculate maxStack and |
||||||
|
maxLocals. |
||||||
|
(setBlocks): fixed calculation of handlers, call updateMaxLocals. |
||||||
|
* net/sf/jode/bytecode/BasicBlockReader.java: |
||||||
|
(maxLocals, maxStack): new fields. |
||||||
|
(readCode): read maxStack/Locals into private fields. |
||||||
|
(convert): check that maxStack/Locals match what we calculate. |
||||||
|
* net/sf/jode/bytecode/BinaryInfo.java: |
||||||
|
(getKnownAttributeCount): renamed to... |
||||||
|
(getAttributeCount): ... this, and also count internal attributes. |
||||||
|
Made it protected. |
||||||
|
(readAttribute): made protected. |
||||||
|
(drop): made protected. |
||||||
|
(prepareAttributes): made protected. |
||||||
|
(writeKnownAttributes): removed. |
||||||
|
(writeAttributes): made protected, use getAttributeCount. |
||||||
|
Changed policy: it doesn't call writeKnownAttribute, but instead |
||||||
|
it expects sub classes to override this method. |
||||||
|
(getAttributeSize): made protected, subclasses should override it. |
||||||
|
Changed all subclasses to new policy. |
||||||
|
* net/sf/jode/bytecode/Block.java: |
||||||
|
(lineNr): Removed, it wasn't used. |
||||||
|
(pop,push): Removed, replaced by ... |
||||||
|
(maxpop,maxpush,delta): ... these, with slightly changed semantics. |
||||||
|
(stackHeight): New variable. |
||||||
|
(Block): Default Constructor doesn't initialize fields now. |
||||||
|
(getCatchers): Renamed to ... |
||||||
|
(getHandlers): ... this, changed all callers. |
||||||
|
(initCode): Calculate maxpop, maxpush, delta correctly. |
||||||
|
(getStackPopPush): Changed accordingly to new fields. |
||||||
|
(setCode): Removed debugging output for illegal contents. |
||||||
|
* net/sf/jode/bytecode/Classes.java: Reworked handling of inner |
||||||
|
classes. |
||||||
|
(innerClasses): Field mustn't be null anymore when loaded. |
||||||
|
(setName): Update class in classpath. |
||||||
|
* net/sf/jode/bytecode/ClassPath.java: |
||||||
|
(renameClassInfo): new function, should only used by ClassInfo. |
||||||
|
* net/sf/jode/bytecode/ConstantPool.java: made public. |
||||||
|
(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. |
||||||
|
|
||||||
|
* jode/swingui/Main.java (main): Also use bootclasspath if no |
||||||
|
classpath given. |
||||||
|
|
||||||
|
* jode/decompiler/MethodAnalyzer.java (skipWriting): Don't skip |
||||||
|
empty constructor that have a throws clause. |
||||||
|
|
||||||
|
* configure.in: Determine whether jdk1.1 resp. jdk1.2. Call jcpp |
||||||
|
in config.status. |
||||||
|
|
||||||
|
* jode/expr/Expression.java (makeInitializer): Now takes the |
||||||
|
type of the initialization. Changed all callers. |
||||||
|
* jode/expr/ConstantArrayOperator.java (makeInitializer): Check |
||||||
|
that type is our array type, otherwise we can't omit new Array[]. |
||||||
|
|
||||||
|
* jode/decompiler/LocalInfo.java (markFinal): Don't check that |
||||||
|
only one write is present. If two writes are in an then and an |
||||||
|
else branch of an if, the local can still be final. |
||||||
|
|
||||||
|
* jode/type/ArrayType.java (getSubType): Handle array of integer |
||||||
|
types correctly: byte[] is something completely different than |
||||||
|
int[]. |
||||||
|
(getSuperType): Likewise. |
||||||
|
|
||||||
|
* jode/expr/FieldOperator.java (getFieldInfo): New function. |
||||||
|
(needsCast): A cast is also needed if the field is private or |
||||||
|
package scope and the current type can't access the field. |
||||||
|
|
||||||
|
* jode/expr/InvokeOperator.java (getMethodInfo): New function. |
||||||
|
(needsCast): A cast is also needed if the method is private or |
||||||
|
package scope and the current type can't access the method. |
||||||
|
|
||||||
|
* jode/expr/ArrayStoreOperator.java (dumpExpression): Check if a |
||||||
|
cast of the array expression is needed. |
||||||
|
|
||||||
|
* jode/expr/TransformConstructors.java |
||||||
|
(transformFieldInitializers): Don't allow moving method invocations |
||||||
|
that throw a checked exception. |
||||||
|
|
||||||
|
* jode/bytecode/MethodInfo.java (readAttribute): Read Exceptions |
||||||
|
attribute even when not all attributes should be read. They are |
||||||
|
needed by TransformConstructors, see above. |
||||||
|
|
||||||
|
* jode/decompiler/TabbedPrintWriter.java (saveOps): Don't allow |
||||||
|
line breaks in not completed expressions since implicit parentheses |
||||||
|
would destroy the syntax. No need to put line break option on stack. |
||||||
|
(restoreOps): Adapted Stack format. |
||||||
|
|
||||||
|
* jode/decompiler/ClassAnalyzer.java (dumpDeclaration): Moved |
||||||
|
Code from dumpSource here. Don't put a line break after closing |
||||||
|
brace. |
||||||
|
(dumpSource): call dumpDeclaration and add a line break. |
||||||
|
(dumpBlock): Moved dropInfo(ATTRIBS) here. |
||||||
|
|
||||||
|
* jode/decompiler/ClassAnalyzer.java (STRICTFP): New Constant. |
||||||
|
(isStrictFP): New function. |
||||||
|
(initialize): Set strictfp modifier if a constructor has it set. |
||||||
|
(dumpSource): Handle strictfp modifier. |
||||||
|
|
||||||
|
* jode/decompiler/MethodAnalyzer.java (STRICTFP): New Constant. |
||||||
|
(isStrictFP): New function. |
||||||
|
(dumpSource): Handle strictfp modifier. |
||||||
|
|
||||||
|
* jode/jvm/SyntheticAnalyzer.java (checkAccess): Check for a |
||||||
|
special putfield access, where the set value is returned. Allow |
||||||
|
the modifier of field/method to be protected and the class to be |
||||||
|
a superclass. |
||||||
|
(checkStaticAccess): Likewise. |
||||||
|
(ACCESSDUPPUTFIELD): New Constant. |
||||||
|
(ACCESSDUPPUTSTATIC): New Constant. |
||||||
|
|
||||||
|
* jode/expr/InvokeOperator.java (simplifyAccess): Handle new |
||||||
|
synthetics. |
||||||
|
|
||||||
|
* jode/flow/SpecialBlock.java (removePop): Remove pop also for |
||||||
|
non void store instructions. |
||||||
|
|
||||||
|
* jode/decompiler/MethodAnalyzer.java (skipWriting): Also skip |
||||||
|
the new synthetics. |
||||||
|
|
||||||
|
* jode/decompiler/Main.java (main): Call System.exit() after |
||||||
|
everything was compiled. |
||||||
|
|
||||||
|
* jode/flow/TransformExceptionHandlers.java (removeJSR): |
||||||
|
Renamed back from removeBadJSR (see patch from 2001-02-04). The |
||||||
|
checkAndRemove* functions mustn't change the successors while they |
||||||
|
iterate over them. Instead of removing good jsr they mark them as |
||||||
|
good and removeJSR will finally remove them. |
||||||
|
(checkAndRemoveJSR): See above. |
||||||
|
(checkAndRemoveMonitorExit): See above. |
||||||
|
|
||||||
|
* jode/flow/JsrBlock.java (good): New variable, see above. |
||||||
|
(setGood): New method. |
||||||
|
(isGood): New method. |
||||||
|
|
||||||
|
2001-07-15 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 |
||||||
|
uninitialized objects in local or stack slots on backwards jump or |
||||||
|
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. |
||||||
|
|
||||||
|
2001-07-15 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
Applied patch from 2001-05-02 of Jode 1.1 tree: |
||||||
|
* jode/obfuscator/modules/ConstantAnalyzer.java (handleOpcode): |
||||||
|
Added divide by zero checks for opc_irem and opc_lrem. |
||||||
|
|
||||||
|
2001-07-15 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
Applied patches from 2001-02-27 of Jode 1.1 tree: |
||||||
|
|
||||||
|
* acinclude.m4 (JODE_CHECK_CLASS): Changed "test -e" to "-f" since |
||||||
|
-e is not supported on all architectures (Solaris) and -f is more |
||||||
|
correct anyway. |
||||||
|
Reported by Erik Modén. |
||||||
|
|
||||||
|
* jode/swingui/Main.java (AreaWriter): Convert all kinds of |
||||||
|
line breaks (CR+LF, CR, LF) to a LF character, which a JTextArea |
||||||
|
understands. |
||||||
|
|
||||||
|
2001-07-15 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
Applied patch from 2001-02-04 of Jode 1.1 tree: |
||||||
|
|
||||||
|
* jode/expr/IfThenElseOperator.java (simplify): Allow in the class$ |
||||||
|
simplification the then and else part to be swapped. |
||||||
|
* jode/type/ClassType.java (keywords): Added the package |
||||||
|
and import keywords. |
||||||
|
|
||||||
|
* jode/flow/TransformExceptionHandlers.java: |
||||||
|
(getPredecessor): New function. |
||||||
|
(getMonitorExitSlot): New function. |
||||||
|
(skipFinExitChain): New function. |
||||||
|
(removeJSR): Replaced by ... |
||||||
|
(removeBadJSR): ... this. |
||||||
|
(checkAndRemoveJSR): Use the new functions. Much simpler and |
||||||
|
handles nested synchronized blocks. It now traces the whole JSR |
||||||
|
and monitorexit chain before a jump to the first entry via |
||||||
|
skipFinExitChain, then checks and remove the first JSR |
||||||
|
resp. monitorexit. JSR jumps are simply ignored now. |
||||||
|
(checkAndRemoveMonitorExit): likewise. |
||||||
|
* jode/flow/StructuredBlock.java (prependBlock): New function. |
||||||
|
* jode/flow/CatchBlock.java (makeDeclaration): Generate name |
||||||
|
of dummyLocal, since nobody else will generate it. |
||||||
|
|
||||||
|
* jode/bytecode/BasicBlockReader.java (readCode): Remove bogus |
||||||
|
exceptionHandlers, whose catchers just throw the exception again. |
||||||
|
This kind of entries are inserted by an obfuscator and would break |
||||||
|
JODE. |
||||||
|
* jode/util/UnifyHash.java (iterateHashCode): Call cleanUp, |
||||||
|
to clean unneeded references. |
||||||
|
* jode/flow/TransformConstructors.java (transformOneField): |
||||||
|
Changed to private. Take field number as parameter. Check that |
||||||
|
expression doesn't contain a FieldOperator for a later field of |
||||||
|
the same class or a PutFieldOperator. Changed all callers. |
||||||
|
|
||||||
|
2001-07-15 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
|
||||||
|
Applied patch from 2001-02-01 of Jode 1.1 tree: |
||||||
|
* jode/jvm/CodeVerifier.java (Type.mergeType): If array elem |
||||||
|
types can't be merged, return tObject as common super type. |
||||||
|
* jode/type/ArrayType.java (getGeneralizedType): If array elem |
||||||
|
type can't be intersected, return tObject as common super type. |
||||||
|
|
||||||
|
2001-07-15 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
Applied patch from Jode 1.1 tree: |
||||||
|
|
||||||
|
* jode/expr/Expression.java (updateParentTypes): Call setType, |
||||||
|
instead of merging the types. Other childs want to know about the |
||||||
|
type change as well. |
||||||
|
* jode/decompiler/LocalInfo.java (combineWith): Reorganized a bit, |
||||||
|
but no changes. |
||||||
|
* jode/expr/InvokeOperator.java (dumpExpression): Always print |
||||||
|
the ThisOperator if a field is from a parent class of an outer |
||||||
|
class is used. And always qualify the this operator if not |
||||||
|
innermost. |
||||||
|
|
||||||
|
2001-07-14 Jochen Hoenicke <jochen@gnu.org> |
||||||
|
Applied patches from the Jode 1.1 tree: |
||||||
|
|
||||||
|
* jode/decompiler/TabbedPrintWriter.java: Better gnu style handling: |
||||||
|
(openBraceClass) (closeBraceClass) |
||||||
|
(openBraceNoIndent) (closeBraceNoIndent): new functions. |
||||||
|
(closeBraceNoSpace): Removed. |
||||||
|
* jode/decompiler/TabbedPrintWriter.java (GNU_SPACING): new constant. |
||||||
|
(printOptionalSpace): Print space for GNU_SPACING. |
||||||
|
* jode/decompiler/Options.java (setOptions): changed gnu style |
||||||
|
to include GNU_SPACING. |
||||||
|
* jode/decompiler/ClassAnalyzer.java (dumpSource): Use |
||||||
|
open/closeBraceClass. |
||||||
|
* jode/decompiler/MethodAnalyzer.java (dumpSource): Use |
||||||
|
open/closeBraceNoIndent. Call printOptionalSpace. |
||||||
|
* jode/decompiler/InvokeOperator.java (dumpExpression): |
||||||
|
Call printOptionalSpace, use open/closeBraceClass for inner |
||||||
|
classes. |
||||||
|
* jode/decompiler/UnaryOperator.java (dumpExpression): Call |
||||||
|
printOptionalSpace. |
||||||
|
|
||||||
|
Added pascal style from Rolf Howarth <rolf@squarebox.co.uk> |
||||||
|
* jode/decompiler/Decompiler.java (setOption): detect pascal option. |
||||||
|
* jode/decompiler/TabbedPrintWriter.java (BRACE_FLUSH_LEFT): |
||||||
|
new constant. |
||||||
|
(openBrace, openBraceContinue, closeBrace, closeBraceNoSpace, |
||||||
|
closeBraceContinue): handle flush left. |
||||||
|
|
||||||
|
* jode/type/NullType.java (intersection): Removed, since the |
||||||
|
version in ReferenceType is more correct. Before |
||||||
|
tNull.isOfType(tRange(X,tNull)) returned false, which lead to |
||||||
|
incorrect behaviour in InvokeOperator.needsCast. |
||||||
|
* jode/decompiler/FieldAnalyzer.java (dumpSource): Removed the |
||||||
|
"= null" hack for final fields; it was not correct, since the |
||||||
|
field could be initialized in a constructor. |
||||||
|
* jode/decompiler/TabbedPrintWriter.java (BreakPoint.endOp): |
||||||
|
Simplified the code, copy options always from child. |
||||||
|
* jode/expr/InvokeOperator.java (isGetClass): Allow the method to |
||||||
|
be declared inside an outer class: We simply check if we can get |
||||||
|
the method analyzer. |
||||||
|
(simplify): handle unifyParam. |
||||||
|
* jode/expr/PopOperator.java (getBreakPenalty): return penalty of |
||||||
|
inner expression. (dumpExpression): Call dumpExpression of |
||||||
|
subexpression immediately without priority. |
@ -0,0 +1,23 @@ |
|||||||
|
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 |
||||||
|
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 |
||||||
|
to compile a 1.1 version. |
||||||
|
|
||||||
|
This package was designed to use the ANT from the jakarta.apache.org |
||||||
|
tools. I assume you have installed it correctly. |
||||||
|
|
||||||
|
Take some time to edit config.props. There are a few options you need |
||||||
|
to take care of. (Unfortunately ant can't test for executables). |
||||||
|
|
||||||
|
Now you are ready to invoke ant. There are many possible targets, here |
||||||
|
are the most useful ones: |
||||||
|
|
||||||
|
all builds class files and documentation. |
||||||
|
build builds class files only (autodetects java version). |
||||||
|
build-1.1 builds JDK1.1 class files. |
||||||
|
doc builds documentation. |
||||||
|
dist creates all release files. |
||||||
|
test does some self tests. You need to have junit installed for this. |
||||||
|
clean cleans everything that doesn't belong to the source distribution. |
||||||
|
cvsclean cleans everything that doesn't belong into the cvs repository. |
@ -0,0 +1,3 @@ |
|||||||
|
Manifest-Version: 1.0 |
||||||
|
Main-Class: net.sf.jode.swingui.Main |
||||||
|
Created-By: SeeksTheMoon |
@ -0,0 +1,36 @@ |
|||||||
|
New in 1.2 |
||||||
|
* New bytecode interface |
||||||
|
* Faster and better flow analyzation |
||||||
|
|
||||||
|
New in 1.1 |
||||||
|
* break long lines |
||||||
|
* handle most of javac v8 constructs (jdk 1.3) |
||||||
|
* bug fixes |
||||||
|
|
||||||
|
New in 1.0.93 |
||||||
|
* anonymous and inner class decompilation reworked. |
||||||
|
* replaced a bash specific construct in acinclude.m4 |
||||||
|
* fixed a funny bug: string += "1" was decompiled as string++ |
||||||
|
* main class of decompiler is now jode.decompiler.Main |
||||||
|
* fixed a bug in ConstantAnalyzer (obfuscator) (produced wrong code) |
||||||
|
* fixed more bugs in obfuscator |
||||||
|
* better memory usage in decompiler |
||||||
|
* fixed a bug in decompiler (couldn't handle nop instruction) |
||||||
|
* web pages updated. |
||||||
|
|
||||||
|
New in 1.0.92 |
||||||
|
* option --pretty works again |
||||||
|
* web pages updated |
||||||
|
* swingui can show class hierarchie |
||||||
|
* KeywordRenamer added to obfuscator. |
||||||
|
|
||||||
|
New in 1.0.91: |
||||||
|
* first version using configure. Jode can now be almost automatically |
||||||
|
build, see INSTALL for instructions. |
||||||
|
* the decompiler can handler inner and anoymous classes. |
||||||
|
* you now need the gnu getopt package. |
||||||
|
* you need JDK 1.2 or alternatively the swing and collection packages |
||||||
|
for 1.1 |
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,90 @@ |
|||||||
|
JODE (Java Optimize and Decompile Environment) |
||||||
|
|
||||||
|
JODE is a java package containing a decompiler and an optimizer for |
||||||
|
java. This package is freely available under the GNU General Public |
||||||
|
License. |
||||||
|
|
||||||
|
The decompiler reads in class files and produces something similar to |
||||||
|
the original java file. Of course this can't be perfect: There is no |
||||||
|
way to produce the comments or the names of local variables (except |
||||||
|
when the java files were compiled with `-g') and there are often more |
||||||
|
ways to write the same thing. However, jode does its job quite well. |
||||||
|
|
||||||
|
The optimizer transforms class files in various ways with |
||||||
|
can be controlled by a script file. |
||||||
|
|
||||||
|
Please note that most software licenses forbid to decompile class |
||||||
|
files. Use this decompiler only, if you have legal rights to |
||||||
|
decompile the class (e.g. on your own code). |
||||||
|
|
||||||
|
The features of the decompilers are: |
||||||
|
|
||||||
|
* Systematic flow analysis, that can decompile every java code |
||||||
|
without the need of goto (which doesn't exists in java). |
||||||
|
* Type deduction, that can guess the type of local variables, even if |
||||||
|
it is an interface type that doesn't occur in the bytecode. |
||||||
|
* Handling of inner and anonymous classes. |
||||||
|
* Different indentation styles available. |
||||||
|
* It can decompile itself (194 classes) without a single error. |
||||||
|
* It can decrypt strings on the fly, that were encrypted by an obfuscator. |
||||||
|
|
||||||
|
Known bugs of the decompiler: |
||||||
|
|
||||||
|
- Some jdk1.3 synthetic access functions aren't understood. The |
||||||
|
produced source contains access$xxx functions, but it still compiles. |
||||||
|
|
||||||
|
- There may be other bugs, that cause Exceptions or invalid code. |
||||||
|
If you have such a problems don't hesitate to issue a bug report. |
||||||
|
Please include the <code>class</code> file if possible. |
||||||
|
|
||||||
|
Limitations: |
||||||
|
|
||||||
|
- If not all dependent classes can be found, the verifier (which is |
||||||
|
run before decompilation starts) may exit with a type error. You |
||||||
|
can decompile it with --verify=off, but take the warning serious, |
||||||
|
that the types may be incorrect. There's sometimes no way to guess |
||||||
|
the right type, if you don't have access the full class hierarchie. |
||||||
|
|
||||||
|
But if you don't have the dependent classes, you can't compile the |
||||||
|
code again, anyway, so why do you want to decompile it? |
||||||
|
|
||||||
|
- There may be situations, where jode doesn't understand complex |
||||||
|
expressions. In this case many ugly temporary variables are used, |
||||||
|
but the code should still be compileable. This does especially |
||||||
|
happen when you compile with `-O' flag and javac has inlined some |
||||||
|
methods. |
||||||
|
|
||||||
|
|
||||||
|
The features of the obfuscator are: |
||||||
|
* Modular design, you can plug the obfuscation transformation you need |
||||||
|
together via the script file. |
||||||
|
* Strong analysis, that optimizes away fields, expressions that are |
||||||
|
known to be constant (and reverts the flow obfuscation of Zelix |
||||||
|
Klassmaster) |
||||||
|
* Can also be used as Optimizer, without any obfuscation at all, it |
||||||
|
will preserve the local variable table and line number table. |
||||||
|
* Many different renaming options, you can also simply add your own. |
||||||
|
|
||||||
|
|
||||||
|
PRELIMINARIES: |
||||||
|
|
||||||
|
See INSTALL for installation instructions. |
||||||
|
|
||||||
|
USAGE: |
||||||
|
|
||||||
|
First set the classpath. It should contain the jar file jode-1.1.jar, |
||||||
|
the gnu getopt package and the sun collection package for 1.1. For |
||||||
|
the swingui program you also need swing in you classpath. |
||||||
|
|
||||||
|
You can then decompile a class file with: |
||||||
|
java jode.decompiler.Main --classpath program.jar,libfoo.jar org.package.Class |
||||||
|
or a complete package with |
||||||
|
java jode.decompiler.Main --classpath libfoo.jar program.jar |
||||||
|
|
||||||
|
For a graphical user interface based on swing try: |
||||||
|
java jode.swingui.Main --classpath jarfile1.jar |
||||||
|
|
||||||
|
The obfuscator/deobfuscator can be run with a script: |
||||||
|
java jode.obfuscator.Main obfuscation.jos |
||||||
|
|
||||||
|
See the web documents for more information about the script syntax. |
@ -0,0 +1,5 @@ |
|||||||
|
Joe Bronkema <joseph.d.bronkema at lmco.com> |
||||||
|
Rolf Howarth <rolf at squarebox.co.uk> for pascal indentaton style. |
||||||
|
Erik Modén <Erik.Moden at emw.ericsson.se> |
||||||
|
Martin Schmitz <m.schmitz at e-sign.com> for finding many bugs in the obfuscator. |
||||||
|
zzzeek <classic at io.com> |
@ -0,0 +1,64 @@ |
|||||||
|
This is a list of features, that would be nice to have: |
||||||
|
|
||||||
|
Decompiler: |
||||||
|
- BUG: public final static null fields aren't initialized (leads to compile error) |
||||||
|
- outline inlined methods. |
||||||
|
- remove string decrypt method. |
||||||
|
- 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: |
||||||
|
- Detect Class.forName() calls with constant parameters and rename |
||||||
|
these constants. Detect class$ methods with constant parameters. |
||||||
|
Warn about all other occurences of Class.forName() |
||||||
|
- work around Class.forName, by creating a new version using a hash |
||||||
|
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 should detect inner/anonymous classes and mark them |
||||||
|
as such. It should be possible with the renaming table to mark |
||||||
|
inner classes as well. Inner classes are easy to detect; there |
||||||
|
constructor has a special form. And the information is very |
||||||
|
useful for the decompiler. |
||||||
|
|
||||||
|
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: |
||||||
|
- make a nice user interface: |
||||||
|
~ list classnames: toggable between class hierarchie/package hierarchie. |
||||||
|
- list fields/method of selected class. |
||||||
|
- show decompilation of selected method. |
||||||
|
- show usage of method/fields. |
||||||
|
- syntax highlighting, hyper links etc. |
||||||
|
(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?) |
||||||
|
|
||||||
|
Internal: |
||||||
|
- clean up package hierarchy, esp. expr, flow and decompiler. |
||||||
|
- move to net.sf.jode package. |
||||||
|
- make the class names more precise, e.g. StructuredBlock is Statement, |
||||||
|
FlowBlock is BasicBlock. |
@ -0,0 +1,2 @@ |
|||||||
|
Makefile |
||||||
|
Makefile.in |
@ -0,0 +1,5 @@ |
|||||||
|
## Input file for automake to generate the Makefile.in used by configure
|
||||||
|
|
||||||
|
bin_SCRIPTS = jode
|
||||||
|
EXTRA_DIST = jode.bat.in jode.in
|
||||||
|
|
@ -0,0 +1,19 @@ |
|||||||
|
; Use this batch file to make invoking the decompiler easier. |
||||||
|
; Please edit this file and insert correct directories where needed. |
||||||
|
; |
||||||
|
; Usage: jode dec [decompiler options] |
||||||
|
; jode swi [swingui options] |
||||||
|
; jode obf [obfuscator options] |
||||||
|
; Since the decompiler is the most important program you can omit `dec': |
||||||
|
; jode [decompiler options] |
||||||
|
|
||||||
|
set CLASSPATH=jode-@VERSION@-1.2.jar;%CLASSPATH% |
||||||
|
set PROGGY=default |
||||||
|
|
||||||
|
if %1 == swi set PROGGY=swingui |
||||||
|
if %1 == obf set PROGGY=obfuscator |
||||||
|
if %1 == dec set PROGGY=decompiler |
||||||
|
if NOT %PROGGY% == default shift |
||||||
|
if %PROGGY% == default set PROGGY=decompiler |
||||||
|
|
||||||
|
java jode.%PROGGY%.Main %1 %2 %3 %4 %5 %6 %7 %8 %9 |
@ -0,0 +1,14 @@ |
|||||||
|
#!@SHELL@ |
||||||
|
prefix=@prefix@ |
||||||
|
|
||||||
|
case $1 in |
||||||
|
[Ss]wi*) CLAZZ=jode.swingui.Main; shift ;; |
||||||
|
[Dd]ec*) CLAZZ=jode.decompiler.Main; shift ;; |
||||||
|
[Oo]bf*) CLAZZ=jode.obfuscator.Main; shift ;; |
||||||
|
*) CLAZZ=jode.decompiler.Main ;; |
||||||
|
esac |
||||||
|
|
||||||
|
|
||||||
|
CP=`echo $CLASSPATH | sed s/:/,/` |
||||||
|
CLASSPATH=@datadir@/jode-@VERSION@.jar:@CLASSPATH@ \ |
||||||
|
@JAVA@ $CLAZZ --classpath $CP $* |
@ -0,0 +1,380 @@ |
|||||||
|
<?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='<b><a href="http://jode.sourceforge.net/">Jode</a> ${version}</b><br><font size="-2">Build ${date}</font>' |
||||||
|
overview="${src}/net/sf/jode/overview.html" |
||||||
|
bottom='Copyright &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" 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" 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> |
@ -0,0 +1,28 @@ |
|||||||
|
# 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,58 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
create_jar() { |
||||||
|
jar -xvf $HOME/java/jars/getopt.jar |
||||||
|
rm -rf META-INF |
||||||
|
jar -cvf ../jode-1.0.92-$1.jar AUTHORS COPYING README INSTALL NEWS doc/*.{html,jos,perl,gif} `find jode -name \*.class` gnu |
||||||
|
rm -rf gnu |
||||||
|
} |
||||||
|
|
||||||
|
create_first() { |
||||||
|
# first 1.1 version. |
||||||
|
tar -xvzf jode-1.0.92.tar.gz |
||||||
|
cd jode-1.0.92 |
||||||
|
CLASSPATH=$HOME/java/jars/getopt.jar:/usr/local/1.1collections/lib/collections.jar:/usr/local/swing-1.1/swingall.jar \ |
||||||
|
./configure --with-java=/usr/lib/java --with-jikes=/home/jochen/bin |
||||||
|
make |
||||||
|
create_jar 1.1 |
||||||
|
cd .. |
||||||
|
rm -rf jode-1.0.92 |
||||||
|
} |
||||||
|
|
||||||
|
create_second() { |
||||||
|
# now 1.2 version. |
||||||
|
tar -xvzf jode-1.0.92.tar.gz |
||||||
|
cd jode-1.0.92 |
||||||
|
find -name \*.java -o -name \*.java.in | xargs jcpp -DJDK12 |
||||||
|
CLASSPATH=$HOME/java/jars/getopt.jar \ |
||||||
|
./configure --with-java=/usr/local/jdk1.2 --with-jikes=/home/jochen/bin |
||||||
|
make |
||||||
|
create_jar 1.2 |
||||||
|
cd .. |
||||||
|
rm -rf jode-1.0.92 |
||||||
|
} |
||||||
|
|
||||||
|
create_applet() { |
||||||
|
cat <<EOF >jode-applet.jos |
||||||
|
# JODE Optimizer Script |
||||||
|
strip = "unreach","source","lnt","lvt","inner" |
||||||
|
load = new WildCard { value = "jode" }, |
||||||
|
new WildCard { value = "gnu" } |
||||||
|
preserve = new WildCard { value = "jode.JodeApplet.<init>.()V" } |
||||||
|
renamer = new StrongRenamer { |
||||||
|
charsetStart = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" |
||||||
|
charsetPart = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$" |
||||||
|
charsetPackage = "abcdefghijklmnopqrstuvwxyz" |
||||||
|
charsetClass = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
||||||
|
} |
||||||
|
analyzer = new ConstantAnalyzer |
||||||
|
post = new LocalOptimizer, new RemovePopAnalyzer |
||||||
|
EOF |
||||||
|
|
||||||
|
CLASSPATH=jode-1.0.92-1.1.jar:$CLASSPATH java jode.obfuscator.Main \ |
||||||
|
--cp jode-1.0.92-1.1.jar --dest jode-applet.jar jode-applet.jos |
||||||
|
} |
||||||
|
|
||||||
|
#create_first |
||||||
|
#create_second |
||||||
|
create_applet |
@ -0,0 +1,3 @@ |
|||||||
|
Makefile |
||||||
|
Makefile.in |
||||||
|
*.html |
@ -0,0 +1,25 @@ |
|||||||
|
<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,69 @@ |
|||||||
|
<section title="Wish List"> |
||||||
|
|
||||||
|
<p>This section contains features that I think would be great to have, |
||||||
|
but are also very hard to implement. </p> |
||||||
|
|
||||||
|
<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> |
||||||
|
|
||||||
|
<h2><i>Out</i>line inlined methods</h2> |
||||||
|
|
||||||
|
<p>If java gets called with `<code>-O</code>' switch, it inlines methods, |
||||||
|
that are private, final, or static and contain no loops. When |
||||||
|
decompiling this it sometimes produces really ugly code. The right |
||||||
|
way to solve this would be to <i>out</i>line the code again. This is |
||||||
|
possible but requires to find the inlined method. </p> |
||||||
|
|
||||||
|
<p>The name `outline' was suggested by <a |
||||||
|
href="http://www.informatik.uni-oldenburg.de/~mw">Michael</a>. |
||||||
|
</p> |
||||||
|
|
||||||
|
<h2>Better names of local variables</h2> |
||||||
|
|
||||||
|
<p>The local variable naming is very stupid. Even with pretty it just |
||||||
|
names the variable after the type with a unifying number appended. A |
||||||
|
method containing very much objects of the same type looks very |
||||||
|
ugly. </p> |
||||||
|
|
||||||
|
<p>My plan is looking at the assignments. If we have locals in |
||||||
|
assignments</p> |
||||||
|
<pre> |
||||||
|
int l_1 = array.length |
||||||
|
String l_2 = object.getName() |
||||||
|
</pre> |
||||||
|
<p>we could name them "length" and "name". If we |
||||||
|
have assignments:</p> |
||||||
|
<pre> |
||||||
|
MenuItem local_1 = new MenuItem("Open"); |
||||||
|
MenuItem local_2 = new MenuItem("Save"); |
||||||
|
</pre> |
||||||
|
<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 |
||||||
|
to a local. If the type matches, the local will be named after |
||||||
|
<i>hint name</i>. This could be extended by giving them several |
||||||
|
weighted hints and constructing the name in an intelligent way. </p> |
||||||
|
|
||||||
|
<h2>Better deobfuscation features</h2> |
||||||
|
<p>First there should be a good Renamer: Methods that simply |
||||||
|
return a field value should be renamed to get<i>FieldName</i>. |
||||||
|
Fields should be named after their type, maybe also by assignments |
||||||
|
(see section about local variable names).</p> |
||||||
|
|
||||||
|
<p>The deobfuscator should detect inner and anonymous variables, |
||||||
|
synthetic methods and so on, and rename them accordingly.</p> |
||||||
|
|
||||||
|
<h2>Handling of Class.forName in obfuscator</h2> |
||||||
|
<p>The obfuscator should detect Class.forName constructs (and |
||||||
|
similarly for methods and fields) and if their parameters are constant |
||||||
|
it should change the parameter according to the rename function. </p> |
||||||
|
|
||||||
|
<h2>Merging javadoc comments</h2> |
||||||
|
<p>It would be nice if the decompiler could merge the javadoc comments |
||||||
|
into the class file. More and more people use javadoc to comment the |
||||||
|
public api of their java classes. It shouldn't be too difficult to |
||||||
|
copy them back into the java code. </p> |
||||||
|
|
||||||
|
<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> |
||||||
|
</section> |
@ -0,0 +1,711 @@ |
|||||||
|
use strict; |
||||||
|
|
||||||
|
my (@tstack, @vstack); |
||||||
|
|
||||||
|
my $incindent = 4; |
||||||
|
|
||||||
|
my $instr_addr; |
||||||
|
my (%instr, %next_instr, %prev_instr); |
||||||
|
|
||||||
|
@tstack = (); |
||||||
|
@vstack = (); |
||||||
|
|
||||||
|
sub print_stack { |
||||||
|
my ($type, $value); |
||||||
|
while (@tstack and @vstack) { |
||||||
|
$type = shift @tstack; |
||||||
|
$value = shift @vstack; |
||||||
|
print STDERR "($type) $value, "; |
||||||
|
} |
||||||
|
if (@tstack) { |
||||||
|
print STDERR "TSTACK to big : @tstack "; |
||||||
|
} elsif (@vstack) { |
||||||
|
print STDERR "VSTACK to big : @vstack "; |
||||||
|
} |
||||||
|
@tstack = (); |
||||||
|
@vstack = (); |
||||||
|
} |
||||||
|
|
||||||
|
sub print_code { |
||||||
|
my ($indent, $code, $addr) = @_; |
||||||
|
#print " "x$indent, $code, (defined addr)?"/* $addr */":"", "\n"; |
||||||
|
print " "x$indent, $code, "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
sub dump_program { |
||||||
|
my $addr; |
||||||
|
foreach $addr (sort { $a <=> $b } keys %instr) { |
||||||
|
print_code (0, "$addr $instr{$addr}"); |
||||||
|
} |
||||||
|
return 1 |
||||||
|
} |
||||||
|
|
||||||
|
sub convert_value($$$) { |
||||||
|
my ($value, $oldtype, $newtype) = @_; |
||||||
|
return "$value" if ($oldtype eq $newtype); |
||||||
|
return "$value" if ($oldtype =~ /\*/ or $newtype =~ /\*/); |
||||||
|
return "$value" if ($oldtype eq "boolean" && $newtype eq "int"); |
||||||
|
if ($oldtype eq "int" && $newtype eq "boolean") { |
||||||
|
$value =~ s/1/true/g; |
||||||
|
$value =~ s/0/false/g; |
||||||
|
$value =~ s/\&/\&\&/g; |
||||||
|
$value =~ s/\|/\|\|/g; |
||||||
|
return $value; |
||||||
|
} |
||||||
|
return $value; # "/*warn: conv: $oldtype => $newtype*/ $value"; |
||||||
|
} |
||||||
|
|
||||||
|
sub get_type ($) { |
||||||
|
my $type; |
||||||
|
$_ = $_[0]; |
||||||
|
SWITCH: { |
||||||
|
/^b$/ && ($type = "byte",last); |
||||||
|
/^c$/ && ($type = "char",last); |
||||||
|
/^s$/ && ($type = "short",last); |
||||||
|
/^i$/ && ($type = "int",last); |
||||||
|
/^l$/ && ($type = "long",last); |
||||||
|
/^f$/ && ($type = "float",last); |
||||||
|
/^d$/ && ($type = "double",last); |
||||||
|
/^a$/ && ($type = "*",last); |
||||||
|
die "internal error in get_type"; |
||||||
|
} |
||||||
|
return $type; |
||||||
|
} |
||||||
|
|
||||||
|
sub pop_value_type ($) { |
||||||
|
if (not @tstack || not @vstack) { |
||||||
|
die "Stack is empty??"; |
||||||
|
} |
||||||
|
my $result = ""; |
||||||
|
my $want_type = $_[0]; |
||||||
|
my $act_type = pop @tstack; |
||||||
|
my $value = pop @vstack; |
||||||
|
warn "want_type not defined" |
||||||
|
if (not defined $want_type); |
||||||
|
warn "act_type not defined" |
||||||
|
if (not defined $act_type); |
||||||
|
$result = convert_value($value, $act_type, $want_type); |
||||||
|
return ($result, $act_type); |
||||||
|
} |
||||||
|
|
||||||
|
sub pop_value($) { |
||||||
|
@_ = pop_value_type($_[0]); |
||||||
|
return $_[0]; |
||||||
|
} |
||||||
|
|
||||||
|
sub parse_type($) { |
||||||
|
$_[0] =~ /^\#\d+\s+ <Class |
||||||
|
\s+(\S+) # type |
||||||
|
>\s*$/x or die "Wrong field parameter `$_[0]'"; |
||||||
|
return $1; |
||||||
|
} |
||||||
|
|
||||||
|
sub parse_field($) { |
||||||
|
$_[0] =~ /^\#\d+\s+ <Field |
||||||
|
\s+(\S+) # type |
||||||
|
\s+([^\[>]+) # name |
||||||
|
((?:\[\])*) # [][]... belongs to type |
||||||
|
>\s*$/x or die "Wrong field parameter `$_[0]'"; |
||||||
|
return $1.$3, $2; |
||||||
|
} |
||||||
|
|
||||||
|
sub parse_special($) { |
||||||
|
$_[0] =~ /\#\d+\s+<Method |
||||||
|
\s+([^\(\s]+) # method |
||||||
|
\s*\(([^\)]*)\) # params |
||||||
|
>\s*$/x or die "Wrong method parameter `$_[0]'"; |
||||||
|
my ($method, $params) = ($1,$2); |
||||||
|
my @params = split /,\s*/, $params; |
||||||
|
return $method, @params; |
||||||
|
} |
||||||
|
|
||||||
|
sub parse_method($) { |
||||||
|
$_[0] =~ /\#\d+\s+<Method |
||||||
|
\s+(\S+) # type |
||||||
|
\s+([^\(\s]+) # method |
||||||
|
\s*\(([^\)]*)\) # params |
||||||
|
>\s*$/x or die "Wrong method parameter `$_[0]'"; |
||||||
|
my ($type, $method, $params) = ($1, $2,$3); |
||||||
|
my @params = split /,\s*/, $params; |
||||||
|
return $type, $method, @params; |
||||||
|
} |
||||||
|
|
||||||
|
sub classify($$) { |
||||||
|
my $class = $_[0] . "."; |
||||||
|
$class = "" if $class eq "this."; |
||||||
|
return $class.$_[1]; |
||||||
|
} |
||||||
|
|
||||||
|
sub new_instr($) { |
||||||
|
if (defined ($instr{$instr_addr})) { |
||||||
|
$instr{$instr_addr} .= "\n/*warn: multiple*/\n".$_[0]; |
||||||
|
} else { |
||||||
|
$instr{$instr_addr} = $_[0]; |
||||||
|
} |
||||||
|
# print STDERR "$instr_addr: $instr{$instr_addr}\n"; |
||||||
|
} |
||||||
|
|
||||||
|
sub new_assign($$) { |
||||||
|
my ($var, $value) = @_; |
||||||
|
if (@vstack) { |
||||||
|
if (("$value" eq "($var + 1)") && |
||||||
|
($vstack[-1] eq "$var")) { |
||||||
|
$vstack[-1] = "$var++"; |
||||||
|
} else { |
||||||
|
warn("`$var = $value' in expression, while vstack"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
new_instr("$var = $value;"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sub combine_if_block { |
||||||
|
my ($addr, $end) = @_; |
||||||
|
|
||||||
|
$instr{$addr} =~ /if (\(.*\)) goto (\d+);/ or return; |
||||||
|
my ($cond, $dest) = ($1, $2); |
||||||
|
|
||||||
|
COMBINE: |
||||||
|
while (1) { |
||||||
|
my $if; |
||||||
|
|
||||||
|
|
||||||
|
# First combine ifs with the same dest addr, that is ors. |
||||||
|
my @conds = ($cond); |
||||||
|
for ($if = $next_instr{$addr}; $if < $end; $if = $next_instr{$addr}) { |
||||||
|
|
||||||
|
$instr{$if} =~ /if (\(.*\)) goto ($dest);/ or last; |
||||||
|
|
||||||
|
push @conds, $1; |
||||||
|
#remove unnecessary ifs (hope there are no goto's) |
||||||
|
$next_instr{$addr} = $next_instr{$if}; |
||||||
|
$prev_instr{$next_instr{$if}}= $addr; |
||||||
|
delete $instr{$if}; |
||||||
|
delete $prev_instr{$if}; |
||||||
|
delete $next_instr{$if}; |
||||||
|
} |
||||||
|
if (@conds > 1) { |
||||||
|
# combine conditions with or and reset the list |
||||||
|
$cond = join " || ", @conds; |
||||||
|
$cond = "($cond)"; |
||||||
|
} |
||||||
|
|
||||||
|
last COMBINE if ($if >= $end || $instr{$if} !~ /^if/); |
||||||
|
|
||||||
|
# Now try if we can combine all further ifs until the destination (that is and) |
||||||
|
combine_if_block($if, $dest) |
||||||
|
unless ($next_instr{$if} == $dest); |
||||||
|
|
||||||
|
last COMBINE if ($next_instr{$if} != $dest); |
||||||
|
|
||||||
|
# This is an and |
||||||
|
$instr{$if} =~ /if (\(.*\)) goto (\d+);/; |
||||||
|
$cond = "(!$cond && $1)"; |
||||||
|
$dest = $2; |
||||||
|
$next_instr{$addr} = $next_instr{$if}; |
||||||
|
$prev_instr{$next_instr{$if}}= $addr; |
||||||
|
delete $instr{$if}; |
||||||
|
delete $prev_instr{$if}; |
||||||
|
delete $next_instr{$if}; |
||||||
|
} |
||||||
|
|
||||||
|
# Okay, we are stuck here, build the combined if. |
||||||
|
$instr{$addr} = "if $cond goto $dest;"; |
||||||
|
} |
||||||
|
|
||||||
|
sub simplify_instructions { |
||||||
|
my ($addr, $end) = @_; |
||||||
|
ADDR: |
||||||
|
for (; $addr < $end; $addr = $next_instr{$addr}) { |
||||||
|
combine_if_block($addr, $end) |
||||||
|
if $instr{$addr} =~ /^if /; |
||||||
|
|
||||||
|
while ( $instr{$addr} =~ |
||||||
|
/^(.*)new (java\.lang\.)?StringBuffer\((\).append\(.*)+\).toString\(\)(.*)$/ ) { |
||||||
|
my ($first, $middle, $last) = ($1,$3,$4); |
||||||
|
$middle =~ s/\).append\(/\+/g; |
||||||
|
$middle =~ s/^\+//; |
||||||
|
$instr{$addr} = $first.$middle.$last; |
||||||
|
} |
||||||
|
|
||||||
|
while ( $instr{$addr} =~ s/([A-Za-z_\$][A-Za-z_\$0-9]*) = \(\1 \+ 1\)/$1++/) { |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# The parameters: |
||||||
|
# start first instruction to decode |
||||||
|
# end last instruction to decode + 1 |
||||||
|
# next instruction where control flows after this block |
||||||
|
# (usually end but may be bigger) |
||||||
|
# break instruction where a break would bring us to |
||||||
|
# indent The indentation of this block |
||||||
|
|
||||||
|
sub print_stmtlist ($$$$$) { |
||||||
|
my ($start, $end, $next, $break, $indent) = @_; |
||||||
|
my $addr; |
||||||
|
$addr = $start; |
||||||
|
ADDR: |
||||||
|
while ($addr < $end) { |
||||||
|
(dump_program && die "Addresses out of range: $addr") if (not defined $next_instr{$addr}); |
||||||
|
|
||||||
|
$_ = $instr{$addr}; |
||||||
|
/^goto (\d+);$/ && do { |
||||||
|
my $dest = $1; |
||||||
|
if ($dest == $break) { |
||||||
|
print_code($indent, "break $dest;", $addr); |
||||||
|
$addr = $next_instr{$addr}; |
||||||
|
next ADDR; |
||||||
|
} |
||||||
|
my $begin = $next_instr{$addr}; |
||||||
|
if ($instr{$dest} =~ /^if\s\((.*)\)\sgoto\s$begin/) { |
||||||
|
# This is a while-loop |
||||||
|
print_code($indent, "while ($1) {", $addr); |
||||||
|
print_stmtlist($begin, $dest, $dest, $dest, $indent+$incindent); |
||||||
|
print_code($indent, "}"); |
||||||
|
$addr = $next_instr{$dest}; |
||||||
|
next ADDR; |
||||||
|
} |
||||||
|
}; |
||||||
|
/^if \((.*)\) goto (\d+);/ && do { |
||||||
|
my $cond = $1; |
||||||
|
my $next_after_if = $2; |
||||||
|
if ($next_after_if > $addr && |
||||||
|
($next_after_if <= $end || $next_after_if == $next)) { |
||||||
|
# This seems to be an if. |
||||||
|
print_code($indent, "if (!($cond)) {", $addr); |
||||||
|
|
||||||
|
# endthen is the last instruction in then block + 1 |
||||||
|
my $endthen = ($next_after_if > $end) ? $end : $next_after_if; |
||||||
|
my $prev = $prev_instr{$endthen}; |
||||||
|
if ($instr{$prev} =~ /^goto\s(.*);/ && |
||||||
|
$1 > $endthen && ($1 <= $end || $1 == $next)) { |
||||||
|
$next_after_if = $1; |
||||||
|
my $endelse = $1; |
||||||
|
if ($endelse > $end) { |
||||||
|
$endelse = $end; |
||||||
|
} |
||||||
|
# there is an else part |
||||||
|
print_stmtlist ($next_instr{$addr}, $prev, |
||||||
|
$next_after_if, $break, $indent+$incindent); |
||||||
|
print_code($indent, "} else {"); |
||||||
|
print_stmtlist ($endthen, $endelse, |
||||||
|
$next_after_if, $break, $indent+$incindent); |
||||||
|
$addr = $endelse; |
||||||
|
} else { |
||||||
|
# no else-part |
||||||
|
print_stmtlist ($next_instr{$addr}, $endthen, |
||||||
|
$next_after_if, $break, $indent+$incindent); |
||||||
|
$addr = $endthen; |
||||||
|
} |
||||||
|
print_code($indent, "}"); |
||||||
|
next ADDR; |
||||||
|
} |
||||||
|
if ($next_after_if == $break) { |
||||||
|
# This is an if () break; |
||||||
|
print_code($indent, "if ($cond) break;", $addr); |
||||||
|
$addr = $next_instr{$addr}; |
||||||
|
next ADDR; |
||||||
|
} |
||||||
|
}; |
||||||
|
/^case ((.|\n)*)$/ && do { |
||||||
|
my $default; |
||||||
|
my $cond = "NONE"; |
||||||
|
my @lines = split "\n", $1; |
||||||
|
$_ = shift @lines; |
||||||
|
/^\((.*)\)$/ and $cond = $1; |
||||||
|
(shift @lines) =~ /^default: goto (\d+);/ and $default = $1; |
||||||
|
my $next_after_switch = $default; |
||||||
|
if ($instr{$prev_instr{$default}} =~ /^goto\s(\d+);/ and |
||||||
|
$1 > $default) { |
||||||
|
$next_after_switch = $1; |
||||||
|
} |
||||||
|
print_code ($indent, "switch ($cond) {", $addr); |
||||||
|
my %cases = ($default => "default"); |
||||||
|
foreach (@lines) { |
||||||
|
(/^(\d+): goto (\d+);$/ and $cases{$2} = "case $1") |
||||||
|
or warn ("ILLEGAL case : `$_'"); |
||||||
|
my $casepos = $1; |
||||||
|
if ($casepos > $next_after_switch) { |
||||||
|
if ($instr{$prev_instr{$1}} =~ /^goto\s(\d+);/ and |
||||||
|
$1 > $casepos) { |
||||||
|
$next_after_switch = $1; |
||||||
|
} else { |
||||||
|
$next_after_switch = $casepos; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
$next_after_switch = $end |
||||||
|
if ($next_after_switch > $end && $next_after_switch != $next); |
||||||
|
my $endswitch = ($next_after_switch > $end) ? $end : $next_after_switch; |
||||||
|
#print STDERR "Addr: $addr, labels: `", |
||||||
|
# (join ":", keys %cases ), "', default: $default, end: $next_after_switch\n"; |
||||||
|
$addr = $next_instr{$addr}; |
||||||
|
foreach $_ (sort { $a <=> $b } keys %cases) { |
||||||
|
my $next_case = $_; |
||||||
|
if ($instr{$prev_instr{$next_case}} eq |
||||||
|
"goto $next_after_switch;") { |
||||||
|
print_stmtlist($addr, $prev_instr{$next_case}, |
||||||
|
$next_after_switch, $next_after_switch, |
||||||
|
$indent+$incindent); |
||||||
|
print_code($indent+$incindent, "break;"); |
||||||
|
} else { |
||||||
|
print_stmtlist($addr, $next_case, |
||||||
|
$next_case, $next_after_switch, |
||||||
|
$indent+$incindent); |
||||||
|
} |
||||||
|
print_code($indent, $cases{$next_case}.":"); |
||||||
|
$addr = $next_case; |
||||||
|
} |
||||||
|
print_stmtlist($addr, $endswitch, |
||||||
|
$endswitch, $next_after_switch, |
||||||
|
$indent+$incindent); |
||||||
|
print_code($indent, "}"); |
||||||
|
$addr = $endswitch; |
||||||
|
next ADDR; |
||||||
|
}; |
||||||
|
print_code($indent, $_, $addr); |
||||||
|
$addr = $next_instr{$addr}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
my %locals = (); |
||||||
|
my $addr; |
||||||
|
|
||||||
|
LINE: while (<>) { |
||||||
|
|
||||||
|
chomp; |
||||||
|
(/^\s*(\d+)\s+(.*)$/ and $addr = $1, $_ = $2) or do { |
||||||
|
warn "Line `$_' ist not formatted correctly\n"; |
||||||
|
next LINE; |
||||||
|
}; |
||||||
|
|
||||||
|
if (not @vstack) { |
||||||
|
if (defined ($instr_addr)) { |
||||||
|
new_instr("/*warn: missing instruction!*/") |
||||||
|
if (not defined $instr{$instr_addr}); |
||||||
|
$next_instr{$instr_addr} = $addr; |
||||||
|
$prev_instr{$addr} = $instr_addr; |
||||||
|
} else { |
||||||
|
$prev_instr{$addr} = -1; |
||||||
|
} |
||||||
|
$instr_addr = $addr; |
||||||
|
} |
||||||
|
|
||||||
|
INSTR: |
||||||
|
{ |
||||||
|
/^([ilfda])load[\s_]+(\d+)\s*$/ && do { |
||||||
|
push @tstack, get_type($1); |
||||||
|
my $local; |
||||||
|
if ($2 == 0) { |
||||||
|
$local = "this"; |
||||||
|
} else { |
||||||
|
$local = "local_$2"; |
||||||
|
} |
||||||
|
push @vstack, $local; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([bcsilfda])aload\s*$/ && do { |
||||||
|
my $warn = ""; |
||||||
|
my $index = pop_value("int"); |
||||||
|
my ($array, $atype) = pop_value_type(get_type($1)."[]"); |
||||||
|
my $type = $atype; |
||||||
|
($atype =~ /(.*)\[\]/ and $type = $1) or |
||||||
|
$warn = "/*warn: `$atype' not an array*/ "; |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, "$warn$array"."[$index]"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
(/^[bs](i)push\s+(-?\d+)\s*$/ || |
||||||
|
/^([ilfda])const[\s_]+([m\-]?[\d.Ee\+\-]+|null)\s*$/) && do { |
||||||
|
push @tstack, get_type($1); |
||||||
|
push @vstack, ($2 eq "m1") ? -1 : $2; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/ldc[12]?_?w?\s+\#\d+\s+\<(\S+)\s+([^\>]+)\>/ && do { |
||||||
|
push @tstack, $1; |
||||||
|
push @vstack, $2; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([ilfda])store[\s_]+(\d+)\s*$/ && do { |
||||||
|
my $local; |
||||||
|
my ($value, $type) = pop_value_type(get_type($1)); |
||||||
|
if ($2 == 0) { |
||||||
|
$local = "this"; |
||||||
|
} else { |
||||||
|
$local = "local_$2"; |
||||||
|
if (not defined $locals{$2}) { |
||||||
|
$locals{$2} = $type; |
||||||
|
$local = "$type $local"; |
||||||
|
} else { |
||||||
|
$local = convert_value($local, $type, $locals{$2}); |
||||||
|
} |
||||||
|
} |
||||||
|
new_assign($local, $value); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([bcsilfda])astore\s*$/ && do { |
||||||
|
my ($value, $type) = pop_value_type(get_type($1)); |
||||||
|
my $index = pop_value("int"); |
||||||
|
my ($array, $atype) = pop_value_type(get_type($1)."[]"); |
||||||
|
($atype =~ /(.*)\[\]/ and $atype = $1) or |
||||||
|
$atype = "`$atype' not an array*/\n\t"; |
||||||
|
new_assign("$array"."[$index]", |
||||||
|
convert_value("$value", $type, $atype)); |
||||||
|
|
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^new\s+(.*)\s*/ && do { |
||||||
|
my ($type) = parse_type($1); |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, "new $type"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^newarray\s+(\S+)\s*$/ && do { |
||||||
|
my $arrtype = $1; |
||||||
|
my $value = pop_value("int"); |
||||||
|
push @tstack, $arrtype."[]"; |
||||||
|
push @vstack, "new ".$arrtype."[$value]"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^getfield\s+(.*)$/ && do { |
||||||
|
my ($type, $field) = parse_field($1); |
||||||
|
my $class = pop_value("*") . "."; |
||||||
|
$class = "" if $class eq "this."; |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, "$class$field"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^getstatic\s+(.*)$/ && do { |
||||||
|
my ($type, $field) = parse_field($1); |
||||||
|
push @tstack, $type; |
||||||
|
my $class="FIXME."; |
||||||
|
push @vstack, "$class$field"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^putfield\s+(.*)$/ && do { |
||||||
|
my ($dtype, $field) = parse_field($1); |
||||||
|
my $value = pop_value($dtype); |
||||||
|
$field = classify(pop_value("*"), $field); |
||||||
|
new_assign($field, $value); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^goto\s+(\d+)\s*$/ && do { |
||||||
|
new_instr("goto $1;"); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^tableswitch\s+(\d+)\s+to\s+(\d+): default=(\d+)\s*$/ && do { |
||||||
|
my $from = $1; |
||||||
|
my $to = $2; |
||||||
|
my $default = $3; |
||||||
|
my $num; |
||||||
|
my $casestmt = "case (" . pop_value("int") . ")\n"; |
||||||
|
$casestmt .= "default: goto $default;\n"; |
||||||
|
for $num ($from .. $to) { |
||||||
|
$_ = <>; |
||||||
|
if ( $_ =~ /\s+$num:\s*(\d+)/ ) { |
||||||
|
$casestmt .= "$num: goto $1;\n"; |
||||||
|
} else { |
||||||
|
warn "unknown case: `$_' at $."; |
||||||
|
} |
||||||
|
} |
||||||
|
new_instr($casestmt); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^lookupswitch\s+(\d+):\s+default=(\d+)\s*$/ && do { |
||||||
|
my $anz = $1; |
||||||
|
my $default = $2; |
||||||
|
my $num; |
||||||
|
my $casestmt = "case (" . pop_value("int") . ")\n"; |
||||||
|
$casestmt .= "default: goto $default;\n"; |
||||||
|
for $num (1 .. $anz) { |
||||||
|
$_ = <>; |
||||||
|
if ( $_ =~ /\s+(\d+):\s*(\d+)/ ) { |
||||||
|
$casestmt .= "$1: goto $2;\n"; |
||||||
|
} else { |
||||||
|
$casestmt .= "error in case"; |
||||||
|
} |
||||||
|
} |
||||||
|
new_instr($casestmt); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
|
||||||
|
/^invokespecial\s+(.*)$/ && do { |
||||||
|
my ($method, @paramtypes) = parse_special ($1); |
||||||
|
my @params=(); |
||||||
|
# Constructoraufruf! Wenn alles glatt laeuft... |
||||||
|
while (@paramtypes) { |
||||||
|
my $ptype = pop @paramtypes; |
||||||
|
my $value = pop_value($ptype); |
||||||
|
unshift @params, $value; |
||||||
|
} |
||||||
|
my ($new_class, $class_type) = pop_value_type ("*"); |
||||||
|
$method = $new_class; |
||||||
|
my $call = "$method(" . join (", ", @params) . ")"; |
||||||
|
if ($vstack[-1] eq $new_class) { |
||||||
|
$vstack[-1] = "$call"; |
||||||
|
} else { |
||||||
|
new_instr("$call;"); |
||||||
|
} |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^invoke(virtual|static)\s+(.*)$/ && do { |
||||||
|
my ($type, $method, @paramtypes) = parse_method ($2); |
||||||
|
my @params=(); |
||||||
|
while (@paramtypes) { |
||||||
|
my $ptype = pop @paramtypes; |
||||||
|
my $value = pop_value($ptype); |
||||||
|
unshift @params, $value; |
||||||
|
} |
||||||
|
my ($class, $class_type) = ($1 eq "virtual")? pop_value_type ("*") : "FIXME"; |
||||||
|
$method = classify($class, $method); |
||||||
|
my $call = "$method(" . join (", ", @params) . ")"; |
||||||
|
if ($type eq "void") { |
||||||
|
new_instr("$call;"); |
||||||
|
} else { |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, $call; |
||||||
|
} |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^return\s*$/ && do { |
||||||
|
new_instr("return;"); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^pop\s*$/ && do { |
||||||
|
unless (@vstack) { |
||||||
|
print STDERR "pop: Stack is empty at $addr"; |
||||||
|
} |
||||||
|
new_instr(pop(@vstack).";"); |
||||||
|
pop @tstack; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^dup\s*$/ && do { |
||||||
|
push @tstack, $tstack[-1]; |
||||||
|
push @vstack, $vstack[-1]; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^dup2\s*$/ && do { |
||||||
|
push @tstack, $tstack[-2]; |
||||||
|
push @vstack, $vstack[-2]; |
||||||
|
push @tstack, $tstack[-2]; |
||||||
|
push @vstack, $vstack[-2]; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^dup_x([12])\s*$/ && do { |
||||||
|
splice @tstack, -1-$1, 0, $tstack[-1]; |
||||||
|
splice @vstack, -1-$1, 0, $vstack[-1]; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([ilfd])neg\s*$/ && do { |
||||||
|
my $type = get_type($1); |
||||||
|
my $op1 = pop_value($type); |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, "-$op1"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([ilfd])(add|sub|mul|div|rem|and|or|xor|shl|shr)\s*$/ && do { |
||||||
|
my $type = get_type($1); |
||||||
|
my $op2 = pop_value($type); |
||||||
|
my $op1 = pop_value($type); |
||||||
|
my $op; |
||||||
|
for ($2) { |
||||||
|
/add/ && ($op="+", last); |
||||||
|
/sub/ && ($op="-", last); |
||||||
|
/mul/ && ($op="*", last); |
||||||
|
/div/ && ($op="/", last); |
||||||
|
/rem/ && ($op="%", last); |
||||||
|
/and/ && ($op="&", last); |
||||||
|
/or/ && ($op="|", last); |
||||||
|
/xor/ && ($op="^", last); |
||||||
|
/shl/ && ($op="<<", last); |
||||||
|
/shr/ && ($op=">>", last); |
||||||
|
} |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, "($op1 $op $op2)"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^iinc\s+(\d+)\s+(-?\d+)\s*$/ && do { |
||||||
|
my $value = $2; |
||||||
|
my $local; |
||||||
|
if ($1 == 0) { |
||||||
|
$local = "this"; |
||||||
|
} else { |
||||||
|
$local = "local_$1"; |
||||||
|
} |
||||||
|
new_instr(convert_value("$local", "int", $locals{$1}). |
||||||
|
(($2 == 1)? "++;" : " += $2;")); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([bcifld])2([bcifld])\s*$/ && do { |
||||||
|
my $value = pop_value(get_type($1)); |
||||||
|
my $type = get_type($2); |
||||||
|
push @tstack, $type; |
||||||
|
push @vstack, "($type) $value"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^([lfd])cmp([lg]?)\s*$/ && do { |
||||||
|
my $type = get_type($1); |
||||||
|
my $op2 = pop_value($type); |
||||||
|
my $op1 = pop_value($type); |
||||||
|
push @tstack, "int"; |
||||||
|
push @vstack, "($op1 <=>$2 $op2)"; |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^if(eq|lt|le|ne|gt|ge)\s+(\d+)\s*$/ && do { |
||||||
|
my $op; |
||||||
|
my $dest = $2; |
||||||
|
for ($1) { |
||||||
|
/eq/ && ($op="==", last); |
||||||
|
/lt/ && ($op="<", last); |
||||||
|
/le/ && ($op="<=", last); |
||||||
|
/ne/ && ($op="!=", last); |
||||||
|
/gt/ && ($op=">", last); |
||||||
|
/ge/ && ($op=">=", last); |
||||||
|
} |
||||||
|
my $op1 = pop_value("int"); |
||||||
|
new_instr("if ($op1 $op 0) goto $dest;"); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^if_icmp(eq|lt|le|ne|gt|ge)\s+(\d+)\s*$/ && do { |
||||||
|
my $op; |
||||||
|
my $dest = $2; |
||||||
|
for ($1) { |
||||||
|
/eq/ && ($op="==", last); |
||||||
|
/lt/ && ($op="<", last); |
||||||
|
/le/ && ($op="<=", last); |
||||||
|
/ne/ && ($op="!=", last); |
||||||
|
/gt/ && ($op=">", last); |
||||||
|
/ge/ && ($op=">=", last); |
||||||
|
} |
||||||
|
my $op2 = pop_value("int"); |
||||||
|
my $op1 = pop_value("int"); |
||||||
|
new_instr("if ($op1 $op $op2) goto $dest;"); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
/^if(null|nonnull)\s+(\d+)\s*$/ && do { |
||||||
|
my $dest = $2; |
||||||
|
my $op; |
||||||
|
for ($1) { |
||||||
|
/notnull/ && ($op="!=", last); |
||||||
|
/null/ && ($op="==", last); |
||||||
|
} |
||||||
|
my $op1 = pop_value("*"); |
||||||
|
new_instr("if ($op1 $op null) goto $dest;"); |
||||||
|
last INSTR; |
||||||
|
}; |
||||||
|
|
||||||
|
do { |
||||||
|
print STDERR "Stack: "; |
||||||
|
&print_stack; |
||||||
|
print STDERR "\nUnknown Instruction: `$_'\n\t"; |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
$addr++; |
||||||
|
$next_instr{$instr_addr} = $addr; |
||||||
|
|
||||||
|
simplify_instructions (0, $addr); |
||||||
|
print_stmtlist(0, $addr, $addr, $addr, 2*$incindent); |
@ -0,0 +1,38 @@ |
|||||||
|
<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 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 1.2 or better. It should run |
||||||
|
without any other package.</li> </ul> |
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="CVS Repository"> |
||||||
|
<p>You can get the latest sources from the <sflink href="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> |
||||||
|
|
||||||
|
<p>Afterwards follow the instruction in the INSTALL file. </p> |
||||||
|
</section> |
@ -0,0 +1,90 @@ |
|||||||
|
<section title="FAQ - Frequently Asked Questions"> |
||||||
|
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> |
||||||
|
|
||||||
|
<p>Try jode-1.1.2pre1 or the latest CVS version. If it still does not |
||||||
|
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 |
||||||
|
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> |
||||||
|
|
||||||
|
<p>The class isn't verifiable, probably because there is not enough |
||||||
|
information about used classes. See the question about the |
||||||
|
classpath.</p> |
||||||
|
|
||||||
|
<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 |
||||||
|
definition of correct bytecode, again.</p> |
||||||
|
|
||||||
|
<h3>What should be included in the classpath?</h3> |
||||||
|
|
||||||
|
<p>Jode needs to know the full class hierarchie to guess the types. |
||||||
|
This includes not only the classes in the program, but also the |
||||||
|
libraries used by the java program, even the Java runtime library. |
||||||
|
You should set the classpath to include all these classes.</p> |
||||||
|
|
||||||
|
<p>If you don't specify the classpath on the command line, Jode uses |
||||||
|
the same as your Java Virtual Machine.</p> |
||||||
|
|
||||||
|
<p>As last resort, if Jode can't find a class in the classpath it uses |
||||||
|
reflection to ask the Virtual Machine. This works quite well, but |
||||||
|
loading classes can have side effects, e.g. when AWT classes are |
||||||
|
loaded, an AWT thread is created, even though Jode doesn't need |
||||||
|
it.</p> |
||||||
|
|
||||||
|
<h3>Why doesn't Jode decompile my inner class |
||||||
|
<code>MyClass$Inner.class</code>?</h3> |
||||||
|
|
||||||
|
<p>You should decompile the outermost class (<code>MyClass</code> in |
||||||
|
this case). The produced code contains the inner class. </p> |
||||||
|
|
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="Obfuscator issues"> |
||||||
|
|
||||||
|
<h3>What should be included in the classpath?</h3> |
||||||
|
|
||||||
|
<p>The program, all libraries, the Java runtime library. Don't omit a |
||||||
|
library even when you don't want to obfuscate it.</p> |
||||||
|
|
||||||
|
<h3>What should I preserve</h3> |
||||||
|
|
||||||
|
<p>The most common mistake is to preserve a class. In most cases this |
||||||
|
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 |
||||||
|
should preserve methods and constructors. The constructor is just a |
||||||
|
method with the special name <tt><init></tt>. </p> |
||||||
|
|
||||||
|
<p> Another common mistake is to omit the type |
||||||
|
signature, e.g. to preserve <tt>Class.main</tt> instead of |
||||||
|
<tt>Class.main.([Ljava/lang/String;)V</tt>. That doesn't work. If |
||||||
|
you don't want to care about the format of the type signature use a |
||||||
|
wildcard as in <tt>Class.main.*</tt>. </p> |
||||||
|
|
||||||
|
<h3>What is a type signature</h3> |
||||||
|
|
||||||
|
<p>The type signature is a machine readable representation of a java |
||||||
|
type that is used all over in java bytecode. The JDK ships a command |
||||||
|
named <tt>javap</tt>. With <tt>java -s</tt> you can lists the fields |
||||||
|
and methods of a class with their type signatures.</p> |
||||||
|
|
||||||
|
<p> If you are interested in the format of type signatures read the |
||||||
|
Java Virtual Machine Specification, Chapter 4.3 Descriptors</p> |
||||||
|
|
||||||
|
</section> |
@ -0,0 +1,106 @@ |
|||||||
|
/* 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 ", |
||||||
|
" ", |
||||||
|
" ", |
||||||
|
" ", |
||||||
|
" "}; |
@ -0,0 +1,11 @@ |
|||||||
|
<section title="Feedback"> |
||||||
|
|
||||||
|
<p>You can report bugs to the <?php sflink("bugs/")?>bug forum</a>. </p> |
||||||
|
|
||||||
|
<p>You can contact me by email via <a |
||||||
|
href="http://sourceforge.net/sendmessage.php?touser=18252">hoenicke at |
||||||
|
users.sourceforge.net</a>. Please mention <i>jode</i> in the |
||||||
|
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> |
||||||
|
</section> |
@ -0,0 +1,13 @@ |
|||||||
|
<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 29-May-2002, |
||||||
|
Copyright © 1998-2002 by Jochen Hoenicke. |
||||||
|
Canonic URL is <a class=boldlink href="http://jode.sourceforge.net/">http://jode.sourceforge.net/</a></SPAN> |
||||||
|
</TD> |
||||||
|
</TR> |
||||||
|
</TABLE> |
||||||
|
|
||||||
|
</BODY> |
||||||
|
</HTML> |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,9 @@ |
|||||||
|
|
||||||
|
21 iconst_4 |
||||||
|
22 iand |
||||||
|
23 ifne 30 |
||||||
|
26 iconst_0 |
||||||
|
27 goto 31 |
||||||
|
30 iconst_1 |
||||||
|
31 ireturn |
||||||
|
|
After Width: | Height: | Size: 192 B |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,9 @@ |
|||||||
|
|
||||||
|
while (i>0) { |
||||||
|
if (a[i] > max) |
||||||
|
max = a[i]; |
||||||
|
if (a[i] == 0) |
||||||
|
break; |
||||||
|
i++; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,42 @@ |
|||||||
|
#FIG 3.2 |
||||||
|
Landscape |
||||||
|
Center |
||||||
|
Metric |
||||||
|
A4 |
||||||
|
100.00 |
||||||
|
Single |
||||||
|
-2 |
||||||
|
1200 2 |
||||||
|
2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 |
||||||
|
1800 4725 2700 4725 2700 5400 1800 5400 1800 4725 |
||||||
|
2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 |
||||||
|
2475 4275 3375 4275 3375 3600 2475 3600 2475 4275 |
||||||
|
2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 |
||||||
|
2475 2475 3375 2475 3375 3150 2475 3150 2475 2475 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 1 |
||||||
|
2475 3825 |
||||||
|
2 2 0 1 0 7 100 0 -1 0.000 0 0 -1 0 0 5 |
||||||
|
2475 5850 3375 5850 3375 6525 2475 6525 2475 5850 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 7 1 0 3 |
||||||
|
1 1 2.00 60.00 120.00 |
||||||
|
3600 5400 2925 5625 2925 5850 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 7 1 0 3 |
||||||
|
1 1 2.00 60.00 120.00 |
||||||
|
2925 4275 2250 4500 2250 4725 |
||||||
|
2 2 0 1 0 7 100 0 -1 0.000 0 0 7 0 0 5 |
||||||
|
3150 4725 4050 4725 4050 5400 3150 5400 3150 4725 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 7 1 0 2 |
||||||
|
1 1 2.00 60.00 120.00 |
||||||
|
2925 3150 2925 3600 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 7 1 0 5 |
||||||
|
1 1 2.00 60.00 120.00 |
||||||
|
2250 5400 2250 5625 1350 5625 1350 3825 2475 3825 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 7 1 0 3 |
||||||
|
1 1 2.00 60.00 120.00 |
||||||
|
2925 4275 3600 4500 3600 4725 |
||||||
|
2 1 0 1 0 7 100 0 -1 0.000 0 0 7 1 0 5 |
||||||
|
1 1 2.00 60.00 120.00 |
||||||
|
3600 5400 3600 5625 4275 5625 4275 3825 3375 3825 |
||||||
|
2 1 0 1 0 0 100 0 20 0.000 0 0 7 0 0 8 |
||||||
|
10800 2250 9675 3375 9675 2700 8325 2700 8325 1800 9675 1800 |
||||||
|
9675 1125 10800 2250 |
@ -0,0 +1,63 @@ |
|||||||
|
<?php |
||||||
|
if (! $extension) { |
||||||
|
$extension = "php"; |
||||||
|
} |
||||||
|
$version="1.1"; |
||||||
|
|
||||||
|
function selflink($link) { |
||||||
|
global $extension; |
||||||
|
echo "<a href=\"./"; |
||||||
|
if ($link != "index") { |
||||||
|
if (ereg("#", $link)) { |
||||||
|
$link = ereg_replace("#", ".$extension#", $link); |
||||||
|
} else { |
||||||
|
$link .= ".$extension"; |
||||||
|
} |
||||||
|
echo "$link"; |
||||||
|
} |
||||||
|
echo "\">"; |
||||||
|
} |
||||||
|
|
||||||
|
function sflink($link) { |
||||||
|
echo "<a href=\"http://sourceforge.net/$link?group_id=3790\">"; |
||||||
|
} |
||||||
|
?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<title>Java Optimize and Decompile Environment (JODE)</title> |
||||||
|
<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.gif" alt="JODE" width=286 height=110 |
||||||
|
></td |
||||||
|
><td align="right" |
||||||
|
>Powered by <a href="http://sourceforge.net"><img |
||||||
|
src="http://sourceforge.net/sflogo.php?group_id=3790&type=1" |
||||||
|
border=0 width=88 height=31 alt="SourceForge"></a><br |
||||||
|
>Best viewed with <a |
||||||
|
href="http://www.anybrowser.org/campaign/"><img |
||||||
|
src="a-logo.gif" border=0 width=88 height=31 alt="Any |
||||||
|
Browser"></a><br |
||||||
|
></td |
||||||
|
></tr |
||||||
|
></table> |
||||||
|
|
||||||
|
<?php require("menu.inc"); ?> |
||||||
|
|
@ -0,0 +1,20 @@ |
|||||||
|
<section title="History"> |
||||||
|
<p>Someday I found <code>guavad</code>, a disassembler for java byte |
||||||
|
code (it does similar things like <code>javap -c</code>). I used |
||||||
|
it on a class file, and found that it was possible to reconstruct the |
||||||
|
original java code. First I did it by hand on some small routines, |
||||||
|
but I soon realized that it was a rather stupid task. So I wrote a |
||||||
|
small <a href="dasm_to_java.perl"><code>perl</code> script</a> that |
||||||
|
did this process automatically. At the end of the next day I had my |
||||||
|
first working decompiler.</p> |
||||||
|
|
||||||
|
<p>Now while the <code>perl</code> script is working, it is not easy |
||||||
|
to use. You have to decompile the code first with a disassembler, cut |
||||||
|
out the code of a single method, and run the perl script on it. I |
||||||
|
decided to get the bytecode directly out of the class files and do |
||||||
|
this all automatically. I decided to write it in <code>java</code> |
||||||
|
now, because it suited best.</p> |
||||||
|
|
||||||
|
<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> |
||||||
|
</section> |
@ -0,0 +1,52 @@ |
|||||||
|
<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> |
@ -0,0 +1,74 @@ |
|||||||
|
<section title="Introduction"> |
||||||
|
|
||||||
|
<P><i>JODE</i> is a java package containing a decompiler and an |
||||||
|
optimizer for java. This package is <a href="license.html">freely |
||||||
|
available</a> under the GNU GPL. The bytecode package and the core |
||||||
|
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 |
||||||
|
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 |
||||||
|
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 |
||||||
|
well, so you should give it a try and <a href="applet.html">start the |
||||||
|
applet</a>.</P> |
||||||
|
|
||||||
|
<P>The optimizer transforms <tt>class</tt> files in various ways with |
||||||
|
can be controlled by a script file. It supports the following |
||||||
|
operations:</p> |
||||||
|
<ul> |
||||||
|
<li>Renaming class, method, field and local names to shorter, |
||||||
|
obfuscated, or unique names or according to a given translation |
||||||
|
table</li> |
||||||
|
<li>Removing debugging information</li> |
||||||
|
<li>Removing dead code (classes, fields, methods) and constant |
||||||
|
fields</li> |
||||||
|
<li>Optimizing local variable allocation</li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="News"> |
||||||
|
|
||||||
|
<ul> |
||||||
|
<li><i>JODE</i> 1.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> |
||||||
|
</ul> |
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="Known Bugs"> |
||||||
|
|
||||||
|
<p>The current version has problems try/catch/finally code produced |
||||||
|
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 |
||||||
|
produced source contains access$xxx functions, but it still compiles.</p> |
||||||
|
|
||||||
|
<p>There may be other bugs, that cause Exceptions or invalid code. |
||||||
|
If you have such a problems don't hesitate to issue a bug report. |
||||||
|
Please include the <code>class</code> file if possible.</p> |
||||||
|
|
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="Limits"> |
||||||
|
|
||||||
|
<p>If not all dependent classes can be found, the verifier (which is |
||||||
|
run before decompilation starts) may exit with a type error. You |
||||||
|
can decompile it with <tt>--verify=off</tt>, but take the warning |
||||||
|
serious, that types may be incorrect. There's sometimes no way to |
||||||
|
guess the right type, if you don't have access the full class |
||||||
|
hierarchie.<br> |
||||||
|
|
||||||
|
This is not a bug in the verifier: java will complain the same way, |
||||||
|
if it is run with bytecode verification turned on. And if you don't |
||||||
|
have the dependent classes, you can't compile the code again.</p> |
||||||
|
|
||||||
|
<p>There may be situations, where the code doesn't understand complex |
||||||
|
expressions. In this case many ugly temporary variables are used, but |
||||||
|
the code should still be compileable. This does especially happen |
||||||
|
when you compile with <tt>`-O'</tt> flag and javac has inlined some |
||||||
|
methods. </p> |
||||||
|
|
||||||
|
</section> |
Binary file not shown.
@ -0,0 +1,67 @@ |
|||||||
|
<!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&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 © 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> |
@ -0,0 +1,11 @@ |
|||||||
|
\input texinfo @c -*- texinfo -*- |
||||||
|
|
||||||
|
@setfilename jode.info |
||||||
|
@settitle Jode, a Java Decompiler |
||||||
|
@direntry |
||||||
|
* Jode: (jode). A Java Decompiler |
||||||
|
@end direntry |
||||||
|
|
||||||
|
|
||||||
|
@include technical.texi |
||||||
|
@bye |
@ -0,0 +1,21 @@ |
|||||||
|
<section title="License"> |
||||||
|
<p><i>JODE</i> is Copyright © 1998-2004 by Jochen Hoenicke. <br><br> |
||||||
|
|
||||||
|
<p>This program is free software; you can redistribute it and/or modify |
||||||
|
it under the terms of the <a |
||||||
|
href="http://www.gnu.org/copyleft/gpl.html">GNU General Public |
||||||
|
License</a> as published by the Free Software Foundation; either |
||||||
|
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, |
||||||
|
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 |
||||||
|
GNU General Public License for more details.</p> |
||||||
|
|
||||||
|
</section> |
@ -0,0 +1,75 @@ |
|||||||
|
<section title="<i>JODE</i> Links"> |
||||||
|
<h3>Other decompilers</h3> |
||||||
|
<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 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>A very fast decompiler is <a |
||||||
|
href="http://www.geocities.com/SiliconValley/Bridge/8617/jad.html">jad</a> |
||||||
|
written in C++. It doesn't come with source code though, and misses |
||||||
|
some features <i>JODE</i> has ;-)</li> <li><a |
||||||
|
href="http://www.javaworld.com/javaworld/jw-07-1997/jw-07-decompilers.html">A |
||||||
|
comparison of three decompilers</a> (but not <i>JODE</i>) was done by Dave |
||||||
|
Dyer. |
||||||
|
</ul> |
||||||
|
<h3>Other obfuscators</h3> |
||||||
|
<ul> |
||||||
|
<li><a href="http://dmoz.org/Computers/Programming/Languages/Java/Development_Tools/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.zelix.com/klassmaster/index.html">Zelix |
||||||
|
Klassmaster</a> does a very good flow optimization and also decrypts |
||||||
|
strings. But <i>JODE</i>'s deobfuscator can undo both.</li> |
||||||
|
<li><a href="http://www.cs.arizona.edu/~collberg/Research/">Christian S. Collberg</a> has some really interesting papers about non reversible obfuscations.</li> |
||||||
|
</ul> |
||||||
|
<h3>Graphical User Interface</h3> |
||||||
|
<ul> |
||||||
|
<li><i>JODE</i> is used by the <a |
||||||
|
href="http://jedit.standmed.com/plugins/JavaInsight">JavaInsight plugin</a> for |
||||||
|
<a href="http://jedit.sourceforge.net/">jEdit</a>.</li> |
||||||
|
</ul> |
||||||
|
<h3>Software Directories</h3> |
||||||
|
<ul> |
||||||
|
<li>Get everything and anything for Linux at the |
||||||
|
<a href="http://www.linux-directory.com" target="_top" |
||||||
|
><IMG SRC="http://www.linux-directory.com/button_88x31.gif" |
||||||
|
WIDTH=88 HEIGHT=31 BORDER=0 ALT="Linux Directory"></a>. |
||||||
|
</li> |
||||||
|
<li>A great place for developing free software is |
||||||
|
<a href="http://sourceforge.net"><img |
||||||
|
src="http://sourceforge.net/sflogo.php?group_id=3790&type=1" |
||||||
|
border=0 width=88 height=31 alt="SourceForge"></a> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
<h3>Miscellanous packages needed to run JODE</h3> |
||||||
|
<dl> |
||||||
|
<dt>CYGWIN (unix tools for win95/NT)</dt> |
||||||
|
<dd> |
||||||
|
<a href="http://sourceware.cygnus.com/cygwin/">http://sourceware.cygnus.com/cygwin/</a> |
||||||
|
</dd> |
||||||
|
<dt>JDK 1.1:</dt> |
||||||
|
<dd> |
||||||
|
<a href="http://java.sun.com/products/jdk/1.1/">http://java.sun.com/products/jdk/1.1/</a> |
||||||
|
</dd> |
||||||
|
<dt><a name="swing">Swing for JDK 1.1:</a><dt> |
||||||
|
<dd> |
||||||
|
<a href="http://java.sun.com/products/jfc/index.html#download-swing">http://java.sun.com/products/jfc/index.html#download-swing</a> |
||||||
|
</dd> |
||||||
|
<dt>JDK 1.2:</dt> |
||||||
|
<dd> |
||||||
|
<a href="http://java.sun.com/products/jdk/1.2/">http://java.sun.com/products/jdk/1.2/</a> |
||||||
|
</dd> |
||||||
|
<dt><a name="getopt">Getopt</a>:</dt> |
||||||
|
<dd> |
||||||
|
<a href="http://www.urbanophile.com/arenn/hacking/download.html#getopt">http://www.urbanophile.com/arenn/hacking/download.html#getopt</a> |
||||||
|
</dd> |
||||||
|
<dt><a name="collections">Collection Classes</a>:</dt> |
||||||
|
<dd>I have written a small script that puts the collection classes |
||||||
|
from the <a href="http://www.classpath.org">GNU Classpath Project</a> |
||||||
|
into its own package (<code>gnu.java.util.collections</code>). This |
||||||
|
script is now part of GNU classpath. For your convenience I have put a |
||||||
|
precompiled <a |
||||||
|
href="http://www.informatik.uni-oldenburg.de/~delwi/jode/collections.jar">jar |
||||||
|
file</a> on this server. |
||||||
|
</dd> |
||||||
|
</dl> |
||||||
|
</section> |
@ -0,0 +1,47 @@ |
|||||||
|
<?php |
||||||
|
$menu = |
||||||
|
array("<B>Home</B>" , "selflink", "index", |
||||||
|
"Project page" , "sflink", "project/", |
||||||
|
"Applet" , "selflink", "applet", |
||||||
|
"Download" , "selflink", "download", |
||||||
|
"FAQ" , "selflink", "faq", |
||||||
|
"Feedback" , "selflink", "feedback", |
||||||
|
"Documentation", "selflink", "usage", |
||||||
|
"License" , "selflink", "license", |
||||||
|
"History" , "selflink", "history", |
||||||
|
"Links" , "selflink", "links", |
||||||
|
"Blue Sky" , "selflink", "bluesky"); |
||||||
|
?> |
||||||
|
|
||||||
|
<table cellspacing=0 cellpadding=3 border=0 bgcolor="#EEEEF8" class="nav"> |
||||||
|
<tr><td class="nav"> |
||||||
|
<?php |
||||||
|
reset($menu); |
||||||
|
$self = ereg_replace("^.*/", "", $PHP_SELF); |
||||||
|
while (list($dummy, $name) = each($menu)) { |
||||||
|
list($dummy, $type) = each($menu); |
||||||
|
list($dummy, $link) = each($menu); |
||||||
|
$name = ereg_replace(" ", " ", $name); |
||||||
|
if ($type == "selflink") { |
||||||
|
if ($self == "$link.$extension") { |
||||||
|
echo "$name"; |
||||||
|
} else { |
||||||
|
selflink($link); |
||||||
|
echo "$name</a>"; |
||||||
|
} |
||||||
|
} else if ($type == "sflink") { |
||||||
|
sflink($link); |
||||||
|
echo "$name</a>"; |
||||||
|
} else if ($type == "-") { |
||||||
|
echo "<br>\n"; |
||||||
|
continue; |
||||||
|
} else if ($type == "link") { |
||||||
|
echo "<a href=\"$link\">$name</a>"; |
||||||
|
} |
||||||
|
if (current($menu)) { |
||||||
|
echo " |\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
</td></tr> |
||||||
|
</table><br> |
@ -0,0 +1,84 @@ |
|||||||
|
# This is a sample script file to obfuscate my project |
||||||
|
|
||||||
|
# The class path should include everything that is needed to run the |
||||||
|
# project. Don't forget the java base classes (rt.jar or classes.zip). |
||||||
|
classpath = "c:\\jdk1.2\\jre\\lib\\rt.jar","d:\\project\\java" |
||||||
|
|
||||||
|
# The jar, zip file or directory in which the obfuscated class files |
||||||
|
# should be written. |
||||||
|
dest = "obfuscated.zip" |
||||||
|
|
||||||
|
# Write the reverse translation table to translat.tbl. With the help of |
||||||
|
# this table you can later undo the renaming. |
||||||
|
revtable = "translat.tbl" |
||||||
|
|
||||||
|
strip = "unreach","lvt","inner" |
||||||
|
|
||||||
|
# this variable will tell, which classes and packages should be included |
||||||
|
# in the obfuscated.jar package. |
||||||
|
load = new WildCard { value = "org.myorg.myproject" }, |
||||||
|
new WildCard { value = "org.myorg.mylib*" }, |
||||||
|
new WildCard { value = "org.otherorg.shortlib" } |
||||||
|
|
||||||
|
# this variable will tell, which classes and packages must not be |
||||||
|
# renamed. |
||||||
|
preserve = new WildCard { value = "org.myorg.ApplicationClass.main.*" }, |
||||||
|
new WildCard { value = "org.myorg.AppletClass.<init>.()V" }, |
||||||
|
new WildCard { value = "org.resources.BundleClass*.<init>.()V" }, |
||||||
|
new MultiIdentifierMatcher { |
||||||
|
and = new WildCard { value = "org.myorg.publiclib.*" }, |
||||||
|
new ModifierMatcher { access = "PUBLIC" } |
||||||
|
} |
||||||
|
|
||||||
|
# There are different renamers currently. This is just an example that |
||||||
|
# produces very good obfuscated code, that is still valid bytecode. |
||||||
|
renamer = new StrongRenamer { |
||||||
|
charsetStart = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ" |
||||||
|
charsetPart = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789_$" |
||||||
|
charsetPackage = "abcdefghijklmnopqrstuvwxyz" |
||||||
|
charsetClass = "abcdefghijklmnopqrstuvwxyz" |
||||||
|
} |
||||||
|
|
||||||
|
# The constant analyzer does a great job to remove constant fields and |
||||||
|
# deadcode. E.g. if you obfuscate the decompiler applet it will |
||||||
|
# remove the whole debugging code, since the applet doesn't need it. |
||||||
|
analyzer = new ConstantAnalyzer |
||||||
|
|
||||||
|
# The LocalOptimizer will reorder local variables to use fewer slots. |
||||||
|
# It may still have some bugs, so remove it if your applet doesn't |
||||||
|
# work (and send me the class). |
||||||
|
# The RemovePopAnalyzer will remove instructions that were optimized |
||||||
|
# away by the ConstantAnalyzer and LocalOptimizer. |
||||||
|
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) |
||||||
|
# |
@ -0,0 +1,65 @@ |
|||||||
|
inner classes: |
||||||
|
access methods: |
||||||
|
Method int access$0(jode.test.InnerClass) |
||||||
|
0 aload_0 |
||||||
|
1 getfield #13 <Field int x> |
||||||
|
4 ireturn |
||||||
|
|
||||||
|
Method void access$1(jode.test.InnerClass, int) |
||||||
|
0 aload_0 |
||||||
|
1 iload_1 |
||||||
|
2 putfield #13 <Field int x> |
||||||
|
5 return |
||||||
|
|
||||||
|
inner class: |
||||||
|
private final jode.test.InnerClass this$0; |
||||||
|
Constructor |
||||||
|
Method jode.test.InnerClass. Inner(jode.test.InnerClass) |
||||||
|
0 aload_0 |
||||||
|
1 invokespecial #6 <Method java.lang.Object()> |
||||||
|
4 aload_0 |
||||||
|
5 aload_1 |
||||||
|
6 putfield #11 <Field jode.test.InnerClass this$0> |
||||||
|
9 aload_0 |
||||||
|
10 aload_1 |
||||||
|
11 putfield #11 <Field jode.test.InnerClass this$0> |
||||||
|
14 aload_0 |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.class operator: |
||||||
|
|
||||||
|
usage: |
||||||
|
0 getstatic #23 <Field java.lang.Class class$Ljava$lang$Object> |
||||||
|
3 ifnull 12 |
||||||
|
6 getstatic #23 <Field java.lang.Class class$Ljava$lang$Object> |
||||||
|
9 goto 21 |
||||||
|
12 ldc #25 <String "java.lang.Object"> |
||||||
|
14 invokestatic #21 <Method java.lang.Class class$(java.lang.String)> |
||||||
|
17 dup |
||||||
|
18 putstatic #23 <Field java.lang.Class class$Ljava$lang$Object> |
||||||
|
|
||||||
|
or: |
||||||
|
0 getstatic #13 <Field java.lang.Class class$java$lang$Object> |
||||||
|
3 ifnonnull 14 |
||||||
|
6 ldc #1 <String "java.lang.Object"> |
||||||
|
8 invokestatic #12 <Method java.lang.Class class$(java.lang.String)> |
||||||
|
11 putstatic #13 <Field java.lang.Class class$java$lang$Object> |
||||||
|
|
||||||
|
|
||||||
|
Method java.lang.Class class$(java.lang.String) |
||||||
|
0 aload_0 |
||||||
|
1 invokestatic #39 <Method java.lang.Class forName(java.lang.String)> |
||||||
|
4 areturn |
||||||
|
5 astore_1 |
||||||
|
6 new #41 <Class java.lang.NoClassDefFoundError> |
||||||
|
9 dup |
||||||
|
10 aload_1 |
||||||
|
11 invokevirtual #47 <Method java.lang.String getMessage()> |
||||||
|
14 invokespecial #51 <Method java.lang.NoClassDefFoundError(java.lang.String)> |
||||||
|
17 athrow |
||||||
|
Exception table: |
||||||
|
from to target type |
||||||
|
0 5 5 <Class java.lang.ClassNotFoundException> |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,279 @@ |
|||||||
|
@node Technical Info, Top, Top, Top |
||||||
|
@chapter Technical Information |
||||||
|
|
||||||
|
This chapter contains information, how the decompiler works. |
||||||
|
|
||||||
|
@menu |
||||||
|
* Types:: |
||||||
|
* Expression analysis:: |
||||||
|
* Flow analysis:: |
||||||
|
* Solving unknown stack-ops:: |
||||||
|
* Highlevel analysis:: |
||||||
|
@end menu |
||||||
|
|
||||||
|
@node Types, Expression analysis, Technical Info, Technical Info |
||||||
|
@section Type checking and guessing |
||||||
|
@cindex Types, Conversions |
||||||
|
|
||||||
|
The class jode.Type is the base class of all types (except MethodType). |
||||||
|
A type under jode is really a set of types, since it sometimes cannot |
||||||
|
know the exact type. A special type is Type.tError which represents the |
||||||
|
empty set and means, that something has gone wrong. |
||||||
|
|
||||||
|
A type has the following operators: |
||||||
|
|
||||||
|
@table @asis |
||||||
|
@item getSubType |
||||||
|
Get the set of types, that are implicitly castable to one of the types |
||||||
|
in this type set. |
||||||
|
|
||||||
|
@item getSuperType |
||||||
|
Get the set of types, to which the types in this type set can be casted |
||||||
|
without a bytecode cast. |
||||||
|
|
||||||
|
@item intersection |
||||||
|
Get the intersection of the type sets. |
||||||
|
|
||||||
|
(getCastHelper?) |
||||||
|
(getImplicitCast?) |
||||||
|
@end table |
||||||
|
|
||||||
|
There are simple types, that can only casted to themself (like long, |
||||||
|
float, double, void), 32 bit integer types (boolean, byte, char, short, |
||||||
|
int) and reference types (classes, interfaces, arrays and null type). |
||||||
|
There is also a type range to represent sets of reference types. |
||||||
|
|
||||||
|
createRangeType |
||||||
|
getSpecializedType |
||||||
|
getGeneralizedType |
||||||
|
|
||||||
|
The meaning is |
||||||
|
ref1.intersection(ref2) = |
||||||
|
ref1.getSpecializedType(ref2).createRangeType(ref1.getGeneralizedType(ref2)) |
||||||
|
|
||||||
|
<Object - NULL> |
||||||
|
<Object[] - NULL> |
||||||
|
<Object[] - Serialized[]> |
||||||
|
<tUnknown[]> |
||||||
|
<Object - tUnknown[]> |
||||||
|
{IBCS}[] intersect {CSZ}[] = {CS}[] |
||||||
|
<Object - {ICSB}[]> intersect <Serializable - {CSZ}[]> |
||||||
|
--> <Serializable - {CS}[]> |
||||||
|
|
||||||
|
The byte code distinguishes five different types: |
||||||
|
|
||||||
|
@enumerate |
||||||
|
@item 16 bit integral types (boolean, byte, short, char and int) |
||||||
|
@item long |
||||||
|
@item float |
||||||
|
@item double |
||||||
|
@item reference types (objects, interfaces, arrays, null type) |
||||||
|
@end enumerate |
||||||
|
|
||||||
|
It sometimes makes a difference between byte, short, char and int, but |
||||||
|
not always. |
||||||
|
|
||||||
|
|
||||||
|
16bit integral types: |
||||||
|
|
||||||
|
We differ seven different 16 bit integral types: |
||||||
|
@table @asis |
||||||
|
@item I |
||||||
|
@code{int} type |
||||||
|
@item C |
||||||
|
@code{char} type |
||||||
|
@item S |
||||||
|
@code{short} type |
||||||
|
@item B |
||||||
|
@code{byte} type |
||||||
|
@item Z |
||||||
|
@code{boolean} type |
||||||
|
@item cS |
||||||
|
An @code{int} constant whose value is in @code{short} range. |
||||||
|
@item cB |
||||||
|
An @code{int} constant whose value is in @code{byte} range. |
||||||
|
@end table |
||||||
|
|
||||||
|
Each of this types has a super range and a sub range: |
||||||
|
@multitable {type} {(I,C,S,B,Z,cS,cB)} {(I,C,S,B,Z,cS,cB)} |
||||||
|
@item type @tab sub types @tab super types |
||||||
|
@item I @tab (I,C,S,B,cS,cB) @tab (I) |
||||||
|
@item C @tab (C) @tab (I,C) |
||||||
|
@item S @tab (S,B,cS,cB) @tab (I,S) |
||||||
|
@item B @tab (B,cB) @tab (I,S,B) |
||||||
|
@item Z @tab (Z) @tab (Z) |
||||||
|
@item cS @tab (cS,cB) @tab (I,S,cS) |
||||||
|
@item cB @tab (cB) @tab (I,S,B,cS,cB) |
||||||
|
@end multitable |
||||||
|
|
||||||
|
getTop() getBottom() give the type directly. |
||||||
|
|
||||||
|
createRangeType(Type bottom) does the following: |
||||||
|
If top == tUnknown , union all supertypes |
||||||
|
If top is 16bit type, |
||||||
|
intersect (union of subtypes of top) (union of supertypes) |
||||||
|
Return tError otherwise. |
||||||
|
|
||||||
|
Type.createRangeType(Type top) does the following: |
||||||
|
if Type == tUnknown |
||||||
|
if top is IntegerType |
||||||
|
new IntegerType(union of subtypes of top) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Hints. We distinguish strong and weak Hints: |
||||||
|
|
||||||
|
strong Hints: |
||||||
|
assignment: |
||||||
|
lhs.strongHint = mergeHint(lhs.strongHint, rhs.strongHint) |
||||||
|
lhs.weakHint = mergeHint(lhs.weakHint, rhs.weakHint) |
||||||
|
rhs.strongHint = lhs.strongHint |
||||||
|
|
||||||
|
|
||||||
|
binary op: |
||||||
|
left.weakHint = mergeHints(left.weakHint, right.strongHint?strongHint: weakHint) |
||||||
|
|
||||||
|
binary op |
||||||
|
types that may occur directly in bytecode: |
||||||
|
(I,Z) |
||||||
|
(I) |
||||||
|
(Z) |
||||||
|
(I,C,S,B,Z) |
||||||
|
(I,cS,cB) |
||||||
|
(I,cS) |
||||||
|
(I,C,cS,cB) |
||||||
|
(I,C,cS) |
||||||
|
(I,C) |
||||||
|
(C) |
||||||
|
(S) |
||||||
|
(B) |
||||||
|
(B,Z) |
||||||
|
|
||||||
|
now the sub (>) and super (<) operators |
||||||
|
|
||||||
|
>(I,Z) = (I,C,S,B,Z,cS,cB) New! |
||||||
|
>(I) = (I,C,S,B,cS,cB) New! |
||||||
|
>(Z) = (Z) |
||||||
|
>(I,C,S,B,Z) = (I,C,S,B,Z,cS,cB) |
||||||
|
>(I,cS,cB) = (I,C,S,B,cS,cB) |
||||||
|
>(I,cS) = (I,C,S,B,cS,cB) |
||||||
|
>(I,C,cS,cB) = (I,C,S,B,cS,cB) |
||||||
|
>(I,C,cS) = (I,C,S,B,cS,cB) |
||||||
|
>(I,C) = (I,C,S,B,cS,cB) |
||||||
|
>(C) = (C) |
||||||
|
>(S) = (S,B,cS,cB) New! |
||||||
|
>(B) = (B,cB) New! |
||||||
|
>(B,Z) = (B,Z,cB) New! |
||||||
|
|
||||||
|
<(I,Z) = (I,Z) |
||||||
|
<(I) = (I) |
||||||
|
<(Z) = (Z) |
||||||
|
<(I,C,S,B,Z) = (I,C,S,B,Z) |
||||||
|
<(I,cS,cB) = (I,S,B,cS,cB) New! |
||||||
|
<(I,cS) = (I,S,cS) New! |
||||||
|
<(I,C,cS,cB) = (I,C,S,B,cS,cB) |
||||||
|
<(I,C,cS) = (I,C,S,cS) New! |
||||||
|
<(I,C) = (I,C) |
||||||
|
<(C) = (I,C) |
||||||
|
<(S) = (I,S) New! |
||||||
|
<(B) = (I,S,B) New! |
||||||
|
<(B,Z) = (I,S,B,Z) New! |
||||||
|
|
||||||
|
>(I,C,S,B,Z,cS,cB) = (I,C,S,B,Z,cS,cB) |
||||||
|
>(I,C,S,B,cS,cB) = (I,C,S,B,cS,cB) |
||||||
|
>(B,Z,cB) = (B,Z,cB) |
||||||
|
>(I,C,S,cS) = (I,C,S,B,cS,cB) |
||||||
|
>(I,S,B,Z) = (I,C,S,B,Z,cS,cB) |
||||||
|
>(I,S,B,cS,cB) = (I,C,S,B,cS,cB) |
||||||
|
|
||||||
|
<(I,C,S,B,Z,cS,cB) = (I,C,S,B,Z,cS,cB) |
||||||
|
<(I,C,S,B,cS,cB) = (I,C,S,B,cS,cB) |
||||||
|
<(B,Z,cB) = (I,S,B,Z,cS,cB) |
||||||
|
<(I,C,S,cS) = (I,C,S,cS) |
||||||
|
<(I,S,B,Z) = (I,S,B,Z) |
||||||
|
<(I,S,B,cS,cB) = (I,S,B,cS,cB) |
||||||
|
|
||||||
|
|
||||||
|
Zu betrachtende 32bit Typen: |
||||||
|
|
||||||
|
(I,Z) = (I,Z) |
||||||
|
(I) = (I) |
||||||
|
(Z) = (Z) |
||||||
|
(I,C,S,B,Z) |
||||||
|
(I,cS,cB) |
||||||
|
(I,cS) |
||||||
|
(I,C,cS,cB) |
||||||
|
(I,C,cS) |
||||||
|
(I,C) |
||||||
|
(B,Z) |
||||||
|
(I,C,S,B,Z,cS,cB) |
||||||
|
(I,C,S,B,cS,cB) |
||||||
|
(B,Z,cB) |
||||||
|
(I,C,S,cS) |
||||||
|
(I,S,B,Z) |
||||||
|
(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. |
||||||
|
|
||||||
|
|
@ -0,0 +1,250 @@ |
|||||||
|
<if we_want_a_menu> |
||||||
|
<p>On this page:<br> |
||||||
|
<a href="#decompiler">Decompiler</a><br> |
||||||
|
<a href="#cmdline">Command Line</a><br> |
||||||
|
<a href="#awt">AWT Interface</a><br> |
||||||
|
<a href="#swing">Swing Interface</a><br> |
||||||
|
<a href="#java">Java Interface</a><br> |
||||||
|
<a href="#optimizer">Obfuscator</a><br> |
||||||
|
</p> |
||||||
|
</if> |
||||||
|
|
||||||
|
<section title="Using the Decompiler"> |
||||||
|
<p>After you have <a href="download.html">downloaded</a> the jar archive |
||||||
|
put it into your <tt>CLASSPATH</tt>. The package |
||||||
|
<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 |
||||||
|
something like: |
||||||
|
<pre> |
||||||
|
set CLASSPATH=C:\download\jode-<use version>.jar;C:\swing\swingall.jar |
||||||
|
</pre> |
||||||
|
|
||||||
|
<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> |
||||||
|
or for csh: |
||||||
|
<pre>setenv CLASSPATH /tmp/jode-<use version>.jar:/usr/local/swing/swingall.jar</pre> |
||||||
|
</ul> |
||||||
|
<br> |
||||||
|
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: |
||||||
|
<pre> |
||||||
|
jar -xvf jode-<use version>".jar bin/jode.bat <i>resp.</i> bin/jode |
||||||
|
</pre> |
||||||
|
Edit the file to adapt it to your paths and put it to a convenient location. |
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="Command Line Interface"> |
||||||
|
|
||||||
|
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 |
||||||
|
should go to the next section. <br> |
||||||
|
|
||||||
|
Start the class <tt>jode.decompiler.Main</tt> with the options. The |
||||||
|
following command will give a complete list of the available commands: |
||||||
|
|
||||||
|
<pre>java jode.decompiler.Main --help</pre> |
||||||
|
|
||||||
|
If you want to decompile a jar package you can do it this way: |
||||||
|
|
||||||
|
<pre>java jode.decompiler.Main --dest srcdir program.jar</pre> |
||||||
|
|
||||||
|
If you have installed the batch file/script, you can use it like this: |
||||||
|
<pre>jode --dest srcdir program.jar</pre> |
||||||
|
|
||||||
|
<h3><a name="awt">AWT Interface</a></h3> |
||||||
|
|
||||||
|
The AWT Interface looks exactly like the <a href="applet.html"> |
||||||
|
applet</a>. In fact the applet uses the AWT Interface. You start it |
||||||
|
after setting the <tt>CLASSPATH</tt> (see <a |
||||||
|
href="#decompiler">above</a>), with |
||||||
|
|
||||||
|
<pre>java jode.decompiler.Window</pre> |
||||||
|
|
||||||
|
In the classpath line you can enter a number of jar files, zip files |
||||||
|
and directories separated by comma(<tt>,</tt>). Then enter the |
||||||
|
dot(<tt>.</tt>) separated name of the class you want to decompile. |
||||||
|
Press the <code>start</code> button and the decompiled class should |
||||||
|
appear. You can save it via the <code>save</code> button. |
||||||
|
|
||||||
|
<h3><a name="swing">Swing Interface</a></h3> |
||||||
|
|
||||||
|
For the swing interface you need java version 1.2 or the separately |
||||||
|
available swing package (see <a href="links.html#swing">link |
||||||
|
page</a>. You can invoke it with the following command (JDK1.2 only): |
||||||
|
<pre> |
||||||
|
java -jar jode-<use version>.jar classes.jar |
||||||
|
</pre> |
||||||
|
or if you have set the classpath (see above) |
||||||
|
<pre> |
||||||
|
java jode.swingui.Main classes.jar |
||||||
|
<i>resp.</i> jode swi classes.jar |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>The swing interface will show the package hierarchie of all classes |
||||||
|
in the classpath on the left side. You can now select a class and the |
||||||
|
decompiled code will appear on the right side. Via the menu, you may |
||||||
|
change the classpath or switch between package hierarchie tree and |
||||||
|
class inheritence tree.</p> |
||||||
|
|
||||||
|
<p>The swing interface is very useful to browse through class files if |
||||||
|
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 |
||||||
|
you won't find a save option there.</p> |
||||||
|
|
||||||
|
<h3><a name="java">Java Interface</a></h3> |
||||||
|
|
||||||
|
<p>If you want to integrate <i>JODE</i> into your own java program, |
||||||
|
you can use the <a |
||||||
|
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> |
||||||
|
class. Note that the LGPL allows dynamic linking as long as you don't change |
||||||
|
Jode itself. 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 |
||||||
|
available in the <sflink href="project/showfiles.php">download area</a>. |
||||||
|
It works only under JDK 1.2 and above.</p> |
||||||
|
</section> |
||||||
|
|
||||||
|
<section title="Using the Obfuscator"> |
||||||
|
<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 |
||||||
|
obfuscator with:</p> |
||||||
|
<pre> |
||||||
|
java jode.obfuscator.Main myproject.jos |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>The script file should contain the following options: </p> |
||||||
|
|
||||||
|
<p>First select the classpath. You should include everything in the |
||||||
|
classpath that you need to run your application. This also includes |
||||||
|
the system class files (Sun puts them into <code>classes.zip</code> or |
||||||
|
<code>rt.jar</code>))</p> |
||||||
|
<pre> |
||||||
|
classpath = "c:\\jdk1.2\\jre\\lib\\rt.jar","d:\\project\\java", |
||||||
|
"ftp://www.myorg.org/pub/classlib.jar" |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>Specify where you want the obfuscated classes to go. I recommend |
||||||
|
to write them directly into a zip file, but you can also give a |
||||||
|
directory.</p> |
||||||
|
<pre> |
||||||
|
dest = "obfuscated.zip" |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>You can make <i>JODE</i> write its translation table. This table |
||||||
|
can be used later to undo the name obfuscation, or you can look there |
||||||
|
to decrypt exceptions you may get.</p> |
||||||
|
<pre> |
||||||
|
revtable = "translat.tbl" |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>Select what you want to strip. There are several |
||||||
|
possibilities, which can be separated by comma(<tt>,</tt>):</p> |
||||||
|
<dl> |
||||||
|
<dt>unreach</dt> |
||||||
|
<dd>strip unreachable methods and classes.</dd> |
||||||
|
<dt>source</dt> |
||||||
|
<dd>remove the name of the java file (exceptions will get unreadable).</dd> |
||||||
|
<dt>lnt</dt> |
||||||
|
<dd>remove the line number table (exceptions will get unreadable).</dd> |
||||||
|
<dt>lvt</dt> |
||||||
|
<dd>remove the local variable table (debugging doesn't work).</dd> |
||||||
|
<dt>inner</dt> |
||||||
|
<dd>strip inner class info (reflection doesn't work correctly).</dd> |
||||||
|
</dl> |
||||||
|
<pre> |
||||||
|
strip = "unreach","lvt","inner" |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>Select the packages and classes you want to obfuscate. You should |
||||||
|
only include libraries, that you don't ship separately. If you give a |
||||||
|
package, all classes and subpackages are loaded. You can also use |
||||||
|
<code>*</code> as wild card, that matches everything (including dots). |
||||||
|
</p> |
||||||
|
<pre> |
||||||
|
load = new WildCard { value = "org.myorg.myproject" }, |
||||||
|
new WildCard { value = "org.myorg.mylib*" }, |
||||||
|
new WildCard { value = "org.otherorg.shortlib" } |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>Select the methods and classes you want to preserve. This is |
||||||
|
the <tt>main</tt> method for applications and the default constructor |
||||||
|
<tt><init>.()V</tt> for applets, resource bundles and other classes |
||||||
|
that you load manually at runtime. <br> |
||||||
|
You have to give the method |
||||||
|
name and the type signature to identify your method. <tt>javap |
||||||
|
-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. |
||||||
|
If you have serializable classes and want to preserve their serialized |
||||||
|
form you can use the <tt>SerializePreserver</tt>. </p> |
||||||
|
<pre> |
||||||
|
preserve = new SerializePreserver, |
||||||
|
new WildCard { value = "org.myorg.ApplicationClass.main.*" }, |
||||||
|
new WildCard { value = "org.myorg.AppletClass.<init>.()V" }, |
||||||
|
new WildCard { value = "org.resources.Bundle*.<init>.()V" }, |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>If you want to obfuscate (or just shorten) the identifier you can |
||||||
|
specify a renamer. There are currently following renamer |
||||||
|
available</p> |
||||||
|
<dl><dt>StrongRenamer</dt> |
||||||
|
<dd>Renames to the shortest possible name. You can give a charset |
||||||
|
that should be used. It uses the same name as much as possible.</dd> |
||||||
|
<dt>UniqueRenamer</dt> |
||||||
|
<dd>Renames to unique identifier of the form <tt>xxx123</tt>. Useful |
||||||
|
to reduce name conflicts, before you decompile an obfuscated package.</dd> |
||||||
|
<dt>NameSwapper</dt> |
||||||
|
<dd>This renamer just swaps the names. This is a funny obfuscation |
||||||
|
option that is not very strong, but very confusing.</dd> |
||||||
|
<dt>KeywordRenamer</dt> |
||||||
|
<dd>Renames identifiers to keyword. You can give your own list of |
||||||
|
keywords as parameters. Resulting code is not decompilable directly, |
||||||
|
<b>but it is <i>not</i> legal bytecode either</b>. Some paranoid |
||||||
|
web browsers refuse to run applets containing keywords as identifiers |
||||||
|
(and they are completely within the Java VM spec).</dd> |
||||||
|
</dl> |
||||||
|
<pre> |
||||||
|
renamer = new StrongRenamer |
||||||
|
</pre> |
||||||
|
|
||||||
|
<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 |
||||||
|
over renamer. Entries not in the table will get renamed by the |
||||||
|
renamer.</p> |
||||||
|
<pre> |
||||||
|
table = "translat.tbl" |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>Now you can select the analyzer. The purpose of the |
||||||
|
analyzer is to mark all reachable methods, find out which methods |
||||||
|
needs to get the same name (overloading), and which method names |
||||||
|
mustn't change (overload of library methods, e.g. <tt>nextElement</tt> |
||||||
|
for <tt>Enumeration</tt>s). There are currently two analyzers. |
||||||
|
</p> |
||||||
|
<dl><dt>SimpleAnalyzer</dt> |
||||||
|
<dd>Straight forward analyzer. It is fast and will remove dead code |
||||||
|
on method basis.</dd> |
||||||
|
|
||||||
|
<dd><dt>ConstantAnalyzer</dt> |
||||||
|
<dd>Strong analyzer that will determine, which fields and instructions |
||||||
|
have constant values. It will remove dead code on instruction basis |
||||||
|
and replace constant instruction with a load of the constant, or |
||||||
|
remove them completely.<br> This analyzer is especially useful to |
||||||
|
revert the flow obfuscation of some other obfuscators.</dd> |
||||||
|
</dl> |
||||||
|
<pre> |
||||||
|
analyzer = new ConstantAnalyzer |
||||||
|
</pre> |
||||||
|
|
||||||
|
<p>Pre- and Post transformers transform the bytecode before |
||||||
|
resp. after the Analyzer runs. Using this default should be okay. |
||||||
|
You may remove the LocalOptimizer, though, if you have problems.</p> |
||||||
|
<p>In the future I may add some new post transformers, that do string |
||||||
|
encryption, flow obfuscation and similar things. If you want to write |
||||||
|
your own Transformers please contact me, since the next version will |
||||||
|
change the bytecode interface.</p> |
||||||
|
<pre> |
||||||
|
post = new LocalOptimizer, new RemovePopAnalyzer |
||||||
|
</pre> |
||||||
|
</section> |
Binary file not shown.
@ -0,0 +1,28 @@ |
|||||||
|
# This is a sample script file to obfuscate the JODE project. |
||||||
|
|
||||||
|
# First we select what we want to strip. There are several possibilities: |
||||||
|
# unreach - strip unreachable methods and classes. |
||||||
|
# source - strip source file attribute. |
||||||
|
# lnt - strip line number table. |
||||||
|
# lvt - strip local variable table. |
||||||
|
# inner - strip inner class info |
||||||
|
strip = "unreach" |
||||||
|
|
||||||
|
load = new WildCard { value = "jode" } |
||||||
|
|
||||||
|
preserve = new WildCard { value = "jode.Decompiler.main.*" }, |
||||||
|
new WildCard { value = "jode.JodeApplet.<init>.()V" }, |
||||||
|
new WildCard { value = "jode.JodeWindow.main.*" }, |
||||||
|
new WildCard { value = "jode.obfuscator.Main.main.*" }, |
||||||
|
new WildCard { value = "jode.swingui.Main.main.*" }, |
||||||
|
new WildCard { value = "jode.obfuscator.modules.*.<init>.()V" }, |
||||||
|
new WildCard { value = "jode.obfuscator.modules.*.setOption.*" } |
||||||
|
|
||||||
|
# value = "jode.Decompiler.main.*", |
||||||
|
# "jode.JodeApplet.<init>.()V", |
||||||
|
# "jode.JodeWindow.main.*", |
||||||
|
# "jode.obfuscator.Main.main.*", |
||||||
|
# "jode.swingui.Main.main.*" |
||||||
|
|
||||||
|
analyzer = new SimpleAnalyzer |
||||||
|
post = new LocalOptimizer, new RemovePopAnalyzer |
Binary file not shown.
@ -0,0 +1,40 @@ |
|||||||
|
#!/bin/sh |
||||||
|
|
||||||
|
OLDDIR=`pwd` |
||||||
|
TIME=`date +"%Y%m%d %H:%M"` |
||||||
|
if [ "${1#-D}" != "$1" ] ; then |
||||||
|
TIME=`date +"%Y%m%d %H:%M" --date="${1#-D}"` |
||||||
|
shift; |
||||||
|
fi |
||||||
|
DATE=`echo $TIME | cut -c0-8` |
||||||
|
echo $TIME |
||||||
|
echo $DATE |
||||||
|
|
||||||
|
TEMP=`mktemp -d $HOME/tmp.XXXXXX` |
||||||
|
|
||||||
|
trap "cd $OLDDIR; rm -rf $TEMP" EXIT |
||||||
|
cd $TEMP |
||||||
|
|
||||||
|
CLASSPATH=$TEMP:/usr/local/swing-1.1/swing.jar |
||||||
|
export CLASSPATH |
||||||
|
|
||||||
|
cvs export -D"$TIME" jode |
||||||
|
cd jode |
||||||
|
perl -i -pe's/(snapshot )[0-9]+/${1}'"$TIME"'/ |
||||||
|
if /public final static String version/;' GlobalOptions.java |
||||||
|
|
||||||
|
COMPILER=${1:-jikes} |
||||||
|
if [ -z "$1" ]; then |
||||||
|
FLAGS="-g" |
||||||
|
else |
||||||
|
shift |
||||||
|
FLAGS="$*" |
||||||
|
fi |
||||||
|
|
||||||
|
# jasmin -d .. jvm/Interpreter.j |
||||||
|
|
||||||
|
eval $COMPILER $FLAGS -d .. \ |
||||||
|
Decompiler.java obfuscator/Main.java JodeApplet.java swingui/Main.java |
||||||
|
|
||||||
|
cd .. |
||||||
|
zip -r $HOME/jode-$DATE.zip jode |
@ -0,0 +1,34 @@ |
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Copyright (c) 2000 Michel CASABIANCA. All Rights Reserved. |
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its |
||||||
|
documentation for any purpose and without fee or royalty is hereby |
||||||
|
granted, provided that both the above copyright notice and this |
||||||
|
permission notice appear in all copies of the software and |
||||||
|
documentation or portions thereof, including modifications, that you |
||||||
|
make. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO |
||||||
|
REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, |
||||||
|
BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR |
||||||
|
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR |
||||||
|
THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY |
||||||
|
THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. |
||||||
|
COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE |
||||||
|
OR DOCUMENTATION. |
||||||
|
--> |
||||||
|
|
||||||
|
<!-- project ext DTD for Ant --> |
||||||
|
<!-- 2000-04-03 --> |
||||||
|
|
||||||
|
<!ENTITY % ext "| xt"> |
||||||
|
|
||||||
|
<!ELEMENT xt EMPTY> |
||||||
|
<!ATTLIST xt |
||||||
|
xml CDATA #REQUIRED |
||||||
|
xsl CDATA #REQUIRED |
||||||
|
out CDATA #REQUIRED> |
||||||
|
|
||||||
|
|
@ -0,0 +1,273 @@ |
|||||||
|
<?xml version="1.0" encoding="iso-8859-1"?> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Copyright (c) 2000 Michel CASABIANCA. All Rights Reserved. |
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its |
||||||
|
documentation for any purpose and without fee or royalty is hereby |
||||||
|
granted, provided that both the above copyright notice and this |
||||||
|
permission notice appear in all copies of the software and |
||||||
|
documentation or portions thereof, including modifications, that you |
||||||
|
make. |
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO |
||||||
|
REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, |
||||||
|
BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR |
||||||
|
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR |
||||||
|
THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY |
||||||
|
THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. |
||||||
|
COPYRIGHT HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE |
||||||
|
OR DOCUMENTATION. |
||||||
|
--> |
||||||
|
|
||||||
|
<!-- project DTD for Ant --> |
||||||
|
<!-- 2000-04-03 --> |
||||||
|
|
||||||
|
<!ENTITY % ext-file SYSTEM "project-ext.dtd"> |
||||||
|
%ext-file; |
||||||
|
|
||||||
|
<!ELEMENT project (target | property)*> |
||||||
|
<!ATTLIST project |
||||||
|
name CDATA #REQUIRED |
||||||
|
default CDATA #REQUIRED |
||||||
|
basedir CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT target (ant | available | chmod | copydir | copyfile | |
||||||
|
cvs | delete | deltree | echo | exec | expand | |
||||||
|
filter | get | gzip | fixcrlf | jar | java | |
||||||
|
javac | javadoc | javadoc2 | keysubst | mkdir | |
||||||
|
property | rename | replace | rmic | tar | |
||||||
|
taskdef | tstamp | zip %ext;)*> |
||||||
|
<!ATTLIST target |
||||||
|
name CDATA #REQUIRED |
||||||
|
depends CDATA #IMPLIED |
||||||
|
if CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT property EMPTY> |
||||||
|
<!ATTLIST property |
||||||
|
name CDATA #IMPLIED |
||||||
|
value CDATA #IMPLIED |
||||||
|
resource CDATA #IMPLIED |
||||||
|
file CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT ant EMPTY> |
||||||
|
<!ATTLIST ant |
||||||
|
antfile CDATA #IMPLIED |
||||||
|
dir CDATA #REQUIRED |
||||||
|
target CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT available EMPTY> |
||||||
|
<!ATTLIST available |
||||||
|
property CDATA #REQUIRED |
||||||
|
classname CDATA #REQUIRED |
||||||
|
resource CDATA #REQUIRED |
||||||
|
file CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT chmod EMPTY> |
||||||
|
<!ATTLIST chmod |
||||||
|
src CDATA #REQUIRED |
||||||
|
perm CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT copydir EMPTY> |
||||||
|
<!ATTLIST copydir |
||||||
|
src CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED |
||||||
|
ignore CDATA #IMPLIED |
||||||
|
includes CDATA #IMPLIED |
||||||
|
excludes CDATA #IMPLIED |
||||||
|
defaultexcludes CDATA #IMPLIED |
||||||
|
filtering CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT copyfile EMPTY> |
||||||
|
<!ATTLIST copyfile |
||||||
|
src CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED |
||||||
|
filtering CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT cvs EMPTY> |
||||||
|
<!ATTLIST cvs |
||||||
|
cvsRoot CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED |
||||||
|
package CDATA #REQUIRED |
||||||
|
tag CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT delete EMPTY> |
||||||
|
<!ATTLIST delete |
||||||
|
file CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT deltree EMPTY> |
||||||
|
<!ATTLIST deltree |
||||||
|
dir CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT echo EMPTY> |
||||||
|
<!ATTLIST echo |
||||||
|
message CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT exec EMPTY> |
||||||
|
<!ATTLIST exec |
||||||
|
command CDATA #REQUIRED |
||||||
|
dir CDATA #REQUIRED |
||||||
|
os CDATA #IMPLIED |
||||||
|
output CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT expand EMPTY> |
||||||
|
<!ATTLIST expand |
||||||
|
src CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT filter EMPTY> |
||||||
|
<!ATTLIST filter |
||||||
|
token CDATA #REQUIRED |
||||||
|
value CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT get EMPTY> |
||||||
|
<!ATTLIST get |
||||||
|
src CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED |
||||||
|
verbose CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT gzip EMPTY> |
||||||
|
<!ATTLIST gzip |
||||||
|
src CDATA #REQUIRED |
||||||
|
zipfile CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT fixcrlf EMPTY> |
||||||
|
<!ATTLIST fixcrlf |
||||||
|
srcdir CDATA #REQUIRED |
||||||
|
destDir CDATA #IMPLIED |
||||||
|
includes CDATA #IMPLIED |
||||||
|
excludes CDATA #IMPLIED |
||||||
|
cr CDATA #IMPLIED |
||||||
|
tab CDATA #IMPLIED |
||||||
|
eof CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT jar EMPTY> |
||||||
|
<!ATTLIST jar |
||||||
|
jarfile CDATA #REQUIRED |
||||||
|
basedir CDATA #REQUIRED |
||||||
|
items CDATA #IMPLIED |
||||||
|
ignore CDATA #IMPLIED |
||||||
|
includes CDATA #IMPLIED |
||||||
|
excludes CDATA #IMPLIED |
||||||
|
defaultexcludes CDATA #IMPLIED |
||||||
|
manifest CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT java EMPTY> |
||||||
|
<!ATTLIST java |
||||||
|
classname CDATA #REQUIRED |
||||||
|
args CDATA #IMPLIED |
||||||
|
fork CDATA #IMPLIED |
||||||
|
jvmargs CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT javac EMPTY> |
||||||
|
<!ATTLIST javac |
||||||
|
srcdir CDATA #REQUIRED |
||||||
|
destdir CDATA #REQUIRED |
||||||
|
includes CDATA #IMPLIED |
||||||
|
excludes CDATA #IMPLIED |
||||||
|
defaultexcludes CDATA #IMPLIED |
||||||
|
classpath CDATA #IMPLIED |
||||||
|
bootclasspath CDATA #IMPLIED |
||||||
|
extdirs CDATA #IMPLIED |
||||||
|
debug CDATA #IMPLIED |
||||||
|
optimize CDATA #IMPLIED |
||||||
|
deprecation CDATA #IMPLIED |
||||||
|
filtering CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT javadoc EMPTY> |
||||||
|
<!ATTLIST javadoc |
||||||
|
sourcepath CDATA #REQUIRED |
||||||
|
destdir CDATA #REQUIRED |
||||||
|
sourcefiles CDATA #IMPLIED |
||||||
|
packagenames CDATA #IMPLIED |
||||||
|
classpath CDATA #IMPLIED |
||||||
|
bootclasspath CDATA #IMPLIED |
||||||
|
extdirs CDATA #IMPLIED |
||||||
|
overview CDATA #IMPLIED |
||||||
|
public CDATA #IMPLIED |
||||||
|
protected CDATA #IMPLIED |
||||||
|
package CDATA #IMPLIED |
||||||
|
private CDATA #IMPLIED |
||||||
|
old CDATA #IMPLIED |
||||||
|
verbose CDATA #IMPLIED |
||||||
|
locale CDATA #IMPLIED |
||||||
|
encoding CDATA #IMPLIED |
||||||
|
version CDATA #IMPLIED |
||||||
|
use CDATA #IMPLIED |
||||||
|
author CDATA #IMPLIED |
||||||
|
splitindex CDATA #IMPLIED |
||||||
|
windowtitle CDATA #IMPLIED |
||||||
|
doctitle CDATA #IMPLIED |
||||||
|
header CDATA #IMPLIED |
||||||
|
footer CDATA #IMPLIED |
||||||
|
bottom CDATA #IMPLIED |
||||||
|
link CDATA #IMPLIED |
||||||
|
linkoffline CDATA #IMPLIED |
||||||
|
group CDATA #IMPLIED |
||||||
|
nodedeprecated CDATA #IMPLIED |
||||||
|
nodedeprecatedlist CDATA #IMPLIED |
||||||
|
notree CDATA #IMPLIED |
||||||
|
noindex CDATA #IMPLIED |
||||||
|
nohelp CDATA #IMPLIED |
||||||
|
nonavbar CDATA #IMPLIED |
||||||
|
serialwarn CDATA #IMPLIED |
||||||
|
helpfile CDATA #IMPLIED |
||||||
|
stylesheetfile CDATA #IMPLIED |
||||||
|
charset CDATA #IMPLIED |
||||||
|
docencoding CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT keysubst EMPTY> |
||||||
|
<!ATTLIST keysubst |
||||||
|
src CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED |
||||||
|
sep CDATA #IMPLIED |
||||||
|
keys CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT mkdir EMPTY> |
||||||
|
<!ATTLIST mkdir |
||||||
|
dir CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT rename EMPTY> |
||||||
|
<!ATTLIST rename |
||||||
|
src CDATA #REQUIRED |
||||||
|
dest CDATA #REQUIRED |
||||||
|
replace CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT replace EMPTY> |
||||||
|
<!ATTLIST replace |
||||||
|
file CDATA #REQUIRED |
||||||
|
token CDATA #REQUIRED |
||||||
|
value CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT rmic EMPTY> |
||||||
|
<!ATTLIST rmic |
||||||
|
base CDATA #REQUIRED |
||||||
|
classname CDATA #REQUIRED |
||||||
|
filtering CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT tar EMPTY> |
||||||
|
<!ATTLIST tar |
||||||
|
tarfile CDATA #REQUIRED |
||||||
|
basedir CDATA #REQUIRED |
||||||
|
includes CDATA #IMPLIED |
||||||
|
excludes CDATA #IMPLIED |
||||||
|
defaultexcludes CDATA #IMPLIED> |
||||||
|
|
||||||
|
<!ELEMENT taskdef EMPTY> |
||||||
|
<!ATTLIST taskdef |
||||||
|
name CDATA #REQUIRED |
||||||
|
classname CDATA #REQUIRED> |
||||||
|
|
||||||
|
<!ELEMENT tstamp EMPTY> |
||||||
|
|
||||||
|
<!ELEMENT zip EMPTY> |
||||||
|
<!ATTLIST zip |
||||||
|
zipfile CDATA #REQUIRED |
||||||
|
basedir CDATA #REQUIRED |
||||||
|
items CDATA #IMPLIED |
||||||
|
ignore CDATA #IMPLIED |
||||||
|
includes CDATA #IMPLIED |
||||||
|
excludes CDATA #IMPLIED |
||||||
|
defaultexcludes CDATA #IMPLIED> |
||||||
|
|
||||||
|
|
@ -0,0 +1,96 @@ |
|||||||
|
options = tabwidth,indent,style,linewidth,import,verbose,lvt,inner,anonymous,push,pretty,decrypt,onetime,immediate,verify,contrafo,debug |
||||||
|
|
||||||
|
tabwidth.0=<n> |
||||||
|
tabwidth.1=Set tab width to n. |
||||||
|
tabwidth.2=This means that Jode uses tabs to replace n spaces. \ |
||||||
|
Don't confound this with the indent option. Use 0 if you don't want \ |
||||||
|
tabs. Default is 8. |
||||||
|
|
||||||
|
indent.0=<n> |
||||||
|
indent.1=Indent blocks by n spaces. |
||||||
|
|
||||||
|
style.0={sun|gnu} |
||||||
|
style.1=Specify indentation style. |
||||||
|
|
||||||
|
linewidth.0=<n> |
||||||
|
linewidth.1=Set maximum line width to n. |
||||||
|
linewidth.2=Jode breaks lines that are longer than this. It tries it's best \ |
||||||
|
to make all lines fit in this limit, but sometimes this won't succeed. |
||||||
|
|
||||||
|
import.0=<pkglimit>,<clslimit> |
||||||
|
import.1=import classes if they occur more than clslimit times and packages \ |
||||||
|
with more than pkglimit used classes. Default is 0,1 which means that all \ |
||||||
|
used classes are imported, but never a whole package. |
||||||
|
|
||||||
|
verbose.0=<n> |
||||||
|
verbose.1=Be verbose (higher n means more verbose). |
||||||
|
verbose.2=This prints some information about the currently decompiled \ |
||||||
|
class or method to the console. |
||||||
|
|
||||||
|
debug.0=<flag>,... |
||||||
|
debug.1=Enable debugging options. Useful to track errors in the decompiler. |
||||||
|
debug.2=Possible flags are: \ |
||||||
|
"bytecode", to print raw bytecode. \ |
||||||
|
"lvt", dump LocalVariableTable. \ |
||||||
|
"verifier", to trace bytecode verification. \ |
||||||
|
"check", do time consuming sanity checks; useful to spot serious errors. \ |
||||||
|
"types", to see the type intersections. \ |
||||||
|
"flow", for a very verbose trace of the decompile process. \ |
||||||
|
"analyze", briefly inform about "T1/T2" analyzation. \ |
||||||
|
"inout", to view the in/out local variable analyzation. \ |
||||||
|
"locals", to see how local variable merging is done. \ |
||||||
|
"constructors", to trace constructor transformation. \ |
||||||
|
"interpreter", to follow the execution of the interpreter \ |
||||||
|
(the interpreter is used for string decryption). |
||||||
|
|
||||||
|
inner.0={yes|no} |
||||||
|
inner.1=(Don't) decompiler inner classes. |
||||||
|
|
||||||
|
anonymous.0={yes|no} |
||||||
|
anonymous.1=(Don't) decompiler method scoped classes. |
||||||
|
|
||||||
|
contrafo.0={yes|no} |
||||||
|
contrafo.1=(Don't) transform constructors. |
||||||
|
|
||||||
|
lvt.0={yes|no} |
||||||
|
lvt.1=(Don't) use the local variable table. |
||||||
|
lvt.2=Turning it off is useful if an obfuscator filled it with bogus values. |
||||||
|
|
||||||
|
pretty.0={yes|no} |
||||||
|
pretty.1=(Don't) use `pretty' names for local variables. |
||||||
|
pretty.2=The non pretty names have the advantage, that their names are \ |
||||||
|
unique. This make search & replace possible. |
||||||
|
|
||||||
|
push.0={yes|no} |
||||||
|
push.1=Allow PUSH pseudo instructions in output. |
||||||
|
push.2=Sometimes, when methods were inlined, Jode can't reconstruct \ |
||||||
|
the original expression. It has to split a complex expression into \ |
||||||
|
several ones, using temporary variables. If this option is on, it won't \ |
||||||
|
use the temporary variables, but uses pseudo PUSH/POP instructions instead, \ |
||||||
|
as they are in the bytecode. |
||||||
|
|
||||||
|
decrypt.0={yes|no} |
||||||
|
decrypt.1=(Don't) decrypt encrypted strings. |
||||||
|
decrypt.2=Some obfuscators encrypt all strings. To decrypt them at runtime \ |
||||||
|
they add a small decryption routine to the code. If Jode detects such a \ |
||||||
|
decryption routine it interprets it to decrypt the strings at decompile time. |
||||||
|
|
||||||
|
onetime.0={yes|no} |
||||||
|
onetime.1=(Don't) remove locals that written and then immediately read. |
||||||
|
onetime.2=When javac inlines a method it uses temporary local variables for \ |
||||||
|
the parameters. Often these local variables can be removed, which makes \ |
||||||
|
the code much better to read. |
||||||
|
|
||||||
|
immediate.0={yes|no} |
||||||
|
immediate.1=Output the source immediately as it gets decompiled. |
||||||
|
immediate.2=This leads to more instant output, but has many disadvantages.\ |
||||||
|
For one the import statements can't be correct. But it also may lead to \ |
||||||
|
buggy code. The advantage is, that you can get partial output even if an |
||||||
|
exception is thrown. |
||||||
|
|
||||||
|
verify.0={yes|no} |
||||||
|
verify.1=(Don't) verify code before decompiling it. |
||||||
|
verify.2=Jode assumes at many places that your byte code is legal. To \ |
||||||
|
be sure it verifies it before decompiling. If verification fails it \ |
||||||
|
rejects the code. Since verification can fail for legal code if the \ |
||||||
|
type hierarchy is not known, you can turn this option off. |
@ -0,0 +1,28 @@ |
|||||||
|
usage.count=3 |
||||||
|
usage.0 = usage: java jode.swingui.Main [CLASSPATH] |
||||||
|
usage.1 = The directories in CLASSPATH should be separated by ','. |
||||||
|
usage.2 = If no CLASSPATH is given the virtual machine classpath is used. |
||||||
|
|
||||||
|
browse.filter.description = *.jar, *.zip Archives |
||||||
|
browse.title = Browse |
||||||
|
cpdialog.title = Set Class Path |
||||||
|
|
||||||
|
button.okay=Okay |
||||||
|
button.apply=Apply |
||||||
|
button.cancel=Cancel |
||||||
|
button.select=Select |
||||||
|
button.browse=Browse... |
||||||
|
button.add=Add |
||||||
|
button.remove=Remove |
||||||
|
|
||||||
|
main.decompiling=decompiling |
||||||
|
main.exception = \nException while decompiling: |
||||||
|
|
||||||
|
menu.file = File |
||||||
|
menu.file.save = Save... |
||||||
|
menu.file.save.ex = An Exception occured on filewriting! |
||||||
|
menu.file.gc = Collect Garbage |
||||||
|
menu.file.exit = Exit |
||||||
|
menu.opt = Options |
||||||
|
menu.opt.hier = Class Hierarchy |
||||||
|
menu.opt.cp = Set Class Path... |
@ -0,0 +1,28 @@ |
|||||||
|
usage.count=3 |
||||||
|
usage.0 = Syntax: java jode.swingui.Main [CLASSPATH] |
||||||
|
usage.1 = Die Verzeichnisse in CLASSPATH werden durch Kommas abgetrennt. |
||||||
|
usage.2 = Wird kein CLASSPATH angegeben, so wird Java's standard classpath verwendet. |
||||||
|
|
||||||
|
browse.filter.description = *.jar, *.zip Archive |
||||||
|
browse.title = Durchsuchen |
||||||
|
cpdialog.title = Setze Classpath |
||||||
|
|
||||||
|
button.okay = Ok |
||||||
|
button.apply = Anwenden |
||||||
|
button.cancel = Abbruch |
||||||
|
button.select = Selektieren |
||||||
|
button.browse = Durchsuchen... |
||||||
|
button.add = HinzufĂĽgen |
||||||
|
button.remove = Entfernen |
||||||
|
|
||||||
|
main.decompiling = dekompiliere |
||||||
|
main.exception = \nBeim Dekompilieren ist ein Fehler aufgetreten: |
||||||
|
|
||||||
|
menu.file = Datei |
||||||
|
menu.file.save = Speichern... |
||||||
|
menu.file.save.ex = Beim Schreiben der Datei trat eine Ausnahme auf! |
||||||
|
menu.file.gc = Speicher freiräumen |
||||||
|
menu.file.exit = Beenden |
||||||
|
menu.opt = Optionen |
||||||
|
menu.opt.hier = Klassen-Hierarchie |
||||||
|
menu.opt.cp = Setze Classpath... |
@ -0,0 +1,94 @@ |
|||||||
|
#!/usr/bin/perl |
||||||
|
# addHeader.pl Copyright (C) 1999 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$ |
||||||
|
|
||||||
|
# This perl script just adds the copyright header to all given java files, |
||||||
|
# removing a possible previous header. |
||||||
|
|
||||||
|
|
||||||
|
for (@ARGV) { |
||||||
|
my $file = $_; |
||||||
|
$file =~ m=([^/]*)\.java(\.in)?$= or do { |
||||||
|
print STDERR "$file is not a java file"; |
||||||
|
next; |
||||||
|
}; |
||||||
|
my $class = $1; |
||||||
|
my $curyear = `date +%Y`; |
||||||
|
chomp $curyear; |
||||||
|
# my $firstcheckin = `rlog $file 2>/dev/null |grep date| tail -1`; |
||||||
|
# my $firstyear = |
||||||
|
# ($firstcheckin =~ m=date: ([0-9]+)/[0-9]+/[0-9]+=) ? $1 : $curyear; |
||||||
|
# my $lastcheckin = `rlog $file 2>/dev/null |grep date| head -1`; |
||||||
|
# my $lastyear = |
||||||
|
# ($firstcheckin =~ m=date: ([0-9]+)/[0-9]+/[0-9]+=) ? $1 : $curyear; |
||||||
|
open FILE, "<$file"; |
||||||
|
while (<FILE>) { |
||||||
|
$firstyear = $1 if /Copyright \(C\) (\d{4})/; |
||||||
|
last if /^package/; |
||||||
|
} |
||||||
|
|
||||||
|
$firstyear = $curyear if ! $firstyear; |
||||||
|
my $lastyear = $curyear; |
||||||
|
my $years = ($firstyear == $lastyear) |
||||||
|
? $firstyear : "$firstyear-$lastyear"; |
||||||
|
my $lesser = ""; |
||||||
|
my $dotlesser = ""; |
||||||
|
|
||||||
|
if ($file =~ m!jode/(util|bytecode|jvm|flow|expr|decompiler |
||||||
|
|GlobalOptions|AssertError)!x) { |
||||||
|
$lesser = " Lesser"; |
||||||
|
$dotlesser = ".LESSER"; |
||||||
|
} |
||||||
|
|
||||||
|
rename "$file", "$file.orig" or do { |
||||||
|
print STDERR "Can't open file $file\n"; |
||||||
|
next; |
||||||
|
}; |
||||||
|
open OLD, "<$file.orig"; |
||||||
|
open NEW, ">$file"; |
||||||
|
print NEW <<"EOF"; |
||||||
|
/* $class Copyright (C) $years 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$dotlesser. If not, write to |
||||||
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
||||||
|
* |
||||||
|
* \$Id\$ |
||||||
|
*/ |
||||||
|
|
||||||
|
EOF |
||||||
|
|
||||||
|
while (<OLD>) { |
||||||
|
/^package/ and last; |
||||||
|
} |
||||||
|
print NEW $_; |
||||||
|
while (<OLD>) { |
||||||
|
print NEW $_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,134 @@ |
|||||||
|
#!/usr/bin/perl |
||||||
|
# createStackDelta.pl Copyright (C) 1999 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$ |
||||||
|
|
||||||
|
# This perl script creates the stackDelta string needed by |
||||||
|
# jode.bytecode.Instruction. |
||||||
|
|
||||||
|
my $OPCODEFILE="jode/bytecode/Opcodes.java"; |
||||||
|
my @delta; |
||||||
|
my $lastOpcode = 0; |
||||||
|
my $nr; |
||||||
|
|
||||||
|
open OPCODES, "<$OPCODEFILE"; |
||||||
|
while (<OPCODES>) { |
||||||
|
next unless /opc_([a-z0-9_]+)\s*=\s*(\d+)/; |
||||||
|
|
||||||
|
$_ = $1; |
||||||
|
$nr = $2; |
||||||
|
if (/^(nop|iinc)$/) { |
||||||
|
# no pop, no push |
||||||
|
$delta[$nr] = "000"; |
||||||
|
} elsif (/^([aif](const|load).*|[sb]ipush|ldc(_w)?)$/) { |
||||||
|
# no pop, one push |
||||||
|
$delta[$nr] = "010"; |
||||||
|
} elsif (/^([ld](const|load).*|ldc2_w)$/) { |
||||||
|
# no pop, two push |
||||||
|
$delta[$nr] = "020"; |
||||||
|
} elsif (/^([aif]store.*|pop)$/) { |
||||||
|
# one pop, no push |
||||||
|
$delta[$nr] = "001"; |
||||||
|
} elsif (/^([ld]store.*|pop2)$/) { |
||||||
|
# two pop, no push |
||||||
|
$delta[$nr] = "002"; |
||||||
|
} elsif (/^[aifbcs]aload$/) { |
||||||
|
# two pop, one push |
||||||
|
$delta[$nr] = "012"; |
||||||
|
} elsif (/^[dl]aload$/) { |
||||||
|
# two pop, two push |
||||||
|
$delta[$nr] = "022"; |
||||||
|
} elsif (/^[aifbcs]astore$/) { |
||||||
|
# three pop, no push |
||||||
|
$delta[$nr] = "003"; |
||||||
|
} elsif (/^[dl]astore$/) { |
||||||
|
# four pop, no push |
||||||
|
$delta[$nr] = "004"; |
||||||
|
} elsif (/^dup(2)?(_x([12]))?$/) { |
||||||
|
$count = $1 ? 2 : 1; |
||||||
|
$depth = $2 ? $3 : 0; |
||||||
|
$pop = $count + $depth; |
||||||
|
$push = $pop + $count; |
||||||
|
$delta[$nr] = "0".$push.$pop; |
||||||
|
} elsif (/^swap$/) { |
||||||
|
# two pop, two push |
||||||
|
$delta[$nr] = "022"; |
||||||
|
} elsif (/^[if](add|sub|mul|div|rem|u?sh[lr]|and|or|xor)$/) { |
||||||
|
# two pop, one push |
||||||
|
$delta[$nr] = "012"; |
||||||
|
} elsif (/^[ld](add|sub|mul|div|rem|and|or|xor)$/) { |
||||||
|
# four pop, two push |
||||||
|
$delta[$nr] = "024"; |
||||||
|
} elsif (/^[if]neg$/) { |
||||||
|
# one pop, one push |
||||||
|
$delta[$nr] = "011"; |
||||||
|
} elsif (/^[ld]neg$/) { |
||||||
|
# two pop, two push |
||||||
|
$delta[$nr] = "022"; |
||||||
|
} elsif (/^lu?sh[lr]$/) { |
||||||
|
# 3 pop, two push |
||||||
|
$delta[$nr] = "023"; |
||||||
|
} elsif (/^[if]2[ifbcs]$/) { |
||||||
|
# one pop, one push |
||||||
|
$delta[$nr] = "011"; |
||||||
|
} elsif (/^[if]2[ld]$/) { |
||||||
|
# one pop, two push |
||||||
|
$delta[$nr] = "021"; |
||||||
|
} elsif (/^[ld]2[if]$/) { |
||||||
|
# two pop, one push |
||||||
|
$delta[$nr] = "012"; |
||||||
|
} elsif (/^[ld]2[ld]$/) { |
||||||
|
# two pop, two push |
||||||
|
$delta[$nr] = "022"; |
||||||
|
} elsif (/^fcmp[lg]$/) { |
||||||
|
$delta[$nr] = "012"; |
||||||
|
} elsif (/^[ld]cmp[lg]?$/) { |
||||||
|
$delta[$nr] = "014"; |
||||||
|
} elsif (/^if_[ia]cmp(eq|ne|lt|ge|le|gt)$/) { |
||||||
|
$delta[$nr] = "002"; |
||||||
|
} elsif (/^(if(eq|ne|lt|ge|le|gt|(non)?null)|tableswitch|lookupswitch)$/) { |
||||||
|
# order does matter |
||||||
|
$delta[$nr] = "001"; |
||||||
|
} elsif (/^(goto(_w)?|jsr(_w)?|ret|return)$/) { |
||||||
|
$delta[$nr] = "000"; |
||||||
|
} elsif (/^([ifa]return)$/) { |
||||||
|
$delta[$nr] = "001"; |
||||||
|
} elsif (/^([ld]return)$/) { |
||||||
|
$delta[$nr] = "002"; |
||||||
|
} elsif (/^(new)$/) { |
||||||
|
$delta[$nr] = "010"; |
||||||
|
} elsif (/^(multianewarray|(get|put|invoke).*)$/) { |
||||||
|
# unknown |
||||||
|
$delta[$nr] = "100"; |
||||||
|
} elsif (/^(athrow|monitor(enter|exit))$/) { |
||||||
|
$delta[$nr] = "001"; |
||||||
|
} elsif (/^(a?newarray|arraylength|checkcast|instanceof)$/) { |
||||||
|
$delta[$nr] = "011"; |
||||||
|
} else { |
||||||
|
# illegal |
||||||
|
next; |
||||||
|
} |
||||||
|
$lastOpcode = $nr |
||||||
|
if ($nr > $lastOpcode); |
||||||
|
} |
||||||
|
|
||||||
|
print " private final static String stackDelta = \n\t\""; |
||||||
|
for ($nr = 0; $nr <= $lastOpcode; $nr ++) { |
||||||
|
defined $delta[$nr] or $delta[$nr] = "177"; |
||||||
|
print "\\$delta[$nr]"; |
||||||
|
} |
||||||
|
print "\";\n"; |
@ -0,0 +1,184 @@ |
|||||||
|
#!/usr/bin/perl -s -w |
||||||
|
# |
||||||
|
# javaDependencies Copyright (C) 1999 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$ |
||||||
|
|
||||||
|
# This scripts create Makefile dependencies out of class files. It |
||||||
|
# simply scans the constant pool of the class files, finding all |
||||||
|
# references to other classes and adding a dependency to that class. |
||||||
|
# |
||||||
|
# It doesn't do a perfect job, since it can't handle dependencies to |
||||||
|
# constant values in different classes: The compiler inlines the |
||||||
|
# constant and thus doesn't include a reference to the class. |
||||||
|
# |
||||||
|
# Usage: |
||||||
|
# javaDependencies.pl -classpath <cp> [-dependdir <depdir> [-subdir <subdir>]] |
||||||
|
# [-depfile <depfile>] |
||||||
|
# <classfiles> |
||||||
|
# |
||||||
|
# cp: colon separated paths to the java files we should depend on. |
||||||
|
# depdir: if set, use this path as path to the java files when printing |
||||||
|
# dependencies, not the path where the java files were found. |
||||||
|
# useful, if you want to make use of VPATH settings in Makefile. |
||||||
|
# subdir: if set, this is the path from depdir to the current directory. |
||||||
|
# Use it to remove unneccessary ../../$subdir/ |
||||||
|
# depfile: the name of the dependency file, default is "Makefile.dep". |
||||||
|
# class: The class files (not inner classes) for which the dependencies |
||||||
|
# should be generated. We will also look for inner and anon |
||||||
|
# classes. |
||||||
|
|
||||||
|
my $buff; |
||||||
|
|
||||||
|
sub readInBuff ($) { |
||||||
|
my $count = $_[0]; |
||||||
|
my $offset = 0; |
||||||
|
while ($count > 0) { |
||||||
|
my $result; |
||||||
|
$result = read FILE, $buff, $count, $offset or return 0; |
||||||
|
$offset += $result; |
||||||
|
$count -= $result; |
||||||
|
} |
||||||
|
$offset; |
||||||
|
} |
||||||
|
|
||||||
|
sub readUTF () { |
||||||
|
readInBuff 2 or die "Can't read UTF8 length"; |
||||||
|
my $ulength = unpack("n", $buff) & 0xffff; |
||||||
|
return "" if $ulength == 0; |
||||||
|
readInBuff $ulength or die "Can't read UTF8 string $ulength"; |
||||||
|
unpack("a$ulength", $buff); |
||||||
|
} |
||||||
|
|
||||||
|
$depfile = "Makefile.dep" if (!defined($depfile)); |
||||||
|
open DEPFILE, ">$depfile"; |
||||||
|
print DEPFILE <<EOF; |
||||||
|
# This dependency file is automatically created by $0 from class files. |
||||||
|
# Do not edit. |
||||||
|
|
||||||
|
EOF |
||||||
|
foreach $clazz (@ARGV) { |
||||||
|
next if $clazz =~ (/^.*\$.*\.class/); |
||||||
|
$clazz =~ /([^\$]*)(\$.*)?\.class/ or die "not a class file"; |
||||||
|
$base = $1; |
||||||
|
my ($filename, %done); |
||||||
|
%done=(); |
||||||
|
for $filename ($clazz, glob("$base\\\$*.class")) { |
||||||
|
open FILE, $filename; |
||||||
|
binmode FILE; |
||||||
|
|
||||||
|
readInBuff 8 or die "Can't read header"; |
||||||
|
my ($magic, $major, $minor) = unpack("Nnn", $buff); |
||||||
|
|
||||||
|
die "Wrong magic $magic" if $magic != 0xcafebabe; |
||||||
|
die "Wrong major $major" if $major != 3; |
||||||
|
die "Wrong minor $minor" if $minor < 45; |
||||||
|
|
||||||
|
readInBuff 2 or die "Can't read cpool length"; |
||||||
|
|
||||||
|
|
||||||
|
my ($length) = unpack("n", $buff) & 0xffff; |
||||||
|
|
||||||
|
my $number; |
||||||
|
my @strings = (); |
||||||
|
my @clazzes; |
||||||
|
for ($number = 1; $number < $length; $number++) { |
||||||
|
|
||||||
|
readInBuff 1 or die "Can't read constant tag"; |
||||||
|
my ($tag) = unpack("C", $buff); |
||||||
|
|
||||||
|
#print STDERR "$number/$length: $tag"; |
||||||
|
tags: |
||||||
|
for ($tag) { |
||||||
|
/^1$/ && do { |
||||||
|
# UTF 8 |
||||||
|
$strings[$number] = &readUTF(); |
||||||
|
#print STDERR ": $strings[$number]"; |
||||||
|
last tags; |
||||||
|
}; |
||||||
|
|
||||||
|
/^(3|4|9|10|11|12)$/ && do { |
||||||
|
# INTEGER, FLOAT, FIELDREF, METHODREF, IFACEREF, NAMEANDTYPE |
||||||
|
readInBuff 4; |
||||||
|
last tags; |
||||||
|
}; |
||||||
|
|
||||||
|
/^(5|6)$/ && do { |
||||||
|
# LONG, DOUBLE |
||||||
|
readInBuff 8; |
||||||
|
$number++; |
||||||
|
last tags; |
||||||
|
}; |
||||||
|
|
||||||
|
/^7$/ && do { |
||||||
|
# CLASS |
||||||
|
readInBuff 2; |
||||||
|
push @clazzes, (unpack("n", $buff) & 0xffff); |
||||||
|
last tags; |
||||||
|
}; |
||||||
|
|
||||||
|
/^8$/ && do { |
||||||
|
# STRING |
||||||
|
readInBuff 2; |
||||||
|
last tags; |
||||||
|
}; |
||||||
|
die "Unknown tag: $tag, $number/$length, $filename"; |
||||||
|
} |
||||||
|
#print STDERR "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
my @deplist = (); |
||||||
|
clazz: |
||||||
|
for $c (@clazzes) { |
||||||
|
$clzz = $strings[$c]; |
||||||
|
next if $clzz =~ /^\[/; |
||||||
|
next if defined $done{"$clzz"}; |
||||||
|
$done{$clzz} = 1; |
||||||
|
|
||||||
|
my $p; |
||||||
|
for $p (split ':', $classpath) { |
||||||
|
if (-e "$p/$clzz.java") { |
||||||
|
my $path="$p/"; |
||||||
|
if (defined $dependdir) { |
||||||
|
$path = "$dependdir/"; |
||||||
|
if (defined $subdir) { |
||||||
|
my $currsubdir = "$subdir/"; |
||||||
|
while ($currsubdir =~ m<^([A-Za-z0-9]+)/+(.*)>) { |
||||||
|
$currsubdir = $2; |
||||||
|
my $firstcomp = $1; |
||||||
|
if ($clzz =~ m<$firstcomp/(.*)>) { |
||||||
|
my $remain = $1; |
||||||
|
if ($path =~ m<^(|.*/)\.\./+$>) { |
||||||
|
$path = $1; |
||||||
|
$clzz = $remain; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
push @deplist, "$path$clzz.java"; |
||||||
|
next clazz; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (@deplist) { |
||||||
|
print DEPFILE "$clazz: " . join (" ", @deplist) . "\n"; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
close DEPFILE; |
@ -0,0 +1,234 @@ |
|||||||
|
#!/usr/bin/perl -w |
||||||
|
# |
||||||
|
# jcpp Copyright (C) 1999-2001 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$ |
||||||
|
|
||||||
|
# This is a program to allow conditional compiled code in java files. |
||||||
|
# The key idea is, not to run the file always through the |
||||||
|
# preprocessor, but to modify the java files directly and make use of |
||||||
|
# comments. |
||||||
|
# |
||||||
|
# The comments all have the form /// and start at the beginning of the |
||||||
|
# line to distinguish them from normal comments. You must not use |
||||||
|
# such comments for other purposes. |
||||||
|
# |
||||||
|
# Usage is simple: jcpp -Ddefine1 -Ddefine2 first.java second.java |
||||||
|
# The files should contain comments of the form |
||||||
|
# |
||||||
|
# ///#ifdef JDK12 |
||||||
|
# jdk1.2 code |
||||||
|
# ///#else |
||||||
|
# jdk1.1 code |
||||||
|
# ///#endif |
||||||
|
# |
||||||
|
# After running jcpp the false branch is commented out. If the true |
||||||
|
# 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 %defs; |
||||||
|
|
||||||
|
for (@ARGV) { |
||||||
|
if ($_ =~ /^-D([^=]*)$/) { |
||||||
|
$defs{$1} = 1; |
||||||
|
} elsif ($_ =~ /^-D([^=]*)=([^=]*)$/) { |
||||||
|
$defs{$1} = $2; |
||||||
|
} else { |
||||||
|
push @files, $_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (@files) { |
||||||
|
# Number of nested #if directives. Initially 0, will be increased |
||||||
|
# on every #if directive and decreased on every #endif directive. |
||||||
|
my $level = 0; |
||||||
|
|
||||||
|
# The number of the outermost level, whose #if directive was |
||||||
|
# false. This is 0, if there wasn't an false #if directive, yet. |
||||||
|
# As long as it is != 0, we comment every line, and ignore |
||||||
|
# directives except for increasing/decreasing $level. |
||||||
|
my $falselevel = 0; |
||||||
|
|
||||||
|
# Tells if an error occured and the transformation shouldn't |
||||||
|
# be done. |
||||||
|
my $error = 0; |
||||||
|
my $changes = 0; |
||||||
|
|
||||||
|
# The list of #def replacements, @replold is the previous value, |
||||||
|
# @replnew the new one. |
||||||
|
my @replold = (); |
||||||
|
my @replnew = (); |
||||||
|
|
||||||
|
my $file = $_; |
||||||
|
open OLD, "<$file" or do { |
||||||
|
print STDERR "Can't open file $file\n"; |
||||||
|
next; |
||||||
|
}; |
||||||
|
open NEW, ">$file.tmp" or do { |
||||||
|
print STDERR "Can't open tmp file $file.tmp\n"; |
||||||
|
next; |
||||||
|
}; |
||||||
|
my $linenr = 0; |
||||||
|
LINE: |
||||||
|
while (<OLD>) { |
||||||
|
$linenr++; |
||||||
|
if (m'^///#') { |
||||||
|
# This is a directive. First we print it out. |
||||||
|
if (m'^///#\s*if') { |
||||||
|
$level++; |
||||||
|
if (m'^///#\s*ifdef\s+(\w+)\s*$') { |
||||||
|
# If there was an outer false #if directive, we ignore the |
||||||
|
# condition. |
||||||
|
next LINE if ($falselevel); |
||||||
|
|
||||||
|
my $label=$1; |
||||||
|
|
||||||
|
# An ifdef directive, look if -D is defined. |
||||||
|
$falselevel = $level |
||||||
|
unless (defined $defs{$label}); |
||||||
|
} elsif (m'^///#\s*ifndef\s+(\w+)\s*$') { |
||||||
|
# If there was an outer false #if directive, we ignore the |
||||||
|
# condition. |
||||||
|
next LINE if ($falselevel); |
||||||
|
|
||||||
|
my $label=$1; |
||||||
|
# An ifndef directive, look if -D is defined |
||||||
|
$falselevel = $level |
||||||
|
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*$') { |
||||||
|
# An else directive. We switch from true to false and |
||||||
|
# if level is falselevel we switch from false to true |
||||||
|
if ($level == 0) { |
||||||
|
# An else outside of any directives; warn. |
||||||
|
print STDERR "$file: $linenr: unmatched $_"; |
||||||
|
$error = 1; |
||||||
|
} elsif ($falselevel == $level) { |
||||||
|
$falselevel = 0; |
||||||
|
} elsif ($falselevel == 0) { |
||||||
|
$falselevel = $level; |
||||||
|
} |
||||||
|
} elsif (m'^///#\s*endif\s*$') { |
||||||
|
# set $falselevel to 0, if the false branch is over now. |
||||||
|
$falselevel = 0 if ($falselevel == $level); |
||||||
|
# decrease level. |
||||||
|
if ($level == 0) { |
||||||
|
print STDERR "$file: $linenr: unmatched $_"; |
||||||
|
$error = 1; |
||||||
|
} else { |
||||||
|
$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 { |
||||||
|
print STDERR "$file: $linenr: ignoring unknown directive $_"; |
||||||
|
$error = 1; |
||||||
|
} |
||||||
|
} elsif (m'^///(.*)') { |
||||||
|
$line = $1; |
||||||
|
if ($falselevel == 0 && $level > 0) { |
||||||
|
# remove comments in true branch, but not in outermost level. |
||||||
|
$_ = "$line\n"; |
||||||
|
$changes = 1; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if ($falselevel != 0) { |
||||||
|
# add comments in false branch |
||||||
|
$_ = "///$_"; |
||||||
|
$changes = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
for ($i = 0; $i < @replold; $i++) { |
||||||
|
$_ =~ s/\Q$replold[$i]\E/$replnew[$i]/ if ($replold[$i] ne ""); |
||||||
|
} |
||||||
|
print NEW $_; |
||||||
|
} |
||||||
|
|
||||||
|
if ($level != 0 || $falselevel != 0) { |
||||||
|
# something got wrong |
||||||
|
print STDERR "$file: unmatched directives: level $level, ". |
||||||
|
"falselevel $falselevel\n"; |
||||||
|
$error = 1; |
||||||
|
} |
||||||
|
|
||||||
|
if ($error == 0) { |
||||||
|
if ($changes == 0) { |
||||||
|
unlink "$file.tmp"; |
||||||
|
} else { |
||||||
|
(unlink "$file" and rename "$file.tmp", "$file") |
||||||
|
or print STDERR "$file: Couldn't rename files.\n"; |
||||||
|
} |
||||||
|
} else { |
||||||
|
print STDERR "$file: errors occured, file not transformed.\n"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
#!/usr/bin/perl -w |
||||||
|
|
||||||
|
my $num = 0; |
||||||
|
for (@ARGV) { |
||||||
|
next if $_ !~ /\.php$/; |
||||||
|
$html = $php = $_; |
||||||
|
$html =~ s/\.php$/.html/; |
||||||
|
|
||||||
|
$ENV{extension}="html"; |
||||||
|
if (! -e "$html" || (-M "$php" <= -M "$html")) { |
||||||
|
$num++; |
||||||
|
system "php -f $php >$html"; |
||||||
|
} |
||||||
|
} |
||||||
|
print $num . " html files updated.\n"; |
@ -0,0 +1,107 @@ |
|||||||
|
/* GlobalOptions 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 net.sf.jode; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.util.StringTokenizer; |
||||||
|
|
||||||
|
public class GlobalOptions { |
||||||
|
public final static String version = "@VERSION@"; |
||||||
|
public final static String email = "jochen@gnu.org"; |
||||||
|
public final static String copyright = |
||||||
|
"Jode (c) 1998-2004 Jochen Hoenicke <"+email+">"; |
||||||
|
public final static String URL = "http://jode.sourceforge.net/"; |
||||||
|
|
||||||
|
public static PrintWriter err = new PrintWriter(System.err, true); |
||||||
|
public static int verboseLevel = 0; |
||||||
|
public static int debuggingFlags = 0; |
||||||
|
|
||||||
|
public static final int DEBUG_BYTECODE = 0x001; |
||||||
|
public static final int DEBUG_VERIFIER = 0x002; |
||||||
|
public static final int DEBUG_TYPES = 0x004; |
||||||
|
public static final int DEBUG_FLOW = 0x008; |
||||||
|
public static final int DEBUG_INOUT = 0x010; |
||||||
|
public static final int DEBUG_ANALYZE = 0x020; |
||||||
|
public static final int DEBUG_LVT = 0x040; |
||||||
|
public static final int DEBUG_CHECK = 0x080; |
||||||
|
public static final int DEBUG_LOCALS = 0x100; |
||||||
|
public static final int DEBUG_CONSTRS = 0x200; |
||||||
|
public static final int DEBUG_INTERPRT = 0x400; |
||||||
|
|
||||||
|
public static final String[] debuggingNames = { |
||||||
|
"bytecode", "verifier", "types", "flow", |
||||||
|
"inout", "analyze", "lvt", "check", "locals", |
||||||
|
"constructors", "interpreter" |
||||||
|
}; |
||||||
|
|
||||||
|
public static void usageDebugging() { |
||||||
|
err.println("Debugging option: --debug=flag1,flag2,..."); |
||||||
|
err.println("possible flags:"); |
||||||
|
err.println(" bytecode " + |
||||||
|
"show bytecode, as it is read from class file."); |
||||||
|
err.println(" verifier " + |
||||||
|
"show result of bytecode verification."); |
||||||
|
err.println(" types " + |
||||||
|
"show type intersections"); |
||||||
|
err.println(" flow " + |
||||||
|
"show flow block merging."); |
||||||
|
err.println(" analyze " + |
||||||
|
"show T1/T2 analyzation of flow blocks."); |
||||||
|
err.println(" inout " + |
||||||
|
"show in/out set analysis."); |
||||||
|
err.println(" lvt " + |
||||||
|
"dump LocalVariableTable."); |
||||||
|
err.println(" check " + |
||||||
|
"do time consuming sanity checks."); |
||||||
|
err.println(" locals " + |
||||||
|
"dump local merging information."); |
||||||
|
err.println(" constructors " + |
||||||
|
"dump constructor simplification."); |
||||||
|
err.println(" interpreter " + |
||||||
|
"debug execution of interpreter."); |
||||||
|
System.exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Parse the argument given to the debugging flag. |
||||||
|
* @exception IllegalArgumentException |
||||||
|
* if a problem occured while parsing the argument. |
||||||
|
*/ |
||||||
|
public static boolean setDebugging(String debuggingString) { |
||||||
|
if (debuggingString.length() == 0 || debuggingString.equals("help")) { |
||||||
|
usageDebugging(); |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
StringTokenizer st = new StringTokenizer(debuggingString, ","); |
||||||
|
next_token: |
||||||
|
while (st.hasMoreTokens()) { |
||||||
|
String token = st.nextToken().intern(); |
||||||
|
for (int i=0; i<debuggingNames.length; i++) { |
||||||
|
if (token == debuggingNames[i]) { |
||||||
|
debuggingFlags |= 1 << i; |
||||||
|
continue next_token; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new IllegalArgumentException("Illegal debugging flag: " |
||||||
|
+token); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,409 @@ |
|||||||
|
/* BasicBlocks Copyright (C) 2000-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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
import net.sf.jode.GlobalOptions; |
||||||
|
|
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.DataOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.PrintWriter; |
||||||
|
|
||||||
|
import java.util.BitSet; |
||||||
|
import java.util.Stack; |
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.NoSuchElementException; |
||||||
|
///#enddef
|
||||||
|
///#def COLLECTIONEXTRA java.lang
|
||||||
|
import java.lang.UnsupportedOperationException; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Represents the byte code of a method in form of basic blocks. A |
||||||
|
* basic block is a bunch of instructions, that must always execute in |
||||||
|
* sequential order. Every basic block is represented by an Block |
||||||
|
* object.</p> |
||||||
|
* |
||||||
|
* <p>All jump instructions must be at the end of the block, and the |
||||||
|
* jump instructions doesn't have to remember where they jump to. |
||||||
|
* Instead this information is stored inside the blocks. See |
||||||
|
* <code>Block</code> for details.</p> |
||||||
|
* |
||||||
|
* <p>Exception Handlers are represented by the Handler class. Their |
||||||
|
* start/end range must span over some consecutive BasicBlocks and |
||||||
|
* there handler must be another basic block.</p> |
||||||
|
* |
||||||
|
* <!-- <p>Future work: A subroutine block, i.e. a block where some jsr |
||||||
|
* instructions may jump to, must store its return address in a local |
||||||
|
* variable immediately. There must be exactly one block with the |
||||||
|
* corresponding <code>opc_ret</code> instruction and all blocks that |
||||||
|
* belong to this subroutine must point to the ret block. Bytecode |
||||||
|
* that doesn't have this condition is automatically transformed on |
||||||
|
* reading.</p> --> |
||||||
|
* |
||||||
|
* <p>When the code is written to a class file, the blocks are written |
||||||
|
* in the given order. Goto and return instructions are inserted as |
||||||
|
* necessary, you don't need to care about that.</p> |
||||||
|
* |
||||||
|
* <h3>Creating new BasicBlocks</h3> |
||||||
|
* |
||||||
|
* <p>If you want to create a new BasicBlocks object, first create the |
||||||
|
* Block objects, then initialize them (you need to have all successor |
||||||
|
* blocks created for this). Afterwards create a new BasicBlock and |
||||||
|
* fill its sub blocks: </p> |
||||||
|
* |
||||||
|
* <pre> |
||||||
|
* MethodInfo myMethod = new MethodInfo("foo", "()V", PUBLIC); |
||||||
|
* Block blocks = new Block[10]; |
||||||
|
* for (int i = 0; i < 10; i++) blocks[i] = new Block(); |
||||||
|
* blocks[0].setCode(new Instruction[] {...}, |
||||||
|
* new Block[] {blocks[3], blocks[1]}); |
||||||
|
* ... |
||||||
|
* Handler[] excHandlers = new Handler[1]; |
||||||
|
* excHandlers[0] = new Handler(blocks[2], blocks[5], blocks[6], |
||||||
|
* "java.lang.NullPointerException"); |
||||||
|
* BasicBlocks bb = new BasicBlocks(myMethod); |
||||||
|
* bb.setCode(blocks, blocks[0], excHandlers); |
||||||
|
* classInfo.setMethods(new MethodInfo[] { myMethod }); |
||||||
|
* </pre> |
||||||
|
* |
||||||
|
* @see net.sf.jode.bytecode.Block |
||||||
|
* @see net.sf.jode.bytecode.Instruction |
||||||
|
*/ |
||||||
|
public class BasicBlocks extends BinaryInfo implements Opcodes { |
||||||
|
|
||||||
|
/** |
||||||
|
* The method info which contains the basic blocks. |
||||||
|
*/ |
||||||
|
private MethodInfo methodInfo; |
||||||
|
/** |
||||||
|
* The maximal number of stack entries, that may be used in this |
||||||
|
* method. |
||||||
|
*/ |
||||||
|
int maxStack; |
||||||
|
/** |
||||||
|
* The maximal number of local slots, that may be used in this |
||||||
|
* method. |
||||||
|
*/ |
||||||
|
int maxLocals; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is an array of blocks, which are arrays |
||||||
|
* of Instructions. |
||||||
|
*/ |
||||||
|
private Block[] blocks; |
||||||
|
|
||||||
|
/** |
||||||
|
* The start block. Normally the first block, but differs if method start |
||||||
|
* with a goto, e.g a while. This may be null, if this method is empty. |
||||||
|
*/ |
||||||
|
private Block startBlock; |
||||||
|
|
||||||
|
/** |
||||||
|
* The local variable infos for the method parameters. |
||||||
|
*/ |
||||||
|
private LocalVariableInfo[] paramInfos; |
||||||
|
|
||||||
|
/** |
||||||
|
* The array of exception handlers. |
||||||
|
*/ |
||||||
|
private Handler[] exceptionHandlers; |
||||||
|
|
||||||
|
public BasicBlocks(MethodInfo mi) { |
||||||
|
methodInfo = mi; |
||||||
|
int paramSize = (mi.isStatic() ? 0 : 1) |
||||||
|
+ TypeSignature.getParameterSize(mi.getType()); |
||||||
|
paramInfos = new LocalVariableInfo[paramSize]; |
||||||
|
for (int i=0; i< paramSize; i++) |
||||||
|
paramInfos[i] = LocalVariableInfo.getInfo(i); |
||||||
|
} |
||||||
|
|
||||||
|
public int getMaxStack() { |
||||||
|
return maxStack; |
||||||
|
} |
||||||
|
|
||||||
|
public int getMaxLocals() { |
||||||
|
return maxLocals; |
||||||
|
} |
||||||
|
|
||||||
|
public MethodInfo getMethodInfo() { |
||||||
|
return methodInfo; |
||||||
|
} |
||||||
|
|
||||||
|
public Block getStartBlock() { |
||||||
|
return startBlock; |
||||||
|
} |
||||||
|
|
||||||
|
public Block[] getBlocks() { |
||||||
|
return blocks; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return the exception handlers, or null if the method has no |
||||||
|
* exception handlers. |
||||||
|
*/ |
||||||
|
public Handler[] getExceptionHandlers() { |
||||||
|
return exceptionHandlers; |
||||||
|
} |
||||||
|
|
||||||
|
public LocalVariableInfo getParamInfo(int i) { |
||||||
|
return paramInfos[i]; |
||||||
|
} |
||||||
|
|
||||||
|
public int getParamCount() { |
||||||
|
return paramInfos.length; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Updates the maxStack and maxLocals according to the current code. |
||||||
|
* Call this every time you change the code. |
||||||
|
*/ |
||||||
|
public void updateMaxStackLocals() { |
||||||
|
maxLocals = getParamCount(); |
||||||
|
maxStack = 0; |
||||||
|
|
||||||
|
if (startBlock == null) |
||||||
|
return; |
||||||
|
|
||||||
|
BitSet visited = new BitSet(); |
||||||
|
Stack todo = new Stack(); |
||||||
|
int[] poppush = new int[2]; |
||||||
|
|
||||||
|
startBlock.stackHeight = 0; |
||||||
|
todo.push(startBlock); |
||||||
|
while (!todo.isEmpty()) { |
||||||
|
Block block = (Block) todo.pop(); |
||||||
|
int stackHeight = block.stackHeight; |
||||||
|
if (stackHeight + block.maxpush > maxStack) |
||||||
|
maxStack = stackHeight + block.maxpush; |
||||||
|
stackHeight += block.delta; |
||||||
|
|
||||||
|
Block[] succs = block.getSuccs(); |
||||||
|
Instruction[] instr = block.getInstructions(); |
||||||
|
for (int i = 0; i < instr.length; i++) { |
||||||
|
if (instr[i].hasLocal()) { |
||||||
|
int slotlimit = instr[i].getLocalSlot() + 1; |
||||||
|
int opcode = instr[i].getOpcode(); |
||||||
|
if (opcode == opc_lstore || opcode == opc_dstore |
||||||
|
|| opcode == opc_lload || opcode == opc_dload) |
||||||
|
slotlimit++; |
||||||
|
if (slotlimit > maxLocals) |
||||||
|
maxLocals = slotlimit; |
||||||
|
} |
||||||
|
} |
||||||
|
if (instr.length > 0 |
||||||
|
&& instr[instr.length-1].getOpcode() == opc_jsr) { |
||||||
|
if (!visited.get(succs[0].blockNr)) { |
||||||
|
succs[0].stackHeight = stackHeight + 1; |
||||||
|
todo.push(succs[0]); |
||||||
|
visited.set(succs[0].blockNr); |
||||||
|
} else if (succs[0].stackHeight != stackHeight + 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Block has two different stack heights."); |
||||||
|
|
||||||
|
if (succs[1] != null && !visited.get(succs[1].blockNr)) { |
||||||
|
succs[1].stackHeight = stackHeight; |
||||||
|
todo.push(succs[1]); |
||||||
|
visited.set(succs[1].blockNr); |
||||||
|
} else if ((succs[1] == null ? 0 : succs[1].stackHeight) |
||||||
|
!= stackHeight) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Block has two different stack heights."); |
||||||
|
} else { |
||||||
|
for (int i = 0; i < succs.length; i++) { |
||||||
|
if (succs[i] != null && !visited.get(succs[i].blockNr)) { |
||||||
|
succs[i].stackHeight = stackHeight; |
||||||
|
todo.push(succs[i]); |
||||||
|
visited.set(succs[i].blockNr); |
||||||
|
} else if ((succs[i] == null ? 0 : succs[i].stackHeight) |
||||||
|
!= stackHeight) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Block has two different stack heights."); |
||||||
|
} |
||||||
|
} |
||||||
|
Handler[] handler = block.getHandlers(); |
||||||
|
for (int i = 0; i < handler.length; i++) { |
||||||
|
if (!visited.get(handler[i].getCatcher().blockNr)) { |
||||||
|
handler[i].getCatcher().stackHeight = 1; |
||||||
|
todo.push(handler[i].getCatcher()); |
||||||
|
visited.set(handler[i].getCatcher().blockNr); |
||||||
|
} else if (handler[i].getCatcher().stackHeight != 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Block has two different stack heights."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setBlocks(Block[] blocks, Block startBlock, |
||||||
|
Handler[] handlers) { |
||||||
|
this.blocks = blocks; |
||||||
|
this.startBlock = startBlock; |
||||||
|
|
||||||
|
exceptionHandlers = handlers.length == 0 ? Handler.EMPTY : handlers; |
||||||
|
ArrayList activeHandlers = new ArrayList(); |
||||||
|
for (int i = 0; i < blocks.length; i++) { |
||||||
|
blocks[i].blockNr = i; |
||||||
|
for (int j = 0; j < handlers.length; j++) { |
||||||
|
if (handlers[j].getStart() == blocks[i]) |
||||||
|
activeHandlers.add(handlers[j]); |
||||||
|
} |
||||||
|
if (activeHandlers.size() == 0) |
||||||
|
blocks[i].catchers = Handler.EMPTY; |
||||||
|
else |
||||||
|
blocks[i].catchers = |
||||||
|
(Handler[]) activeHandlers.toArray(Handler.EMPTY); |
||||||
|
for (int j = 0; j < handlers.length; j++) { |
||||||
|
if (handlers[j].getEnd() == blocks[i]) |
||||||
|
activeHandlers.remove(handlers[j]); |
||||||
|
} |
||||||
|
} |
||||||
|
/* Check if all successor blocks are in this basic block */ |
||||||
|
for (int i = 0; i < blocks.length; i++) { |
||||||
|
Block[] succs = blocks[i].getSuccs(); |
||||||
|
for (int j = 0; j < succs.length; j++) { |
||||||
|
if (succs[j] != null |
||||||
|
&& succs[j] != blocks[succs[j].blockNr]) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Succ " + j + " of block " + i |
||||||
|
+ " not in basicblocks"); |
||||||
|
} |
||||||
|
} |
||||||
|
updateMaxStackLocals(); |
||||||
|
// TransformSubroutine.createSubroutineInfo(this);
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the name and type of a method parameter. This overwrites |
||||||
|
* any previously set parameter info for this slot. |
||||||
|
* @param info a local variable info mapping a slot nr to a name |
||||||
|
* and a type. |
||||||
|
*/ |
||||||
|
public void setParamInfo(LocalVariableInfo info) { |
||||||
|
paramInfos[info.getSlot()] = info; |
||||||
|
} |
||||||
|
|
||||||
|
private BasicBlockReader reader; |
||||||
|
void read(ConstantPool cp, |
||||||
|
DataInputStream input, |
||||||
|
int howMuch) throws IOException { |
||||||
|
if ((GlobalOptions.debuggingFlags |
||||||
|
& GlobalOptions.DEBUG_BYTECODE) != 0) |
||||||
|
GlobalOptions.err.println("Reading "+methodInfo); |
||||||
|
reader = new BasicBlockReader(this); |
||||||
|
reader.readCode(cp, input); |
||||||
|
readAttributes(cp, input, howMuch); |
||||||
|
reader.convert(); |
||||||
|
reader = null; |
||||||
|
if ((GlobalOptions.debuggingFlags |
||||||
|
& GlobalOptions.DEBUG_BYTECODE) != 0) |
||||||
|
dumpCode(GlobalOptions.err); |
||||||
|
} |
||||||
|
|
||||||
|
protected void readAttribute(String name, int length, ConstantPool cp, |
||||||
|
DataInputStream input, |
||||||
|
int howMuch) throws IOException { |
||||||
|
if (howMuch >= ClassInfo.ALMOSTALL |
||||||
|
&& name.equals("LocalVariableTable")) { |
||||||
|
reader.readLVT(length, cp, input); |
||||||
|
} else if (howMuch >= ClassInfo.ALMOSTALL |
||||||
|
&& name.equals("LineNumberTable")) { |
||||||
|
reader.readLNT(length, cp, input); |
||||||
|
} else |
||||||
|
super.readAttribute(name, length, cp, input, howMuch); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void reserveSmallConstants(GrowableConstantPool gcp) { |
||||||
|
for (int i=0; i < blocks.length; i++) { |
||||||
|
next_instr: |
||||||
|
for (Iterator iter |
||||||
|
= Arrays.asList(blocks[i].getInstructions()).iterator(); |
||||||
|
iter.hasNext(); ) { |
||||||
|
Instruction instr = (Instruction) iter.next(); |
||||||
|
if (instr.getOpcode() == Opcodes.opc_ldc) { |
||||||
|
Object constant = instr.getConstant(); |
||||||
|
if (constant == null) |
||||||
|
continue next_instr; |
||||||
|
for (int j=1; j < Opcodes.constants.length; j++) { |
||||||
|
if (constant.equals(Opcodes.constants[j])) |
||||||
|
continue next_instr; |
||||||
|
} |
||||||
|
if (constant instanceof Integer) { |
||||||
|
int value = ((Integer) constant).intValue(); |
||||||
|
if (value >= Short.MIN_VALUE |
||||||
|
&& value <= Short.MAX_VALUE) |
||||||
|
continue next_instr; |
||||||
|
} |
||||||
|
gcp.reserveConstant(constant); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
BasicBlockWriter bbw; |
||||||
|
void prepareWriting(GrowableConstantPool gcp) { |
||||||
|
bbw = new BasicBlockWriter(this, gcp); |
||||||
|
prepareAttributes(gcp); |
||||||
|
} |
||||||
|
|
||||||
|
protected int getAttributeCount() { |
||||||
|
return super.getAttributeCount() + bbw.getAttributeCount(); |
||||||
|
} |
||||||
|
|
||||||
|
protected void writeAttributes(GrowableConstantPool gcp, |
||||||
|
DataOutputStream output) |
||||||
|
throws IOException { |
||||||
|
super.writeAttributes(gcp, output); |
||||||
|
bbw.writeAttributes(gcp, output); |
||||||
|
} |
||||||
|
|
||||||
|
void write(GrowableConstantPool gcp, |
||||||
|
DataOutputStream output) throws IOException { |
||||||
|
output.writeInt(bbw.getSize() + getAttributeSize()); |
||||||
|
bbw.write(gcp, output); |
||||||
|
writeAttributes(gcp, output); |
||||||
|
bbw = null; |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpCode(PrintWriter output) { |
||||||
|
output.println(methodInfo.getName()+methodInfo.getType()+":"); |
||||||
|
if (startBlock == null) |
||||||
|
output.println("\treturn"); |
||||||
|
else if (startBlock != blocks[0]) |
||||||
|
output.println("\tgoto "+startBlock); |
||||||
|
|
||||||
|
for (int i=0; i< blocks.length; i++) { |
||||||
|
blocks[i].dumpCode(output); |
||||||
|
} |
||||||
|
for (int i=0; i< exceptionHandlers.length; i++) { |
||||||
|
output.println("catch " + exceptionHandlers[i].type |
||||||
|
+ " from " + exceptionHandlers[i].start |
||||||
|
+ " to " + exceptionHandlers[i].end |
||||||
|
+ " catcher " + exceptionHandlers[i].catcher); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "BasicBlocks["+methodInfo+"]"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,405 @@ |
|||||||
|
/* 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 net.sf.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 net.sf.jode.util.SimpleMap; |
||||||
|
|
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Map; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Iterator; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Represents a container for user specified attributes.</p> |
||||||
|
* |
||||||
|
* <p>Java bytecode is extensible: Classes, Methods and Fields may |
||||||
|
* have any number of attributes. Every attribute has a name and some |
||||||
|
* unformatted data.</p> |
||||||
|
* |
||||||
|
* <p>There are some predefined attributes, even the Code of a Method |
||||||
|
* is an attribute. These predefined attributes are all handled by |
||||||
|
* this package as appropriate. These methods are only useful for non |
||||||
|
* standard attributes.</p> |
||||||
|
* |
||||||
|
* <p>You can provide new attributes by overriding the protected |
||||||
|
* methods of this class. This makes it possible to use constant pool |
||||||
|
* entries in the attributes.</p> |
||||||
|
* |
||||||
|
* <p>Another possibility is to add the attributes with the public |
||||||
|
* method. This way you don't need to extend the classes, but you |
||||||
|
* can't use a constant pool for the contents of the attributes. One |
||||||
|
* possible application of this are installation classes. These |
||||||
|
* classes have a special attribute containing a zip archive of the |
||||||
|
* files that should be installed. There are other possible uses, |
||||||
|
* e.g. putting native machine code for some architectures into the |
||||||
|
* class.</p> |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class BinaryInfo { |
||||||
|
/** |
||||||
|
* The bit mask representing public modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_PUBLIC = 0x0001; |
||||||
|
/** |
||||||
|
* The bit mask representing private modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_PRIVATE = 0x0002; |
||||||
|
/** |
||||||
|
* The bit mask representing protected modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_PROTECTED = 0x0004; |
||||||
|
/** |
||||||
|
* The bit mask representing static modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_STATIC = 0x0008; |
||||||
|
/** |
||||||
|
* The bit mask representing final modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_FINAL = 0x0010; |
||||||
|
/** |
||||||
|
* The bit mask representing the ACC_SUPER modifier for classes. |
||||||
|
* This is a special modifier that only has historic meaning. Every |
||||||
|
* class should have this set. |
||||||
|
*/ |
||||||
|
public static int ACC_SUPER = 0x0020; |
||||||
|
/** |
||||||
|
* The bit mask representing volatile modifier for fields. |
||||||
|
*/ |
||||||
|
public static int ACC_VOLATILE = 0x0040; |
||||||
|
/** |
||||||
|
* The bit mask representing synthetic bridge method. This is |
||||||
|
* used when a non-generic method overrides a generic method of |
||||||
|
* super class/interface. |
||||||
|
*/ |
||||||
|
public static int ACC_BRIDGE = 0x0040; |
||||||
|
/** |
||||||
|
* The bit mask representing transient fields. |
||||||
|
*/ |
||||||
|
public static int ACC_TRANSIENT = 0x0080; |
||||||
|
/** |
||||||
|
* The bit mask representing varargs methods. |
||||||
|
*/ |
||||||
|
public static int ACC_VARARGS = 0x0080; |
||||||
|
/** |
||||||
|
* The bit mask representing enumeration fields. |
||||||
|
*/ |
||||||
|
public static int ACC_ENUM = 0x0100; |
||||||
|
/** |
||||||
|
* The bit mask representing native methods. |
||||||
|
*/ |
||||||
|
public static int ACC_NATIVE = 0x0100; |
||||||
|
/** |
||||||
|
* The bit mask representing interfaces. |
||||||
|
*/ |
||||||
|
public static int ACC_INTERFACE = 0x0200; |
||||||
|
/** |
||||||
|
* The bit mask representing abstract modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_ABSTRACT = 0x0400; |
||||||
|
/** |
||||||
|
* The bit mask representing annotation classes. |
||||||
|
*/ |
||||||
|
public static int ACC_ANNOTATION = 0x0800; |
||||||
|
/** |
||||||
|
* The bit mask representing strictfp modifier. |
||||||
|
*/ |
||||||
|
public static int ACC_STRICT = 0x0800; |
||||||
|
/** |
||||||
|
* The bit mask representing synthetic fields/methods and classes. |
||||||
|
*/ |
||||||
|
public static int ACC_SYNTHETIC = 0x1000; |
||||||
|
|
||||||
|
private Map unknownAttributes = null; |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reads in an attributes of this class. Overwrite this method if |
||||||
|
* you want to handle your own attributes. If you don't know how |
||||||
|
* to handle an attribute call this method for the super class. |
||||||
|
* @param name the attribute name. |
||||||
|
* @param length the length of the attribute. |
||||||
|
* @param constantPool the constant pool of the class. |
||||||
|
* @param input a data input stream where you can read the attribute |
||||||
|
* from. It will protect you to read more over the attribute boundary. |
||||||
|
* @param howMuch the constant that was given to the {@link |
||||||
|
* ClassInfo#load} function when loading this class. |
||||||
|
*/ |
||||||
|
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 >= ClassInfo.ALL) { |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Drops information from this info. Override this to drop your |
||||||
|
* own info and don't forget to call the method of the super class. |
||||||
|
* @param keep the constant representing how much information we |
||||||
|
* should keep (see {@link ClassInfo#load}). |
||||||
|
*/ |
||||||
|
protected void drop(int keep) { |
||||||
|
if (keep < ClassInfo.ALL) |
||||||
|
unknownAttributes = null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of attributes of this class. Overwrite this |
||||||
|
* method if you want to add your own attributes by providing a |
||||||
|
* writeAttributes method. You should call this method for the |
||||||
|
* super class and add the number of your own attributes to the |
||||||
|
* returned value. |
||||||
|
* @return the number of attributes of this class. |
||||||
|
*/ |
||||||
|
protected int getAttributeCount() { |
||||||
|
return unknownAttributes != null ? unknownAttributes.size() : 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Prepare writing your attributes. Overwrite this method if you |
||||||
|
* want to add your own attributes, which need constants on the |
||||||
|
* class pool. Add the necessary constants to the constant pool |
||||||
|
* and call this method for the super class. |
||||||
|
* @param gcp The growable constant pool. |
||||||
|
*/ |
||||||
|
protected void prepareAttributes(GrowableConstantPool gcp) { |
||||||
|
if (unknownAttributes == null) |
||||||
|
return; |
||||||
|
Iterator i = unknownAttributes.keySet().iterator(); |
||||||
|
while (i.hasNext()) |
||||||
|
gcp.putUTF8((String) i.next()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Writes the attributes to the output stream. |
||||||
|
* Overwrite this method if you want to add your own attributes. |
||||||
|
* All constants you need from the growable constant pool must |
||||||
|
* have been previously registered by the {@link #prepareAttributes} |
||||||
|
* method. This method must not add new constants to the pool</p> |
||||||
|
* |
||||||
|
* First call the method of the super class. Afterwrites write |
||||||
|
* each of your own attributes including the attribute header |
||||||
|
* (name and length entry). |
||||||
|
* |
||||||
|
* @param constantPool The growable constant pool, which is not |
||||||
|
* growable anymore (see above). |
||||||
|
* @param output the data output stream. You must write exactly |
||||||
|
* as many bytes to it as you have told with the {@link |
||||||
|
* #getAttributeSize} method. |
||||||
|
*/ |
||||||
|
protected void writeAttributes |
||||||
|
(GrowableConstantPool constantPool, |
||||||
|
DataOutputStream output) throws IOException { |
||||||
|
int count = getAttributeCount(); |
||||||
|
output.writeShort(count); |
||||||
|
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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the total length of all attributes in this binary info. |
||||||
|
* Overwrite this method if you want to add your own attributes |
||||||
|
* and add the size of your attributes to the value returned by |
||||||
|
* the super class.<br> |
||||||
|
* |
||||||
|
* Currently you only need to write this if you extend |
||||||
|
* BasicBlocks. |
||||||
|
* |
||||||
|
* @return the total length of all attributes, including their |
||||||
|
* headers and the "number of attributes" field. |
||||||
|
*/ |
||||||
|
protected 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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Finds a non standard attribute with the given name. You don't |
||||||
|
* have access to the constant pool. If you need the pool don't |
||||||
|
* use this method but extend this class and override |
||||||
|
* readAttribute method. |
||||||
|
* @param name the name of the attribute. |
||||||
|
* @return the contents of the attribute, null if not found. |
||||||
|
* @see #readAttribute |
||||||
|
*/ |
||||||
|
public byte[] findAttribute(String name) { |
||||||
|
if (unknownAttributes != null) |
||||||
|
return (byte[]) unknownAttributes.get(name); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets all non standard attributes. |
||||||
|
* @return an iterator for all attributes. The values returned by |
||||||
|
* the next() method of the iterator are of Map.Entry type. The |
||||||
|
* key of the entry is the name of the attribute, while the values |
||||||
|
* are the byte[] contents. |
||||||
|
* @see #findAttribute |
||||||
|
*/ |
||||||
|
public Iterator getAttributes() { |
||||||
|
if (unknownAttributes != null) |
||||||
|
return unknownAttributes.entrySet().iterator(); |
||||||
|
return Collections.EMPTY_SET.iterator(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a new non standard attribute or replaces an old one with |
||||||
|
* the same name. If it already exists, it will be overwritten. |
||||||
|
* Note that there's now way to correlate the contents with a |
||||||
|
* constant pool. If you need that extend this class and override |
||||||
|
* the methods {@link #getAttributeCount}, {@link |
||||||
|
* #prepareAttributes}, {@link #writeAttributes}, and {@link |
||||||
|
* #getAttributeSize}. |
||||||
|
* @param name the name of the attribute. |
||||||
|
* @param contents the new contens. |
||||||
|
*/ |
||||||
|
public void addAttribute(String name, byte[] contents) { |
||||||
|
if (unknownAttributes == null) |
||||||
|
unknownAttributes = new SimpleMap(); |
||||||
|
unknownAttributes.put(name, contents); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes a non standard attributes. |
||||||
|
* @param name the name of the attribute. |
||||||
|
* @return the old contents of the attribute. |
||||||
|
*/ |
||||||
|
public byte[] removeAttribute(String name) { |
||||||
|
if (unknownAttributes != null) |
||||||
|
return (byte[]) unknownAttributes.remove(name); |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all non standard attributes. |
||||||
|
*/ |
||||||
|
public void removeAllAttributes() { |
||||||
|
unknownAttributes = null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,296 @@ |
|||||||
|
/* Block Copyright (C) 2000-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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
import java.io.PrintWriter; |
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Iterator; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p>Represents a single basic block. It contains a list of |
||||||
|
* instructions and the successor blocks.</p> |
||||||
|
* |
||||||
|
* <p>All jump instructions must be at the end of the block. These |
||||||
|
* jump instructions are <code>opc_lookupswitch</code>, |
||||||
|
* <code>opc_if</code>xxx, <code>opc_jsr</code>, <code>opc_ret</code>, |
||||||
|
* <code>opc_</code>x<code>return</code> and <code>opc_return</code>. |
||||||
|
* An <code>opc_goto</code> is implicit if the basic block doesn't end |
||||||
|
* with a jump instructions, or if it ends with an conditional jump or |
||||||
|
* jsr.</p> |
||||||
|
* |
||||||
|
* <p>The jump instructions don't remember their destinations, instead |
||||||
|
* the Block does it. This are the successor block. There are |
||||||
|
* several cases:</p> |
||||||
|
* |
||||||
|
* <ul> |
||||||
|
* <li>Block ends with <code>opc_lookupswitch</code> with |
||||||
|
* <code>n</code> values. Then there must be <code>n+1</code> |
||||||
|
* successors where the first <code>n</code> successors correspond to |
||||||
|
* the values and the last successor is the default successor.</li> |
||||||
|
* <li>Block ends with <code>opc_if</code>xxx, then there must be two |
||||||
|
* successors: The first one is the successor if the condition evaluates |
||||||
|
* to true, the second one is for the false branch. </li> |
||||||
|
* <li>Block ends with <code>opc_jsr</code>, then there must be two |
||||||
|
* successors: The first one is the subroutine, the second is the next |
||||||
|
* block after the subroutine. </li> |
||||||
|
* <li>Block ends with <code>opc_</code>x</code>return</code> or |
||||||
|
* <code>opc_ret</code>, then there must no successor at all. </li> |
||||||
|
* <li>In any other case there must be exactly one successor.</li> |
||||||
|
* </ul> |
||||||
|
* |
||||||
|
* <p>If any successor is <code>null</code> it represents end of |
||||||
|
* method, i.e. a return instruction. You can also use |
||||||
|
* <code>null</code> successors for conditional jumps and switch |
||||||
|
* instruction. You normally shouldn't use <code>opc_return</code> |
||||||
|
* instructions. They are only necessary, if you want to return with |
||||||
|
* a non-empty stack. </p> |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @see net.sf.jode.bytecode.BasicBlocks |
||||||
|
* @see net.sf.jode.bytecode.Instruction |
||||||
|
*/ |
||||||
|
public final class Block { |
||||||
|
/** |
||||||
|
* The opcodes of the instructions in this block. |
||||||
|
*/ |
||||||
|
private Instruction[] instrs; |
||||||
|
|
||||||
|
/** |
||||||
|
* The blockNr of successor blocks |
||||||
|
*/ |
||||||
|
private Block[] succs; |
||||||
|
|
||||||
|
/** |
||||||
|
* The catching blocks. Set by BasicBlocks. |
||||||
|
*/ |
||||||
|
Handler[] catchers; |
||||||
|
|
||||||
|
/** |
||||||
|
* The blockNr of this block. Set by BasicBlocks. |
||||||
|
*/ |
||||||
|
int blockNr; |
||||||
|
|
||||||
|
/** |
||||||
|
* The number of items this block takes from the stack with |
||||||
|
* respect to the stack items at the beginning of the block. |
||||||
|
*/ |
||||||
|
int maxpop; |
||||||
|
/** |
||||||
|
* The maximum number of items the stack may grow. |
||||||
|
*/ |
||||||
|
int maxpush; |
||||||
|
/** |
||||||
|
* The difference stack items after the block minus stack items |
||||||
|
* before block. |
||||||
|
*/ |
||||||
|
int delta; |
||||||
|
/** |
||||||
|
* The stack height at the beginning of this block. |
||||||
|
* Only valid after the block was inserted in a BasicBlocks and |
||||||
|
* the updateMaxStackLocals() of BasicBlocks was called. |
||||||
|
*/ |
||||||
|
int stackHeight; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new block uninitialized block. You mustn't really |
||||||
|
* use it (except as successor for other blocks) until you have |
||||||
|
* set the code. |
||||||
|
*/ |
||||||
|
public Block() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the list of instructions. The returned list should not be |
||||||
|
* modified, except that the instructions (but not their opcodes) |
||||||
|
* may be modified. |
||||||
|
*/ |
||||||
|
public Instruction[] getInstructions() { |
||||||
|
return instrs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the successor array. The last successor is the next basic |
||||||
|
* block that is jumped to via goto or the default part of a |
||||||
|
* switch. For conditional jumps and jsrs the second successor gives |
||||||
|
* the destination. |
||||||
|
*/ |
||||||
|
public Block[] getSuccs() { |
||||||
|
return succs; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the exception handlers whose try region contains this |
||||||
|
* block. You can't set them since they are calculated |
||||||
|
* automatically. |
||||||
|
* @return the exception handlers. |
||||||
|
* @see BasicBlocks#setBlocks |
||||||
|
*/ |
||||||
|
public Handler[] getHandlers() { |
||||||
|
return catchers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the block number. The block numbers are consecutive number |
||||||
|
* from 0 to the number of blocks in a method. The equation |
||||||
|
* <pre> BasicBlocks.getBlock()[i].getBlockNr() == i </pre> |
||||||
|
* always holds (as long as you don't do something dirty, like adding |
||||||
|
* the same block to different BasicBlocks, or to the same but more |
||||||
|
* than once). |
||||||
|
* @return the block number. |
||||||
|
*/ |
||||||
|
public int getBlockNr() { |
||||||
|
return blockNr; |
||||||
|
} |
||||||
|
|
||||||
|
private void initCode() { |
||||||
|
int size = instrs.length; |
||||||
|
maxpop = maxpush = 0; |
||||||
|
int depth = 0; |
||||||
|
int poppush[] = new int[2]; |
||||||
|
boolean needGoto = true; |
||||||
|
for (int i = 0; i < size; i++) { |
||||||
|
instrs[i].getStackPopPush(poppush); |
||||||
|
depth -= poppush[0]; |
||||||
|
if (maxpop < -depth) |
||||||
|
maxpop = -depth; |
||||||
|
depth += poppush[1]; |
||||||
|
if (maxpush < depth) |
||||||
|
maxpush = depth; |
||||||
|
|
||||||
|
int opcode = instrs[i].getOpcode(); |
||||||
|
switch (opcode) { |
||||||
|
case Opcodes.opc_goto: |
||||||
|
throw new IllegalArgumentException("goto in block"); |
||||||
|
|
||||||
|
case Opcodes.opc_lookupswitch: |
||||||
|
if (succs.length != instrs[i].getValues().length + 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("number of successors for switch doesn't match"); |
||||||
|
if (i != size - 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("switch in the middle!"); |
||||||
|
needGoto = false; |
||||||
|
break; |
||||||
|
|
||||||
|
case Opcodes.opc_ret: case Opcodes.opc_athrow: |
||||||
|
case Opcodes.opc_ireturn: case Opcodes.opc_lreturn: |
||||||
|
case Opcodes.opc_freturn: case Opcodes.opc_dreturn: |
||||||
|
case Opcodes.opc_areturn: case Opcodes.opc_return: |
||||||
|
if (succs.length != 0) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("throw or return with successor."); |
||||||
|
if (i != size - 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("return in the middle!"); |
||||||
|
needGoto = false; |
||||||
|
break; |
||||||
|
|
||||||
|
case Opcodes.opc_ifeq: case Opcodes.opc_ifne: |
||||||
|
case Opcodes.opc_iflt: case Opcodes.opc_ifge: |
||||||
|
case Opcodes.opc_ifgt: case Opcodes.opc_ifle: |
||||||
|
case Opcodes.opc_if_icmpeq: case Opcodes.opc_if_icmpne: |
||||||
|
case Opcodes.opc_if_icmplt: case Opcodes.opc_if_icmpge: |
||||||
|
case Opcodes.opc_if_icmpgt: case Opcodes.opc_if_icmple: |
||||||
|
case Opcodes.opc_if_acmpeq: case Opcodes.opc_if_acmpne: |
||||||
|
case Opcodes.opc_ifnull: case Opcodes.opc_ifnonnull: |
||||||
|
case Opcodes.opc_jsr: |
||||||
|
if (succs.length != 2) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("successors inappropriate for if/jsr"); |
||||||
|
if (succs[0] == null && opcode == Opcodes.opc_jsr) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("null successors inappropriate for jsr"); |
||||||
|
if (i != size - 1) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("if/jsr in the middle!"); |
||||||
|
needGoto = false; |
||||||
|
} |
||||||
|
} |
||||||
|
delta = depth; |
||||||
|
if (needGoto && succs.length != 1) |
||||||
|
throw new IllegalArgumentException("no single successor block"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the stack height at the beginning of the block. This |
||||||
|
* is automatically calculated, when the block is inserted in a |
||||||
|
* basic block. |
||||||
|
*/ |
||||||
|
public int getStackHeight () { |
||||||
|
return stackHeight; |
||||||
|
} |
||||||
|
|
||||||
|
public void getStackPopPush (int[] poppush) { |
||||||
|
poppush[0] = maxpop; |
||||||
|
poppush[1] = delta + maxpop; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the code, i.e. instructions and successor blocks. |
||||||
|
* The instructions must be valid and match the successors. |
||||||
|
*/ |
||||||
|
public void setCode(Instruction[] instrs, Block[] succs) { |
||||||
|
this.instrs = instrs; |
||||||
|
this.succs = succs; |
||||||
|
initCode(); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpCode(PrintWriter output) { |
||||||
|
output.println(" "+this+":"); |
||||||
|
for (int i = 0; i < instrs.length; i++) { |
||||||
|
Instruction instr = instrs[i]; |
||||||
|
if (i == instrs.length - 1 && succs != null) { |
||||||
|
int opcode = instr.getOpcode(); |
||||||
|
if (opcode == Opcodes.opc_lookupswitch) { |
||||||
|
// Special case for switch:
|
||||||
|
output.println("\tswitch"); |
||||||
|
int[] values = instr.getValues(); |
||||||
|
for (int j = 0; j < values.length; j++) |
||||||
|
output.println("\t case"+values[j] |
||||||
|
+": goto "+succs[j]); |
||||||
|
output.println("\t default: goto"+ |
||||||
|
succs[values.length]); |
||||||
|
return; |
||||||
|
} else if (succs.length > 1) { |
||||||
|
output.println("\t"+instr.getDescription() |
||||||
|
+" "+succs[0]); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
output.println("\t"+instr.getDescription()); |
||||||
|
|
||||||
|
} |
||||||
|
if (succs != null && succs.length > 0) { |
||||||
|
if (succs[succs.length-1] == null) |
||||||
|
output.println("\treturn"); |
||||||
|
else |
||||||
|
output.println("\tgoto "+succs[succs.length-1]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "Block_"+blockNr; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
/* ClassFormatException 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* Thrown when a class file with an unknown or illegal format is loaded. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
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) { |
||||||
|
super(detail); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new class format exception. |
||||||
|
*/ |
||||||
|
public ClassFormatException() { |
||||||
|
super(); |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,53 @@ |
|||||||
|
/* ConstantInstruction 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 net.sf.jode.bytecode; |
||||||
|
import net.sf.jode.util.StringQuoter; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction in the byte code. |
||||||
|
* |
||||||
|
*/ |
||||||
|
class ConstantInstruction extends Instruction { |
||||||
|
/** |
||||||
|
* The typesignature of the class/array. |
||||||
|
*/ |
||||||
|
private Object constant; |
||||||
|
|
||||||
|
ConstantInstruction(int opcode, Object constant) { |
||||||
|
super(opcode); |
||||||
|
this.constant = constant; |
||||||
|
} |
||||||
|
|
||||||
|
public final Object getConstant() |
||||||
|
{ |
||||||
|
return constant; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setConstant(Object constant) |
||||||
|
{ |
||||||
|
this.constant = constant; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return super.toString() + ' ' + |
||||||
|
(constant instanceof String |
||||||
|
? StringQuoter.quote((String) constant) : constant); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,280 @@ |
|||||||
|
/* ConstantPool 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 net.sf.jode.bytecode; |
||||||
|
import java.io.DataInputStream; |
||||||
|
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 |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
public class ConstantPool { |
||||||
|
public final static int CLASS = 7; |
||||||
|
public final static int FIELDREF = 9; |
||||||
|
public final static int METHODREF = 10; |
||||||
|
public final static int INTERFACEMETHODREF = 11; |
||||||
|
public final static int STRING = 8; |
||||||
|
public final static int INTEGER = 3; |
||||||
|
public final static int FLOAT = 4; |
||||||
|
public final static int LONG = 5; |
||||||
|
public final static int DOUBLE = 6; |
||||||
|
public final static int NAMEANDTYPE = 12; |
||||||
|
public final static int UTF8 = 1; |
||||||
|
|
||||||
|
int count; |
||||||
|
int[] tags; |
||||||
|
int[] indices1, indices2; |
||||||
|
|
||||||
|
Object[] constants; |
||||||
|
|
||||||
|
public ConstantPool () { |
||||||
|
} |
||||||
|
|
||||||
|
public void read(DataInputStream stream) |
||||||
|
throws IOException { |
||||||
|
count = stream.readUnsignedShort(); |
||||||
|
tags = new int[count]; |
||||||
|
indices1 = new int[count]; |
||||||
|
indices2 = new int[count]; |
||||||
|
constants = new Object[count]; |
||||||
|
|
||||||
|
for (int i=1; i< count; i++) { |
||||||
|
int tag = stream.readUnsignedByte(); |
||||||
|
tags[i] = tag; |
||||||
|
switch (tag) { |
||||||
|
case CLASS: |
||||||
|
indices1[i] = stream.readUnsignedShort(); |
||||||
|
break; |
||||||
|
case FIELDREF: |
||||||
|
case METHODREF: |
||||||
|
case INTERFACEMETHODREF: |
||||||
|
indices1[i] = stream.readUnsignedShort(); |
||||||
|
indices2[i] = stream.readUnsignedShort(); |
||||||
|
break; |
||||||
|
case STRING: |
||||||
|
indices1[i] = stream.readUnsignedShort(); |
||||||
|
break; |
||||||
|
case INTEGER: |
||||||
|
constants[i] = new Integer(stream.readInt()); |
||||||
|
break; |
||||||
|
case FLOAT: |
||||||
|
constants[i] = new Float(stream.readFloat()); |
||||||
|
break; |
||||||
|
case LONG: |
||||||
|
constants[i] = new Long(stream.readLong()); |
||||||
|
tags[++i] = -LONG; |
||||||
|
break; |
||||||
|
case DOUBLE: |
||||||
|
constants[i] = new Double(stream.readDouble()); |
||||||
|
tags[++i] = -DOUBLE; |
||||||
|
break; |
||||||
|
case NAMEANDTYPE: |
||||||
|
indices1[i] = stream.readUnsignedShort(); |
||||||
|
indices2[i] = stream.readUnsignedShort(); |
||||||
|
break; |
||||||
|
case UTF8: |
||||||
|
constants[i] = stream.readUTF().intern(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new ClassFormatException("unknown constant tag"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int getTag(int i) throws ClassFormatException { |
||||||
|
if (i == 0) |
||||||
|
throw new ClassFormatException("null tag"); |
||||||
|
return tags[i]; |
||||||
|
} |
||||||
|
|
||||||
|
public String getUTF8(int i) throws ClassFormatException { |
||||||
|
if (tags[i] != UTF8) |
||||||
|
throw new ClassFormatException("Tag mismatch"); |
||||||
|
return (String)constants[i]; |
||||||
|
} |
||||||
|
|
||||||
|
public Reference getRef(int i) throws ClassFormatException { |
||||||
|
if (tags[i] != FIELDREF |
||||||
|
&& tags[i] != METHODREF && tags[i] != INTERFACEMETHODREF) |
||||||
|
throw new ClassFormatException("Tag mismatch"); |
||||||
|
if (constants[i] == null) { |
||||||
|
int classIndex = indices1[i]; |
||||||
|
int nameTypeIndex = indices2[i]; |
||||||
|
if (tags[nameTypeIndex] != NAMEANDTYPE) |
||||||
|
throw new ClassFormatException("Tag mismatch"); |
||||||
|
String type = getUTF8(indices2[nameTypeIndex]); |
||||||
|
try { |
||||||
|
if (tags[i] == FIELDREF) |
||||||
|
TypeSignature.checkTypeSig(type); |
||||||
|
else |
||||||
|
TypeSignature.checkMethodTypeSig(type); |
||||||
|
} catch (IllegalArgumentException ex) { |
||||||
|
throw new ClassFormatException(ex.getMessage()); |
||||||
|
} |
||||||
|
String clName = getClassType(classIndex); |
||||||
|
constants[i] = Reference.getReference |
||||||
|
(clName, getUTF8(indices1[nameTypeIndex]), type); |
||||||
|
} |
||||||
|
return (Reference) constants[i]; |
||||||
|
} |
||||||
|
|
||||||
|
public Object getConstant(int i) throws ClassFormatException { |
||||||
|
if (i == 0) |
||||||
|
throw new ClassFormatException("null constant"); |
||||||
|
switch (tags[i]) { |
||||||
|
case INTEGER: |
||||||
|
case FLOAT: |
||||||
|
case LONG: |
||||||
|
case DOUBLE: |
||||||
|
return constants[i]; |
||||||
|
case CLASS: |
||||||
|
return Reference.getReference(getClassType(i), |
||||||
|
"class", "Ljava/lang/Class;"); |
||||||
|
case STRING: |
||||||
|
return getUTF8(indices1[i]); |
||||||
|
} |
||||||
|
throw new ClassFormatException("Tag mismatch: "+tags[i]); |
||||||
|
} |
||||||
|
|
||||||
|
public String getClassType(int i) throws ClassFormatException { |
||||||
|
if (tags[i] != CLASS) |
||||||
|
throw new ClassFormatException("Tag mismatch"); |
||||||
|
String clName = getUTF8(indices1[i]); |
||||||
|
if (clName.charAt(0) != '[') { |
||||||
|
clName = ("L"+clName+';').intern(); |
||||||
|
} |
||||||
|
try { |
||||||
|
TypeSignature.checkTypeSig(clName); |
||||||
|
} catch (IllegalArgumentException ex) { |
||||||
|
throw new ClassFormatException(ex.getMessage()); |
||||||
|
} |
||||||
|
return clName; |
||||||
|
} |
||||||
|
|
||||||
|
public String getClassName(int i) throws ClassFormatException { |
||||||
|
if (tags[i] != CLASS) |
||||||
|
throw new ClassFormatException("Tag mismatch"); |
||||||
|
if (constants[i] == null) { |
||||||
|
String clName = getUTF8(indices1[i]); |
||||||
|
try { |
||||||
|
TypeSignature.checkTypeSig("L"+clName+";"); |
||||||
|
} catch (IllegalArgumentException ex) { |
||||||
|
throw new ClassFormatException(ex.getMessage()); |
||||||
|
} |
||||||
|
constants[i] = clName.replace('/','.').intern(); |
||||||
|
} |
||||||
|
return (String) constants[i]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) { |
||||||
|
switch (tags[i]) { |
||||||
|
case CLASS: |
||||||
|
return "Class "+toString(indices1[i]); |
||||||
|
case STRING: |
||||||
|
return "String \""+toString(indices1[i])+"\""; |
||||||
|
case INTEGER: |
||||||
|
return "Int "+constants[i].toString(); |
||||||
|
case FLOAT: |
||||||
|
return "Float "+constants[i].toString(); |
||||||
|
case LONG: |
||||||
|
return "Long "+constants[i].toString(); |
||||||
|
case DOUBLE: |
||||||
|
return "Double "+constants[i].toString(); |
||||||
|
case UTF8: |
||||||
|
return constants[i].toString(); |
||||||
|
case FIELDREF: |
||||||
|
return "Fieldref: "+toString(indices1[i])+"; " |
||||||
|
+ toString(indices2[i]); |
||||||
|
case METHODREF: |
||||||
|
return "Methodref: "+toString(indices1[i])+"; " |
||||||
|
+ toString(indices2[i]); |
||||||
|
case INTERFACEMETHODREF: |
||||||
|
return "Interfaceref: "+toString(indices1[i])+"; " |
||||||
|
+ toString(indices2[i]); |
||||||
|
case NAMEANDTYPE: |
||||||
|
return "Name "+toString(indices1[i]) |
||||||
|
+"; Type "+toString(indices2[i]); |
||||||
|
default: |
||||||
|
return "unknown tag: "+tags[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public int size() { |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
StringBuffer result = new StringBuffer("ConstantPool[ null"); |
||||||
|
for (int i=1; i< count; i++) { |
||||||
|
result.append(", ").append(i).append(" = ").append(toString(i)); |
||||||
|
} |
||||||
|
result.append(" ]"); |
||||||
|
return result.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,340 @@ |
|||||||
|
/* 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 net.sf.jode.bytecode; |
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.DataOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
///#def COLLECTIONEXTRA java.lang
|
||||||
|
import java.lang.Comparable; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* Represents a java bytecode field (class variable). A field |
||||||
|
* consists of the following parts: |
||||||
|
* |
||||||
|
* <dl> |
||||||
|
* |
||||||
|
* <dt>name</dt><dd>The field's name</dd> |
||||||
|
* |
||||||
|
* <dt>type</dt><dd>The field's {@link TypeSignature type signature} |
||||||
|
* in bytecode format.</dd> |
||||||
|
* |
||||||
|
* <dt>signature</dt><dd>The field's {@link TypeSignature type signature} |
||||||
|
* in bytecode format including template information.</dd> |
||||||
|
* |
||||||
|
* <dt>modifiers</dt><dd>The modifiers of the field like private, public etc. |
||||||
|
* These are created by or-ing the constants {@link Modifier#PUBLIC}, |
||||||
|
* {@link Modifier#PRIVATE}, {@link Modifier#PROTECTED}, |
||||||
|
* {@link Modifier#STATIC}, {@link Modifier#FINAL}, |
||||||
|
* {@link Modifier#VOLATILE}, {@link Modifier#TRANSIENT}, |
||||||
|
* {@link Modifier#STRICT} |
||||||
|
* of class {@link java.lang.reflect.Modifier}. </dt> |
||||||
|
* |
||||||
|
* <dt>synthetic</dt><dd>true if this field is synthetic.</dd> |
||||||
|
* |
||||||
|
* <dt>deprecated</dt><dd>true if this field is deprecated.</dd> |
||||||
|
* |
||||||
|
* <dt>constant</dt> <dd>Final static fields may have a constant |
||||||
|
* value. This is either of type String, Integer, Long, Float or |
||||||
|
* Double. </dt> |
||||||
|
* |
||||||
|
* </dl> |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @see net.sf.jode.bytecode.TypeSignature |
||||||
|
* @see net.sf.jode.bytecode.BasicBlocks |
||||||
|
*/ |
||||||
|
public final class FieldInfo extends BinaryInfo implements Comparable { |
||||||
|
int modifier; |
||||||
|
String name; |
||||||
|
String typeSig; |
||||||
|
|
||||||
|
Object constant; |
||||||
|
boolean deprecatedFlag; |
||||||
|
/** |
||||||
|
* The type signature that also contains template information. |
||||||
|
*/ |
||||||
|
private String signature; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new empty field info. |
||||||
|
*/ |
||||||
|
public FieldInfo() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new field with given name, type and modifiers. |
||||||
|
* @param name the name of the field. |
||||||
|
* @param typeSig the typeSig the type signature. |
||||||
|
* @param modifier the modifier |
||||||
|
* @see TypeSignature |
||||||
|
* @see Modifier |
||||||
|
*/ |
||||||
|
public FieldInfo(String name, String typeSig, int modifier) { |
||||||
|
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 (howMuch >= ClassInfo.DECLARATIONS |
||||||
|
&& name.equals("ConstantValue")) { |
||||||
|
if (length != 2) |
||||||
|
throw new ClassFormatException |
||||||
|
("ConstantValue attribute has wrong length"); |
||||||
|
int index = input.readUnsignedShort(); |
||||||
|
constant = cp.getConstant(index); |
||||||
|
} else if (name.equals("Synthetic")) { |
||||||
|
modifier |= ACC_SYNTHETIC; |
||||||
|
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 if (name.equals("Signature")) { |
||||||
|
signature = cp.getUTF8(input.readUnsignedShort()); |
||||||
|
} else |
||||||
|
super.readAttribute(name, length, cp, input, howMuch); |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
void reserveSmallConstants(GrowableConstantPool gcp) { |
||||||
|
} |
||||||
|
|
||||||
|
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 (isSynthetic()) |
||||||
|
gcp.putUTF8("Synthetic"); |
||||||
|
if (deprecatedFlag) |
||||||
|
gcp.putUTF8("Deprecated"); |
||||||
|
if (signature != null) { |
||||||
|
gcp.putUTF8("Signature"); |
||||||
|
gcp.putUTF8(signature); |
||||||
|
} |
||||||
|
prepareAttributes(gcp); |
||||||
|
} |
||||||
|
|
||||||
|
protected int getAttributeCount() { |
||||||
|
int count = super.getAttributeCount(); |
||||||
|
if (constant != null) |
||||||
|
count++; |
||||||
|
if (isSynthetic()) |
||||||
|
count++; |
||||||
|
if (deprecatedFlag) |
||||||
|
count++; |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
protected void writeAttributes(GrowableConstantPool gcp, |
||||||
|
DataOutputStream output) |
||||||
|
throws IOException { |
||||||
|
super.writeAttributes(gcp, output); |
||||||
|
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 (isSynthetic()) { |
||||||
|
output.writeShort(gcp.putUTF8("Synthetic")); |
||||||
|
output.writeInt(0); |
||||||
|
} |
||||||
|
if (deprecatedFlag) { |
||||||
|
output.writeShort(gcp.putUTF8("Deprecated")); |
||||||
|
output.writeInt(0); |
||||||
|
} |
||||||
|
if (signature != null) { |
||||||
|
output.writeShort(gcp.putUTF8("Signature")); |
||||||
|
output.writeInt(2); |
||||||
|
output.writeShort(gcp.putUTF8(signature)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void write(GrowableConstantPool constantPool, |
||||||
|
DataOutputStream output) throws IOException { |
||||||
|
output.writeShort(modifier); |
||||||
|
output.writeShort(constantPool.putUTF8(name)); |
||||||
|
output.writeShort(constantPool.putUTF8(typeSig)); |
||||||
|
writeAttributes(constantPool, output); |
||||||
|
} |
||||||
|
|
||||||
|
protected void drop(int keep) { |
||||||
|
if (keep < ClassInfo.DECLARATIONS) |
||||||
|
constant = null; |
||||||
|
super.drop(keep); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the name of the field. |
||||||
|
* @return the name. |
||||||
|
*/ |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the type signature of the field. |
||||||
|
* @return the type signature. |
||||||
|
* @see TypeSignature |
||||||
|
*/ |
||||||
|
public String getType() { |
||||||
|
return typeSig; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the type signature including template information of the field. |
||||||
|
* <b>WARNING:</b> This field may disappear and merged into getType later. |
||||||
|
* @return the type signature. |
||||||
|
* @see TypeSignature |
||||||
|
*/ |
||||||
|
public String getSignature() { |
||||||
|
return signature != null ? signature : typeSig; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the modifier of the field. |
||||||
|
* @return the modifiers. |
||||||
|
* @see Modifier |
||||||
|
*/ |
||||||
|
public int getModifiers() { |
||||||
|
return modifier; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tells whether this field is synthetic. |
||||||
|
* @return true if the field is synthetic. |
||||||
|
*/ |
||||||
|
public boolean isSynthetic() { |
||||||
|
return (modifier & ACC_SYNTHETIC) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tells whether this field is deprecated. |
||||||
|
* @return true if the field is deprecated. |
||||||
|
*/ |
||||||
|
public boolean isDeprecated() { |
||||||
|
return deprecatedFlag; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the constant value of the field. For static final fields |
||||||
|
* that have a simple String, int, float, double or long constant, |
||||||
|
* this returns the corresponding constant as String, Integer, Float |
||||||
|
* Double or long. For other fields it returns null. |
||||||
|
* @return The constant, or null. |
||||||
|
*/ |
||||||
|
public Object getConstant() { |
||||||
|
return constant; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the name of the field. |
||||||
|
* @param newName the name. |
||||||
|
*/ |
||||||
|
public void setName(String newName) { |
||||||
|
name = newName; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the type signature of the field. |
||||||
|
* @param newType the type signature. |
||||||
|
* @see TypeSignature |
||||||
|
*/ |
||||||
|
public void setType(String newType) { |
||||||
|
typeSig = newType; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the modifier of the field. |
||||||
|
* @param newModifier the modifiers. |
||||||
|
* @see Modifier |
||||||
|
*/ |
||||||
|
public void setModifiers(int newModifier) { |
||||||
|
modifier = newModifier; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSynthetic(boolean flag) { |
||||||
|
if (flag) |
||||||
|
modifier |= ACC_SYNTHETIC; |
||||||
|
else |
||||||
|
modifier &= ~ACC_SYNTHETIC; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDeprecated(boolean flag) { |
||||||
|
deprecatedFlag = flag; |
||||||
|
} |
||||||
|
|
||||||
|
public void setConstant(Object newConstant) { |
||||||
|
constant = newConstant; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Compares two FieldInfo objects for field order. The field |
||||||
|
* order is as follows: First the static class intializer followed |
||||||
|
* by constructor with type signature sorted lexicographic. Then |
||||||
|
* all other fields sorted lexicographically by name. If two |
||||||
|
* fields have the same name, they are sorted by type signature, |
||||||
|
* though that can only happen for obfuscated code. |
||||||
|
* |
||||||
|
* @return a positive number if this field follows the other in |
||||||
|
* field order, a negative number if it preceeds the |
||||||
|
* other, and 0 if they are equal. |
||||||
|
* @exception ClassCastException if other is not a ClassInfo. */ |
||||||
|
public int compareTo(Object other) { |
||||||
|
FieldInfo fi = (FieldInfo) other; |
||||||
|
int result = name.compareTo(fi.name); |
||||||
|
if (result == 0) |
||||||
|
result = typeSig.compareTo(fi.typeSig); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return "Field "+Modifier.toString(modifier)+" "+ |
||||||
|
getSignature()+" "+name; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,348 @@ |
|||||||
|
/* GrowableConstantPool 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 net.sf.jode.bytecode; |
||||||
|
import java.io.DataOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Hashtable; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represent a constant pool, where new constants can be |
||||||
|
* 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 |
||||||
|
*/ |
||||||
|
public class GrowableConstantPool extends ConstantPool { |
||||||
|
Hashtable entryToIndex = new Hashtable(); |
||||||
|
boolean written; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class is used as key to the entryToIndex hashtable |
||||||
|
*/ |
||||||
|
private class Key { |
||||||
|
int tag; |
||||||
|
Object objData; |
||||||
|
int intData; |
||||||
|
|
||||||
|
public Key(int tag, Object objData, int intData) { |
||||||
|
this.tag = tag; |
||||||
|
this.objData = objData; |
||||||
|
this.intData = intData; |
||||||
|
} |
||||||
|
|
||||||
|
public int hashCode() { |
||||||
|
return tag ^ objData.hashCode() ^ intData; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean equals(Object o) { |
||||||
|
if (o instanceof Key) { |
||||||
|
Key k = (Key) o; |
||||||
|
return tag == k.tag && intData == k.intData |
||||||
|
&& objData.equals(k.objData); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new growable constant pool |
||||||
|
*/ |
||||||
|
public GrowableConstantPool () { |
||||||
|
count = 1; |
||||||
|
tags = new int[128]; |
||||||
|
indices1 = new int[128]; |
||||||
|
indices2 = new int[128]; |
||||||
|
constants = new Object[128]; |
||||||
|
written = false; |
||||||
|
} |
||||||
|
|
||||||
|
private final void grow(int wantedSize) { |
||||||
|
if (written) |
||||||
|
throw new IllegalStateException("adding to written ConstantPool"); |
||||||
|
if (wantedSize > 65535) |
||||||
|
throw new IllegalArgumentException("Too many constants added"); |
||||||
|
if (tags.length < wantedSize) { |
||||||
|
int newSize = Math.min(65535, Math.max(tags.length*2, wantedSize)); |
||||||
|
int[] tmpints = new int[newSize]; |
||||||
|
System.arraycopy(tags, 0, tmpints, 0, count); |
||||||
|
tags = tmpints; |
||||||
|
tmpints = new int[newSize]; |
||||||
|
System.arraycopy(indices1, 0, tmpints, 0, count); |
||||||
|
indices1 = tmpints; |
||||||
|
tmpints = new int[newSize]; |
||||||
|
System.arraycopy(indices2, 0, tmpints, 0, count); |
||||||
|
indices2 = tmpints; |
||||||
|
Object[] tmpobjs = new Object[newSize]; |
||||||
|
System.arraycopy(constants, 0, tmpobjs, 0, count); |
||||||
|
constants = tmpobjs; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private int putConstant(int tag, Object constant) { |
||||||
|
Key key = new Key(tag, constant, 0); |
||||||
|
Integer index = (Integer) entryToIndex.get(key); |
||||||
|
if (index != null) |
||||||
|
return index.intValue(); |
||||||
|
int newIndex = count; |
||||||
|
grow(count + 1); |
||||||
|
tags[newIndex] = tag; |
||||||
|
constants[newIndex] = constant; |
||||||
|
entryToIndex.put(key, new Integer(newIndex)); |
||||||
|
count++; |
||||||
|
return newIndex; |
||||||
|
} |
||||||
|
|
||||||
|
private int putLongConstant(int tag, Object constant) { |
||||||
|
Key key = new Key(tag, constant, 0); |
||||||
|
Integer index = (Integer) entryToIndex.get(key); |
||||||
|
if (index != null) |
||||||
|
return index.intValue(); |
||||||
|
int newIndex = count; |
||||||
|
grow(count + 2); |
||||||
|
tags[newIndex] = tag; |
||||||
|
tags[newIndex+1] = -tag; |
||||||
|
constants[newIndex] = constant; |
||||||
|
entryToIndex.put(key, new Integer(newIndex)); |
||||||
|
count += 2; |
||||||
|
return newIndex; |
||||||
|
} |
||||||
|
|
||||||
|
private int putIndexed(int tag, Object obj1, int index1, int index2) { |
||||||
|
Key key = new Key(tag, obj1, index2); |
||||||
|
Integer indexObj = (Integer) entryToIndex.get(key); |
||||||
|
if (indexObj != null) { |
||||||
|
/* Maybe this was a reserved, but not filled entry */ |
||||||
|
int index = indexObj.intValue(); |
||||||
|
indices1[index] = index1; |
||||||
|
indices2[index] = index2; |
||||||
|
return index; |
||||||
|
} |
||||||
|
grow(count+1); |
||||||
|
tags[count] = tag; |
||||||
|
indices1[count] = index1; |
||||||
|
indices2[count] = index2; |
||||||
|
entryToIndex.put(key, new Integer(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) { |
||||||
|
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) { |
||||||
|
name = name.replace('.','/'); |
||||||
|
TypeSignature.checkTypeSig("L"+name+";"); |
||||||
|
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) { |
||||||
|
TypeSignature.checkTypeSig(name); |
||||||
|
if (name.charAt(0) == 'L') |
||||||
|
name = name.substring(1, name.length()-1); |
||||||
|
else if (name.charAt(0) != '[') |
||||||
|
throw new IllegalArgumentException("wrong class type: "+name); |
||||||
|
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) { |
||||||
|
String className = ref.getClazz(); |
||||||
|
String typeSig = ref.getType(); |
||||||
|
if (tag == FIELDREF) |
||||||
|
TypeSignature.checkTypeSig(typeSig); |
||||||
|
else |
||||||
|
TypeSignature.checkMethodTypeSig(typeSig); |
||||||
|
|
||||||
|
|
||||||
|
int classIndex = putClassType(className); |
||||||
|
int nameIndex = putUTF8(ref.getName()); |
||||||
|
int typeIndex = putUTF8(typeSig); |
||||||
|
int nameTypeIndex = putIndexed(NAMEANDTYPE, |
||||||
|
ref.getName(), nameIndex, typeIndex); |
||||||
|
return putIndexed(tag, className, classIndex, nameTypeIndex); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Puts a "one slot" constant into this constant pool. If it |
||||||
|
* already exists it will reuse the previous entry. |
||||||
|
* @param c the constant; must be of type |
||||||
|
* Integer, Float or String |
||||||
|
* @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) { |
||||||
|
if (c instanceof String) { |
||||||
|
return putIndexed(STRING, c, putUTF8((String) c), 0); |
||||||
|
} else if (c instanceof Reference) { |
||||||
|
return putClassType(((Reference) c).getClazz()); |
||||||
|
} else { |
||||||
|
int tag; |
||||||
|
if (c instanceof Integer) |
||||||
|
tag = INTEGER; |
||||||
|
else if (c instanceof Float) |
||||||
|
tag = FLOAT; |
||||||
|
else |
||||||
|
throw new IllegalArgumentException |
||||||
|
("illegal constant " + c + " of type: " + c.getClass()); |
||||||
|
return putConstant(tag, c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Puts a "double slot" constant into this constant pool. If it |
||||||
|
* already exists it will reuse the previous entry. |
||||||
|
* @param c the constant; must be of type Long or Double |
||||||
|
* @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 putLongConstant(Object c) { |
||||||
|
int tag; |
||||||
|
if (c instanceof Long) |
||||||
|
tag = LONG; |
||||||
|
else if (c instanceof Double) |
||||||
|
tag = DOUBLE; |
||||||
|
else |
||||||
|
throw new IllegalArgumentException |
||||||
|
("illegal long constant " + c + " of type: " + c.getClass()); |
||||||
|
return putLongConstant(tag, c); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reserves 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 |
||||||
|
* Integer, Float or String |
||||||
|
* @return the reserved index into the pool of this constant. |
||||||
|
*/ |
||||||
|
public int reserveConstant(Object c) { |
||||||
|
if (c instanceof String) { |
||||||
|
return putIndexed(STRING, c, -1, 0); |
||||||
|
} else if (c instanceof Reference) { |
||||||
|
String name = ((Reference) c).getClazz(); |
||||||
|
if (name.charAt(0) == 'L') |
||||||
|
name = name.substring(1, name.length()-1); |
||||||
|
else if (name.charAt(0) != '[') |
||||||
|
throw new IllegalArgumentException("wrong class type: "+name); |
||||||
|
return putIndexed(CLASS, name, -1, 0); |
||||||
|
} else { |
||||||
|
return putConstant(c); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Writes the constant pool to the stream. This is normally called |
||||||
|
* by {@link ClassInfo#write}. |
||||||
|
* @param stream the stream to write to. |
||||||
|
* @exception IOException if it occured while writing. |
||||||
|
*/ |
||||||
|
public void write(DataOutputStream stream) |
||||||
|
throws IOException { |
||||||
|
written = true; |
||||||
|
stream.writeShort(count); |
||||||
|
for (int i=1; i< count; i++) { |
||||||
|
int tag = tags[i]; |
||||||
|
stream.writeByte(tag); |
||||||
|
switch (tag) { |
||||||
|
case CLASS: |
||||||
|
stream.writeShort(indices1[i]); |
||||||
|
break; |
||||||
|
case FIELDREF: |
||||||
|
case METHODREF: |
||||||
|
case INTERFACEMETHODREF: |
||||||
|
stream.writeShort(indices1[i]); |
||||||
|
stream.writeShort(indices2[i]); |
||||||
|
break; |
||||||
|
case STRING: |
||||||
|
stream.writeShort(indices1[i]); |
||||||
|
break; |
||||||
|
case INTEGER: |
||||||
|
stream.writeInt(((Integer)constants[i]).intValue()); |
||||||
|
break; |
||||||
|
case FLOAT: |
||||||
|
stream.writeFloat(((Float)constants[i]).floatValue()); |
||||||
|
break; |
||||||
|
case LONG: |
||||||
|
stream.writeLong(((Long)constants[i]).longValue()); |
||||||
|
i++; |
||||||
|
break; |
||||||
|
case DOUBLE: |
||||||
|
stream.writeDouble(((Double)constants[i]).doubleValue()); |
||||||
|
i++; |
||||||
|
break; |
||||||
|
case NAMEANDTYPE: |
||||||
|
stream.writeShort(indices1[i]); |
||||||
|
stream.writeShort(indices2[i]); |
||||||
|
break; |
||||||
|
case UTF8: |
||||||
|
stream.writeUTF((String)constants[i]); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new ClassFormatException("unknown constant tag"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
/* Handler Copyright (C) 2000-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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* A simple class containing the info about one try-catch block. |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class Handler { |
||||||
|
Block start, end, catcher; |
||||||
|
String type; |
||||||
|
|
||||||
|
/** |
||||||
|
* The empty handler array. Since handlers are often empty, we don't |
||||||
|
* want to create a new object each time. |
||||||
|
*/ |
||||||
|
final static Handler[] EMPTY = new Handler[0]; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new handler. |
||||||
|
*/ |
||||||
|
Handler(Block s, Block e, Block c, String t) { |
||||||
|
start = s; |
||||||
|
end = e; |
||||||
|
catcher = c; |
||||||
|
type = t; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the first basic block of the try. |
||||||
|
*/ |
||||||
|
public Block getStart() { |
||||||
|
return start; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the last basic block of the try. |
||||||
|
*/ |
||||||
|
public Block getEnd() { |
||||||
|
return end; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the first basic block of the exception handler. |
||||||
|
*/ |
||||||
|
public Block getCatcher() { |
||||||
|
return catcher; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the class name of the exception type. |
||||||
|
*/ |
||||||
|
public String getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public void setStart(Block start) { |
||||||
|
this.start = start; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEnd(Block end) { |
||||||
|
this.end = end; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCatcher(Block catcher) { |
||||||
|
this.catcher = catcher; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the class name of the exception type. |
||||||
|
*/ |
||||||
|
public void setType(String type) { |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,60 @@ |
|||||||
|
/* IncInstruction 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction in the byte code. |
||||||
|
* |
||||||
|
*/ |
||||||
|
class IncInstruction extends SlotInstruction { |
||||||
|
/** |
||||||
|
* The amount of increment. |
||||||
|
*/ |
||||||
|
private int increment; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a simple opcode, without any parameters. |
||||||
|
*/ |
||||||
|
IncInstruction(int opcode, LocalVariableInfo lvi, int increment) { |
||||||
|
super(opcode, lvi); |
||||||
|
this.increment = increment; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the increment for an opc_iinc instruction. |
||||||
|
*/ |
||||||
|
public final int getIncrement() |
||||||
|
{ |
||||||
|
return increment; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the increment for an opc_iinc instruction. |
||||||
|
*/ |
||||||
|
public final void setIncrement(int incr) |
||||||
|
{ |
||||||
|
this.increment = incr; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return super.toString()+' '+increment; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,503 @@ |
|||||||
|
/* 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> This class represents an instruction in the byte code. |
||||||
|
* Instructions can be created with the static {@link #forOpcode} |
||||||
|
* methods. </p> |
||||||
|
* |
||||||
|
* <p> We only allow a subset of opcodes. Other opcodes are mapped to |
||||||
|
* their simpler version. Don't worry about this, when writing the |
||||||
|
* bytecode the shortest possible bytecode is produced. </p> |
||||||
|
* |
||||||
|
* The opcodes we map are: |
||||||
|
* <pre> |
||||||
|
* [iflda]load_x -> [iflda]load |
||||||
|
* [iflda]store_x -> [iflda]store |
||||||
|
* [ifa]const_xx, ldc_w -> ldc |
||||||
|
* [dl]const_xx -> ldc2_w |
||||||
|
* wide opcode -> opcode |
||||||
|
* tableswitch -> lookupswitch |
||||||
|
* [a]newarray -> multianewarray |
||||||
|
* </pre> |
||||||
|
*/ |
||||||
|
public class Instruction implements Opcodes{ |
||||||
|
/** |
||||||
|
* The opcode and lineNr of the instruction. |
||||||
|
* opcode is <code>(lineAndOpcode & 0xff)</code>, while |
||||||
|
* lineNr is <code>(lineAndOpcode >> 8)</code>. |
||||||
|
* If line number is not known or unset, it is -1. |
||||||
|
*/ |
||||||
|
private int lineAndOpcode; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new simple Instruction with no parameters. We map |
||||||
|
* some opcodes, so you must always use the mapped opcode. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @exception IllegalArgumentException if opcode is not in our subset |
||||||
|
* or if opcode needs a parameter. */ |
||||||
|
public static Instruction forOpcode(int opcode) { |
||||||
|
switch (opcode) { |
||||||
|
case opc_nop: |
||||||
|
case opc_iaload: case opc_laload: case opc_faload: |
||||||
|
case opc_daload: case opc_aaload: |
||||||
|
case opc_baload: case opc_caload: case opc_saload: |
||||||
|
case opc_iastore: case opc_lastore: case opc_fastore: |
||||||
|
case opc_dastore: case opc_aastore: |
||||||
|
case opc_bastore: case opc_castore: case opc_sastore: |
||||||
|
case opc_pop: case opc_pop2: |
||||||
|
case opc_dup: case opc_dup_x1: case opc_dup_x2: |
||||||
|
case opc_dup2: case opc_dup2_x1: case opc_dup2_x2: |
||||||
|
case opc_swap: |
||||||
|
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_imul: case opc_lmul: case opc_fmul: case opc_dmul: |
||||||
|
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_ineg: case opc_lneg: case opc_fneg: case opc_dneg: |
||||||
|
case opc_ishl: case opc_lshl: |
||||||
|
case opc_ishr: case opc_lshr: |
||||||
|
case opc_iushr: case opc_lushr: |
||||||
|
case opc_iand: case opc_land: |
||||||
|
case opc_ior: case opc_lor: |
||||||
|
case opc_ixor: case opc_lxor: |
||||||
|
case opc_i2l: case opc_i2f: case opc_i2d: |
||||||
|
case opc_l2i: case opc_l2f: case opc_l2d: |
||||||
|
case opc_f2i: case opc_f2l: case opc_f2d: |
||||||
|
case opc_d2i: case opc_d2l: case opc_d2f: |
||||||
|
case opc_i2b: case opc_i2c: case opc_i2s: |
||||||
|
case opc_lcmp: case opc_fcmpl: case opc_fcmpg: |
||||||
|
case opc_dcmpl: case opc_dcmpg: |
||||||
|
case opc_ireturn: case opc_lreturn: |
||||||
|
case opc_freturn: case opc_dreturn: case opc_areturn: |
||||||
|
case opc_return: |
||||||
|
case opc_athrow: |
||||||
|
case opc_arraylength: |
||||||
|
case opc_monitorenter: case opc_monitorexit: |
||||||
|
case opc_goto: |
||||||
|
case opc_jsr: |
||||||
|
case opc_ifeq: case opc_ifne: |
||||||
|
case opc_iflt: case opc_ifge: |
||||||
|
case opc_ifgt: case opc_ifle: |
||||||
|
case opc_if_icmpeq: case opc_if_icmpne: |
||||||
|
case opc_if_icmplt: case opc_if_icmpge: |
||||||
|
case opc_if_icmpgt: case opc_if_icmple: |
||||||
|
case opc_if_acmpeq: case opc_if_acmpne: |
||||||
|
case opc_ifnull: case opc_ifnonnull: |
||||||
|
return new Instruction(opcode); |
||||||
|
default: |
||||||
|
throw new IllegalArgumentException("Instruction has a parameter"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new ldc Instruction. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @param constant the constant parameter. |
||||||
|
* @exception IllegalArgumentException if opcode is not opc_ldc or |
||||||
|
* opc_ldc2_w. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, Object constant) { |
||||||
|
if (opcode == opc_ldc || opcode == opc_ldc2_w) |
||||||
|
return new ConstantInstruction(opcode, constant); |
||||||
|
throw new IllegalArgumentException("Instruction has no constant"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Instruction with a local variable as parameter. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @param lvi the local variable parameter. |
||||||
|
* @exception IllegalArgumentException if opcode is not in our subset |
||||||
|
* or if opcode doesn't need a single local variable as parameter. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, LocalVariableInfo lvi) { |
||||||
|
if (opcode == opc_ret |
||||||
|
|| opcode >= opc_iload && opcode <= opc_aload |
||||||
|
|| opcode >= opc_istore && opcode <= opc_astore) |
||||||
|
return new SlotInstruction(opcode, lvi); |
||||||
|
throw new IllegalArgumentException("Instruction has no slot"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Instruction with reference as parameter. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @param reference the reference parameter. |
||||||
|
* @exception IllegalArgumentException if opcode is not in our subset |
||||||
|
* or if opcode doesn't need a reference as parameter. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, Reference reference) { |
||||||
|
if (opcode >= opc_getstatic && opcode <= opc_invokeinterface) |
||||||
|
return new ReferenceInstruction(opcode, reference); |
||||||
|
throw new IllegalArgumentException("Instruction has no reference"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Instruction with type signature as parameter. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @param typeSig the type signature parameter. |
||||||
|
* @exception IllegalArgumentException if opcode is not in our subset |
||||||
|
* or if opcode doesn't need a type signature as parameter. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, String typeSig) { |
||||||
|
switch (opcode) { |
||||||
|
case opc_new: |
||||||
|
case opc_checkcast: |
||||||
|
case opc_instanceof: |
||||||
|
return new TypeInstruction(opcode, typeSig); |
||||||
|
default: |
||||||
|
throw new IllegalArgumentException("Instruction has no type"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new switch Instruction. |
||||||
|
* @param opcode the opcode of this instruction must be opc_lookupswitch. |
||||||
|
* @param values an array containing the different cases. |
||||||
|
* @exception IllegalArgumentException if opcode is not opc_lookupswitch. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, int[] values) { |
||||||
|
if (opcode == opc_lookupswitch) |
||||||
|
return new SwitchInstruction(opcode, values); |
||||||
|
throw new IllegalArgumentException("Instruction has no values"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new increment Instruction. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @param lvi the local variable parameter. |
||||||
|
* @param increment the increment parameter. |
||||||
|
* @exception IllegalArgumentException if opcode is not opc_iinc. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, |
||||||
|
LocalVariableInfo lvi, int increment) { |
||||||
|
if (opcode == opc_iinc) |
||||||
|
return new IncInstruction(opcode, lvi, increment); |
||||||
|
throw new IllegalArgumentException("Instruction has no increment"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new Instruction with type signature and a dimension |
||||||
|
* as parameter. |
||||||
|
* @param opcode the opcode of this instruction. |
||||||
|
* @param typeSig the type signature parameter. |
||||||
|
* @param dimension the array dimension parameter. |
||||||
|
* @exception IllegalArgumentException if opcode is not |
||||||
|
* opc_multianewarray. |
||||||
|
*/ |
||||||
|
public static Instruction forOpcode(int opcode, |
||||||
|
String typeSig, int dimension) { |
||||||
|
if (opcode == opc_multianewarray) |
||||||
|
return new TypeDimensionInstruction(opcode, typeSig, dimension); |
||||||
|
throw new IllegalArgumentException("Instruction has no dimension"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a simple opcode, without any parameters. |
||||||
|
*/ |
||||||
|
Instruction(int opcode) { |
||||||
|
this.lineAndOpcode = (-1 << 8) | opcode; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the opcode of the instruction. |
||||||
|
* @return the opcode of the instruction. |
||||||
|
*/ |
||||||
|
public final int getOpcode() { |
||||||
|
return lineAndOpcode & 0xff; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tells whether there is a line number information for this |
||||||
|
* instruction. |
||||||
|
* @return true if there is a line number information for this |
||||||
|
* instruction. |
||||||
|
*/ |
||||||
|
public final boolean hasLineNr() { |
||||||
|
return lineAndOpcode >= 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the line number of this instruction. |
||||||
|
* @return the line number, or -1 if there isn't one. |
||||||
|
*/ |
||||||
|
public final int getLineNr() { |
||||||
|
return lineAndOpcode >> 8; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the line number of this instruction. |
||||||
|
* @param nr the line number; use -1 to clear it. |
||||||
|
*/ |
||||||
|
public final void setLineNr(int nr) { |
||||||
|
lineAndOpcode = (nr << 8) | (lineAndOpcode & 0xff); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether this instruction is a local store instruction, i.e. |
||||||
|
* one of <code>astore</code>, <code>istore</code>, <code>lstore</code>, |
||||||
|
* <code>fstore</code> or <code>dstore</code>. |
||||||
|
*/ |
||||||
|
public boolean isStore() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether this instruction accesses a local slot. |
||||||
|
*/ |
||||||
|
public boolean hasLocal() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the slot number of the local this instruction accesses. |
||||||
|
* @return the slot number. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* access a local slot. |
||||||
|
*/ |
||||||
|
public int getLocalSlot() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
// UnsupportedOperationException would be more appropriate
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the information of the local this instruction accesses. |
||||||
|
* @return the local variable info. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* access a local. |
||||||
|
*/ |
||||||
|
public LocalVariableInfo getLocalInfo() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the information of the local this instruction accesses. |
||||||
|
* @param info the local variable info. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* access a local. |
||||||
|
*/ |
||||||
|
public void setLocalInfo(LocalVariableInfo info) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the slot of the local this instruction accesses. |
||||||
|
* @param slot the local slot |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* access a local. |
||||||
|
*/ |
||||||
|
public void setLocalSlot(int slot) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the increment for an opc_iinc instruction. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public int getIncrement() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the increment for an opc_iinc instruction. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public void setIncrement(int incr) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the dimensions for an opc_multianewarray opcode. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public int getDimensions() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the dimensions for an opc_multianewarray opcode. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public void setDimensions(int dims) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the constant for a opc_ldc or opc_ldc2_w opcode. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public Object getConstant() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the constant for a opc_ldc or opc_ldc2_w opcode. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public void setConstant(Object constant) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the reference of the field or method this instruction accesses. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public Reference getReference() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the reference of the field or method this instruction accesses. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public void setReference(Reference ref) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the class type this instruction uses, e.g if its a class cast. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public String getClazzType() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the class type this instruction uses, e.g if its a class cast. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public void setClazzType(String type) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the values of a opc_lookupswitch opcode. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public int[] getValues() |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the values of a opc_lookupswitch opcode. |
||||||
|
* @throws IllegalArgumentException if this instruction doesn't |
||||||
|
* support this. |
||||||
|
*/ |
||||||
|
public void setValues(int[] values) |
||||||
|
{ |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether this instruction always changes program flow. |
||||||
|
* Returns false for opc_jsr it. |
||||||
|
* @return true if this instruction always changes flow, i.e. if |
||||||
|
* its an unconditional jump, a return, a throw, a ret or a switch. |
||||||
|
*/ |
||||||
|
public final boolean doesAlwaysJump() { |
||||||
|
switch (getOpcode()) { |
||||||
|
case opc_ret: |
||||||
|
case opc_goto: |
||||||
|
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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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(getOpcode()); |
||||||
|
poppush[0] = delta & 7; |
||||||
|
poppush[1] = delta >> 3; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets a printable representation of the opcode with its |
||||||
|
* parameters. This will not include the destination for jump |
||||||
|
* instructions, since this information is not stored inside the |
||||||
|
* instruction. |
||||||
|
*/ |
||||||
|
public final String getDescription() { |
||||||
|
return toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets a printable representation of the opcode with its |
||||||
|
* parameters. This will not include the destination for jump |
||||||
|
* instructions, since this information is not stored inside the |
||||||
|
* instruction. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return opcodeString[getOpcode()]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* stackDelta contains \100 if stack count of opcode is variable |
||||||
|
* \177 if opcode is illegal, or 8*stack_push + stack_pop otherwise |
||||||
|
* The string is created by scripts/createStackDelta.pl |
||||||
|
*/ |
||||||
|
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\000\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\000"; |
||||||
|
} |
@ -0,0 +1,135 @@ |
|||||||
|
/* 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 net.sf.jode.bytecode; |
||||||
|
import net.sf.jode.util.UnifyHash; |
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Iterator; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* A simple class containing the info of the LocalVariableTable. This |
||||||
|
* info is stored decentral: every load, store or iinc instruction contains |
||||||
|
* the info for its local. When writing code it will automatically be |
||||||
|
* collected again. <br> |
||||||
|
* |
||||||
|
* You can't modify a LocalVariableInfo, for this reason they can and |
||||||
|
* will be shared.<br> |
||||||
|
* |
||||||
|
* This information consists of name, type signature and slot number. |
||||||
|
* There is no public constructor; use the static getInfo() methods |
||||||
|
* instead. |
||||||
|
*/ |
||||||
|
public final class LocalVariableInfo { |
||||||
|
private String name, type; |
||||||
|
private int slot; |
||||||
|
private static LocalVariableInfo anonymous[]; |
||||||
|
static { |
||||||
|
grow(5); |
||||||
|
} |
||||||
|
private static final UnifyHash unifier = new UnifyHash(); |
||||||
|
|
||||||
|
private LocalVariableInfo(int slot) { |
||||||
|
this.slot = slot; |
||||||
|
} |
||||||
|
|
||||||
|
private LocalVariableInfo(int slot, String name, String type) { |
||||||
|
this.slot = slot; |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
private static void grow(int upper) { |
||||||
|
LocalVariableInfo[] newAnon = new LocalVariableInfo[upper]; |
||||||
|
int start = 0; |
||||||
|
if (anonymous != null) { |
||||||
|
start = anonymous.length; |
||||||
|
System.arraycopy(anonymous, 0, newAnon, 0, start); |
||||||
|
} |
||||||
|
anonymous = newAnon; |
||||||
|
for (int i=start; i< upper; i++) |
||||||
|
anonymous[i] = new LocalVariableInfo(i); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new local variable info, with no name or type. |
||||||
|
* @param slot the slot number. |
||||||
|
*/ |
||||||
|
public static LocalVariableInfo getInfo(int slot) { |
||||||
|
if (slot >= anonymous.length) |
||||||
|
grow(Math.max(slot + 1, anonymous.length * 2)); |
||||||
|
return anonymous[slot]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new local variable info, with given name and type. |
||||||
|
* @param slot the slot number. |
||||||
|
* @param name the name of the local. |
||||||
|
* @param type the type signature of the local. |
||||||
|
*/ |
||||||
|
public static LocalVariableInfo getInfo(int slot, |
||||||
|
String name, String type) { |
||||||
|
if (name == null && type == null) |
||||||
|
return getInfo(slot); |
||||||
|
int hash = slot ^ name.hashCode() ^ type.hashCode(); |
||||||
|
Iterator iter = unifier.iterateHashCode(hash); |
||||||
|
while (iter.hasNext()) { |
||||||
|
LocalVariableInfo lvi = (LocalVariableInfo) iter.next(); |
||||||
|
if (lvi.slot == slot |
||||||
|
&& lvi.name.equals(name) |
||||||
|
&& lvi.type.equals(type)) |
||||||
|
return lvi; |
||||||
|
} |
||||||
|
LocalVariableInfo lvi = new LocalVariableInfo(slot, name, type); |
||||||
|
unifier.put(hash, lvi); |
||||||
|
return lvi; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the slot number. |
||||||
|
*/ |
||||||
|
public int getSlot() { |
||||||
|
return slot; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the name. |
||||||
|
*/ |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the type signature. |
||||||
|
* @see TypeSignature |
||||||
|
*/ |
||||||
|
public String getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets a string representation for debugging purposes. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
String result = ""+slot; |
||||||
|
if (name != null) |
||||||
|
result += " ["+name+","+type+"]"; |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,339 @@ |
|||||||
|
/* 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 net.sf.jode.bytecode; |
||||||
|
import java.io.DataInputStream; |
||||||
|
import java.io.DataOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
///#def COLLECTIONEXTRA java.lang
|
||||||
|
import java.lang.Comparable; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* Represents a java bytecode method. A method consists of the following |
||||||
|
* parts: |
||||||
|
* |
||||||
|
* <dl> |
||||||
|
* |
||||||
|
* <dt>name</dt><dd>The method's name</dd> |
||||||
|
* |
||||||
|
* <dt>type</dt><dd>The method's {@link TypeSignature type signature} |
||||||
|
* in bytecode format.</dd> |
||||||
|
* |
||||||
|
* <dt>signature</dt><dd>The method's {@link TypeSignature type signature} |
||||||
|
* in bytecode format including template information.</dd> |
||||||
|
* |
||||||
|
* <dt>modifiers</dt><dd>The modifiers of the field like private, public etc. |
||||||
|
* These are created by or-ing the constants {@link Modifier#PUBLIC}, |
||||||
|
* {@link Modifier#PRIVATE}, {@link Modifier#PROTECTED}, |
||||||
|
* {@link Modifier#STATIC}, {@link Modifier#FINAL}, |
||||||
|
* {@link Modifier#SYNCHRONIZED}, {@link Modifier#NATIVE}, |
||||||
|
* {@link Modifier#ABSTRACT}, {@link Modifier#STRICT} |
||||||
|
* of class {@link java.lang.reflect.Modifier}. </dt> |
||||||
|
* |
||||||
|
* <dt>basicblocks</dt><dd>the bytecode of the method in form of |
||||||
|
* {@link BasicBlocks basic blocks}, null if it is native or |
||||||
|
* abstract.</dd> |
||||||
|
* |
||||||
|
* <dt>synthetic</dt><dd>true if this method is synthetic</dd> |
||||||
|
* |
||||||
|
* <dt>deprecated</dt><dd>true if this method is deprecated</dd> |
||||||
|
* |
||||||
|
* <dt>exceptions</dt> <dd>the exceptions that the method declared in |
||||||
|
* its throws clause</dd> |
||||||
|
* |
||||||
|
* </dl> |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
* @see net.sf.jode.bytecode.TypeSignature |
||||||
|
* @see net.sf.jode.bytecode.BasicBlocks |
||||||
|
*/ |
||||||
|
public final class MethodInfo extends BinaryInfo implements Comparable { |
||||||
|
int modifier; |
||||||
|
String name; |
||||||
|
String typeSig; |
||||||
|
|
||||||
|
BasicBlocks basicblocks; |
||||||
|
String[] exceptions; |
||||||
|
boolean deprecatedFlag; |
||||||
|
/** |
||||||
|
* The type signature that also contains template information. |
||||||
|
*/ |
||||||
|
private String signature; |
||||||
|
|
||||||
|
public MethodInfo() { |
||||||
|
} |
||||||
|
|
||||||
|
public MethodInfo(String name, String typeSig, int modifier) { |
||||||
|
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 (howMuch >= ClassInfo.NODEBUG && name.equals("Code")) { |
||||||
|
basicblocks = new BasicBlocks(this); |
||||||
|
basicblocks.read(cp, input, howMuch); |
||||||
|
} else if (howMuch >= ClassInfo.DECLARATIONS |
||||||
|
&& 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")) { |
||||||
|
modifier |= ACC_SYNTHETIC; |
||||||
|
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 if (name.equals("Signature")) { |
||||||
|
signature = cp.getUTF8(input.readUnsignedShort()); |
||||||
|
} else |
||||||
|
super.readAttribute(name, length, cp, input, howMuch); |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
void reserveSmallConstants(GrowableConstantPool gcp) { |
||||||
|
if (basicblocks != null) |
||||||
|
basicblocks.reserveSmallConstants(gcp); |
||||||
|
} |
||||||
|
|
||||||
|
void prepareWriting(GrowableConstantPool gcp) { |
||||||
|
gcp.putUTF8(name); |
||||||
|
gcp.putUTF8(typeSig); |
||||||
|
if (basicblocks != null) { |
||||||
|
gcp.putUTF8("Code"); |
||||||
|
basicblocks.prepareWriting(gcp); |
||||||
|
} |
||||||
|
if (exceptions != null) { |
||||||
|
gcp.putUTF8("Exceptions"); |
||||||
|
for (int i = 0; i < exceptions.length; i++) |
||||||
|
gcp.putClassName(exceptions[i]); |
||||||
|
} |
||||||
|
if (isSynthetic()) |
||||||
|
gcp.putUTF8("Synthetic"); |
||||||
|
if (deprecatedFlag) |
||||||
|
gcp.putUTF8("Deprecated"); |
||||||
|
if (signature != null) { |
||||||
|
gcp.putUTF8("Signature"); |
||||||
|
gcp.putUTF8(signature); |
||||||
|
} |
||||||
|
prepareAttributes(gcp); |
||||||
|
} |
||||||
|
|
||||||
|
protected int getAttributeCount() { |
||||||
|
int count = super.getAttributeCount(); |
||||||
|
if (basicblocks != null) |
||||||
|
count++; |
||||||
|
if (exceptions != null) |
||||||
|
count++; |
||||||
|
if (isSynthetic()) |
||||||
|
count++; |
||||||
|
if (deprecatedFlag) |
||||||
|
count++; |
||||||
|
return count; |
||||||
|
} |
||||||
|
|
||||||
|
protected void writeAttributes(GrowableConstantPool gcp, |
||||||
|
DataOutputStream output) |
||||||
|
throws IOException { |
||||||
|
super.writeAttributes(gcp, output); |
||||||
|
if (basicblocks != null) { |
||||||
|
output.writeShort(gcp.putUTF8("Code")); |
||||||
|
basicblocks.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 (isSynthetic()) { |
||||||
|
output.writeShort(gcp.putUTF8("Synthetic")); |
||||||
|
output.writeInt(0); |
||||||
|
} |
||||||
|
if (deprecatedFlag) { |
||||||
|
output.writeShort(gcp.putUTF8("Deprecated")); |
||||||
|
output.writeInt(0); |
||||||
|
} |
||||||
|
if (signature != null) { |
||||||
|
output.writeShort(gcp.putUTF8("Signature")); |
||||||
|
output.writeInt(2); |
||||||
|
output.writeShort(gcp.putUTF8(signature)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void write(GrowableConstantPool constantPool, |
||||||
|
DataOutputStream output) throws IOException { |
||||||
|
output.writeShort(modifier); |
||||||
|
output.writeShort(constantPool.putUTF8(name)); |
||||||
|
output.writeShort(constantPool.putUTF8(typeSig)); |
||||||
|
writeAttributes(constantPool, output); |
||||||
|
} |
||||||
|
|
||||||
|
protected void drop(int keep) { |
||||||
|
if (keep < ClassInfo.DECLARATIONS) |
||||||
|
exceptions = null; |
||||||
|
if (keep < ClassInfo.NODEBUG) |
||||||
|
basicblocks = null; |
||||||
|
else |
||||||
|
basicblocks.drop(keep); |
||||||
|
super.drop(keep); |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getType() { |
||||||
|
return typeSig; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the type signature including template information of the method. |
||||||
|
* <b>WARNING:</b> This field may disappear and merged into getType later. |
||||||
|
* @return the type signature. |
||||||
|
* @see TypeSignature |
||||||
|
*/ |
||||||
|
public String getSignature() { |
||||||
|
return signature != null ? signature : typeSig; |
||||||
|
} |
||||||
|
|
||||||
|
public int getModifiers() { |
||||||
|
return modifier; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isConstructor() { |
||||||
|
return name.charAt(0) == '<'; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStatic() { |
||||||
|
return Modifier.isStatic(modifier); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isSynthetic() { |
||||||
|
return (modifier & ACC_SYNTHETIC) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isDeprecated() { |
||||||
|
return deprecatedFlag; |
||||||
|
} |
||||||
|
|
||||||
|
public BasicBlocks getBasicBlocks() { |
||||||
|
return basicblocks; |
||||||
|
} |
||||||
|
|
||||||
|
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) { |
||||||
|
if (flag) |
||||||
|
modifier |= ACC_SYNTHETIC; |
||||||
|
else |
||||||
|
modifier &= ~ACC_SYNTHETIC; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDeprecated(boolean flag) { |
||||||
|
deprecatedFlag = flag; |
||||||
|
} |
||||||
|
|
||||||
|
public void setBasicBlocks(BasicBlocks newBasicblocks) { |
||||||
|
basicblocks = newBasicblocks; |
||||||
|
} |
||||||
|
|
||||||
|
public void setExceptions(String[] newExceptions) { |
||||||
|
exceptions = newExceptions; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Compares two MethodInfo objects for method order. The method |
||||||
|
* order is as follows: First the static class intializer followed |
||||||
|
* by constructor with type signature sorted lexicographic. Then |
||||||
|
* all other methods sorted lexicographically by name. If two |
||||||
|
* methods have the same name, they are sorted by type signature. |
||||||
|
* |
||||||
|
* @return a positive number if this method follows the other in |
||||||
|
* method order, a negative number if it preceeds the |
||||||
|
* other, and 0 if they are equal. |
||||||
|
* @exception ClassCastException if other is not a ClassInfo. |
||||||
|
*/ |
||||||
|
public int compareTo(Object other) { |
||||||
|
MethodInfo mi = (MethodInfo) other; |
||||||
|
/* Normally constructors should automatically sort themself to |
||||||
|
* the beginning, but if method name starts with a digit, the |
||||||
|
* order would be destroyed. |
||||||
|
* |
||||||
|
* The JVM explicitly forbids methods starting with digits, |
||||||
|
* nonetheless some obfuscators break this rule. |
||||||
|
* |
||||||
|
* But note that <clinit> comes lexicographically before <init>. |
||||||
|
*/ |
||||||
|
if (name.charAt(0) != mi.name.charAt(0)) { |
||||||
|
if (name.charAt(0) == '<') |
||||||
|
return -1; |
||||||
|
if (mi.name.charAt(0) == '<') |
||||||
|
return 1; |
||||||
|
} |
||||||
|
int result = name.compareTo(mi.name); |
||||||
|
if (result == 0) |
||||||
|
result = typeSig.compareTo(mi.typeSig); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a string representation of this method. It consists |
||||||
|
* of the method's name and type signature. |
||||||
|
* @return a string representation of this method. |
||||||
|
*/ |
||||||
|
public String toString() { |
||||||
|
return "MethodInfo[name="+name+",sig="+getSignature()+"]"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,280 @@ |
|||||||
|
/* Opcodes 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is an interface containing the constants for the byte code opcodes. |
||||||
|
*/ |
||||||
|
public interface Opcodes { |
||||||
|
public final static int opc_nop = 0; |
||||||
|
public final static int opc_aconst_null = 1; |
||||||
|
public final static int opc_iconst_m1 = 2; |
||||||
|
public final static int opc_iconst_0 = 3; |
||||||
|
public final static int opc_iconst_1 = 4; |
||||||
|
public final static int opc_iconst_2 = 5; |
||||||
|
public final static int opc_iconst_3 = 6; |
||||||
|
public final static int opc_iconst_4 = 7; |
||||||
|
public final static int opc_iconst_5 = 8; |
||||||
|
public final static int opc_lconst_0 = 9; |
||||||
|
public final static int opc_lconst_1 = 10; |
||||||
|
public final static int opc_fconst_0 = 11; |
||||||
|
public final static int opc_fconst_1 = 12; |
||||||
|
public final static int opc_fconst_2 = 13; |
||||||
|
public final static int opc_dconst_0 = 14; |
||||||
|
public final static int opc_dconst_1 = 15; |
||||||
|
public final static int opc_bipush = 16; |
||||||
|
public final static int opc_sipush = 17; |
||||||
|
public final static int opc_ldc = 18; |
||||||
|
public final static int opc_ldc_w = 19; |
||||||
|
public final static int opc_ldc2_w = 20; |
||||||
|
public final static int opc_iload = 21; |
||||||
|
public final static int opc_lload = 22; |
||||||
|
public final static int opc_fload = 23; |
||||||
|
public final static int opc_dload = 24; |
||||||
|
public final static int opc_aload = 25; |
||||||
|
public final static int opc_iload_0 = 26; |
||||||
|
public final static int opc_iload_1 = 27; |
||||||
|
public final static int opc_iload_2 = 28; |
||||||
|
public final static int opc_iload_3 = 29; |
||||||
|
public final static int opc_lload_0 = 30; |
||||||
|
public final static int opc_lload_1 = 31; |
||||||
|
public final static int opc_lload_2 = 32; |
||||||
|
public final static int opc_lload_3 = 33; |
||||||
|
public final static int opc_fload_0 = 34; |
||||||
|
public final static int opc_fload_1 = 35; |
||||||
|
public final static int opc_fload_2 = 36; |
||||||
|
public final static int opc_fload_3 = 37; |
||||||
|
public final static int opc_dload_0 = 38; |
||||||
|
public final static int opc_dload_1 = 39; |
||||||
|
public final static int opc_dload_2 = 40; |
||||||
|
public final static int opc_dload_3 = 41; |
||||||
|
public final static int opc_aload_0 = 42; |
||||||
|
public final static int opc_aload_1 = 43; |
||||||
|
public final static int opc_aload_2 = 44; |
||||||
|
public final static int opc_aload_3 = 45; |
||||||
|
public final static int opc_iaload = 46; |
||||||
|
public final static int opc_laload = 47; |
||||||
|
public final static int opc_faload = 48; |
||||||
|
public final static int opc_daload = 49; |
||||||
|
public final static int opc_aaload = 50; |
||||||
|
public final static int opc_baload = 51; |
||||||
|
public final static int opc_caload = 52; |
||||||
|
public final static int opc_saload = 53; |
||||||
|
public final static int opc_istore = 54; |
||||||
|
public final static int opc_lstore = 55; |
||||||
|
public final static int opc_fstore = 56; |
||||||
|
public final static int opc_dstore = 57; |
||||||
|
public final static int opc_astore = 58; |
||||||
|
public final static int opc_istore_0 = 59; |
||||||
|
public final static int opc_istore_1 = 60; |
||||||
|
public final static int opc_istore_2 = 61; |
||||||
|
public final static int opc_istore_3 = 62; |
||||||
|
public final static int opc_lstore_0 = 63; |
||||||
|
public final static int opc_lstore_1 = 64; |
||||||
|
public final static int opc_lstore_2 = 65; |
||||||
|
public final static int opc_lstore_3 = 66; |
||||||
|
public final static int opc_fstore_0 = 67; |
||||||
|
public final static int opc_fstore_1 = 68; |
||||||
|
public final static int opc_fstore_2 = 69; |
||||||
|
public final static int opc_fstore_3 = 70; |
||||||
|
public final static int opc_dstore_0 = 71; |
||||||
|
public final static int opc_dstore_1 = 72; |
||||||
|
public final static int opc_dstore_2 = 73; |
||||||
|
public final static int opc_dstore_3 = 74; |
||||||
|
public final static int opc_astore_0 = 75; |
||||||
|
public final static int opc_astore_1 = 76; |
||||||
|
public final static int opc_astore_2 = 77; |
||||||
|
public final static int opc_astore_3 = 78; |
||||||
|
public final static int opc_iastore = 79; |
||||||
|
public final static int opc_lastore = 80; |
||||||
|
public final static int opc_fastore = 81; |
||||||
|
public final static int opc_dastore = 82; |
||||||
|
public final static int opc_aastore = 83; |
||||||
|
public final static int opc_bastore = 84; |
||||||
|
public final static int opc_castore = 85; |
||||||
|
public final static int opc_sastore = 86; |
||||||
|
public final static int opc_pop = 87; |
||||||
|
public final static int opc_pop2 = 88; |
||||||
|
public final static int opc_dup = 89; |
||||||
|
public final static int opc_dup_x1 = 90; |
||||||
|
public final static int opc_dup_x2 = 91; |
||||||
|
public final static int opc_dup2 = 92; |
||||||
|
public final static int opc_dup2_x1 = 93; |
||||||
|
public final static int opc_dup2_x2 = 94; |
||||||
|
public final static int opc_swap = 95; |
||||||
|
public final static int opc_iadd = 96; |
||||||
|
public final static int opc_ladd = 97; |
||||||
|
public final static int opc_fadd = 98; |
||||||
|
public final static int opc_dadd = 99; |
||||||
|
public final static int opc_isub = 100; |
||||||
|
public final static int opc_lsub = 101; |
||||||
|
public final static int opc_fsub = 102; |
||||||
|
public final static int opc_dsub = 103; |
||||||
|
public final static int opc_imul = 104; |
||||||
|
public final static int opc_lmul = 105; |
||||||
|
public final static int opc_fmul = 106; |
||||||
|
public final static int opc_dmul = 107; |
||||||
|
public final static int opc_idiv = 108; |
||||||
|
public final static int opc_ldiv = 109; |
||||||
|
public final static int opc_fdiv = 110; |
||||||
|
public final static int opc_ddiv = 111; |
||||||
|
public final static int opc_irem = 112; |
||||||
|
public final static int opc_lrem = 113; |
||||||
|
public final static int opc_frem = 114; |
||||||
|
public final static int opc_drem = 115; |
||||||
|
public final static int opc_ineg = 116; |
||||||
|
public final static int opc_lneg = 117; |
||||||
|
public final static int opc_fneg = 118; |
||||||
|
public final static int opc_dneg = 119; |
||||||
|
public final static int opc_ishl = 120; |
||||||
|
public final static int opc_lshl = 121; |
||||||
|
public final static int opc_ishr = 122; |
||||||
|
public final static int opc_lshr = 123; |
||||||
|
public final static int opc_iushr = 124; |
||||||
|
public final static int opc_lushr = 125; |
||||||
|
public final static int opc_iand = 126; |
||||||
|
public final static int opc_land = 127; |
||||||
|
public final static int opc_ior = 128; |
||||||
|
public final static int opc_lor = 129; |
||||||
|
public final static int opc_ixor = 130; |
||||||
|
public final static int opc_lxor = 131; |
||||||
|
public final static int opc_iinc = 132; |
||||||
|
public final static int opc_i2l = 133; |
||||||
|
public final static int opc_i2f = 134; |
||||||
|
public final static int opc_i2d = 135; |
||||||
|
public final static int opc_l2i = 136; |
||||||
|
public final static int opc_l2f = 137; |
||||||
|
public final static int opc_l2d = 138; |
||||||
|
public final static int opc_f2i = 139; |
||||||
|
public final static int opc_f2l = 140; |
||||||
|
public final static int opc_f2d = 141; |
||||||
|
public final static int opc_d2i = 142; |
||||||
|
public final static int opc_d2l = 143; |
||||||
|
public final static int opc_d2f = 144; |
||||||
|
public final static int opc_i2b = 145; |
||||||
|
public final static int opc_i2c = 146; |
||||||
|
public final static int opc_i2s = 147; |
||||||
|
public final static int opc_lcmp = 148; |
||||||
|
public final static int opc_fcmpl = 149; |
||||||
|
public final static int opc_fcmpg = 150; |
||||||
|
public final static int opc_dcmpl = 151; |
||||||
|
public final static int opc_dcmpg = 152; |
||||||
|
public final static int opc_ifeq = 153; |
||||||
|
public final static int opc_ifne = 154; |
||||||
|
public final static int opc_iflt = 155; |
||||||
|
public final static int opc_ifge = 156; |
||||||
|
public final static int opc_ifgt = 157; |
||||||
|
public final static int opc_ifle = 158; |
||||||
|
public final static int opc_if_icmpeq = 159; |
||||||
|
public final static int opc_if_icmpne = 160; |
||||||
|
public final static int opc_if_icmplt = 161; |
||||||
|
public final static int opc_if_icmpge = 162; |
||||||
|
public final static int opc_if_icmpgt = 163; |
||||||
|
public final static int opc_if_icmple = 164; |
||||||
|
public final static int opc_if_acmpeq = 165; |
||||||
|
public final static int opc_if_acmpne = 166; |
||||||
|
public final static int opc_goto = 167; |
||||||
|
public final static int opc_jsr = 168; |
||||||
|
public final static int opc_ret = 169; |
||||||
|
public final static int opc_tableswitch = 170; |
||||||
|
public final static int opc_lookupswitch = 171; |
||||||
|
public final static int opc_ireturn = 172; |
||||||
|
public final static int opc_lreturn = 173; |
||||||
|
public final static int opc_freturn = 174; |
||||||
|
public final static int opc_dreturn = 175; |
||||||
|
public final static int opc_areturn = 176; |
||||||
|
public final static int opc_return = 177; |
||||||
|
public final static int opc_getstatic = 178; |
||||||
|
public final static int opc_putstatic = 179; |
||||||
|
public final static int opc_getfield = 180; |
||||||
|
public final static int opc_putfield = 181; |
||||||
|
public final static int opc_invokevirtual = 182; |
||||||
|
public final static int opc_invokespecial = 183; |
||||||
|
public final static int opc_invokestatic = 184; |
||||||
|
public final static int opc_invokeinterface = 185; |
||||||
|
public final static int opc_xxxunusedxxx = 186; |
||||||
|
public final static int opc_new = 187; |
||||||
|
public final static int opc_newarray = 188; |
||||||
|
public final static int opc_anewarray = 189; |
||||||
|
public final static int opc_arraylength = 190; |
||||||
|
public final static int opc_athrow = 191; |
||||||
|
public final static int opc_checkcast = 192; |
||||||
|
public final static int opc_instanceof = 193; |
||||||
|
public final static int opc_monitorenter = 194; |
||||||
|
public final static int opc_monitorexit = 195; |
||||||
|
public final static int opc_wide = 196; |
||||||
|
public final static int opc_multianewarray = 197; |
||||||
|
public final static int opc_ifnull = 198; |
||||||
|
public final static int opc_ifnonnull = 199; |
||||||
|
public final static int opc_goto_w = 200; |
||||||
|
public final static int opc_jsr_w = 201; |
||||||
|
public final static int opc_breakpoint = 202; |
||||||
|
public final static int opc_impdep1 = 254; |
||||||
|
public final static int opc_impdep2 = 255; |
||||||
|
|
||||||
|
public final static String[] opcodeString = { |
||||||
|
"nop", "aconst_null", "iconst_m1", "iconst_0", "iconst_1", |
||||||
|
"iconst_2", "iconst_3", "iconst_4", "iconst_5", "lconst_0", |
||||||
|
"lconst_1", "fconst_0", "fconst_1", "fconst_2", "dconst_0", |
||||||
|
"dconst_1", "bipush", "sipush", "ldc", "ldc_w", "ldc2_w", |
||||||
|
"iload", "lload", "fload", "dload", "aload", "iload_0", |
||||||
|
"iload_1", "iload_2", "iload_3", "lload_0", "lload_1", "lload_2", |
||||||
|
"lload_3", "fload_0", "fload_1", "fload_2", "fload_3", "dload_0", |
||||||
|
"dload_1", "dload_2", "dload_3", "aload_0", "aload_1", "aload_2", |
||||||
|
"aload_3", "iaload", "laload", "faload", "daload", "aaload", |
||||||
|
"baload", "caload", "saload", "istore", "lstore", "fstore", |
||||||
|
"dstore", "astore", "istore_0", "istore_1", "istore_2", "istore_3", |
||||||
|
"lstore_0", "lstore_1", "lstore_2", "lstore_3", "fstore_0", |
||||||
|
"fstore_1", "fstore_2", "fstore_3", "dstore_0", "dstore_1", |
||||||
|
"dstore_2", "dstore_3", "astore_0", "astore_1", "astore_2", |
||||||
|
"astore_3", "iastore", "lastore", "fastore", "dastore", "aastore", |
||||||
|
"bastore", "castore", "sastore", "pop", "pop2", "dup", "dup_x1", |
||||||
|
"dup_x2", "dup2", "dup2_x1", "dup2_x2", "swap", "iadd", "ladd", |
||||||
|
"fadd", "dadd", "isub", "lsub", "fsub", "dsub", "imul", "lmul", |
||||||
|
"fmul", "dmul", "idiv", "ldiv", "fdiv", "ddiv", "irem", "lrem", |
||||||
|
"frem", "drem", "ineg", "lneg", "fneg", "dneg", "ishl", "lshl", |
||||||
|
"ishr", "lshr", "iushr", "lushr", "iand", "land", "ior", "lor", |
||||||
|
"ixor", "lxor", "iinc", "i2l", "i2f", "i2d", "l2i", "l2f", "l2d", |
||||||
|
"f2i", "f2l", "f2d", "d2i", "d2l", "d2f", "i2b", "i2c", "i2s", |
||||||
|
"lcmp", "fcmpl", "fcmpg", "dcmpl", "dcmpg", "ifeq", "ifne", "iflt", |
||||||
|
"ifge", "ifgt", "ifle", "if_icmpeq", "if_icmpne", "if_icmplt", |
||||||
|
"if_icmpge", "if_icmpgt", "if_icmple", "if_acmpeq", "if_acmpne", |
||||||
|
"goto", "jsr", "ret", "tableswitch", "lookupswitch", "ireturn", |
||||||
|
"lreturn", "freturn", "dreturn", "areturn", "return", "getstatic", |
||||||
|
"putstatic", "getfield", "putfield", "invokevirtual", |
||||||
|
"invokespecial", "invokestatic", "invokeinterface", "xxxunusedxxx", |
||||||
|
"new", "newarray", "anewarray", "arraylength", "athrow", "checkcast", |
||||||
|
"instanceof", "monitorenter", "monitorexit", "wide", "multianewarray", |
||||||
|
"ifnull", "ifnonnull", "goto_w", "jsr_w", "breakpoint" |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
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) |
||||||
|
}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
/* Reference 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 net.sf.jode.bytecode; |
||||||
|
import net.sf.jode.util.UnifyHash; |
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Iterator; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents a field or method reference. It consists of |
||||||
|
* the class name the method/field name and the type signature. |
||||||
|
*/ |
||||||
|
public class Reference { |
||||||
|
/** |
||||||
|
* A reference consists of a class name, a member name and a type. |
||||||
|
*/ |
||||||
|
private final String clazz, name, type; |
||||||
|
|
||||||
|
private static final UnifyHash unifier = new UnifyHash(); |
||||||
|
|
||||||
|
public static Reference getReference(String className, |
||||||
|
String name, String type) { |
||||||
|
int hash = className.hashCode() ^ name.hashCode() ^ type.hashCode(); |
||||||
|
Iterator iter = unifier.iterateHashCode(hash); |
||||||
|
while (iter.hasNext()) { |
||||||
|
Reference ref = (Reference) iter.next(); |
||||||
|
if (ref.clazz.equals(className) |
||||||
|
&& ref.name.equals(name) |
||||||
|
&& ref.type.equals(type)) |
||||||
|
return ref; |
||||||
|
} |
||||||
|
Reference ref = new Reference(className, name, type); |
||||||
|
unifier.put(hash, ref); |
||||||
|
return ref; |
||||||
|
} |
||||||
|
|
||||||
|
private Reference(String clazz, String name, String type) { |
||||||
|
this.clazz = clazz; |
||||||
|
this.name = name; |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public String getClazz() { |
||||||
|
return clazz; |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public String getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
String classStr = clazz; |
||||||
|
if (clazz.startsWith("L")) |
||||||
|
classStr = clazz.substring(1, clazz.length() - 1) |
||||||
|
.replace('/', '.'); |
||||||
|
return classStr + "." + name + " " + type; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
/* ReferenceInstruction 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction that needs a reference, i.e. |
||||||
|
* a method invocation or field access instruction. |
||||||
|
*/ |
||||||
|
class ReferenceInstruction extends Instruction { |
||||||
|
private Reference reference; |
||||||
|
|
||||||
|
public ReferenceInstruction(int opcode, Reference ref) { |
||||||
|
super(opcode); |
||||||
|
this.reference = ref; |
||||||
|
} |
||||||
|
|
||||||
|
public final Reference getReference() |
||||||
|
{ |
||||||
|
return reference; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setReference(Reference ref) |
||||||
|
{ |
||||||
|
reference = ref; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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" } } */ |
||||||
|
{ |
||||||
|
String typeSig = reference.getType(); |
||||||
|
int opcode = getOpcode(); |
||||||
|
switch (opcode) { |
||||||
|
case opc_invokevirtual: |
||||||
|
case opc_invokespecial: |
||||||
|
case opc_invokestatic: |
||||||
|
case opc_invokeinterface: |
||||||
|
poppush[0] = opcode != opc_invokestatic ? 1 : 0; |
||||||
|
poppush[0] += TypeSignature.getParameterSize(typeSig); |
||||||
|
poppush[1] = TypeSignature.getReturnSize(typeSig); |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_putfield: |
||||||
|
case opc_putstatic: |
||||||
|
poppush[1] = 0; |
||||||
|
poppush[0] = TypeSignature.getTypeSize(typeSig); |
||||||
|
if (opcode == opc_putfield) |
||||||
|
poppush[0]++; |
||||||
|
break; |
||||||
|
|
||||||
|
case opc_getstatic: |
||||||
|
case opc_getfield: |
||||||
|
poppush[1] = TypeSignature.getTypeSize(typeSig); |
||||||
|
poppush[0] = opcode == opc_getfield ? 1 : 0; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
public String toString() { |
||||||
|
return super.toString()+' '+reference; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
/* SlotInstruction 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction in the byte code. |
||||||
|
* |
||||||
|
*/ |
||||||
|
class SlotInstruction extends Instruction { |
||||||
|
private LocalVariableInfo lvi; |
||||||
|
|
||||||
|
/** |
||||||
|
*/ |
||||||
|
public SlotInstruction(int opcode, LocalVariableInfo lvi) { |
||||||
|
super(opcode); |
||||||
|
this.lvi = lvi; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isStore() { |
||||||
|
int opcode = getOpcode(); |
||||||
|
return opcode >= opc_istore && opcode <= opc_astore; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean hasLocal() { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public final int getLocalSlot() |
||||||
|
{ |
||||||
|
return lvi.getSlot(); |
||||||
|
} |
||||||
|
|
||||||
|
public final LocalVariableInfo getLocalInfo() |
||||||
|
{ |
||||||
|
return lvi; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setLocalInfo(LocalVariableInfo info) |
||||||
|
{ |
||||||
|
this.lvi = info; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setLocalSlot(int slot) |
||||||
|
{ |
||||||
|
if (lvi.getName() == null) |
||||||
|
this.lvi = LocalVariableInfo.getInfo(slot); |
||||||
|
else |
||||||
|
this.lvi = LocalVariableInfo.getInfo(slot, |
||||||
|
lvi.getName(), lvi.getType()); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return super.toString()+' '+lvi; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/* SwitchInstruction 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 net.sf.jode.bytecode; |
||||||
|
import net.sf.jode.util.StringQuoter; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction in the byte code. |
||||||
|
* |
||||||
|
*/ |
||||||
|
class SwitchInstruction extends Instruction { |
||||||
|
/** |
||||||
|
* The values for this switch. |
||||||
|
*/ |
||||||
|
private int[] values; |
||||||
|
|
||||||
|
/** |
||||||
|
* Standard constructor: creates an opcode with parameter and |
||||||
|
* lineNr. |
||||||
|
*/ |
||||||
|
SwitchInstruction(int opcode, int[] values) { |
||||||
|
super(opcode); |
||||||
|
this.values = values; |
||||||
|
} |
||||||
|
|
||||||
|
public final int[] getValues() |
||||||
|
{ |
||||||
|
return values; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setValues(int[] values) |
||||||
|
{ |
||||||
|
this.values = values; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
StringBuffer sb = new StringBuffer(opcodeString[getOpcode()]); |
||||||
|
for (int i=0; i< values.length; i++) |
||||||
|
sb.append(' ').append(values[i]); |
||||||
|
return sb.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
/* TypeDimensionInstruction 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an opc_multianewarray instruction. |
||||||
|
* |
||||||
|
*/ |
||||||
|
class TypeDimensionInstruction extends TypeInstruction { |
||||||
|
/** |
||||||
|
* The dimension of this multianewarray operation. |
||||||
|
*/ |
||||||
|
private int dimension; |
||||||
|
|
||||||
|
public TypeDimensionInstruction(int opcode, String type, int dimension) { |
||||||
|
super(opcode, type); |
||||||
|
this.dimension = dimension; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the dimensions for an opc_anewarray opcode. |
||||||
|
*/ |
||||||
|
public final int getDimensions() |
||||||
|
{ |
||||||
|
return dimension; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the dimensions for an opc_anewarray opcode. |
||||||
|
*/ |
||||||
|
public final void setDimensions(int dim) |
||||||
|
{ |
||||||
|
dimension = dim; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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" } } */ |
||||||
|
{ |
||||||
|
poppush[0] = dimension; |
||||||
|
poppush[1] = 1; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return super.toString()+' '+dimension; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
/* TypeInstruction 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 net.sf.jode.bytecode; |
||||||
|
|
||||||
|
/** |
||||||
|
* This class represents an instruction in the byte code. |
||||||
|
* |
||||||
|
*/ |
||||||
|
class TypeInstruction extends Instruction { |
||||||
|
/** |
||||||
|
* The typesignature of the class/array. |
||||||
|
*/ |
||||||
|
private String typeSig; |
||||||
|
|
||||||
|
public TypeInstruction(int opcode, String typeSig) { |
||||||
|
super(opcode); |
||||||
|
this.typeSig = typeSig; |
||||||
|
} |
||||||
|
|
||||||
|
public final String getClazzType() |
||||||
|
{ |
||||||
|
return typeSig; |
||||||
|
} |
||||||
|
|
||||||
|
public final void setClazzType(String type) |
||||||
|
{ |
||||||
|
typeSig = type; |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return super.toString()+' '+typeSig; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,710 @@ |
|||||||
|
/* TypeSignature 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 net.sf.jode.bytecode; |
||||||
|
import net.sf.jode.util.UnifyHash; |
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Map; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
/** |
||||||
|
* This class contains some static methods to handle type signatures. <br> |
||||||
|
* |
||||||
|
* 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><E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;</code></td> |
||||||
|
* </tr><tr><td></td> |
||||||
|
* <td> generic class over <E extends Object> extending |
||||||
|
* Object and implementing Collections<E></td></tr> |
||||||
|
* <tr><td colspan="2"><code><T:Ljava/lang/Object;>([TT;)[TT;</code></td> |
||||||
|
* </tr><tr><td></td> |
||||||
|
* <td> generic method over <T extends Object> taking an |
||||||
|
* array of T as parameters and returning an array of T.</td></tr> |
||||||
|
* </table> |
||||||
|
* |
||||||
|
* |
||||||
|
* @author Jochen Hoenicke |
||||||
|
*/ |
||||||
|
public class TypeSignature { |
||||||
|
/** |
||||||
|
* This is a private method for generating the signature of a |
||||||
|
* given type. |
||||||
|
*/ |
||||||
|
private static final StringBuffer appendSignature(StringBuffer sb, |
||||||
|
Class javaType) { |
||||||
|
if (javaType.isPrimitive()) { |
||||||
|
if (javaType == Boolean.TYPE) |
||||||
|
return sb.append('Z'); |
||||||
|
else if (javaType == Byte.TYPE) |
||||||
|
return sb.append('B'); |
||||||
|
else if (javaType == Character.TYPE) |
||||||
|
return sb.append('C'); |
||||||
|
else if (javaType == Short.TYPE) |
||||||
|
return sb.append('S'); |
||||||
|
else if (javaType == Integer.TYPE) |
||||||
|
return sb.append('I'); |
||||||
|
else if (javaType == Long.TYPE) |
||||||
|
return sb.append('J'); |
||||||
|
else if (javaType == Float.TYPE) |
||||||
|
return sb.append('F'); |
||||||
|
else if (javaType == Double.TYPE) |
||||||
|
return sb.append('D'); |
||||||
|
else if (javaType == Void.TYPE) |
||||||
|
return sb.append('V'); |
||||||
|
else |
||||||
|
throw new InternalError("Unknown primitive type: "+javaType); |
||||||
|
} else if (javaType.isArray()) { |
||||||
|
return appendSignature(sb.append('['), |
||||||
|
javaType.getComponentType()); |
||||||
|
} else { |
||||||
|
return sb.append('L') |
||||||
|
.append(javaType.getName().replace('.','/')).append(';'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates the type signature of the given Class. |
||||||
|
* @param clazz a java.lang.Class, this may also be a primitive or |
||||||
|
* array type. |
||||||
|
* @return the type signature. |
||||||
|
*/ |
||||||
|
public static String getSignature(Class clazz) { |
||||||
|
return appendSignature(new StringBuffer(), clazz).toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a method signature. |
||||||
|
* @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. |
||||||
|
* @return the method type signature |
||||||
|
*/ |
||||||
|
public static String getSignature(Class paramT[], Class returnT) { |
||||||
|
StringBuffer sig = new StringBuffer("("); |
||||||
|
for (int i=0; i< paramT.length; i++) |
||||||
|
appendSignature(sig, paramT[i]); |
||||||
|
return appendSignature(sig.append(')'), returnT).toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generates a Class object for a type signature. This is the |
||||||
|
* inverse function of getSignature. |
||||||
|
* @param typeSig a single type signature |
||||||
|
* @return the Class object representing that type. |
||||||
|
*/ |
||||||
|
public static Class getClass(String typeSig) |
||||||
|
throws ClassNotFoundException |
||||||
|
{ |
||||||
|
switch(typeSig.charAt(0)) { |
||||||
|
case 'Z': |
||||||
|
return Boolean.TYPE; |
||||||
|
case 'B': |
||||||
|
return Byte.TYPE; |
||||||
|
case 'C': |
||||||
|
return Character.TYPE; |
||||||
|
case 'S': |
||||||
|
return Short.TYPE; |
||||||
|
case 'I': |
||||||
|
return Integer.TYPE; |
||||||
|
case 'F': |
||||||
|
return Float.TYPE; |
||||||
|
case 'J': |
||||||
|
return Long.TYPE; |
||||||
|
case 'D': |
||||||
|
return Double.TYPE; |
||||||
|
case 'V': |
||||||
|
return Void.TYPE; |
||||||
|
case 'L': |
||||||
|
typeSig = typeSig.substring(1, typeSig.length()-1) |
||||||
|
.replace('/','.'); |
||||||
|
/* fall through */ |
||||||
|
case '[': |
||||||
|
return Class.forName(typeSig); |
||||||
|
} |
||||||
|
throw new IllegalArgumentException(typeSig); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Check if the given type is a two slot type. The only two slot |
||||||
|
* types are long and double. |
||||||
|
*/ |
||||||
|
private static boolean usingTwoSlots(char type) { |
||||||
|
return "JD".indexOf(type) >= 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the number of words, an object of the given simple type |
||||||
|
* signature takes. For long and double this is two, for all other |
||||||
|
* types it is one. |
||||||
|
*/ |
||||||
|
public static int getTypeSize(String typeSig) { |
||||||
|
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) { |
||||||
|
if (typeSig.charAt(0) != '[') |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
return typeSig.substring(1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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') |
||||||
|
throw new IllegalArgumentException(); |
||||||
|
return classpath.getClassInfo |
||||||
|
(typeSig.substring(1, typeSig.length()-1).replace('/', '.')); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
public static int skipType(String methodTypeSig, int position) { |
||||||
|
char c = methodTypeSig.charAt(position++); |
||||||
|
while (c == '[') |
||||||
|
c = methodTypeSig.charAt(position++); |
||||||
|
if (c == 'L' || c == 'T') { |
||||||
|
int angledepth = 0; |
||||||
|
c = methodTypeSig.charAt(position++); |
||||||
|
while (c != ';' || angledepth > 0) { |
||||||
|
if (c == '<') |
||||||
|
angledepth++; |
||||||
|
else if (c == '>') |
||||||
|
angledepth--; |
||||||
|
c = methodTypeSig.charAt(position++); |
||||||
|
} |
||||||
|
} |
||||||
|
return position; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the number of words the parameters for the given method |
||||||
|
* type signature takes. This is the sum of getTypeSize() for |
||||||
|
* each parameter type. |
||||||
|
* @param methodTypeSig the method type signature. |
||||||
|
* @return the number of words the parameters take. |
||||||
|
*/ |
||||||
|
public static int getParameterSize(String methodTypeSig) { |
||||||
|
int nargs = 0; |
||||||
|
int i = 1; |
||||||
|
for (;;) { |
||||||
|
char c = methodTypeSig.charAt(i); |
||||||
|
if (c == ')') |
||||||
|
return nargs; |
||||||
|
i = skipType(methodTypeSig, i); |
||||||
|
if (usingTwoSlots(c)) |
||||||
|
nargs += 2; |
||||||
|
else |
||||||
|
nargs++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the size of the return type of the given method in words. |
||||||
|
* This is zero for void return type, two for double or long return |
||||||
|
* 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) { |
||||||
|
int length = methodTypeSig.length(); |
||||||
|
if (methodTypeSig.charAt(length - 2) == ')') { |
||||||
|
// This is a single character return type.
|
||||||
|
char returnType = methodTypeSig.charAt(length - 1); |
||||||
|
return returnType == 'V' ? 0 |
||||||
|
: usingTwoSlots(returnType) ? 2 : 1; |
||||||
|
} else |
||||||
|
// All multi character return types take one parameter
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the argument type signatures of the given template signature. |
||||||
|
* @param templateTypeSig the template type signature. |
||||||
|
* @return an array containing all parameter types in correct order. |
||||||
|
*/ |
||||||
|
public static String[] getArgumentTypes(String templateTypeSig) { |
||||||
|
System.err.println(templateTypeSig); |
||||||
|
int pos = 1; |
||||||
|
int count = 0; |
||||||
|
char c; |
||||||
|
while ((c = templateTypeSig.charAt(pos)) != '>') { |
||||||
|
if (c == '*') { |
||||||
|
pos++; |
||||||
|
} else { |
||||||
|
if (c == '+' || c == '-') |
||||||
|
pos++; |
||||||
|
pos = skipType(templateTypeSig, pos); |
||||||
|
} |
||||||
|
count++; |
||||||
|
} |
||||||
|
String[] params = new String[count]; |
||||||
|
pos = 1; |
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
int start = pos; |
||||||
|
c = templateTypeSig.charAt(pos); |
||||||
|
if (c == '*') { |
||||||
|
pos++; |
||||||
|
} else { |
||||||
|
if (c == '+' || c == '-') |
||||||
|
pos++; |
||||||
|
pos = skipType(templateTypeSig, pos); |
||||||
|
} |
||||||
|
params[i] = templateTypeSig.substring(start, pos); |
||||||
|
} |
||||||
|
return params; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the parameter type signatures of the given method signature. |
||||||
|
* @param methodTypeSig the method type signature. |
||||||
|
* @return an array containing all parameter types in correct order. |
||||||
|
*/ |
||||||
|
public static String[] getParameterTypes(String methodTypeSig) { |
||||||
|
System.err.println(methodTypeSig); |
||||||
|
int pos = 1; |
||||||
|
int count = 0; |
||||||
|
while (methodTypeSig.charAt(pos) != ')') { |
||||||
|
pos = skipType(methodTypeSig, pos); |
||||||
|
count++; |
||||||
|
} |
||||||
|
String[] params = new String[count]; |
||||||
|
pos = 1; |
||||||
|
for (int i = 0; i < count; i++) { |
||||||
|
int start = pos; |
||||||
|
pos = skipType(methodTypeSig, pos); |
||||||
|
params[i] = methodTypeSig.substring(start, pos); |
||||||
|
} |
||||||
|
return params; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the return type for a method signature |
||||||
|
* @param methodTypeSig the method signature. |
||||||
|
* @return the return type for a method signature, `V' for void methods. |
||||||
|
*/ |
||||||
|
public static String getReturnType(String methodTypeSig) { |
||||||
|
return methodTypeSig.substring(methodTypeSig.lastIndexOf(')')+1); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the names of the generic parameters of the given type signature. |
||||||
|
* @param typeSig the type signature. |
||||||
|
* @return an array containing all generic parameter types |
||||||
|
* in correct order, or null if there aren't any generic parameters. |
||||||
|
*/ |
||||||
|
public static String[] getGenericNames(String typeSig) { |
||||||
|
System.err.println(typeSig); |
||||||
|
if (typeSig.charAt(0) != '<') |
||||||
|
return null; |
||||||
|
int pos = 1; |
||||||
|
int count = 0; |
||||||
|
while (typeSig.charAt(pos) != '>') { |
||||||
|
while (typeSig.charAt(pos) != ':') |
||||||
|
pos++; |
||||||
|
/* check for empty entry */ |
||||||
|
if (typeSig.charAt(pos+1) == ':') |
||||||
|
pos++; |
||||||
|
while (typeSig.charAt(pos) == ':') { |
||||||
|
/* skip colon and type */ |
||||||
|
pos = skipType(typeSig, pos + 1); |
||||||
|
} |
||||||
|
count++; |
||||||
|
} |
||||||
|
String[] params = new String[count]; |
||||||
|
pos = 1; |
||||||
|
count = 0; |
||||||
|
while (typeSig.charAt(pos) != '>') { |
||||||
|
int spos = pos; |
||||||
|
while (typeSig.charAt(pos) != ':') |
||||||
|
pos++; |
||||||
|
params[count++] = typeSig.substring(spos, pos); |
||||||
|
/* check for empty entry */ |
||||||
|
if (typeSig.charAt(pos+1) == ':') |
||||||
|
pos++; |
||||||
|
while (typeSig.charAt(pos) == ':') { |
||||||
|
/* skip colon and type */ |
||||||
|
pos = skipType(typeSig, pos + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
return params; |
||||||
|
} |
||||||
|
|
||||||
|
private static int mapGenericsInType(String typeSig, Map generics, |
||||||
|
StringBuffer mapped, int spos) { |
||||||
|
int pos = spos; |
||||||
|
char c = typeSig.charAt(pos++); |
||||||
|
while (c == '[') |
||||||
|
c = typeSig.charAt(pos++); |
||||||
|
if (c == 'T') { |
||||||
|
int epos = typeSig.indexOf(';', pos); |
||||||
|
String key = typeSig.substring(pos, epos); |
||||||
|
String mapval = (String) generics.get(key); |
||||||
|
if (mapval != null) { |
||||||
|
mapped.append(typeSig.substring(spos, pos - 1)) |
||||||
|
.append(key); |
||||||
|
spos = epos + 1; |
||||||
|
} |
||||||
|
pos = epos + 1; |
||||||
|
} else if (c == 'L') { |
||||||
|
c = typeSig.charAt(pos++); |
||||||
|
while (c != ';' && c != '<') |
||||||
|
c = typeSig.charAt(pos++); |
||||||
|
if (c == '<') { |
||||||
|
mapped.append(typeSig.substring(spos, pos)); |
||||||
|
while (typeSig.charAt(pos) != '>') { |
||||||
|
pos = mapGenericsInType(typeSig, generics, mapped, pos); |
||||||
|
} |
||||||
|
spos = pos; |
||||||
|
pos += 2; |
||||||
|
} |
||||||
|
} |
||||||
|
mapped.append(typeSig.substring(spos, pos)); |
||||||
|
return pos; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Map the names of the generic parameters in the given type signature |
||||||
|
* and return the type signature with the generic parameters mapped to |
||||||
|
* (more or less) real types. |
||||||
|
* @param typeSig the type signature. |
||||||
|
* @param generics A map from generic names to type signatures. |
||||||
|
* @return the mapped generic type signature. |
||||||
|
*/ |
||||||
|
public static String mapGenerics(String typeSig, Map generics) { |
||||||
|
StringBuffer mapped = new StringBuffer(); |
||||||
|
int pos = 0; |
||||||
|
int spos = 0; |
||||||
|
if (typeSig.length() == 0) |
||||||
|
return ""; |
||||||
|
char c = typeSig.charAt(pos++); |
||||||
|
if (c == '<') { |
||||||
|
c = typeSig.charAt(pos++); |
||||||
|
while (c != '>') { |
||||||
|
while (c != ':') { |
||||||
|
c = typeSig.charAt(pos++); |
||||||
|
} |
||||||
|
if (typeSig.charAt(pos) == ':') |
||||||
|
pos++; |
||||||
|
while (c == ':') { |
||||||
|
mapped.append(typeSig.substring(spos, pos)); |
||||||
|
pos = mapGenericsInType(typeSig, generics, mapped, pos); |
||||||
|
spos = pos; |
||||||
|
c = typeSig.charAt(pos); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (c == '(') { |
||||||
|
while (typeSig.charAt(pos) != ')') { |
||||||
|
mapped.append(typeSig.substring(spos, pos)); |
||||||
|
pos = mapGenericsInType(typeSig, generics, mapped, pos); |
||||||
|
spos = pos; |
||||||
|
} |
||||||
|
pos++; |
||||||
|
} |
||||||
|
mapped.append(typeSig.substring(spos, pos)); |
||||||
|
while (pos < typeSig.length()) { |
||||||
|
pos = mapGenericsInType(typeSig, generics, mapped, pos); |
||||||
|
} |
||||||
|
return mapped.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the default value an object of the given type has. It is |
||||||
|
* 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. |
||||||
|
* @return the index at which the class name ends. |
||||||
|
* @exception IllegalArgumentException if there was an illegal character. |
||||||
|
* @exception StringIndexOutOfBoundsException if the typeSig ended early. |
||||||
|
*/ |
||||||
|
private static int checkClassName(String clName, int i) |
||||||
|
throws IllegalArgumentException, StringIndexOutOfBoundsException |
||||||
|
{ |
||||||
|
while (true) { |
||||||
|
char c = clName.charAt(i++); |
||||||
|
if (c == '<') { |
||||||
|
c = clName.charAt(i++); |
||||||
|
do { |
||||||
|
if (c == '*') |
||||||
|
i++; |
||||||
|
else { |
||||||
|
if (c == '+' || c == '-') |
||||||
|
c = clName.charAt(i++); |
||||||
|
if (c != 'L' && c != 'T' && c != '[') |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Wrong class instantiation: "+clName); |
||||||
|
i = checkTypeSig(clName, i - 1); |
||||||
|
} |
||||||
|
c = clName.charAt(i++); |
||||||
|
} while (c != '>'); |
||||||
|
c = clName.charAt(i++); |
||||||
|
if (c != ';') |
||||||
|
throw new IllegalArgumentException |
||||||
|
("no ; after > in "+clName); |
||||||
|
} |
||||||
|
if (c == ';') |
||||||
|
return i; |
||||||
|
if (c != '/' && !Character.isJavaIdentifierPart(c)) |
||||||
|
throw new IllegalArgumentException("Illegal java class name: " |
||||||
|
+ clName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks if there is a valid class name starting at index |
||||||
|
* in string typeSig and ending with a semicolon. |
||||||
|
* @return the index at which the class name ends. |
||||||
|
* @exception IllegalArgumentException if there was an illegal character. |
||||||
|
* @exception StringIndexOutOfBoundsException if the typeSig ended early. |
||||||
|
*/ |
||||||
|
private static int checkTemplateName(String clName, int i) |
||||||
|
throws IllegalArgumentException, StringIndexOutOfBoundsException |
||||||
|
{ |
||||||
|
while (true) { |
||||||
|
char c = clName.charAt(i++); |
||||||
|
if (c == ';') |
||||||
|
return i; |
||||||
|
if (!Character.isJavaIdentifierPart(c)) |
||||||
|
throw new IllegalArgumentException("Illegal java class name: " |
||||||
|
+ clName); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks if there is a valid simple type signature starting at index |
||||||
|
* in string typeSig. |
||||||
|
* @return the index at which the type signature ends. |
||||||
|
* @exception IllegalArgumentException if there was an illegal character. |
||||||
|
* @exception StringIndexOutOfBoundsException if the typeSig ended early. |
||||||
|
*/ |
||||||
|
private static int checkTypeSig(String typeSig, int index) { |
||||||
|
char c = typeSig.charAt(index++); |
||||||
|
while (c == '[') |
||||||
|
c = typeSig.charAt(index++); |
||||||
|
if (c == 'L') { |
||||||
|
index = checkClassName(typeSig, index); |
||||||
|
} else if (c == 'T') { |
||||||
|
index = checkTemplateName(typeSig, index); |
||||||
|
} else { |
||||||
|
if ("ZBSCIJFD".indexOf(c) == -1) |
||||||
|
throw new IllegalArgumentException("Type sig error: "+typeSig); |
||||||
|
} |
||||||
|
return index; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether a given type signature starts with valid generic |
||||||
|
* part and returns the index where generics end. |
||||||
|
* @param typeSig the type signature. |
||||||
|
* @param i the start index. |
||||||
|
* @exception NullPointerException if typeSig is null. |
||||||
|
* @exception IllegalArgumentException if typeSig is not a valid |
||||||
|
* type signature. |
||||||
|
* @return 0 if no generics at beginning, otherwise the |
||||||
|
* index after the > sign. |
||||||
|
*/ |
||||||
|
private static int checkGenerics(String typeSig, int i) { |
||||||
|
if (typeSig.charAt(i) == '<') { |
||||||
|
i++; |
||||||
|
char c = typeSig.charAt(i++); |
||||||
|
if (c == '>') |
||||||
|
throw new IllegalArgumentException("Empty Generics: "+typeSig); |
||||||
|
while (c != '>') { |
||||||
|
if (c == ':') |
||||||
|
throw new IllegalArgumentException("Empty type name: "+typeSig); |
||||||
|
while (c != ':') { |
||||||
|
if (!Character.isJavaIdentifierPart(c)) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Illegal generic name: "+ typeSig); |
||||||
|
c = typeSig.charAt(i++); |
||||||
|
} |
||||||
|
c = typeSig.charAt(i++); |
||||||
|
if (c != 'L' && c != 'T' && c != ':') |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Wrong generic extends: "+typeSig); |
||||||
|
if (c != ':') |
||||||
|
i = checkTypeSig(typeSig, i - 1); |
||||||
|
|
||||||
|
c = typeSig.charAt(i++); |
||||||
|
while(c == ':') { |
||||||
|
i = checkTypeSig(typeSig, i); |
||||||
|
c = typeSig.charAt(i++); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return i; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) |
||||||
|
throws IllegalArgumentException |
||||||
|
{ |
||||||
|
try { |
||||||
|
int i = checkGenerics(typeSig, 0); |
||||||
|
if (checkTypeSig(typeSig, i) != typeSig.length()) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Type sig too long: "+typeSig); |
||||||
|
} catch (StringIndexOutOfBoundsException ex) { |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Incomplete type sig: "+typeSig); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks whether a given type signature is a valid class
|
||||||
|
* type signature. Throws an exception otherwise. |
||||||
|
* A class type signature starts optionally with generics, |
||||||
|
* followed by type signature of super class, followed by |
||||||
|
* type signature of super interfaces. |
||||||
|
* @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 checkClassTypeSig(String typeSig) |
||||||
|
throws IllegalArgumentException |
||||||
|
{ |
||||||
|
try { |
||||||
|
int i = checkGenerics(typeSig, 0); |
||||||
|
i = checkTypeSig(typeSig, i); |
||||||
|
while (i != typeSig.length()) { |
||||||
|
i = checkTypeSig(typeSig, i); |
||||||
|
} |
||||||
|
} catch (StringIndexOutOfBoundsException ex) { |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Incomplete type sig: "+typeSig); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) |
||||||
|
throws IllegalArgumentException |
||||||
|
{ |
||||||
|
try { |
||||||
|
int i = checkGenerics(typeSig, 0); |
||||||
|
if (typeSig.charAt(i) != '(') |
||||||
|
throw new IllegalArgumentException |
||||||
|
("No method signature: "+typeSig); |
||||||
|
i++; |
||||||
|
while (typeSig.charAt(i) != ')') |
||||||
|
i = checkTypeSig(typeSig, i); |
||||||
|
// skip closing parenthesis.
|
||||||
|
i++; |
||||||
|
if (typeSig.charAt(i) == 'V') |
||||||
|
// accept void return type.
|
||||||
|
i++; |
||||||
|
else |
||||||
|
i = checkTypeSig(typeSig, i); |
||||||
|
if (i != typeSig.length()) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Type sig too long: "+typeSig); |
||||||
|
} catch (StringIndexOutOfBoundsException ex) { |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Incomplete type sig: "+typeSig); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,106 @@ |
|||||||
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<!-- |
||||||
|
Copyright (C) 1998-1999 Jochen Hoenicke. |
||||||
|
|
||||||
|
This documentation is free; 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. |
||||||
|
--> |
||||||
|
<title>Jode Bytecode Package</title> |
||||||
|
</head> |
||||||
|
|
||||||
|
<body> |
||||||
|
Provides easy access to class files and their contents. To use it you |
||||||
|
create a <code>ClassPath</code> object giving it the locations where |
||||||
|
it should search for classes. Then you can ask this object for a |
||||||
|
class and get a ClassInfo object. As third step you can actually load |
||||||
|
the class.<br><br> |
||||||
|
|
||||||
|
Please notify me if you want to use this package. I will inform you |
||||||
|
about updates, help you with problems, etc. <b> WARNING: </b> Some |
||||||
|
parts of this package may change in the future in incompatible ways. |
||||||
|
Ask me for more information. <br><br> |
||||||
|
|
||||||
|
Here is a short example, how you can use this package, see the |
||||||
|
documentation of the classes for more details. |
||||||
|
<pre> |
||||||
|
... |
||||||
|
ClassPath path = new ClassPath("/usr/lib/java/lib/classes.zip"); |
||||||
|
ClassInfo clazz = path.getClassInfo("java.util.Hashtable"); |
||||||
|
|
||||||
|
try { |
||||||
|
clazz.load(ClassInfo.DECLARATIONS); |
||||||
|
} catch (ClassFormatException ex) { |
||||||
|
System.err.println("Something is wrong with HashTable, giving up!"); |
||||||
|
return; |
||||||
|
} catch (IOException ex) { |
||||||
|
System.err.println("Can't load HashTable, giving up!"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
MethodInfo[] methods = clazz.getMethods(); |
||||||
|
for (int i = 0; i < methods.length; i++) { |
||||||
|
String type = methods[i].getType(); |
||||||
|
if (TypeSignature.getReturnType(type) == TypeSignature.INT_TYPE) |
||||||
|
System.out.println("Found integer method: "+method.getName()); |
||||||
|
} |
||||||
|
... |
||||||
|
</pre> |
||||||
|
|
||||||
|
You can also use this package to create and write new classes: |
||||||
|
<pre> |
||||||
|
... |
||||||
|
ClassPath path = new ClassPath("/usr/lib/java/lib/classes.zip"); |
||||||
|
ClassInfo clazz = path.getClassInfo("my.new.Class"); |
||||||
|
clazz.setModifiers(Modifier.PUBLIC); |
||||||
|
clazz.setSourceFile("Class.pl"); |
||||||
|
clazz.set... |
||||||
|
clazz.write(zipOutputStream); |
||||||
|
... |
||||||
|
</pre> |
||||||
|
|
||||||
|
<h3><a name="advantages">Advantages of this bytecode package</a></h3> |
||||||
|
<ul> |
||||||
|
<li>You don't need to think of the constant pool, except when you want |
||||||
|
to write your custom attributes.</li> |
||||||
|
<li>The set of opcodes is drastically reduced: For example you don't |
||||||
|
have to handle 20 different opcodes that all push a constant value on |
||||||
|
the stack. When reading it will automatically convert them to |
||||||
|
<code>ldc</code> or <code>ldc2</code> and on writing it will convert |
||||||
|
them back.</li> |
||||||
|
<li>Wide instructions are automatically generated when needed, large |
||||||
|
methods are supported.</li> |
||||||
|
<li>The code is organized in {@link net.sf.jode.bytecode.BasicBlocks} |
||||||
|
which makes flow analysis much easier.</li> |
||||||
|
<li>The memory consumption is quite moderate.</li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
<h3>Disadvantages</h3> |
||||||
|
<ul> |
||||||
|
<li>You can't change every byte. For example Jode decides itself if |
||||||
|
a lookup switch or table switch is generated.</li> |
||||||
|
<li>Jode does a lot of checks when reading the bytecode and it is |
||||||
|
impossible to recover from errors. This makes it sometime hard to |
||||||
|
find out why the bytecode of a particular class files is invalid.</li> |
||||||
|
</ul> |
||||||
|
|
||||||
|
<hr> |
||||||
|
<address><a href="mailto:jochen@gnu.org">Jochen Hoenicke</a></address> |
||||||
|
<!-- Created: Thu Jun 22 2000 --> |
||||||
|
<!-- hhmts start --> |
||||||
|
Last modified: Sat Aug 11 18:44:19 MEST 2001 |
||||||
|
<!-- hhmts end --> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,26 @@ |
|||||||
|
/* Analyzer 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 net.sf.jode.decompiler; |
||||||
|
|
||||||
|
public interface Analyzer { |
||||||
|
|
||||||
|
public void analyze(); |
||||||
|
public void dumpSource(TabbedPrintWriter writer) throws java.io.IOException; |
||||||
|
} |
@ -0,0 +1,124 @@ |
|||||||
|
/* Applet 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 net.sf.jode.decompiler; |
||||||
|
import java.awt.Color; |
||||||
|
import java.awt.Dimension; |
||||||
|
import java.awt.Graphics; |
||||||
|
import java.awt.Insets; |
||||||
|
|
||||||
|
public class Applet extends java.applet.Applet { |
||||||
|
private final int BORDER = 10; |
||||||
|
private final int BEVEL = 2; |
||||||
|
private Window jodeWin = new Window(this); |
||||||
|
private Insets myInsets; |
||||||
|
private Color pageColor; |
||||||
|
|
||||||
|
///#ifdef AWT10
|
||||||
|
/// public boolean action(Event e, Object arg) {
|
||||||
|
/// jodeWin.action(e, arg);
|
||||||
|
/// return true;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// public Insets insets() {
|
||||||
|
/// if (myInsets == null) {
|
||||||
|
/// Insets appInsets = super.insets();
|
||||||
|
///#else
|
||||||
|
public Insets getInsets() { |
||||||
|
if (myInsets == null) { |
||||||
|
Insets appInsets = super.getInsets(); |
||||||
|
///#endif
|
||||||
|
myInsets = new Insets |
||||||
|
(appInsets.top+BORDER, appInsets.left+BORDER, |
||||||
|
appInsets.bottom+BORDER, appInsets.right+BORDER); |
||||||
|
} |
||||||
|
return myInsets; |
||||||
|
} |
||||||
|
|
||||||
|
public void paint(Graphics g) { |
||||||
|
super.paint(g); |
||||||
|
Color back = getBackground(); |
||||||
|
Color bright = back.brighter(); |
||||||
|
Color dark = back.darker(); |
||||||
|
///#ifdef AWT10
|
||||||
|
/// Dimension size = size();
|
||||||
|
///#else
|
||||||
|
Dimension size = getSize(); |
||||||
|
///#endif
|
||||||
|
|
||||||
|
// Fill corners with page color:
|
||||||
|
g.setColor(pageColor); |
||||||
|
g.fillRect(0 , 0 , BORDER, BORDER); |
||||||
|
g.fillRect(size.width - BORDER, 0 , BORDER, BORDER); |
||||||
|
g.fillRect(size.width - BORDER, size.height - BORDER, BORDER, BORDER); |
||||||
|
g.fillRect(0 , size.height - BORDER, BORDER, BORDER); |
||||||
|
|
||||||
|
// put filled arcs into corners with highlight color
|
||||||
|
g.setColor(bright); |
||||||
|
g.fillArc(0, 0, |
||||||
|
2*BORDER, 2*BORDER, 90, 90); |
||||||
|
g.fillArc(size.width - 2*BORDER, 0, |
||||||
|
2*BORDER, 2*BORDER, 45, 45); |
||||||
|
g.fillArc(0, size.height - 2*BORDER, |
||||||
|
2*BORDER, 2*BORDER, 180, 45); |
||||||
|
|
||||||
|
// draw highlighted edges
|
||||||
|
g.fillRect(BORDER, 0, size.width - 2*BORDER, BEVEL); |
||||||
|
g.fillRect(0, BORDER, BEVEL, size.height - 2*BORDER); |
||||||
|
|
||||||
|
// The same as above on the other side with dark color.
|
||||||
|
g.setColor(dark); |
||||||
|
g.fillArc(size.width - 2*BORDER, 0, |
||||||
|
2*BORDER, 2*BORDER, 0, 45); |
||||||
|
g.fillArc(0, size.height - 2*BORDER, |
||||||
|
2*BORDER, 2*BORDER, 225, 45); |
||||||
|
g.fillArc(size.width - 2*BORDER, size.height - 2*BORDER, |
||||||
|
2*BORDER, 2*BORDER, -90, 90); |
||||||
|
g.fillRect(BORDER, size.height - BEVEL, size.width - 2*BORDER, BEVEL); |
||||||
|
g.fillRect(size.width - BEVEL, BORDER, BEVEL, size.height - 2*BORDER); |
||||||
|
|
||||||
|
// Finally fill the corners with background color again.
|
||||||
|
g.setColor(back); |
||||||
|
g.fillArc(BEVEL, BEVEL, |
||||||
|
2*(BORDER-BEVEL), 2*(BORDER-BEVEL), 90, 90); |
||||||
|
g.fillArc(size.width - (2*BORDER-BEVEL), BEVEL, |
||||||
|
2*(BORDER-BEVEL), 2*(BORDER-BEVEL), 0, 90); |
||||||
|
g.fillArc(BEVEL, size.height - 2*BORDER + BEVEL, |
||||||
|
2*(BORDER-BEVEL), 2*(BORDER-BEVEL), 180, 90); |
||||||
|
g.fillArc(size.width - (2*BORDER-BEVEL), |
||||||
|
size.height - (2*BORDER-BEVEL), |
||||||
|
2*(BORDER-BEVEL), 2*(BORDER-BEVEL), -90, 90); |
||||||
|
} |
||||||
|
|
||||||
|
public void init() { |
||||||
|
String colorstr = getParameter("pagecolor"); |
||||||
|
if (colorstr == null) |
||||||
|
colorstr = "ffffff"; |
||||||
|
this.pageColor = new Color(Integer.parseInt(colorstr, 16)); |
||||||
|
colorstr = getParameter("bgcolor"); |
||||||
|
if (colorstr != null) |
||||||
|
setBackground(new Color(Integer.parseInt(colorstr, 16))); |
||||||
|
String cp = getParameter("classpath"); |
||||||
|
if (cp != null) |
||||||
|
jodeWin.setClassPath(cp); |
||||||
|
String cls = getParameter("class"); |
||||||
|
if (cls != null) |
||||||
|
jodeWin.setClass(cls); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,825 @@ |
|||||||
|
/* ClassAnalyzer 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 net.sf.jode.decompiler; |
||||||
|
import net.sf.jode.GlobalOptions; |
||||||
|
import net.sf.jode.type.MethodType; |
||||||
|
import net.sf.jode.type.Type; |
||||||
|
import net.sf.jode.bytecode.ClassFormatException; |
||||||
|
import net.sf.jode.bytecode.ClassInfo; |
||||||
|
import net.sf.jode.bytecode.ClassPath; |
||||||
|
import net.sf.jode.bytecode.FieldInfo; |
||||||
|
import net.sf.jode.bytecode.MethodInfo; |
||||||
|
import net.sf.jode.expr.Expression; |
||||||
|
import net.sf.jode.expr.ThisOperator; |
||||||
|
import net.sf.jode.flow.TransformConstructors; |
||||||
|
import net.sf.jode.flow.StructuredBlock; |
||||||
|
import net.sf.jode.util.SimpleSet; |
||||||
|
|
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
import java.util.NoSuchElementException; |
||||||
|
import java.util.Vector; |
||||||
|
import java.util.Enumeration; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.Set; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
public class ClassAnalyzer |
||||||
|
implements Scope, Declarable, ClassDeclarer |
||||||
|
{ |
||||||
|
ImportHandler imports; |
||||||
|
ClassInfo clazz; |
||||||
|
ClassDeclarer parent; |
||||||
|
ProgressListener progressListener; |
||||||
|
String[] generics; |
||||||
|
Type[] genericTypes; |
||||||
|
|
||||||
|
/** |
||||||
|
* The complexity for initi#alizing a class. |
||||||
|
*/ |
||||||
|
private static double INITIALIZE_COMPLEXITY = 0.03; |
||||||
|
/** |
||||||
|
* The minimal visible complexity. |
||||||
|
*/ |
||||||
|
private static double STEP_COMPLEXITY = 0.03; |
||||||
|
/** |
||||||
|
* The value of the strictfp modifier. |
||||||
|
* JDK1.1 doesn't define it. |
||||||
|
*/ |
||||||
|
private static int STRICTFP = 0x800; |
||||||
|
|
||||||
|
double methodComplexity = 0.0; |
||||||
|
double innerComplexity = 0.0; |
||||||
|
|
||||||
|
String name; |
||||||
|
StructuredBlock[] blockInitializers; |
||||||
|
FieldAnalyzer[] fields; |
||||||
|
MethodAnalyzer[] methods; |
||||||
|
ClassAnalyzer[] inners; |
||||||
|
int modifiers; |
||||||
|
|
||||||
|
TransformConstructors constrAna; |
||||||
|
MethodAnalyzer staticConstructor; |
||||||
|
MethodAnalyzer[] constructors; |
||||||
|
|
||||||
|
/** |
||||||
|
* The outer values for method scoped classes. |
||||||
|
*/ |
||||||
|
OuterValues outerValues; |
||||||
|
/** |
||||||
|
* The outer instance for non-static class scope classes. |
||||||
|
*/ |
||||||
|
Expression outerInstance; |
||||||
|
|
||||||
|
public ClassAnalyzer(ClassDeclarer parent, |
||||||
|
ClassInfo clazz, ImportHandler imports, |
||||||
|
Expression[] outerValues) |
||||||
|
throws ClassFormatException, IOException |
||||||
|
{ |
||||||
|
clazz.load(ClassInfo.ALL); |
||||||
|
String signatures = clazz.getSignature(); |
||||||
|
if (signatures.charAt(0) == '<') { |
||||||
|
String[] genericSignatures = signature.getGenericNames(); |
||||||
|
generics = new String[genericSignatures.length]; |
||||||
|
for (int i = 0; i < generics.length; i++) { |
||||||
|
int colon = genericSignatures[i].charAt(':'); |
||||||
|
String genName; |
||||||
|
if (colon == -1) { |
||||||
|
generics[i] = genericSignatures[i]; |
||||||
|
genericType[i] = |
||||||
|
new GenericParameterType(genericSignatures[i], |
||||||
|
Type.tObject, |
||||||
|
new ClassType[0]); |
||||||
|
} else { |
||||||
|
generics[i] = genericSignatures[i].substring(0, colon); |
||||||
|
String remainder = genericSignatures[i].substring(colon+1); |
||||||
|
int nextIndex = remainder.skipType(remainder, 0); |
||||||
|
List superClazzes; |
||||||
|
for (;;) { |
||||||
|
String clazzSig = remainder.substring(0, nextIndex); |
||||||
|
superClazzes.add(Type.tType(clazzSig)); |
||||||
|
if (nextIndex >= remainder.length()) |
||||||
|
break; |
||||||
|
remainder = remainder.substring(nextIndex+1); |
||||||
|
} |
||||||
|
ClassType genSupClass = (ClassType) superClazzes.get(0); |
||||||
|
if (!genSupClass.isInterface()) |
||||||
|
superClazzes.remove(0); |
||||||
|
else |
||||||
|
genSupClass = Type.tObject; |
||||||
|
ClassType[] genSupIfaces = (ClassType[]) |
||||||
|
superClazzes.toArray(new ClassType[0]); |
||||||
|
genericType[i] = new GenericParameterType(generics[i], |
||||||
|
genSupClass, |
||||||
|
genSupIfaces); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
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.clazz = clazz; |
||||||
|
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) |
||||||
|
this.outerValues = new OuterValues(this, outerValues); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassAnalyzer(ClassDeclarer parent, |
||||||
|
ClassInfo clazz, ImportHandler imports) |
||||||
|
throws ClassFormatException, IOException |
||||||
|
{ |
||||||
|
this(parent, clazz, imports, null); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassAnalyzer(ClassInfo clazz, ImportHandler imports) |
||||||
|
throws ClassFormatException, IOException |
||||||
|
{ |
||||||
|
this(null, clazz, imports); |
||||||
|
} |
||||||
|
|
||||||
|
public ClassPath getClassPath() { |
||||||
|
return clazz.getClassPath(); |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isStatic() { |
||||||
|
return Modifier.isStatic(modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
public final boolean isStrictFP() { |
||||||
|
return (modifiers & STRICTFP) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
public FieldAnalyzer getField(int index) { |
||||||
|
return fields[index]; |
||||||
|
} |
||||||
|
|
||||||
|
public int getFieldIndex(String fieldName, Type fieldType) { |
||||||
|
for (int i=0; i< fields.length; i++) { |
||||||
|
if (fields[i].getName().equals(fieldName) |
||||||
|
&& fields[i].getType().equals(fieldType)) |
||||||
|
return i; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
public MethodAnalyzer getMethod(String methodName, MethodType methodType) { |
||||||
|
for (int i=0; i< methods.length; i++) { |
||||||
|
if (methods[i].getName().equals(methodName) |
||||||
|
&& methods[i].getType().equals(methodType)) |
||||||
|
return methods[i]; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public int getModifiers() { |
||||||
|
return modifiers; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassDeclarer getParent() { |
||||||
|
return parent; |
||||||
|
} |
||||||
|
|
||||||
|
public void setParent(ClassDeclarer newParent) { |
||||||
|
this.parent = newParent; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassInfo getClazz() { |
||||||
|
return clazz; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
public void setName(String name) { |
||||||
|
this.name = name; |
||||||
|
} |
||||||
|
|
||||||
|
public OuterValues getOuterValues() { |
||||||
|
return outerValues; |
||||||
|
} |
||||||
|
|
||||||
|
public Expression getOuterInstance() { |
||||||
|
return outerInstance; |
||||||
|
} |
||||||
|
|
||||||
|
public void addBlockInitializer(int index, StructuredBlock initializer) { |
||||||
|
if (blockInitializers[index] == null) |
||||||
|
blockInitializers[index] = initializer; |
||||||
|
else |
||||||
|
blockInitializers[index].appendBlock(initializer); |
||||||
|
} |
||||||
|
|
||||||
|
public void initialize() { |
||||||
|
FieldInfo[] finfos = clazz.getFields(); |
||||||
|
MethodInfo[] minfos = clazz.getMethods(); |
||||||
|
ClassInfo[] innerInfos = clazz.getClasses(); |
||||||
|
|
||||||
|
if (finfos == null) { |
||||||
|
/* This means that the class could not be loaded. |
||||||
|
* give up. |
||||||
|
*/ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if ((Options.options & Options.OPTION_INNER) != 0 |
||||||
|
&& innerInfos != null) { |
||||||
|
/* Create inner classes */ |
||||||
|
int innerCount = innerInfos.length; |
||||||
|
inners = new ClassAnalyzer[innerCount]; |
||||||
|
for (int i=0; i < innerCount; i++) { |
||||||
|
try { |
||||||
|
inners[i] = new ClassAnalyzer |
||||||
|
(this, innerInfos[i], imports, null); |
||||||
|
} catch (ClassFormatException ex) { |
||||||
|
GlobalOptions.err.println("Inner class "+innerInfos[i] |
||||||
|
+" malformed!"); |
||||||
|
ex.printStackTrace(GlobalOptions.err); |
||||||
|
} catch (IOException ex) { |
||||||
|
GlobalOptions.err.println("Can't read inner class " |
||||||
|
+innerInfos[i]+"."); |
||||||
|
ex.printStackTrace(GlobalOptions.err); |
||||||
|
} |
||||||
|
} |
||||||
|
} else |
||||||
|
inners = new ClassAnalyzer[0]; |
||||||
|
|
||||||
|
fields = new FieldAnalyzer[finfos.length]; |
||||||
|
methods = new MethodAnalyzer[minfos.length]; |
||||||
|
blockInitializers = new StructuredBlock[finfos.length+1]; |
||||||
|
for (int j=0; j < finfos.length; j++) |
||||||
|
fields[j] = new FieldAnalyzer(this, finfos[j], imports); |
||||||
|
|
||||||
|
staticConstructor = null; |
||||||
|
Vector constrVector = new Vector(); |
||||||
|
for (int j=0; j < methods.length; j++) { |
||||||
|
methods[j] = new MethodAnalyzer(this, minfos[j], imports); |
||||||
|
|
||||||
|
if (methods[j].isConstructor()) { |
||||||
|
if (methods[j].isStatic()) |
||||||
|
staticConstructor = methods[j]; |
||||||
|
else |
||||||
|
constrVector.addElement(methods[j]); |
||||||
|
|
||||||
|
/* Java bytecode can't have strictfp modifier for |
||||||
|
* classes, while java can't have strictfp modifier |
||||||
|
* for constructors. We handle the difference here. |
||||||
|
* |
||||||
|
* If only a few constructors are strictfp and the |
||||||
|
* methods aren't this would add too much strictfp, |
||||||
|
* but that isn't really dangerous. |
||||||
|
*/ |
||||||
|
if (methods[j].isStrictFP()) |
||||||
|
modifiers |= STRICTFP; |
||||||
|
} |
||||||
|
methodComplexity += methods[j].getComplexity(); |
||||||
|
} |
||||||
|
|
||||||
|
constructors = new MethodAnalyzer[constrVector.size()]; |
||||||
|
constrVector.copyInto(constructors); |
||||||
|
|
||||||
|
// initialize the inner classes.
|
||||||
|
for (int j=0; j < inners.length; j++) { |
||||||
|
if (inners[j] == null) |
||||||
|
continue; |
||||||
|
inners[j].initialize(); |
||||||
|
innerComplexity += inners[j].getComplexity(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets the complexity of this class. Must be called after it has |
||||||
|
* been initialized. This is used for a nice progress bar. |
||||||
|
*/ |
||||||
|
public double getComplexity() { |
||||||
|
return (methodComplexity + innerComplexity); |
||||||
|
} |
||||||
|
|
||||||
|
public void analyze(ProgressListener pl, double done, double scale) { |
||||||
|
if (GlobalOptions.verboseLevel > 0) |
||||||
|
GlobalOptions.err.println("Class " + name); |
||||||
|
double subScale = scale / methodComplexity; |
||||||
|
if (pl != null) |
||||||
|
pl.updateProgress(done, name); |
||||||
|
|
||||||
|
imports.useClass(clazz); |
||||||
|
if (clazz.getSuperclass() != null) |
||||||
|
imports.useClass(clazz.getSuperclass()); |
||||||
|
ClassInfo[] interfaces = clazz.getInterfaces(); |
||||||
|
for (int j=0; j< interfaces.length; j++) |
||||||
|
imports.useClass(interfaces[j]); |
||||||
|
|
||||||
|
if (fields == null) { |
||||||
|
/* This means that the class could not be loaded. |
||||||
|
* give up. |
||||||
|
*/ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// First analyze constructors and synthetic fields:
|
||||||
|
constrAna = null; |
||||||
|
if (constructors.length > 0) { |
||||||
|
for (int j=0; j< constructors.length; j++) { |
||||||
|
if (pl != null) { |
||||||
|
double constrCompl = constructors[j].getComplexity() |
||||||
|
* subScale; |
||||||
|
if (constrCompl > STEP_COMPLEXITY) |
||||||
|
constructors[j].analyze(pl, done, constrCompl); |
||||||
|
else { |
||||||
|
pl.updateProgress(done, name); |
||||||
|
constructors[j].analyze(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
done += constrCompl; |
||||||
|
} else |
||||||
|
constructors[j].analyze(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
constrAna = new TransformConstructors(this, false, constructors); |
||||||
|
constrAna.removeSynthInitializers(); |
||||||
|
} |
||||||
|
if (staticConstructor != null) { |
||||||
|
if (pl != null) { |
||||||
|
double constrCompl |
||||||
|
= staticConstructor.getComplexity() * subScale; |
||||||
|
if (constrCompl > STEP_COMPLEXITY) |
||||||
|
staticConstructor.analyze(pl, done, constrCompl); |
||||||
|
else { |
||||||
|
pl.updateProgress(done, name); |
||||||
|
staticConstructor.analyze(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
done += constrCompl; |
||||||
|
} else |
||||||
|
staticConstructor.analyze(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
|
||||||
|
// If output should be immediate, we delay analyzation to output.
|
||||||
|
// Note that this may break anonymous classes, but the user
|
||||||
|
// has been warned.
|
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) |
||||||
|
return; |
||||||
|
|
||||||
|
// Analyze fields
|
||||||
|
for (int j=0; j < fields.length; j++) |
||||||
|
fields[j].analyze(); |
||||||
|
|
||||||
|
// Now analyze remaining methods.
|
||||||
|
for (int j=0; j < methods.length; j++) { |
||||||
|
if (!methods[j].isConstructor()) |
||||||
|
if (pl != null) { |
||||||
|
double methodCompl = methods[j].getComplexity() |
||||||
|
* subScale; |
||||||
|
if (methodCompl > STEP_COMPLEXITY) |
||||||
|
methods[j].analyze(pl, done, methodCompl); |
||||||
|
else { |
||||||
|
pl.updateProgress(done, methods[j].getName()); |
||||||
|
methods[j].analyze(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
done += methodCompl; |
||||||
|
} else |
||||||
|
methods[j].analyze(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void analyzeInnerClasses(ProgressListener pl, |
||||||
|
double done, double scale) { |
||||||
|
double subScale = scale / innerComplexity; |
||||||
|
// If output should be immediate, we delay analyzation to output.
|
||||||
|
// Note that this may break anonymous classes, but the user
|
||||||
|
// has been warned.
|
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) |
||||||
|
return; |
||||||
|
|
||||||
|
// Now analyze the inner classes.
|
||||||
|
for (int j=0; j < inners.length; j++) { |
||||||
|
if (inners[j] == null) |
||||||
|
continue; |
||||||
|
if (pl != null) { |
||||||
|
double innerCompl = inners[j].getComplexity() * subScale; |
||||||
|
if (innerCompl > STEP_COMPLEXITY) { |
||||||
|
double innerscale = subScale * inners[j].methodComplexity; |
||||||
|
inners[j].analyze(pl, done, innerscale); |
||||||
|
inners[j].analyzeInnerClasses(null, done + innerscale, |
||||||
|
innerCompl - innerscale); |
||||||
|
} else { |
||||||
|
pl.updateProgress(done, inners[j].name); |
||||||
|
inners[j].analyze(null, 0.0, 0.0); |
||||||
|
inners[j].analyzeInnerClasses(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
done += innerCompl; |
||||||
|
} else { |
||||||
|
inners[j].analyze(null, 0.0, 0.0); |
||||||
|
inners[j].analyzeInnerClasses(null, 0.0, 0.0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Now analyze the method scoped classes.
|
||||||
|
for (int j=0; j < methods.length; j++) |
||||||
|
methods[j].analyzeInnerClasses(); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void makeDeclaration(Set done) { |
||||||
|
// First prepare constructors:
|
||||||
|
if (constrAna != null) |
||||||
|
constrAna.transform(); |
||||||
|
if (staticConstructor != null) { |
||||||
|
new TransformConstructors |
||||||
|
(this, true, new MethodAnalyzer[] { staticConstructor }) |
||||||
|
.transform(); |
||||||
|
} |
||||||
|
|
||||||
|
// If output should be immediate, we delay analyzation to output.
|
||||||
|
// Note that this may break anonymous classes, but the user
|
||||||
|
// has been warned.
|
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) |
||||||
|
return; |
||||||
|
|
||||||
|
for (int j=0; j < fields.length; j++) |
||||||
|
fields[j].makeDeclaration(done); |
||||||
|
for (int j=0; j < inners.length; j++) |
||||||
|
if (inners[j] != null) |
||||||
|
inners[j].makeDeclaration(done); |
||||||
|
for (int j=0; j < methods.length; j++) |
||||||
|
methods[j].makeDeclaration(done); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpDeclaration(TabbedPrintWriter writer) throws IOException |
||||||
|
{ |
||||||
|
dumpDeclaration(writer, null, 0.0, 0.0); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpDeclaration(TabbedPrintWriter writer, |
||||||
|
ProgressListener pl, double done, double scale) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
if (fields == null) { |
||||||
|
/* This means that the class could not be loaded. |
||||||
|
* give up. |
||||||
|
*/ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
writer.startOp(writer.NO_PAREN, 0); |
||||||
|
/* Clear the SUPER bit, which is also used as SYNCHRONIZED bit. */ |
||||||
|
int modifiedModifiers = modifiers & ~(Modifier.SYNCHRONIZED |
||||||
|
| STRICTFP); |
||||||
|
if (clazz.isInterface()) |
||||||
|
/* interfaces are implicitily abstract */ |
||||||
|
modifiedModifiers &= ~Modifier.ABSTRACT; |
||||||
|
if (parent instanceof MethodAnalyzer) { |
||||||
|
/* method scope classes are implicitly private */ |
||||||
|
modifiedModifiers &= ~Modifier.PRIVATE; |
||||||
|
/* anonymous classes are implicitly final */ |
||||||
|
if (name == null) |
||||||
|
modifiedModifiers &= ~Modifier.FINAL; |
||||||
|
} |
||||||
|
String modif = Modifier.toString(modifiedModifiers); |
||||||
|
if (modif.length() > 0) |
||||||
|
writer.print(modif + " "); |
||||||
|
if (isStrictFP()) { |
||||||
|
/* The STRICTFP modifier is set. |
||||||
|
* We handle it, since java.lang.reflect.Modifier is too dumb. |
||||||
|
*/ |
||||||
|
writer.print("strictfp "); |
||||||
|
} |
||||||
|
/* interface is in modif */ |
||||||
|
if (!clazz.isInterface()) |
||||||
|
writer.print("class "); |
||||||
|
writer.print(name); |
||||||
|
String signature = clazz.getSignature(); |
||||||
|
System.err.println("Class Signature: "+signature+ " (class "+name+")"); |
||||||
|
ClassInfo superClazz = clazz.getSuperclass(); |
||||||
|
if (superClazz != null && |
||||||
|
superClazz.getName() != "java.lang.Object") { |
||||||
|
writer.breakOp(); |
||||||
|
writer.print(" extends " + (writer.getClassString |
||||||
|
(superClazz, Scope.CLASSNAME))); |
||||||
|
} |
||||||
|
ClassInfo[] interfaces = clazz.getInterfaces(); |
||||||
|
if (interfaces.length > 0) { |
||||||
|
writer.breakOp(); |
||||||
|
writer.print(clazz.isInterface() ? " extends " : " implements "); |
||||||
|
writer.startOp(writer.EXPL_PAREN, 1); |
||||||
|
for (int i=0; i < interfaces.length; i++) { |
||||||
|
if (i > 0) { |
||||||
|
writer.print(", "); |
||||||
|
writer.breakOp(); |
||||||
|
} |
||||||
|
writer.print(writer.getClassString |
||||||
|
(interfaces[i], Scope.CLASSNAME)); |
||||||
|
} |
||||||
|
writer.endOp(); |
||||||
|
} |
||||||
|
writer.println(); |
||||||
|
|
||||||
|
writer.openBraceClass(); |
||||||
|
writer.tab(); |
||||||
|
dumpBlock(writer, pl, done, scale); |
||||||
|
writer.untab(); |
||||||
|
writer.closeBraceClass(); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpBlock(TabbedPrintWriter writer) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
dumpBlock(writer, null, 0.0, 0.0); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpBlock(TabbedPrintWriter writer, |
||||||
|
ProgressListener pl, double done, double scale) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
|
||||||
|
double subScale = scale / getComplexity(); |
||||||
|
writer.pushScope(this); |
||||||
|
boolean needFieldNewLine = false; |
||||||
|
boolean needNewLine = false; |
||||||
|
Set declared = null; |
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) |
||||||
|
declared = new SimpleSet(); |
||||||
|
for (int i=0; i< fields.length; i++) { |
||||||
|
if (blockInitializers[i] != null) { |
||||||
|
if (needNewLine) |
||||||
|
writer.println(); |
||||||
|
writer.openBrace(); |
||||||
|
writer.tab(); |
||||||
|
blockInitializers[i].dumpSource(writer); |
||||||
|
writer.untab(); |
||||||
|
writer.closeBrace(); |
||||||
|
needFieldNewLine = needNewLine = true; |
||||||
|
} |
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { |
||||||
|
// We now do the analyzation we skipped before.
|
||||||
|
fields[i].analyze(); |
||||||
|
fields[i].makeDeclaration(declared); |
||||||
|
} |
||||||
|
if (fields[i].skipWriting()) |
||||||
|
continue; |
||||||
|
if (needFieldNewLine) |
||||||
|
writer.println(); |
||||||
|
fields[i].dumpSource(writer); |
||||||
|
needNewLine = true; |
||||||
|
} |
||||||
|
if (blockInitializers[fields.length] != null) { |
||||||
|
if (needNewLine) |
||||||
|
writer.println(); |
||||||
|
writer.openBrace(); |
||||||
|
writer.tab(); |
||||||
|
blockInitializers[fields.length].dumpSource(writer); |
||||||
|
writer.untab(); |
||||||
|
writer.closeBrace(); |
||||||
|
needNewLine = true; |
||||||
|
} |
||||||
|
for (int i=0; i< inners.length; i++) { |
||||||
|
if (needNewLine) |
||||||
|
writer.println(); |
||||||
|
|
||||||
|
if (inners[i] == null) { |
||||||
|
writer.println("COULDN'T READ INNER CLASS!"); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { |
||||||
|
// We now do the analyzation we skipped before.
|
||||||
|
inners[i].analyze(null, 0.0, 0.0); |
||||||
|
inners[i].analyzeInnerClasses(null, 0.0, 0.0); |
||||||
|
inners[i].makeDeclaration(declared); |
||||||
|
} |
||||||
|
|
||||||
|
if (pl != null) { |
||||||
|
double innerCompl = inners[i].getComplexity() * subScale; |
||||||
|
if (innerCompl > STEP_COMPLEXITY) |
||||||
|
inners[i].dumpSource(writer, pl, done, innerCompl); |
||||||
|
else { |
||||||
|
pl.updateProgress(done, name); |
||||||
|
inners[i].dumpSource(writer); |
||||||
|
} |
||||||
|
done += innerCompl; |
||||||
|
} else |
||||||
|
inners[i].dumpSource(writer); |
||||||
|
needNewLine = true; |
||||||
|
} |
||||||
|
for (int i=0; i< methods.length; i++) { |
||||||
|
if ((Options.options & Options.OPTION_IMMEDIATE) != 0) { |
||||||
|
// We now do the analyzation we skipped before.
|
||||||
|
if (!methods[i].isConstructor()) |
||||||
|
methods[i].analyze(null, 0.0, 0.0); |
||||||
|
methods[i].analyzeInnerClasses(); |
||||||
|
methods[i].makeDeclaration(declared); |
||||||
|
} |
||||||
|
|
||||||
|
if (methods[i].skipWriting()) |
||||||
|
continue; |
||||||
|
if (needNewLine) |
||||||
|
writer.println(); |
||||||
|
|
||||||
|
if (pl != null) { |
||||||
|
double methodCompl = methods[i].getComplexity() * subScale; |
||||||
|
pl.updateProgress(done, methods[i].getName()); |
||||||
|
methods[i].dumpSource(writer); |
||||||
|
done += methodCompl; |
||||||
|
} else |
||||||
|
methods[i].dumpSource(writer); |
||||||
|
needNewLine = true; |
||||||
|
} |
||||||
|
writer.popScope(); |
||||||
|
clazz.drop(clazz.DECLARATIONS); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpSource(TabbedPrintWriter writer) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
dumpSource(writer, null, 0.0, 0.0); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpSource(TabbedPrintWriter writer, |
||||||
|
ProgressListener pl, double done, double scale) |
||||||
|
throws IOException |
||||||
|
{ |
||||||
|
dumpDeclaration(writer, pl, done, scale); |
||||||
|
writer.println(); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpJavaFile(TabbedPrintWriter writer) |
||||||
|
throws IOException { |
||||||
|
dumpJavaFile(writer, null); |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpJavaFile(TabbedPrintWriter writer, ProgressListener pl) |
||||||
|
throws IOException { |
||||||
|
imports.init(clazz.getName()); |
||||||
|
LocalInfo.init(); |
||||||
|
initialize(); |
||||||
|
double done = 0.05; |
||||||
|
double scale = (0.75) * methodComplexity |
||||||
|
/ (methodComplexity + innerComplexity); |
||||||
|
analyze(pl, INITIALIZE_COMPLEXITY, scale); |
||||||
|
done += scale; |
||||||
|
analyzeInnerClasses(pl, done, 0.8 - done); |
||||||
|
makeDeclaration(new SimpleSet()); |
||||||
|
imports.dumpHeader(writer); |
||||||
|
dumpSource(writer, pl, 0.8, 0.2); |
||||||
|
if (pl != null) |
||||||
|
pl.updateProgress(1.0, name); |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isScopeOf(Object obj, int scopeType) { |
||||||
|
if (clazz.equals(obj) && scopeType == CLASSSCOPE) |
||||||
|
return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
static int serialnr = 0; |
||||||
|
public void makeNameUnique() { |
||||||
|
name = name + "_" + serialnr++ + "_"; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean conflicts(String name, int usageType) { |
||||||
|
return conflicts(clazz, name, usageType); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean conflicts(ClassInfo info, |
||||||
|
String name, int usageType) { |
||||||
|
while (info != null) { |
||||||
|
if (usageType == NOSUPERMETHODNAME || usageType == METHODNAME) { |
||||||
|
MethodInfo[] minfos = info.getMethods(); |
||||||
|
for (int i = 0; i< minfos.length; i++) |
||||||
|
if (minfos[i].getName().equals(name)) |
||||||
|
return true; |
||||||
|
} |
||||||
|
if (usageType == NOSUPERFIELDNAME || usageType == FIELDNAME |
||||||
|
|| usageType == AMBIGUOUSNAME) { |
||||||
|
FieldInfo[] finfos = info.getFields(); |
||||||
|
for (int i=0; i < finfos.length; i++) { |
||||||
|
if (finfos[i].getName().equals(name)) |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
if (usageType == CLASSNAME || usageType == AMBIGUOUSNAME) { |
||||||
|
try { |
||||||
|
info.load(info.DECLARATIONS); |
||||||
|
} catch (IOException ex) { |
||||||
|
info.guess(info.DECLARATIONS); |
||||||
|
} |
||||||
|
ClassInfo[] iinfos = info.getClasses(); |
||||||
|
if (iinfos != null) { |
||||||
|
for (int i=0; i < iinfos.length; i++) { |
||||||
|
if (iinfos[i].getClassName().equals(name)) |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (usageType == NOSUPERFIELDNAME |
||||||
|
|| usageType == NOSUPERMETHODNAME) |
||||||
|
return false; |
||||||
|
|
||||||
|
ClassInfo[] ifaces = info.getInterfaces(); |
||||||
|
for (int i = 0; i < ifaces.length; i++) |
||||||
|
if (conflicts(ifaces[i], name, usageType)) |
||||||
|
return true; |
||||||
|
info = info.getSuperclass(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class analyzer for the given class info. This searches |
||||||
|
* the method scoped/anonymous classes in this method and all |
||||||
|
* outer methods and the outer classes for the class analyzer. |
||||||
|
* @param cinfo the classinfo for which the analyzer is searched. |
||||||
|
* @return the class analyzer, or null if there is not an outer |
||||||
|
* class that equals cinfo, and not a method scope/inner class in |
||||||
|
* an outer method. |
||||||
|
*/ |
||||||
|
public ClassAnalyzer getClassAnalyzer(ClassInfo cinfo) { |
||||||
|
if (cinfo == getClazz()) |
||||||
|
return this; |
||||||
|
if (parent == null) |
||||||
|
return null; |
||||||
|
return getParent().getClassAnalyzer(cinfo); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class analyzer for the given inner class. |
||||||
|
* @param name the short name of the inner class
|
||||||
|
* @return the class analyzer, or null if there is no inner |
||||||
|
* class with the given name. |
||||||
|
*/ |
||||||
|
public ClassAnalyzer getInnerClassAnalyzer(String name) { |
||||||
|
/** require name != null; **/ |
||||||
|
int innerCount = inners.length; |
||||||
|
for (int i=0; i < innerCount; i++) { |
||||||
|
if (inners[i] != null && inners[i].name.equals(name)) |
||||||
|
return inners[i]; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* We add the named method scoped classes to the declarables. |
||||||
|
*/ |
||||||
|
public void fillDeclarables(Collection used) { |
||||||
|
for (int j=0; j < methods.length; j++) |
||||||
|
methods[j].fillDeclarables(used); |
||||||
|
} |
||||||
|
|
||||||
|
public void addClassAnalyzer(ClassAnalyzer clazzAna) { |
||||||
|
if (parent != null) |
||||||
|
parent.addClassAnalyzer(clazzAna); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return getClass().getName()+"["+getClazz()+"]"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
/* ClassDeclarer 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 net.sf.jode.decompiler; |
||||||
|
import net.sf.jode.bytecode.ClassInfo; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the interface for objects, that a method can declare |
||||||
|
*/ |
||||||
|
public interface ClassDeclarer { |
||||||
|
/** |
||||||
|
* Get the parent of this ClassDeclarer. |
||||||
|
* @return null if this is the outermost instance. |
||||||
|
*/ |
||||||
|
public ClassDeclarer getParent(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Get the class analyzer for the given anonymous class info. It |
||||||
|
* will search it in the classes we declare and in the parent |
||||||
|
* class declarer. |
||||||
|
* @return null if the class analyzer doesn't yet exists. |
||||||
|
*/ |
||||||
|
public ClassAnalyzer getClassAnalyzer(ClassInfo ci); |
||||||
|
|
||||||
|
public void addClassAnalyzer(ClassAnalyzer classAna); |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/* Declarable 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 net.sf.jode.decompiler; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the interface for objects, that a method can declare |
||||||
|
*/ |
||||||
|
public interface Declarable { |
||||||
|
/** |
||||||
|
* Get the name of this declarable. |
||||||
|
*/ |
||||||
|
public String getName(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the name of this local. |
||||||
|
*/ |
||||||
|
public void makeNameUnique(); |
||||||
|
|
||||||
|
public void dumpDeclaration(TabbedPrintWriter writer) |
||||||
|
throws java.io.IOException; |
||||||
|
} |
@ -0,0 +1,233 @@ |
|||||||
|
/* Decompiler Copyright (C) 2000-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 net.sf.jode.decompiler; |
||||||
|
import net.sf.jode.GlobalOptions; |
||||||
|
import net.sf.jode.bytecode.ClassPath; |
||||||
|
import net.sf.jode.bytecode.ClassInfo; |
||||||
|
import java.io.File; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.io.Writer; |
||||||
|
import java.io.BufferedWriter; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the interface that other java classes may use to decompile |
||||||
|
* classes. Feel free to use it in your own GNU GPL'ed project. |
||||||
|
* Please tell me about your project.<br> |
||||||
|
* |
||||||
|
* Note that the GNU GPL doesn't allow you to use this interface in |
||||||
|
* commercial programs. |
||||||
|
* |
||||||
|
* @author <a href="mailto:jochen@gnu.org">Jochen Hoenicke</a> |
||||||
|
* @version 1.0 |
||||||
|
*/ |
||||||
|
public class Decompiler { |
||||||
|
private ClassPath classPath = null; |
||||||
|
private int importPackageLimit = ImportHandler.DEFAULT_PACKAGE_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 |
||||||
|
* 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 |
||||||
|
= ClassPath.altPathSeparatorChar; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new decompiler. Normally you need only one, but you |
||||||
|
* can have more around, with different options and different |
||||||
|
* class paths. |
||||||
|
*/ |
||||||
|
public Decompiler() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the class path. Should be called once before decompile is |
||||||
|
* called, otherwise the system class path is used. |
||||||
|
* @param classpath A comma separated classpath. |
||||||
|
* @exception NullPointerException if classpath is null. |
||||||
|
* @see #setClassPath(String[]) |
||||||
|
*/ |
||||||
|
public void setClassPath(String classpath) { |
||||||
|
this.classPath = new ClassPath(classpath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Set the class path. Should be called once before decompile is |
||||||
|
* called, otherwise the system class path is used. |
||||||
|
* @param classpath a non empty array of jar files and directories; |
||||||
|
* URLs are allowed, too. |
||||||
|
* @exception NullPointerException if classpath is null. |
||||||
|
* @exception IndexOutOfBoundsException if classpath array is empty. |
||||||
|
* @see #setClassPath(String) |
||||||
|
*/ |
||||||
|
public void setClassPath(String[] classpath) { |
||||||
|
this.classPath = new ClassPath(classpath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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 = { |
||||||
|
"lvt", "inner", "anonymous", "push", "pretty", "decrypt", |
||||||
|
"onetime", "immediate", "verify", "contrafo" |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Set an option. |
||||||
|
* @param option the option (pretty, style, decrypt, verify, etc.) |
||||||
|
* @param value ("1"/"0" for on/off, "sun"/"gnu" for style) |
||||||
|
* @exception IllegalArgumentException if option or value is invalid. |
||||||
|
*/ |
||||||
|
public void setOption(String option, String value) { |
||||||
|
if (option.equals("style")) { |
||||||
|
if (value.equals("gnu")) { |
||||||
|
outputStyle = TabbedPrintWriter.GNU_SPACING |
||||||
|
| TabbedPrintWriter.INDENT_BRACES; |
||||||
|
indentSize = 2; |
||||||
|
} else if (value.equals("sun")) { |
||||||
|
outputStyle = TabbedPrintWriter.BRACE_AT_EOL; |
||||||
|
indentSize = 4; |
||||||
|
} else if (value.equals("pascal")) { |
||||||
|
outputStyle = 0; |
||||||
|
indentSize = 4; |
||||||
|
} else |
||||||
|
throw new IllegalArgumentException("Invalid style "+value); |
||||||
|
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")) { |
||||||
|
int comma = value.indexOf(','); |
||||||
|
int packLimit = Integer.parseInt(value.substring(0, comma)); |
||||||
|
if (packLimit == 0) |
||||||
|
packLimit = Integer.MAX_VALUE; |
||||||
|
int clazzLimit = Integer.parseInt(value.substring(comma+1)); |
||||||
|
if (clazzLimit == 0) |
||||||
|
clazzLimit = Integer.MAX_VALUE; |
||||||
|
if (clazzLimit < 0 || packLimit < 0) |
||||||
|
throw new IllegalArgumentException |
||||||
|
("Option import doesn't allow negative parameters"); |
||||||
|
importPackageLimit = packLimit; |
||||||
|
importClassLimit = clazzLimit; |
||||||
|
return; |
||||||
|
} |
||||||
|
if (option.equals("verbose")) { |
||||||
|
GlobalOptions.verboseLevel = Integer.parseInt(value); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (option.equals("debug")) { |
||||||
|
GlobalOptions.setDebugging(value); |
||||||
|
return; |
||||||
|
} |
||||||
|
for (int i=0; i < optionStrings.length; i++) { |
||||||
|
if (option.equals(optionStrings[i])) { |
||||||
|
if (value.equals("0") |
||||||
|
|| value.equals("off") |
||||||
|
|| value.equals("no")) |
||||||
|
Options.options &= ~(1 << i); |
||||||
|
else if (value.equals("1") |
||||||
|
|| value.equals("on") |
||||||
|
|| value.equals("yes")) |
||||||
|
Options.options |= 1 << i; |
||||||
|
else |
||||||
|
throw new IllegalArgumentException("Illegal value for "+ |
||||||
|
option); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new IllegalArgumentException("Illegal option: "+option); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set the stream where copyright and warnings/errors are printed |
||||||
|
* to. |
||||||
|
* @param errorStream the error stream. Note that this is a |
||||||
|
* PrintWriter, not a PrintStream (which are deprecated since 1.1). |
||||||
|
*/ |
||||||
|
public void setErr(PrintWriter errorStream) { |
||||||
|
GlobalOptions.err = errorStream; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Decompile a class. |
||||||
|
* @param className full-qualified classname, dot separated, e.g. |
||||||
|
* "java.lang.Object" |
||||||
|
* @param writer The stream where the decompiled code should be |
||||||
|
* written. Hint: Use a BufferedWriter for good performance. |
||||||
|
* @param progress A progress listener (see below). Null if you |
||||||
|
* don't need information about progress. |
||||||
|
* @exception IllegalArgumentException if className isn't correct. |
||||||
|
* @exception IOException if writer throws an exception. |
||||||
|
* @exception RuntimeException If jode has a bug ;-) |
||||||
|
*/ |
||||||
|
public void decompile(String className, Writer writer, |
||||||
|
ProgressListener progress) |
||||||
|
throws java.io.IOException { |
||||||
|
if (classPath == null) { |
||||||
|
String cp = System.getProperty("java.class.path"); |
||||||
|
String bootcp = System.getProperty("sun.boot.class.path"); |
||||||
|
if (bootcp != null) |
||||||
|
cp = bootcp + altPathSeparatorChar + cp; |
||||||
|
cp = cp.replace(File.pathSeparatorChar, altPathSeparatorChar); |
||||||
|
classPath = new ClassPath(cp); |
||||||
|
} |
||||||
|
|
||||||
|
ClassInfo clazz = classPath.getClassInfo(className); |
||||||
|
ImportHandler imports = new ImportHandler(classPath, |
||||||
|
importPackageLimit, |
||||||
|
importClassLimit); |
||||||
|
TabbedPrintWriter tabbedWriter = |
||||||
|
new TabbedPrintWriter(writer, imports, false, |
||||||
|
outputStyle, indentSize, |
||||||
|
tabWidth, lineWidth); |
||||||
|
ClassAnalyzer clazzAna = new ClassAnalyzer(null, clazz, imports); |
||||||
|
clazzAna.dumpJavaFile(tabbedWriter, progress); |
||||||
|
writer.flush(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,206 @@ |
|||||||
|
/* FieldAnalyzer 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 net.sf.jode.decompiler; |
||||||
|
import net.sf.jode.type.Type; |
||||||
|
import net.sf.jode.bytecode.FieldInfo; |
||||||
|
import net.sf.jode.expr.Expression; |
||||||
|
import net.sf.jode.expr.ThisOperator; |
||||||
|
import net.sf.jode.expr.LocalLoadOperator; |
||||||
|
import net.sf.jode.expr.ConstOperator; |
||||||
|
import net.sf.jode.expr.OuterLocalOperator; |
||||||
|
|
||||||
|
import java.lang.reflect.Modifier; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
///#def COLLECTIONS java.util
|
||||||
|
import java.util.Set; |
||||||
|
///#enddef
|
||||||
|
|
||||||
|
public class FieldAnalyzer implements Analyzer { |
||||||
|
ClassAnalyzer clazz; |
||||||
|
ImportHandler imports; |
||||||
|
int modifiers; |
||||||
|
Type type; |
||||||
|
String fieldName; |
||||||
|
Expression constant; |
||||||
|
boolean isSynthetic; |
||||||
|
boolean isDeprecated; |
||||||
|
boolean analyzedSynthetic = false; |
||||||
|
|
||||||
|
public FieldAnalyzer(ClassAnalyzer cla, FieldInfo fd, |
||||||
|
ImportHandler i) |
||||||
|
{ |
||||||
|
clazz = cla; |
||||||
|
imports = i; |
||||||
|
|
||||||
|
modifiers = fd.getModifiers(); |
||||||
|
type = Type.tType(cla.getClassPath(), fd.getType()); |
||||||
|
fieldName = fd.getName(); |
||||||
|
constant = null; |
||||||
|
this.isSynthetic = fd.isSynthetic(); |
||||||
|
this.isDeprecated = fd.isDeprecated(); |
||||||
|
if (fd.getConstant() != null) { |
||||||
|
if (fd.getConstant() instanceof String) { |
||||||
|
constant = new ConstOperator(cla.getClassPath(), |
||||||
|
(String) fd.getConstant()); |
||||||
|
} else { |
||||||
|
constant = new ConstOperator(fd.getConstant()); |
||||||
|
} |
||||||
|
constant.setType(type); |
||||||
|
constant.makeInitializer(type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public String getName() { |
||||||
|
return fieldName; |
||||||
|
} |
||||||
|
|
||||||
|
public Type getType() { |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public ClassAnalyzer getClassAnalyzer() { |
||||||
|
return clazz; |
||||||
|
} |
||||||
|
|
||||||
|
public Expression getConstant() { |
||||||
|
return constant; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isSynthetic() { |
||||||
|
return isSynthetic; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isFinal() { |
||||||
|
return Modifier.isFinal(modifiers); |
||||||
|
} |
||||||
|
|
||||||
|
public void analyzedSynthetic() { |
||||||
|
analyzedSynthetic = true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean setInitializer(Expression expr) { |
||||||
|
if (constant != null) |
||||||
|
return constant.equals(expr); |
||||||
|
|
||||||
|
/* This should check for isFinal(), but sadly, sometimes jikes |
||||||
|
* doesn't make a val$ field final. I don't know when, or why, |
||||||
|
* so I currently ignore isFinal. |
||||||
|
*/ |
||||||
|
if (isSynthetic |
||||||
|
&& (fieldName.startsWith("this$") |
||||||
|
|| fieldName.startsWith("val$"))) { |
||||||
|
if (fieldName.startsWith("val$") && fieldName.length() > 4 |
||||||
|
&& expr instanceof OuterLocalOperator) { |
||||||
|
LocalInfo li = ((OuterLocalOperator) expr).getLocalInfo(); |
||||||
|
li.addHint(fieldName.substring(4), type); |
||||||
|
} |
||||||
|
analyzedSynthetic(); |
||||||
|
} else |
||||||
|
expr.makeInitializer(type); |
||||||
|
|
||||||
|
constant = expr; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean setClassConstant(String clazzName) { |
||||||
|
if (constant != null) |
||||||
|
return false; |
||||||
|
if (clazzName.charAt(0) == '[') { |
||||||
|
if (clazzName.charAt(clazzName.length()-1) == ';') |
||||||
|
clazzName = clazzName.substring(0, clazzName.length()-1); |
||||||
|
|
||||||
|
if (fieldName.equals("array"+ (clazzName.replace('[', '$') |
||||||
|
.replace('.', '$')))) { |
||||||
|
analyzedSynthetic(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (fieldName.equals("class$" + clazzName.replace('.', '$')) |
||||||
|
|| fieldName.equals("class$L" + clazzName.replace('.', '$'))) { |
||||||
|
analyzedSynthetic(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public void analyze() { |
||||||
|
imports.useType(type); |
||||||
|
} |
||||||
|
|
||||||
|
public void makeDeclaration(Set done) { |
||||||
|
if (constant != null) { |
||||||
|
constant.makeDeclaration(done); |
||||||
|
constant = constant.simplify(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean skipWriting() { |
||||||
|
return analyzedSynthetic; |
||||||
|
} |
||||||
|
|
||||||
|
public void dumpSource(TabbedPrintWriter writer) throws IOException |
||||||
|
{ |
||||||
|
if (isDeprecated) { |
||||||
|
writer.println("/**"); |
||||||
|
writer.println(" * @deprecated"); |
||||||
|
writer.println(" */"); |
||||||
|
} |
||||||
|
if (isSynthetic) |
||||||
|
writer.print("/*synthetic*/ "); |
||||||
|
int modifiedModifiers = modifiers; |
||||||
|
/* |
||||||
|
* JLS-1.0, section 9.3: |
||||||
|
* |
||||||
|
* Every field declaration in the body of an interface is |
||||||
|
* implicitly public, static, and final. It is permitted, but |
||||||
|
* strongly discouraged as a matter of style, to redundantly |
||||||
|
* specify any or all of these modifiers for such fields. |
||||||
|
* |
||||||
|
* But I personally don't like this style..., move the |
||||||
|
* comment mark if you think different. |
||||||
|
|
||||||
|
if (clazz.getClazz().isInterface()) |
||||||
|
modifiedModifiers &= ~(Modifier.PUBLIC |
||||||
|
| Modifier.STATIC |
||||||
|
| Modifier.FINAL); |
||||||
|
*/ |
||||||
|
writer.startOp(writer.NO_PAREN, 0); |
||||||
|
String modif = Modifier.toString(modifiedModifiers); |
||||||
|
if (modif.length() > 0) |
||||||
|
writer.print(modif+" "); |
||||||
|
|
||||||
|
writer.printType(type); |
||||||
|
writer.print(" " + fieldName); |
||||||
|
if (constant != null) { |
||||||
|
writer.breakOp(); |
||||||
|
writer.print(" = "); |
||||||
|
constant.dumpExpression(writer.IMPL_PAREN, writer); |
||||||
|
} |
||||||
|
writer.endOp(); |
||||||
|
writer.println(";"); |
||||||
|
} |
||||||
|
|
||||||
|
public String toString() { |
||||||
|
return getClass().getName()+"["+clazz.getClazz()+"."+getName()+"]"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue