Object subclass:#JavaDecompiler
instanceVariableNames:'code pc javaMethod outStream classToCompileFor'
classVariableNames:'DecoderTable'
poolDictionaries:''
category:'Java-Support'
!
!JavaDecompiler class methodsFor:'initialization'!
initialize
DecoderTable := #(
(nop) "/ 0
(aconst_null) "/ 1
(iconst_m1) "/ 2
(iconst_0) "/ 3
(iconst_1) "/ 4
(iconst_2) "/ 5
(iconst_3) "/ 6
(iconst_4) "/ 7
(iconst_5) "/ 8
(lconst_0) "/ 9
(lconst_1) "/ 10
(fconst_0) "/ 11
(fconst_1) "/ 12
(fconst_2) "/ 13
(dconst_0) "/ 14
(dconst_1) "/ 15
(bipush signedByte) "/ 16
(sipush signedShort) "/ 17
(ldc1 constIndexByte) "/ 18
(ldc2 constIndexShort) "/ 19
(ldc2w constIndexShort) "/ 20
(iload localIndexByte) "/ 21
(lload localIndexByte) "/ 22
(fload localIndexByte) "/ 23
(dload localIndexByte) "/ 24
(aload localIndexByte) "/ 25
(iload_0) "/ 26
(iload_1) "/ 27
(iload_2) "/ 28
(iload_3) "/ 29
(lload_0) "/ 30
(lload_1) "/ 31
(lload_2) "/ 32
(lload_3) "/ 33
(fload_0) "/ 34
(fload_1) "/ 35
(fload_2) "/ 36
(fload_3) "/ 37
(dload_0) "/ 38
(dload_1) "/ 39
(dload_2) "/ 40
(dload_3) "/ 41
(aload_0) "/ 42
(aload_1) "/ 43
(aload_2) "/ 44
(aload_3) "/ 45
(iaload) "/ 46
(laload) "/ 47
(faload) "/ 48
(daload) "/ 49
(aaload) "/ 50
(baload) "/ 51
(caload) "/ 52
(saload) "/ 53
(istore localIndexByte) "/ 54
(lstore localIndexByte) "/ 55
(fstore localIndexByte) "/ 56
(dstore localIndexByte) "/ 57
(astore localIndexByte) "/ 58
(istore_0) "/ 59
(istore_1) "/ 60
(istore_2) "/ 61
(istore_3) "/ 62
(lstore_0) "/ 63
(lstore_1) "/ 64
(lstore_2) "/ 65
(lstore_3) "/ 66
(fstore_0) "/ 67
(fstore_1) "/ 68
(fstore_2) "/ 69
(fstore_3) "/ 70
(dstore_0) "/ 71
(dstore_1) "/ 72
(dstore_2) "/ 73
(dstore_3) "/ 74
(astore_0) "/ 75
(astore_1) "/ 76
(astore_2) "/ 77
(astore_3) "/ 78
(iastore) "/ 79
(lastore) "/ 80
(fastore) "/ 81
(dastore) "/ 82
(aastore) "/ 83
(bastore) "/ 84
(castore) "/ 85
(sastore) "/ 86
(pop) "/ 87
(pop2) "/ 88
(dup) "/ 89
(dup_x1) "/ 90
(dup_x2) "/ 91
(dup2) "/ 92
(dup2_x1) "/ 93
(dup2_x2) "/ 94
(swap) "/ 95
(iadd) "/ 96
(ladd) "/ 97
(fadd) "/ 98
(dadd) "/ 99
(isub) "/ 100
(lsub) "/ 101
(fsub) "/ 102
(dsub) "/ 103
(imul) "/ 104
(lmul) "/ 105
(fmul) "/ 106
(dmul) "/ 107
(idiv) "/ 108
(ldiv) "/ 109
(fdiv) "/ 110
(ddiv) "/ 111
"/ (imod) "/ 112 "/ obsolete
(irem) "/ 112
"/ (lmod) "/ 113 "/ obsolete
(lrem) "/ 113
"/ (fmod) "/ 114 "/ obsolete
(frem) "/ 114
"/ (dmod) "/ 115 "/ obsolete
(drem) "/ 115
(ineg) "/ 116
(lneg) "/ 117
(fneg) "/ 118
(dneg) "/ 119
(ishl) "/ 120
(lshl) "/ 121
(ishr) "/ 122
(lshr) "/ 123
(iushr) "/ 124
(lushr) "/ 125
(iand) "/ 126
(land) "/ 127
(ior) "/ 128
(lor) "/ 129
(ixor) "/ 130
(lxor) "/ 131
(iinc localIndexByte signedByte) "/ 132
(i2l) "/ 133
(i2f) "/ 134
(i2d) "/ 135
(l2i) "/ 136
(l2f) "/ 137
(l2d) "/ 138
(f2i) "/ 139
(f2l) "/ 140
(f2d) "/ 141
(d2i) "/ 142
(d2l) "/ 143
(d2f) "/ 144
(int2byte) "/ 145
(int2char) "/ 146
(int2short) "/ 147
(lcmp) "/ 148
(fcmpl) "/ 149
(fcmpg) "/ 150
(dcmpl) "/ 151
(dcmpg) "/ 152
(ifeq signedBranchShort) "/ 153
(ifne signedBranchShort) "/ 154
(iflt signedBranchShort) "/ 155
(ifge signedBranchShort) "/ 156
(ifgt signedBranchShort) "/ 157
(ifle signedBranchShort) "/ 158
(if_icmpeq signedBranchShort) "/ 159
(if_icmpne signedBranchShort) "/ 160
(if_icmplt signedBranchShort) "/ 161
(if_icmpge signedBranchShort) "/ 162
(if_icmpgt signedBranchShort) "/ 163
(if_icmple signedBranchShort) "/ 164
(if_acmpeq signedBranchShort) "/ 165
(if_acmpne signedBranchShort) "/ 166
(goto signedBranchShort) "/ 167
(jsr signedBranchShort) "/ 168
(ret localIndexByte) "/ 169
(tableswitch tableSwitchBytes) "/ 170
(lookupswitch lookupSwitchBytes) "/ 171
(ireturn) "/ 172
(lreturn) "/ 173
(freturn) "/ 174
(dreturn) "/ 175
(areturn) "/ 176
(return) "/ 177
(getstatic constIndexShort) "/ 178
(putstatic constIndexShort) "/ 179
(getfield constIndexShort) "/ 180
(putfield constIndexShort) "/ 181
(invokevirtual constIndexShort) "/ 182
(invokenonvirtual constIndexShort) "/ 183
(invokestatic constIndexShort) "/ 184
(invokeinterface constIndexShort nargsByte reservedByte) "/ 185
(newfromname) "/ 186
(new constIndexShort) "/ 187
(newarray arrayTypeByte) "/ 188
(anewarray constIndexShort) "/ 189
(arraylength) "/ 190
(athrow) "/ 191
(checkcast constIndexShort) "/ 192
(instanceof constIndexShort) "/ 193
(monitorenter) "/ 194
(monitorexit) "/ 195
"/ (verifystack) "/ 196 obsolete (Alpha release)
(wide) "/ 196
(multianewarray constIndexShort dimensionsByte) "/ 197
(ifnull signedBranchShort) "/ 198
(ifnonnull signedBranchShort) "/ 199
(#'goto_w' signedBranchLong) "/ 200
(#'jsr_w' signedBranchShort) "/ 201
(breakpoint) "/ 202
nil "/ 203
nil "/ 204
nil "/ 205
nil "/ 206
nil "/ 207
nil "/ 208
(#'ret_w' signedBranchShort) "/ 209
nil "/ 210
nil "/ 211
nil "/ 212
nil "/ 213
nil "/ 214
nil "/ 215
nil "/ 216
nil "/ 217
nil "/ 218
nil "/ 219
nil "/ 220
nil "/ 221
nil "/ 222
nil "/ 223
nil "/ 224
nil "/ 225
nil "/ 226
nil "/ 227
nil "/ 228
nil "/ 229
nil "/ 230
nil "/ 231
nil "/ 232
nil "/ 233
nil "/ 234
nil "/ 235
nil "/ 236
nil "/ 237
nil "/ 238
nil "/ 239
nil "/ 240
nil "/ 241
nil "/ 242
nil "/ 243
nil "/ 244
nil "/ 245
nil "/ 246
nil "/ 247
nil "/ 248
nil "/ 249
nil "/ 250
nil "/ 251
nil "/ 252
nil "/ 253
nil "/ 254
(invokenonvirtual_d constIndexShort) "/ 255 special - dummy invokenonvirtual
)
"
JavaDecompiler initialize
"
"Modified: 4.8.1997 / 19:12:45 / cg"
! !
!JavaDecompiler class methodsFor:'accessing'!
instructionTable
^ DecoderTable
! !
!JavaDecompiler class methodsFor:'class definition generation'!
definitionOf:aJavaClass on:s
|pckgName needCR fields staticFields superClass
interfaces methodList
accessAttributeStyle codeStyle classNameStyle methodNameStyle|
accessAttributeStyle := Java prettyPrintStyle at:#accessAttribute.
codeStyle := Java prettyPrintStyle at:#code.
classNameStyle := Java prettyPrintStyle at:#className.
methodNameStyle := Java prettyPrintStyle at:#className.
pckgName := aJavaClass package.
pckgName ~= aJavaClass name ifTrue:[
s nextPutAll:'package ' , (pckgName copyReplaceAll:$/ with:$.).
s nextPutAll:';'; cr; cr.
].
needCR := false.
s emphasis:accessAttributeStyle.
aJavaClass isPublic ifTrue:[
s nextPutAll:'public '.
needCR := true.
].
aJavaClass isAbstract ifTrue:[
s nextPutAll:'abstract '.
needCR := true.
].
aJavaClass isFinal ifTrue:[
s nextPutAll:'final '.
needCR := true.
].
s emphasis:codeStyle.
needCR ifTrue:[
s cr
].
aJavaClass isInterface ifTrue:[
s nextPutAll:'interface '.
] ifFalse:[
s nextPutAll:'class '.
].
s emphasis:classNameStyle;
nextPutAll:aJavaClass lastName;
emphasis:codeStyle;
space.
superClass := aJavaClass superclass.
superClass ~~ (Java at:'java.lang.Object') ifTrue:[
s nextPutAll:'extends '.
s emphasis:classNameStyle.
(superClass package ~= 'java/lang'
and:[superClass package ~= pckgName]) ifTrue:[
s nextPutAll:(superClass displayString).
] ifFalse:[
s nextPutAll:(superClass lastName).
].
s emphasis:codeStyle.
s space.
].
(interfaces := aJavaClass interfaces) size > 0 ifTrue:[
(aJavaClass isInterface
and:[interfaces size == 1]) ifTrue:[
s nextPutAll:'extends '.
] ifFalse:[
s nextPutAll:'implements '.
].
interfaces keysAndValuesDo:[:nr :if |
|nameString|
nr ~~ 1 ifTrue:[
s nextPutAll:', '.
].
s emphasis:classNameStyle.
(if package ~= 'java/lang'
and:[if package ~= pckgName]) ifTrue:[
if isJavaClass ifFalse:[
"/ ought to be an unresolved class ...
nameString := if fullName copyReplaceAll:$/ with:$.
] ifTrue:[
nameString := if displayString
].
] ifFalse:[
nameString := if lastName
].
s nextPutAll:nameString.
s emphasis:codeStyle.
].
s space.
].
s nextPutAll:'{'; cr.
needCR := false.
staticFields := aJavaClass staticFields.
staticFields size > 0 ifTrue:[
needCR := true.
staticFields do:[:aField |
|type v|
s nextPutAll:' '.
s emphasis:accessAttributeStyle.
aField isPublic ifTrue:[
s nextPutAll:'public '.
] ifFalse:[
s nextPutAll:'private '.
].
s nextPutAll:'static '.
aField isFinal ifTrue:[
s nextPutAll:'final '.
].
s emphasis:codeStyle.
((type := aField type) startsWith:pckgName) ifTrue:[
type := type copyFrom:(pckgName size + 1 + 1).
] ifFalse:[
(type startsWith:'java.lang.') ifTrue:[
type := type copyFrom:(11).
]
].
type := type copy replaceAll:$/ with:$..
s nextPutAll:type; space;
nextPutAll:aField name.
(v := aField constantValue) notNil ifTrue:[
s nextPutAll:' = '; nextPutAll:v displayString.
].
s nextPutAll:';'; cr.
needCR := true.
].
].
fields := aJavaClass fields.
fields size > 0 ifTrue:[
needCR ifTrue:[
s cr
].
needCR := true.
fields do:[:aField |
|type|
s nextPutAll:' '.
s emphasis:accessAttributeStyle.
aField isPublic ifTrue:[
s nextPutAll:'public '.
] ifFalse:[
s nextPutAll:'private '.
].
aField isFinal ifTrue:[
s nextPutAll:'final '.
].
s emphasis:codeStyle.
((type := aField type) startsWith:pckgName) ifTrue:[
type := type copyFrom:(pckgName size + 1 + 1).
].
type := type copy replaceAll:$/ with:$..
s nextPutAll:type; space;
nextPutAll:aField name; nextPutAll:';'; cr.
].
].
aJavaClass methodDictionary size > 0 ifTrue:[
needCR ifTrue:[
s cr.
needCR := false.
].
"/
"/ static methods first ...
"/
methodList := OrderedCollection new.
aJavaClass methodDictionary do:[:aMethod |
aMethod isStatic ifTrue:[
methodList add:aMethod.
].
].
methodList notEmpty ifTrue:[
methodList sort:[:a :b | a name < b name].
methodList do:[:aMethod |
self methodDefinitionOf:aMethod inPackage:pckgName on:s.
].
s cr.
].
"/
"/ instance methods ...
"/
methodList := OrderedCollection new.
aJavaClass methodDictionary do:[:aMethod |
aMethod isStatic ifFalse:[
methodList add:aMethod.
].
].
methodList notEmpty ifTrue:[
methodList sort:[:a :b | a name < b name].
methodList do:[:aMethod |
self methodDefinitionOf:aMethod inPackage:pckgName on:s.
].
].
].
s nextPutAll:'}'; cr.
"Created: / 22.3.1997 / 14:29:37 / cg"
"Modified: / 12.11.1998 / 21:20:03 / cg"
!
methodDefinitionOf:aMethod inPackage:pckgName on:s
|type nm specComponents accessAttributeStyle codeStyle methodNameStyle|
accessAttributeStyle := Java prettyPrintStyle at:#accessAttribute.
codeStyle := Java prettyPrintStyle at:#code.
methodNameStyle := Java prettyPrintStyle at:#methodName.
s nextPutAll:' '.
s emphasis:accessAttributeStyle.
aMethod isPublic ifTrue:[
s nextPutAll:'public '.
] ifFalse:[
aMethod isProtected ifTrue:[
s nextPutAll:'protected '.
] ifFalse:[
s nextPutAll:'private '.
]
].
aMethod isFinal ifTrue:[
s nextPutAll:'final '.
].
aMethod isStatic ifTrue:[
s nextPutAll:'static '.
].
aMethod isNative ifTrue:[
s nextPutAll:'native '.
].
aMethod isSynchronized ifTrue:[
s nextPutAll:'synchronized '.
].
s emphasis:codeStyle.
(nm := aMethod name) = '<init>' ifTrue:[
nm := aMethod javaClass lastName.
].
specComponents := JavaMethod
specComponentsWithArgsFromSignature:(aMethod signature)
withName:nm
in:(aMethod javaClass package).
s nextPutAll:(specComponents at:1).
s space.
s emphasis:methodNameStyle.
s nextPutAll:(specComponents at:2).
s emphasis:codeStyle.
s nextPutAll:' ('.
s nextPutAll:(specComponents at:3).
s nextPutAll:')'.
aMethod exceptionTable size > 0 ifTrue:[
s nextPutAll:' throws '.
aMethod exceptionTable keysAndValuesDo:[:index :aClassRef |
|nm|
nm := aClassRef fullName.
(nm startsWith:pckgName) ifTrue:[
nm := nm copyFrom:pckgName size + 2
] ifFalse:[
(nm startsWith:'java/lang') ifTrue:[
nm := nm copyFrom:'java/lang' size + 2.
].
].
index ~~ 1 ifTrue:[
s nextPutAll:', '
].
s nextPutAll:(nm copyReplaceAll:$/ with:$.)
]
].
s nextPutAll:';'; cr.
"Created: / 1.8.1997 / 12:24:11 / cg"
"Modified: / 5.11.1998 / 19:44:19 / cg"
! !
!JavaDecompiler class methodsFor:'decompiling'!
decompile:aJavaMethod
self decompile:aJavaMethod to:Transcript
!
decompile:aJavaMethod to:aStream
self new decompile:aJavaMethod to:aStream
! !
!JavaDecompiler methodsFor:'decompiling'!
decompile:aJavaMethod to:aStream
|who endPC insn spec op wide|
outStream := aStream.
who := aJavaMethod who.
who notNil ifTrue:[
classToCompileFor := who methodClass.
aStream cr.
"/ aStream showCR:'decompiling ' , classToCompileFor name , '>>' , (who methodSelector) , ':'.
aStream showCR:'decompiling ' , aJavaMethod displayString , ':'.
aJavaMethod isStatic ifTrue:[
aStream show:'static method'.
].
aStream cr.
aStream cr.
].
javaMethod := aJavaMethod.
code := aJavaMethod javaByteCode.
endPC := code size.
endPC == 0 ifTrue:[
javaMethod isNative ifTrue:[
outStream nextPutAll:'// native method'; cr
] ifFalse:[
outStream nextPutAll:'{} // no bytecode'; cr
].
^ self
].
wide := false.
pc := 1.
[pc <= endPC] whileTrue:[
insn := code at:pc.
pc := pc + 1.
spec := DecoderTable at:(insn + 1).
spec isNil ifTrue:[
op := '** invalid opcode: ',insn printString, ' **'
] ifFalse:[
op := spec at:1
].
outStream
show:((pc - 1 - 1) printStringPaddedTo:4);
show:' '.
outStream
show:(wide ifTrue:[op , '(w)'] ifFalse:[op]).
outStream
show:' '.
op == #wide ifTrue:[
wide := true.
] ifFalse:[
spec notNil ifTrue:[
spec from:2 to:spec size do:[:what |
wide ifTrue:[
self perform:(what , '_wide') asSymbol.
] ifFalse:[
self perform:what
]
].
].
wide := false.
].
outStream cr.
]
"Created: / 16.4.1996 / 14:59:29 / cg"
"Modified: / 13.1.1998 / 23:58:34 / cg"
! !
!JavaDecompiler methodsFor:'operand decoding'!
arrayTypeByte
|hi low index type|
index := code at:pc.
pc := pc + 1.
type := #('T_ARRAY' 'invalid2' 'invalid3' 'T_BOOLEAN'
'T_CHAR' 'T_FLOAT' 'T_DOUBLE' 'T_BYTE'
'T_SHORT' 'T_INT' 'T_LONG' ) at:index ifAbsent:['invalid' , index printString].
outStream
show:index;
show:' [';
show:type;
show:']'
"Created: / 16.4.1996 / 15:00:04 / cg"
"Modified: / 12.11.1998 / 22:58:06 / cg"
!
constIndexByte
|index|
index := code at:pc.
pc := pc + 1.
outStream
show:index;
show:' [';
show:(javaMethod constantPool at:index ifAbsent:['??']) displayString;
show:'] '
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
!
constIndexShort
|index|
index := code wordAt:pc MSB:true.
pc := pc + 2.
outStream
show:index;
show:' [';
show:(javaMethod constantPool at:index ifAbsent:'???') displayString;
show:'] '
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
!
dimensionsByte
outStream show:' dims= '.
self unsignedByte
!
localIndexByte
|index|
index := code at:pc.
pc := pc + 1.
outStream
show:index;
show:' '
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 4.8.1997 / 19:01:52 / cg"
!
localIndexByte_wide
|index|
index := code wordAt:pc MSB:true.
pc := pc + 2.
outStream
show:index;
show:' '
"Modified: 16.4.1996 / 15:30:55 / cg"
"Created: 4.8.1997 / 19:02:09 / cg"
!
lookupSwitchBytes
|defaultOffset delta nPairs match pc0|
pc0 := pc-1. "/ i.e. the pc of the tableSwitch op
[(pc-1) \\ 4 ~~ 0] whileTrue:[
pc := pc + 1
].
defaultOffset := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
nPairs := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
outStream show:'n='; show:nPairs; cr.
nPairs timesRepeat:[
match := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
delta := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
outStream show:' ';
show:match;
show:' -> ';
show:delta;
show:' [';
show:(pc0 - 1 + delta);
show:']';
cr.
].
outStream show:' ';
show:'default';
show:' -> ';
show:defaultOffset;
show:' [';
show:(pc0 - 1 + defaultOffset);
show:']'.
"Modified: / 5.1.1998 / 00:33:57 / cg"
!
nargsByte
|byte constants|
byte := code byteAt:pc.
pc := pc + 1.
outStream
show:byte
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
!
reservedByte
|byte constants|
byte := code byteAt:pc.
pc := pc + 1.
outStream
show:byte
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
!
signedBranchShort
|index constants|
index := code signedWordAt:pc MSB:true.
outStream
show:index;
show:' [';
show:(pc - 1 + index - 1);
show:']'.
pc := pc + 2.
"Created: / 16.4.1996 / 15:00:04 / cg"
"Modified: / 5.1.1998 / 00:34:14 / cg"
!
signedByte
|byte constants|
byte := code signedByteAt:pc.
pc := pc + 1.
outStream
show:byte
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
!
signedByte_wide
self signedShort
"Created: / 13.1.1998 / 23:56:36 / cg"
!
signedShort
|word constants|
word := code signedWordAt:pc MSB:true.
pc := pc + 2.
outStream
show:word
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
!
tableSwitchBytes
|defaultOffset delta low high pc0|
pc0 := pc-1. "/ i.e. the pc of the tableSwitch op
[(pc-1) \\ 4 ~~ 0] whileTrue:[
pc := pc + 1
].
defaultOffset := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
low := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
high := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
outStream cr.
low to:high do:[:switchValue |
delta := code signedDoubleWordAt:pc MSB:true.
pc := pc + 4.
outStream show:' ';
show:switchValue;
show:' -> ';
show:delta;
show:' [';
show:(pc0 - 1 + delta);
show:']';
cr.
].
outStream show:' ';
show:'default';
show:' -> ';
show:defaultOffset;
show:' [';
show:(pc0 - 1 + defaultOffset);
show:']'.
"Modified: / 5.1.1998 / 00:33:46 / cg"
!
unsignedByte
|byte constants|
byte := code byteAt:pc.
pc := pc + 1.
outStream
show:byte
"Created: 16.4.1996 / 15:00:04 / cg"
"Modified: 16.4.1996 / 15:30:55 / cg"
! !
!JavaDecompiler methodsFor:'private'!
popType:type
(type == #long) ifTrue:[
^ self popLong
].
type == #double ifTrue:[
^ self popDouble
].
^ self popStack
"Created: 16.8.1997 / 03:14:11 / cg"
! !
!JavaDecompiler class methodsFor:'documentation'!
version
^ '$Header: /home/jv/Projects/SmalltalkX/repositories/cvs/stx/libjava/JavaDecompiler.st,v 1.38 1998/11/12 21:58:54 cg Exp $'
! !
JavaDecompiler initialize!