1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
package text
import (
"encoding/binary"
"fmt"
"jinx/pkg/lang/vm/code"
"jinx/pkg/libs/rangemap"
"math"
"strconv"
"strings"
)
type Decompiler struct {
c code.Code
pcToLine rangemap.RangeMap[int]
}
func NewDecompiler(c code.Code) *Decompiler {
return &Decompiler{
c: c,
pcToLine: rangemap.New[int](),
}
}
func (d *Decompiler) Decompile() string {
lines := make([]string, 0)
bc := d.c.Code()
for len(bc) != 0 {
line, rest := d.decompileInstruction(bc)
bc = rest
d.pcToLine.AppendToLast(d.c.Len()-len(bc), len(lines))
lines = append(lines, line)
}
return strings.Join(lines, "\n")
}
func (d *Decompiler) decompileInstruction(bc code.Raw) (string, code.Raw) {
op := code.Op(bc[0])
opString := OpToString(op)
if opString == "unknown" {
return fmt.Sprintf("unknown(%x)", bc[0]), bc[1:]
}
switch op {
// Operations that take no arguments.
case code.OpNop,
code.OpHalt,
code.OpPushTrue,
code.OpPushFalse,
code.OpPushNull,
code.OpPushArray,
code.OpPushObject,
code.OpDrop,
code.OpAnchorType,
code.OpAdd,
code.OpSub,
code.OpMod,
code.OpIndex,
code.OpLt,
code.OpLte,
code.OpRet,
code.OpTempArrLen,
code.OpTempArrPush:
return opString, bc[1:]
// Operations that take an int.
case code.OpPushInt,
code.OpGetLocal,
code.OpSetLocal,
code.OpGetEnv,
code.OpSetEnv,
code.OpAddToEnv,
code.OpCall:
i, rest := d.decompileInt(bc[1:])
return fmt.Sprintf("%s %s", opString, i), rest
// Operations that take a float.
case code.OpPushFloat:
f, rest := d.decompileFloat(bc[1:])
return fmt.Sprintf("%s %s", opString, f), rest
// Operations that take a string.
case code.OpPushString,
code.OpPushType,
code.OpGetGlobal,
code.OpSetMember,
code.OpGetMember:
s, rest := d.decompileString(bc[1:])
return fmt.Sprintf("%s %s", opString, s), rest
// Operations that take a pc, belonging to a function.
case code.OpPushFunction:
// TODO: Add function labels to output and give them names.
fallthrough
// Operations that take a pc, belonging to a non-call jump. (when branching)
case code.OpJmp,
code.OpJt,
code.OpJf:
// TODO: Add jump labels to output and give them names.
i, rest := d.decompileInt(bc[1:])
return fmt.Sprintf("%s @%s", opString, i), rest
}
panic("decompiler can't decompile op: " + opString)
}
func (d *Decompiler) decompileInt(bc code.Raw) (string, code.Raw) {
i := binary.LittleEndian.Uint64(bc[:8])
return strconv.FormatInt(int64(i), 10), bc[8:]
}
func (d *Decompiler) decompileFloat(bc code.Raw) (string, code.Raw) {
i := binary.LittleEndian.Uint64(bc[:8])
f := math.Float64frombits(i)
return strconv.FormatFloat(f, 'f', -1, 64), bc[8:]
}
func (d *Decompiler) decompileString(bc code.Raw) (string, code.Raw) {
var buf strings.Builder
buf.WriteString("\"")
end := 0
for i, b := range bc {
if b == 0 {
end = i
break
}
buf.WriteByte(b)
}
buf.WriteString("\"")
return buf.String(), bc[end+1:]
}
|