-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcallframe.go
205 lines (173 loc) · 5.23 KB
/
callframe.go
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
Copyright 2016-2017 by Milo Christiansen
This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product, an
acknowledgment in the product documentation would be appreciated but is not
required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
package lua
import (
"errors"
)
type callFrame struct {
fn *function
stk *stack
pc int32
base int // The index of the last item from the previous frame (-1 if this is the first frame)
// If true then the function is all of the following:
// 1. A Lua function
// 2. A variadic function
// 3. Contains at least one use of ...
// In this case all stack operation must be offset by nArgs to prevent the arguments from being clobbered.
holdArgs bool
// The number of arguments passed in. In the case of a variadic function this will be the number of passed in
// arguments minus the number of named arguments the function has.
nArgs int
nRet int // The number of items expected
retC int // The actual number of items returned
retBase int // First value to return
retTo int // Index (in previous frame) to place the first return value into.
}
// nxtOp gets the next opCode from a Lua function's code.
func (cf *callFrame) nxtOp() (instruction, bool) {
if int(cf.pc) >= len(cf.fn.proto.code) || cf.pc < 0 {
return 0, false
}
i := cf.fn.proto.code[cf.pc]
cf.pc++
return i, true
}
// tryNxtOp gets the next opCode from a Lua function's code and ensures it is of a specific type.
// If the next opCode is not of the required type this returns "0, false".
func (cf *callFrame) tryNxtOp(op opCode) (instruction, bool) {
if int(cf.pc) >= len(cf.fn.proto.code) {
return 0, false
}
i := cf.fn.proto.code[cf.pc]
if i.getOpCode() != op {
return 0, false
}
cf.pc++
return i, true
}
// reqNxtOp gets the next opCode from a Lua function's code and ensures it is of a specific type.
func (cf *callFrame) reqNxtOp(op opCode) instruction {
if int(cf.pc) >= len(cf.fn.proto.code) {
panic(errors.New("VM did not find required opcode!"))
}
i := cf.fn.proto.code[cf.pc]
cf.pc++
if i.getOpCode() != op {
panic(errors.New("VM did not find required opcode!"))
}
return i
}
func (cf *callFrame) getUp(i int) value {
if i < 0 || i >= len(cf.fn.up) {
panic(errors.New("Attempt to get out of range upvalue!"))
}
def := cf.fn.up[i]
if def.isLocal && !def.closed {
return cf.stk.GetAbs(def.absIdx)
}
if !def.closed {
panic("IMPOSSIBLE")
}
return def.val
}
func (cf *callFrame) setUp(i int, v value) {
if i < 0 || i >= len(cf.fn.up) {
panic(errors.New("Attempt to set out of range upvalue!"))
return
}
def := cf.fn.up[i]
if def.isLocal && !def.closed {
cf.stk.SetAbs(def.absIdx, v)
return
}
if !def.closed {
panic("IMPOSSIBLE")
}
def.val = v
}
// Note that the closing functions close upvalues in the TOP frame(s) NOT the frame it was called on (unless called on the top frame).
func (cf *callFrame) closeUpAbs(a int) {
//x := cf.stk.unclosed
//for x != nil {
// println("< ", x.absIdx, ":", toString(cf.stk.GetAbs(x.absIdx)), ":", x.name)
// x = x.next
//}
//println("> close:", a)
nxt := cf.stk.unclosed
for nxt != nil {
if !nxt.isLocal || nxt.absIdx < 0 {
panic("IMPOSSIBLE")
} // All upvalues in actual use are in some way on the stack or already closed
if nxt.absIdx < a {
break // all values beyond this point are lower in the stack
}
//println("> closing", nxt.absIdx)
nxt.val = cf.stk.GetAbs(nxt.absIdx)
nxt.closed = true
nxt = nxt.next
}
cf.stk.unclosed = nxt
}
// Lazy convenience
func (cf *callFrame) closeUp(a int) {
cf.closeUpAbs(cf.stk.absIndex(a))
}
// Lazy convenience
func (cf *callFrame) closeUpAll() {
cf.closeUpAbs(cf.stk.absIndex(0))
}
// Find or create an unclosed *local* upvalue that matches the definition
func (cf *callFrame) findUp(def upDef) *upValue {
idx := cf.stk.absIndex(def.index)
node := cf.stk.unclosed
var pnode *upValue
for {
// Case order is very important!
switch {
case node == nil:
// No list exists yet, add this item as the head.
// This can only happen on the very first iteration, so check it last.
up := def.makeUp()
up.absIdx = idx
cf.stk.unclosed = up
return up
case node.absIdx == idx:
// Found a matching item, return it directly
return node
case node.absIdx < idx:
// New item should be inserted just before this item
up := def.makeUp()
up.absIdx = idx
if pnode == nil {
up.next = node
cf.stk.unclosed = up
} else {
up.next = node
pnode.next = up
}
return up
case node.next == nil:
// If item should be added to the end of the list
up := def.makeUp()
up.absIdx = idx
node.next = up
return up
}
pnode = node
node = node.next
}
}