
Object subclass: #Decompiler
  instanceVariableNames:
    'inputStream isBlock blockReturnsEmptyStack commandQueue stack
     localVariables instanceVariables methodParameters '
  classVariableNames:
    'ByteCodeTable '
  poolDictionaries: '' !

!Decompiler class methods !

decompile: aByteStream
       withLocalVariables:     locVar
       withInstanceVariables:  instVar
       withMethodParameters:   methodParameter

    ByteCodeTable isNil
        ifTrue: [
            self initialize. "Only to be sure that class is initialized"
        ].
    ^(self new)
        localVariables:    locVar;
        instanceVariables: instVar;
        methodParameters:  methodParameter;
        inputStream:       aByteStream;
        decompile!

initialize

    ByteCodeTable := Array new: 256.

    ByteCodeTable
        at:   0+1 put: #(('+'             ) doInfixMethod: 1);
        at:   1+1 put: #(('-'             ) doInfixMethod: 1);
        at:   2+1 put: #(('*'             ) doInfixMethod: 1);
        at:   3+1 put: #(('/'             ) doInfixMethod: 1);
        at:   4+1 put: #(('//'            ) doInfixMethod: 1);
        at:   5+1 put: #(('\\'            ) doInfixMethod: 1);
        at:   6+1 put: #(('|'             ) doInfixMethod: 1);
        at:   7+1 put: #(('&'             ) doInfixMethod: 1);
        at:   8+1 put: #(('<'             ) doInfixMethod: 1);
        at:   9+1 put: #(('<='            ) doInfixMethod: 1);
        at:  10+1 put: #(('>'             ) doInfixMethod: 1);
        at:  11+1 put: #(('>='            ) doInfixMethod: 1);
        at:  12+1 put: #(('='             ) doInfixMethod: 1);
        at:  13+1 put: #(('~~'            ) doInfixMethod: 1);
        at:  14+1 put: #(('=='            ) doInfixMethod: 1);
        at:  15+1 put: #(('@'             ) doInfixMethod: 1);
        at:  16+1 put: #(('bitShift:'    1) doMethod:withArgumentCount: 1);
        at:  17+1 put: #(('bitAnd:'      1) doMethod:withArgumentCount: 1);
        at:  18+1 put: #(('bitOr:'       1) doMethod:withArgumentCount: 1);
        at:  19+1 put: #(('at: '         1) doMethod:withArgumentCount: 1);
        at:  20+1 put: #(('at:put:'      2) doMethod:withArgumentCount: 1);
        at:  21+1 put: #(('size'          ) doSuffixMethod: 1);
        at:  22+1 put: #(('next'          ) doSuffixMethod: 1);
        at:  23+1 put: #(('nextPut:'     1) doMethod:withArgumentCount: 1);
        at:  24+1 put: #(('atEnd'         ) doSuffixMethod: 1);
        at:  25+1 put: #(('class'         ) doSuffixMethod: 1);
        at:  26+1 put: #(('value'         ) doSuffixMethod: 1);
        at:  27+1 put: #(('value:'       1) doMethod:withArgumentCount: 1);
        at:  28+1 put: #(('value:value:' 2) doMethod:withArgumentCount: 1);
        at:  29+1 put: #(('new'           ) doSuffixMethod: 1);
        at:  30+1 put: #(('new:'         1) doMethod:withArgumentCount: 1);
        at:  31+1 put: #((               0) pushLocal: 1);
        at:  32+1 put: #((               1) pushLocal: 1);
        at:  33+1 put: #((               2) pushLocal: 1);
        at:  34+1 put: #((               3) pushLocal: 1);
        at:  35+1 put: #((               4) pushLocal: 1);
        at:  36+1 put: #((               5) pushLocal: 1);
        at:  37+1 put: #((                ) pushLocal 3);
        at:  38+1 put: #((               0) storeLocal: 1);
        at:  39+1 put: #((               1) storeLocal: 1);
        at:  40+1 put: #((               2) storeLocal: 1);
        at:  41+1 put: #((               3) storeLocal: 1);
        at:  42+1 put: #((               4) storeLocal: 1);
        at:  43+1 put: #((               5) storeLocal: 1);
        at:  44+1 put: #((                ) storeLocal 3);
        at:  45+1 put: #((               0) pushInstVar: 1);
        at:  46+1 put: #((               1) pushInstVar: 1);
        at:  47+1 put: #((               2) pushInstVar: 1);
        at:  48+1 put: #((               3) pushInstVar: 1);
        at:  49+1 put: #((               4) pushInstVar: 1);
        at:  50+1 put: #((               5) pushInstVar: 1);
        at:  51+1 put: #((                ) pushInstVar 3);
        at:  52+1 put: #((               0) storeInstVar: 1);
        at:  53+1 put: #((               1) storeInstVar: 1);
        at:  54+1 put: #((               2) storeInstVar: 1);
        at:  55+1 put: #((               3) storeInstVar: 1);
        at:  56+1 put: #((               4) storeInstVar: 1);
        at:  57+1 put: #((               5) storeInstVar: 1);
        at:  58+1 put: #((                ) storeInstVar 3);
        at:  59+1 put: #((               0) pushLocal: 1);
        at:  60+1 put: #((               1) pushLocal: 1);
        at:  61+1 put: #((               2) pushLocal: 1);
        at:  62+1 put: #((               3) pushLocal: 1);
        at:  63+1 put: #((               4) pushLocal: 1);
        at:  64+1 put: #((               5) pushLocal: 1);
        at:  65+1 put: #((                ) pushLocalB 3);
        at:  66+1 put: #((               0) storeLocal: 1);
        at:  67+1 put: #((               1) storeLocal: 1);
        at:  68+1 put: #((               2) storeLocal: 1);
        at:  69+1 put: #((               3) storeLocal: 1);
        at:  70+1 put: #((               4) storeLocal: 1);
        at:  71+1 put: #((               5) storeLocal: 1);
        at:  72+1 put: #((                ) storeLocalB 3);
        at:  73+1 put: #((               0) pushGlobVar: 1);
        at:  74+1 put: #((               1) pushGlobVar: 1);
        at:  75+1 put: #((               2) pushGlobVar: 1);
        at:  76+1 put: #((               3) pushGlobVar: 1);
        at:  77+1 put: #((               4) pushGlobVar: 1);
        at:  78+1 put: #((               5) pushGlobVar: 1);
        at:  79+1 put: #((                ) pushGlobVar 3);
        at:  80+1 put: #((               0) storeGlobVar: 1);
        at:  81+1 put: #((               1) storeGlobVar: 1);
        at:  82+1 put: #((               2) storeGlobVar: 1);
        at:  83+1 put: #((               3) storeGlobVar: 1);
        at:  84+1 put: #((               4) storeGlobVar: 1);
        at:  85+1 put: #((               5) storeGlobVar: 1);
        at:  86+1 put: #((                ) storeGlobVar 3);
        at:  87+1 put: #((               0) pushParameter: 1);
        at:  88+1 put: #((               1) pushParameter: 1);
        at:  89+1 put: #((               2) pushParameter: 1);
        at:  90+1 put: #((               3) pushParameter: 1);
        at:  91+1 put: #((               4) pushParameter: 1);
        at:  92+1 put: #((               5) pushParameter: 1);
        at:  93+1 put: #(('+'            2) doInfixMethod:with: 1);
        at:  94+1 put: #(('-'            2) doInfixMethod:with: 1);
        at:  95+1 put: #((                ) pushParameter 3);
        at:  96+1 put: #((                ) pushWord 3);
        at:  97+1 put: #((                ) pushNegativeWord 3);
        at:  98+1 put: #((                ) pushByte 2);
        at:  99+1 put: #(('self'          ) doPushMethod: 1);
        at: 100+1 put: #(('true'          ) doPushMethod: 1);
        at: 101+1 put: #(('false'         ) doPushMethod: 1);
        at: 102+1 put: #(('nil'           ) doPushMethod: 1);
        at: 104+1 put: #(('0'             ) doPushMethod: 1);
        at: 105+1 put: #(('1'             ) doPushMethod: 1);
        at: 106+1 put: #(('2'             ) doPushMethod: 1);
        at: 107+1 put: #((                ) doDropMethod 1);
        at: 108+1 put: #((                ) doDupMethod 1);
        at: 112+1 put: #((                ) skip 3);
        at: 113+1 put: #((                ) skipIfTrue 3);
        at: 114+1 put: #((                ) skipIfFalse 3);
        at: 115+1 put: #(('='             ) doInfixMethod: 1);
        at: 116+1 put: #(('=='            ) doInfixMethod: 1);
        at: 117+1 put: #(('>'             ) doInfixMethod: 1);
        at: 118+1 put: #(('>='            ) doInfixMethod: 1);
        at: 119+1 put: #(('<'             ) doInfixMethod: 1);
        at: 120+1 put: #(('<='            ) doInfixMethod: 1);
        at: 121+1 put: #((               0) doCall: 3);
        at: 122+1 put: #((               1) doCall: 3);
        at: 123+1 put: #((               2) doCall: 3);
        at: 124+1 put: #((               3) doCall: 3);
        at: 125+1 put: #((               4) doCall: 3);
        at: 126+1 put: #((               5) doCall: 3);
        at: 127+1 put: #((               6) doCall: 3);
        at: 128+1 put: #((               7) doCall: 3);
        at: 129+1 put: #((                ) doCall 5);
        at: 130+1 put: #((                ) doSuperCall 7);
        at: 131+1 put: #((                ) returnTop 1);
        at: 132+1 put: #((                ) returnTopOrSelf 1);
        at: 133+1 put: #((                ) return 1);
        at: 134+1 put: #((                ) return 1);
        at: 135+1 put: #((                ) return 1);
        at: 136+1 put: #((                ) return 1);
        at: 137+1 put: #((                ) returnTop 1);
        at: 138+1 put: #((                ) return 1);
        at: 139+1 put: #((                ) returnFromBlock 1);
        at: 141+1 put: #((               0) pushBlock: 3);
        at: 142+1 put: #((               1) pushBlock: 3);
        at: 143+1 put: #((               2) pushBlock: 3);
        at: 144+1 put: #((                ) pushBlock 5);
        at: 145+1 put: #(('+'            1) doInfixMethod:with: 1);
        at: 146+1 put: #(('-'            1) doInfixMethod:with: 1);
        at: 147+1 put: #(('<'             ) doWithByteMethod: 1);
        at: 148+1 put: #(('<='            ) doWithByteMethod: 1);
        at: 149+1 put: #(('='             ) doWithByteMethod: 1);
        at: 150+1 put: #(('>'             ) doWithByteMethod: 1);
        at: 151+1 put: #(('>='            ) doWithByteMethod: 1);
        at: 152+1 put: #((               0) returnSelf: 1);
        at: 153+1 put: #((               1) returnSelf: 1);
        at: 154+1 put: #((               2) returnSelf: 1);
        at: 155+1 put: #((               3) returnSelf: 1)!

log: x
    Transcript nextPutAll: x;cr!

test

"   To test the decompiler evaluate the following expression:

        Decompiler test

    It decompiles every method in every class and reports those which's
    bytecodeArray doesn't match its compiled decompiled version. To find
    the errors in the Transcript, search for the string 'failed test'. Those
    methods known to fail are the 'DoIt' methods in various classes. I didn't
    find any source code whose bytecodearray match its original. My guess is
    that those DoIt methods are relicts generated from an older compiler"

    |mnew ch x|

    x := Object withAllSubclasses.
    x := x reject: [:c| c name trimBlanks isEmpty].
    x copy do: [:i| x add: i class].
    1 to: x size do: [
    :i|
    c := x at: i.
    Transcript cr.c printOn: Transcript.
    Transcript show: '     ';
               show: (i /x  size) printString;
               show: ' of all classes';
               cr.
    (c methodDictionary asSet)
        do: [
            :m|
            Transcript show: '   '; show: m selector;cr.
            self test: m in: c
        ]
]!

test: m in: c

"   This method tests the decompiler for method m in class c.
    If a compile error occurs or the compiled decompiled byteArray
    doesn't match the original, then monitor that with log:. "
    | aTopPane mnew ch x d1 d2|

    mnew := WriteStream on: ''.
    m decompileTo: mnew.
    mnew := mnew contents asOrderedCollection.
    [ch := mnew removeFirst. ch == $  or: [ch == $:]] whileFalse: [].
    m argumentCount > 0 ifTrue: [ mnew addFirst: $: ].
    mnew addFirst: $q; addFirst: $q.
    x := ''.
    mnew do: [ :ch| x := x,(String with: ch)].
    mnew := c compile: x.
    mnew isNil
        ifTrue: [
            ^self log: m selector,' ',
                       m classField name,' ',
                       'compile error'].
    mnew := mnew value.

    "Set the header bytes to zero which points to sourcecode in
     sources.sml or change.log, before the test takes place"

    ((m byteCodeArray copy) at: 5 put: 0; at: 6 put: 0; at: 7 put: 0; yourself)
    =
    ((mnew byteCodeArray copy) at: 5 put: 0; at: 6 put: 0; at: 7 put: 0; yourself)
        ifFalse: [
            self log: m selector,' ',
                      m classField name,' ',
                      'failed test'
        ].
    c removeSelector: mnew selector! !


!Decompiler methods !

blockReturnsEmptyStack

    "Answer the value of blockReturnsEmptyStack."

    ^blockReturnsEmptyStack!

decompile

    "Decompile the bytecodes from inputStream"

    | ch  x |

    stack := OrderedCollection new: 100.
    commandQueue := OrderedCollection new.
    isBlock := false.

    [inputStream atEnd] whileFalse: [ self decompileNext ].
    self emptyStack.
    ^self getCommands!

decompileBlock: aBlock asBlock: aBoolean

    | x y |
    aBlock isEmpty
        ifTrue:  [
            ^'[]' ]
        ifFalse: [
            y := (x := self class new)
                localVariables:         localVariables;
                instanceVariables:      instanceVariables;
                methodParameters:       methodParameters;
                inputStream:            (ReadStream on: aBlock);
                isBlock:                aBoolean;
                decompileBlockWithArgs: 0.
            blockReturnsEmptyStack := x blockReturnsEmptyStack.
            ^y ]!

decompileBlockWithArgs: n

    "Decompile inputStream as a block with n args"

    | arg  args |

    stack := OrderedCollection new: 100.
    commandQueue := OrderedCollection new.
    isBlock := true.

    args := ''.
    n > 0
        ifTrue: [
            n timesRepeat: [
                self push: '' with: 50.
                self decompileNext.
                arg  := self removeLastCommand asStream nextWord.
                args := ' :',arg,args.
                1 to: localVariables size do: [
                    :i|
                    (localVariables at: i) = arg
                        ifTrue: [
                             localVariables at: i put: arg asSymbol]]].
            args := '\   ' withCrs,args,'|'].
    [inputStream atEnd] whileFalse: [ self decompileNext ].
    blockReturnsEmptyStack := stack isEmpty.
    self emptyStack.
    ^'[',args,self getCommands,']'!

decompileNext

    "Decompile one bytecode from inputStream"

    | ch  x |

    ch := inputStream next.
    (x := ByteCodeTable at: (ch + 1)) isNil
        ifFalse: [
            "ch == 131 ifTrue: [
                self log: methodParameters selector,' ',
                          methodParameters classField name,' ',
                          'Bytecode 131']. "
            self perform: (x at: 2) withArguments: (x  at: 1)]
        ifTrue: [
            self log: methodParameters selector,' ',
                      methodParameters classField name,' ',
                      'Unknown Bytecode ',ch printString]!

decompileNextBytes: n asBlock: aBoolean

    ^self decompileBlock: (self getBlockWithNextBytes: n)
                asBlock: aBoolean!

doCall

    "ByteCode 129 sl sh al ah

                sh sl -- Selector id
                ah al -- ArgumentCount"

    | x |

    x := self getWord / 4.
    self doMethod: ((SymbolTable select:
                                [:s| s id == x]) asOrderedCollection first)
         withArgumentCount: self getWord/4!

doCall: argumentCount

    "ByteCode 121..128 sl sh

                sh sl -- Selector id
              (b-121) -- ArgumentCount"

    | x |

    x := self getWord / 4.
    self doMethod: (SymbolHashArray at: x + 1)
         withArgumentCount: argumentCount!

doDropMethod

    "Bytecode: 107"

    | x |
    x := self popAll.
    self topOfStack == #DUP
        ifTrue: [
            self popDUP.
            self push: ((x at: 1),';\' withCrs,'   ') with: 45]
        ifFalse: [
            self print: (x at: 1)]!

doDupMethod

    "Bytecode: 108"

    | x |
    x := self popAll.
    self push: #DUP with: 100;
         push: (self testForBraces: x with: 25) with: 45!

doInfixMethod: name

    | x y |
    x := self popAll.
    y := self popAll.
    self push: ((self testForBraces: y with: 35),' ',name,' ',
                (self testForBraces: x with: 35))
         with: ((y at: 2) == 45 ifTrue: [17] ifFalse: [30])!

doInfixMethod: name with: arg

    | y |
    y := self popAll.
    self push: ((self testForBraces: y with: 35),' ',name,' ',
                arg printString)
         with: ((y at: 2) == 45 ifTrue: [17] ifFalse: [30])!

doMethod: name withArgumentCount: x

    | arguments y z nameStream|

    x <= 1
        ifFalse: [
            arguments := Array new: x.
            x to: 1 by: -1 do: [
                : i |
                arguments at: i put: (self testForBraces: self popAll with: 25)].
            y := WriteStream on: ''.
            y nextPutAll: (self testForBraces: (z  := self popAll) with: 25).
            nameStream := name asStream.
            1 to: x do: [
                : i |
                y
                  space;
                  nextPutAll: nameStream nextWord;
                  nextPut:    nameStream next;
                  space;
                  nextPutAll: (arguments at: i)].
            self push: y contents with:
                     ((z at: 2) == 45 ifTrue: [17] ifFalse: [20])]
        ifTrue: [
            x == 0
                ifFalse: [
                    y := self popAll.
                    self push:
                            ((self testForBraces: (z := self popAll) with: 25),
                            ' ',name,' ',
                            (self testForBraces: y           with: 25))
                         with: ((z at: 2) == 45 ifTrue: [17] ifFalse: [20])]
                ifTrue: [
                    self doSuffixMethod: name]]!

doPushMethod: arg

    self push: arg with: 50!

doSuffixMethod: name

    | y |
    self push: ((self testForBraces: (y := self popAll) with: 35),' ',name)
         with: ((y at: 2) == 45 ifTrue: [17] ifFalse: [40])!

doSuperCall

    "ByteCode 130 sl sh xx xx al ah

                sh sl -- Selector id
                ah al -- ArgumentCount"

    | x n temp|

    x := self getWord / 4.
    self getWord.
    n := self getWord/4.
    temp := OrderedCollection new.
    n timesRepeat: [ temp addLast: self popAll ].
    self pop. "Pop 'self'"
    self push: 'super' with: 50.
    n timesRepeat: [ self pushAll: temp removeLast].
    self doMethod: (SymbolHashArray at: x + 1)
         withArgumentCount: n!

doWithByteMethod: name

    | x |
    self push: ((self testForBraces: self popAll with: 25),' ',name,' ',inputStream next printString)
         with: 30!

emptyStack

    "Method for internal stack handling"

    | x newStack y|

    newStack := OrderedCollection new.
    [stack isEmpty or: [ (y := self pop) isEmpty or: [y == #WHILE]]]
        whileFalse: [
            newStack addLast: y].
    y == #WHILE
        ifTrue: [
            self push: y ].
    [newStack isEmpty]
        whileFalse: [
            (x := newStack removeLast) == #DUP
                ifFalse: [
                    self queue: x]]!

getBlockWithNextBytes: n

    | aCollection aStream|

    n == 0
        ifTrue:  [
            ^'' ]
        ifFalse: [
            aCollection := OrderedCollection new: n.
            n timesRepeat: [ aCollection add: inputStream next].
            ^aCollection ]!

getCommands

    "return all the commands in the queue"

    | contents output|

    contents := ReadWriteStream on: ''.
    [commandQueue size > 1]
        whileTrue: [
            contents nextPutAll: commandQueue removeFirst;
                     nextPut:    $.;
                     cr].
    commandQueue size == 1
        ifTrue: [
            contents nextPutAll: commandQueue removeFirst].
    contents reset.
    output := WriteStream on: ''.
    [contents atEnd] whileFalse: [
        output cr;
               nextPutAll: '    ';
               nextPutAll: contents nextLine ].
    ^output contents!

getGlobVarName: n

    | name |
    name := (methodParameters at: n + 1) key.
    name isNil
        ifTrue: [
            "Work around for missing pool variables. Only useful
             for numbers"
            name := 'GlobalVar',
                (methodParameters at: n + 1) value printString.
           "(methodParameters at: n + 1) key: name asSymbol"].
    ^name!

getWord

    ^inputStream next + (inputStream next * 256)!

giveLastCommandIn: aStream

    | ch |
    [aStream atEnd]
        whileFalse: [
            ch := aStream next.
            ((ByteCodeTable at: ch + 1) at: 3) - 1 timesRepeat:
                [ aStream next ]].
    ^ch!

inputStream: aValue

    "Set the value of inputStream."

    inputStream := aValue!

instanceVariables: aValue

    "Set the value of instanceVariables."

    instanceVariables := aValue!

isBlock: aValue

    "Set the value of isBlock."

    isBlock := aValue!

localVariables: aValue

    "Set the value of localVariables."

    localVariables := aValue!

log: x
    self class log: x!

methodParameters: aValue

    "Set the value of methodParameters."

    methodParameters := aValue!

pop

    "Method for internal stack handling"

    ^self popAll at: 1!

popAll

    "Method for internal stack handling"

    | x |
    [   x := self popBasic.
        (x  at: 1) == #DUP ]
        whileTrue: [].
    ^x!

popBasic

    "Method for internal stack handling"

    stack isEmpty ifTrue: [^Array with: '' with: 100].
    ^stack removeLast!

popDUP

    "Method for internal stack handling"

    ^self popBasic at: 1!

print: x

    self emptyStack.
    self queue: x!

push: x

    "Method for internal stack handling"

    self push: x with: 50!

push: x with: y

    "Method for internal stack handling

     Push Object x with precedence y

         50 - Block, number, variable or bracket
         45 - Cascaded open
        43 -------
         40 - unarySelector
        35 -------
         30 - binarySelector, comparision
        25 -------
         20 - message
         17 - cascaded
        15 -------
         10 - assignment"

    stack addLast: (Array with: x with: y)!

pushAll: x

    "Method for internal stack handling"

    stack addLast: x!

pushBlock

    "Bytecode: 144 al ah bl bh

        ah al -- Argumentcount
        bh bl -- Bytecount"

    self pushBlock: self getWord!

pushBlock: n

    "Bytecode: 141..143 bl bh
        (b-141) -- Argumentcount
        bh bl   -- Bytecount"

    | aBlock |
    aBlock := self getBlockWithNextBytes: self getWord.
    aBlock isEmpty
        ifTrue:  [
            self push: '[]' with: 50 ]
        ifFalse: [
            self push:
                ((self class new)
                    localVariables:         localVariables;
                    instanceVariables:      instanceVariables;
                    methodParameters:       methodParameters;
                    inputStream:            (ReadStream on: aBlock);
                    isBlock:                true;
                    decompileBlockWithArgs: n)
                 with: 50].!

pushByte

    "Bytecode: 98 ll"

    self push: inputStream next printString
         with: 50!

pushGlobVar

    "Bytecode: 79 ll hh

                n := hhll/4-6 "

    self pushGlobVar: (self getWord / 4 - 7)!

pushGlobVar: n

    "Bytecode: 73..78

                n := b-73 "

    self push: (self getGlobVarName: n)
         with: 50!

pushInstVar

    "Bytecode 51 ll hh

                n := hhll/4-2"

    self pushInstVar: (self getWord / 4 - 3)!

pushInstVar: n

    "Bytecode 45..50

                n := b-45"

    self push: (instanceVariables at: n + 1)
         with: 50!

pushLocal


    "Bytecode:  37 ll hh

               n:=hhll/4-5  "


    self pushLocal: (self getWord / 4 - 5)!

pushLocal: n


    "Bytecode:  31..36 bzw. 59..64

               n:=b-31 bzw. n=b-59 "


    self push: (localVariables at: n + 1)
         with: 50!

pushLocalB


    "Bytecode: 65 ll hh

               n=hhll/4-10 "


    self pushLocal: (self getWord / 4 - 10)!

pushNegativeWord

    "Bytecode: 97 ll hh"

    self push: ('-',self getWord printString) with: 50!

pushParameter

    "Bytecode: 93 ll hh

                n := hhll/4-8"

    self pushParameter: (self getWord / 4 - 8 + 1)!

pushParameter: n

    "Bytecode: 87..92

                n := b-87 "

    | x |

    x := methodParameters at: n + 1.
    ((x isKindOf: Array) or: [x isMemberOf: Symbol])
        ifTrue: [
            self push: ('#',x  printString) with: 50]
        ifFalse: [
            self push: (x  printString) with: 50 ]!

pushWord

    "Bytecode: 96 ll hh"

    self push: self getWord printString
         with: 50!

queue: x

    commandQueue addLast: x!

removeLastCommand

    ^commandQueue removeLast!

return

    "Bytecode 133..137.154"
   | x |
    x := self pop.
    self emptyStack.
    x isEmpty
        ifFalse: [ self print: '^',x ]
        ifTrue:  [ self print: '^',commandQueue removeLast]!

returnFromBlock


    "Bytecode 139"

   | x |
    self emptyStack.!

returnSelf: n

    "Bytecode 132 152"
   | x |
    self emptyStack.
    self print: '^self'!

returnTop

    "Bytecode 131 138"
   | x |
    x := self pop.
    self emptyStack.
    self print: '^',x!

returnTopOrSelf

    "Bytecode 132"
    | x |
    x := stack isEmpty.
    self emptyStack.
    (    (isBlock not and: [ x & commandQueue isEmpty not])
    or: [ isBlock     and: [ x | commandQueue isEmpty]])
        ifTrue: [self print: '^self']!

skip

    "Bytecode: 112 ll hh

        hhll is a signed offset "

    | from to arg y  offset contents position x|

    (offset := self getWord) >= 32768
        ifTrue: [
            offset := offset - 65536].

    offset >= 0
        ifTrue: [
            contents := inputStream contents.
            position := inputStream position + offset.
            (contents at: position) == 146
                ifTrue: [
                    "xx timesRepeat: [] methode"
                    self push: ((self testForBraces: self popAll with: 25),
                                ' timesRepeat: ',(self decompileNextBytes: offset - 1 asBlock: false))
                         with: 20.
                    8 timesRepeat: [inputStream next]
                ]
                ifFalse: [
                    (contents at: position + 1) == 119
                        ifTrue: [
                            "from to: to do: [:arg| ] methode"
                            x := self getBlockWithNextBytes: offset.
                                "remove last commands from block x:
                                        108 dup
                                            Push from xxx
                                            increment
                                            dup
                                            Pop into xxx   "
                            2 timesRepeat: [
                                [ y := self giveLastCommandIn: (ReadStream on: x).
                                  ((ByteCodeTable at: y + 1) at: 3)
                                      timesRepeat: [x removeLast].
                                  y == 108]
                                    whileFalse: []].
                            x := self decompileBlock: x asBlock: false.
                            x := x copyFrom: 2 to: x size - 1.
                            5 timesRepeat: [inputStream next].
                            y := ReadStream on: self removeLastCommand.
                            arg := y nextWord.
                            y skipTo: $:; skipTo: $ .
                            from := '(',(y upTo: nil),')'.
                            self pop asString = arg
                                ifTrue: [
                                    (localVariables at: localVariables size) == #no:to:do:
                                        ifFalse: [
                                            localVariables removeLast].
                                    self pop; popDUP.
                                    y := ReadStream on: self removeLastCommand.
                                    y skipTo: $:; skipTo: $ .
                                    to := '(',(y upTo: nil),')'.
                                    from := '(',self removeLastCommand,')']
                                ifFalse: [
                                    self removeLastCommand.
                                    to := '(',self removeLastCommand,')'].
                            x := from,' to: ',to,   ' do: [ :',arg,'| ',x,']'.
                            self push: x with: 20.
                            (localVariables at: localVariables size) == #no:to:do:
                                ifTrue: [
                                    localVariables removeLast].
                            1 to: localVariables size do: [
                                :i|
                                (localVariables at: i) = arg
                                    ifTrue: [
                                        localVariables at: i put: arg asSymbol]].
                        ]
                        ifFalse: [
                            "[] whilexxx: []"
                            self push:
                                (self decompileNextBytes: offset asBlock: false) with: 50.
                            self push: commandQueue.
                            commandQueue := OrderedCollection new.
                            self push: #WHILE.
                        ]
                ].
        ]!

skipIfFalse

    "Bytecode: 114 ll hh

        hhll is a signed offset"

    | z  offset x y|

    (offset := self getWord) >= 32768
        ifTrue: [
            offset := offset - 65536].

    offset > 0
        ifTrue: [
            x := self getBlockWithNextBytes: offset.
            (x at: 1) == 107
                ifTrue: [
                    "xx and: []"
                    x removeFirst.
                    y := self testForBraces: self popAll with: 25.
                    ((z := self pop) = 'true' or: [z = 'false'])
                        ifTrue: [
                            ^self push: '(',y,') and: ',
                                (self decompileBlock: x asBlock: false) with: 20].
                    self push: y with: 50].
                (x size >= 3 and: [
                        self testLastForSkipIn: (ReadStream on: x)])
                    ifTrue: [
                        "ifTrue:[] ifFalse:[]"
                        offset := x removeLast.
                        offset := offset * 256 + x removeLast.
                        x removeLast.
                        x := self decompileBlock: x asBlock: false.
                        self push: (self testForBraces: self popAll with: 25),' ifTrue: ',x,
                                   ' ifFalse: ',
                                    (self decompileNextBytes: offset asBlock: false)
                             with: 20.
                        blockReturnsEmptyStack ifTrue: [
                            self emptyStack] ]
                    ifFalse: [
                        "ifTrue:[] "
                        self push: (self testForBraces: self popAll with: 25),' ifTrue: ',
                                   (self decompileBlock: x asBlock: false).
                        blockReturnsEmptyStack ifTrue: [
                            self emptyStack] ]
        ].
    offset == 0
        ifTrue: [
            "ifTrue:[] "
             self push: (self testForBraces: self popAll with: 25),' ifTrue: []'.
             blockReturnsEmptyStack ifTrue: [
                  self emptyStack] ].
    offset < 0
        ifTrue: [
            "[] whileFalse: []"
            self emptyStack.
            self pop.
            x := self getCommands.
            commandQueue := self pop.
            self push: '[',x,']\  whileFalse: ' withCrs,self pop
        ]!

skipIfTrue

    "Bytecode: 113 ll hh

        hhll is a signed offset"

    | offset x y z|

    (offset := self getWord) >= 32768
        ifTrue: [
            offset := offset - 65536].

    offset > 0
        ifTrue: [
            x := self getBlockWithNextBytes: offset.
            (x at: 1) == 107
                ifTrue: [
                    "xx or: []"
                    x removeFirst.
                    y := self testForBraces: self popAll with: 25.
                    ((z := self pop) = 'true' or: [z = 'false'])
                        ifTrue: [
                            ^self push: '(',y,') or:  ',
                                (self decompileBlock: x asBlock: false) with: 20].
                    self push: y with: 50].
                (x size >= 3 and: [
                        self testLastForSkipIn: (ReadStream on: x)])
                    ifTrue: [
                        "ifFalse:[] ifTrue:[]"
                        offset := x removeLast.
                        offset := offset * 256 + x removeLast.
                        x removeLast.
                        x := self decompileBlock: x asBlock: false.
                        self push: (self testForBraces: self popAll with: 25),' ifFalse: ',x,
                                   ' ifTrue: ',
                                    (self decompileNextBytes: offset asBlock: false)
                             with: 20.
                        blockReturnsEmptyStack ifTrue: [
                            self emptyStack]]
                ifFalse: [
                        "ifFalse:[] "
                        self push: (self testForBraces: self popAll with: 25),' ifFalse: ',
                                   (self decompileBlock: x asBlock: false).
                        blockReturnsEmptyStack ifTrue: [
                            self emptyStack] ]
        ].
    offset == 0
        ifTrue: [
            "ifFalse:[] "
            self push: (self testForBraces: self popAll with: 25),' ifTrue: []'.
            blockReturnsEmptyStack ifTrue: [
                self emptyStack] ].
    offset < 0
        ifTrue: [
            "[] whileTrue: []"
            self emptyStack.
            self pop.
            x := self getCommands.
            commandQueue := self pop.
            self push: '[',x,']\  whileTrue: ' withCrs,self pop
        ]!

store: variable

    | x |
    x := variable,' := ',(self testForBraces: self popAll with: 15).
    self topOfStack == #DUP
        ifTrue: [
            self popDUP.
            self push: x with: 10]
        ifFalse: [
            self print: x]!

storeGlobVar

    "Bytecode: 86 ll hh

                n := hhll/4-2 "

    self storeGlobVar: (self getWord / 4 - 7)!

storeGlobVar: n

    "Bytecode: 80..85

                n := b-80 "

    self store: (self getGlobVarName: n)!

storeInstVar

    "Bytecode: 58 ll hh

                n := hhll/4-6 "

    self storeInstVar: (self getWord / 4 - 3)!

storeInstVar: n

    "Bytecode: 52..57

                n := b-52 "

    self store: (instanceVariables at: n + 1)!

storeLocal


    "Bytecode:  44 ll hh

               n:=hhll/4-5  "


    self storeLocal: (self getWord / 4 - 5)!

storeLocal: n


    "Bytecode:  38..43 bzw. 66..71

               n:=b-38 bzw. n=b-66 "


    self store: (localVariables at: n + 1)!

storeLocalB


    "Bytecode:  72 ll hh

               n:=hhll/4-10  "


    self storeLocal: (self getWord / 4 - 10)!

testForBraces: x with: y

    (x at: 2) < y
        ifTrue:  [^'(',(x at: 1),')']
        ifFalse: [^x at: 1]!

testLastForSkipIn: aStream

    ^(self giveLastCommandIn: aStream) == 112!

topOfStack

    "Method for internal stack handling"

    stack isEmpty ifTrue: [^nil].
    ^stack last at: 1! !
