/
frame.py
274 lines (233 loc) · 10.3 KB
/
frame.py
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
from rpython.rlib import jit
from topaz.closure import LocalCell
from topaz.objects.hashobject import W_HashObject
from topaz.objects.functionobject import W_FunctionObject
class BaseFrame(object):
_attrs_ = ["backref", "escaped", "back_last_instr"]
def __init__(self):
self.backref = jit.vref_None
self.escaped = False
class Frame(BaseFrame):
_virtualizable_ = [
"bytecode", "localsstack_w[*]", "stackpos", "w_self", "block",
"cells[*]", "lastblock", "lexical_scope", "last_instr",
"parent_interp", "top_parent_interp",
]
@jit.unroll_safe
def __init__(self, bytecode, w_self, lexical_scope, block, parent_interp,
top_parent_interp, regexp_match_cell):
self = jit.hint(self, fresh_virtualizable=True, access_directly=True)
BaseFrame.__init__(self)
self.bytecode = bytecode
self.localsstack_w = [None] * (
len(bytecode.cellvars) + bytecode.max_stackdepth)
self.stackpos = len(bytecode.cellvars)
self.last_instr = 0
self.cells = ([LocalCell() for _ in bytecode.cellvars] +
[None] * len(bytecode.freevars))
self.regexp_match_cell = regexp_match_cell
self.w_self = w_self
self.lexical_scope = lexical_scope
self.block = block
self.parent_interp = parent_interp
self.top_parent_interp = top_parent_interp
self.visibility = W_FunctionObject.PUBLIC
self.lastblock = None
def _set_arg(self, space, pos, w_value):
assert pos >= 0
self.cells[pos].set(space, self, pos, w_value)
def handle_block_args(self, space, bytecode, args_w, block):
if (len(args_w) == 1 and (
len(bytecode.arg_pos) >= 2 or (
len(bytecode.arg_pos) > 0 and
bytecode.splat_arg_pos != -1))):
w_arg = args_w[0]
if (not space.is_kind_of(w_arg, space.w_array) and
space.respond_to(w_arg, "to_ary")):
w_arg = space.convert_type(w_arg, space.w_array, "to_ary",
raise_error=True,
reraise_error=True)
if space.is_kind_of(w_arg, space.w_array):
args_w = space.listview(w_arg)
minargc = len(bytecode.arg_pos) - len(bytecode.defaults)
if len(args_w) < minargc:
args_w.extend([space.w_nil] * (minargc - len(args_w)))
if bytecode.splat_arg_pos == -1:
if len(args_w) > len(bytecode.arg_pos):
del args_w[len(bytecode.arg_pos):]
return self.handle_args(space, bytecode, args_w, block)
@jit.unroll_safe
def handle_args(self, space, bytecode, args_w, block):
from topaz.interpreter import Interpreter
keywords_hash = None
if len(bytecode.kwarg_names) > 0 or bytecode.kwrest_pos != -1:
# we only take the hash if we have more than enough arguments
min_args = max(len(bytecode.arg_pos) - len(bytecode.defaults), 0)
if len(args_w) > min_args:
w_obj = args_w[-1]
if not space.is_kind_of(w_obj, space.w_hash):
w_obj = space.convert_type(
w_obj, space.w_hash, "to_hash", reraise_error=True)
if isinstance(w_obj, W_HashObject):
keywords_hash = space.send(w_obj, "clone")
assert isinstance(keywords_hash, W_HashObject)
if (len(bytecode.kw_defaults) < len(bytecode.kwarg_names) and
not keywords_hash):
raise space.error(
space.w_ArgumentError,
"missing keywords: %s" % ",".join(bytecode.kwarg_names))
pre = 0
post = len(args_w) if keywords_hash is None else len(args_w) - 1
if (post < (len(bytecode.arg_pos) - len(bytecode.defaults)) or
(bytecode.splat_arg_pos == -1 and
post > len(bytecode.arg_pos))):
raise space.error(
space.w_ArgumentError,
"wrong number of arguments (%d for %d)" % (
len(args_w),
len(bytecode.arg_pos) - len(bytecode.defaults)))
if bytecode.default_arg_begin != -1:
len_pre_args = bytecode.default_arg_begin
elif bytecode.splat_arg_pos != -1:
len_pre_args = bytecode.splat_arg_pos
else:
len_pre_args = len(bytecode.arg_pos)
len_post_arg = (len(bytecode.arg_pos) - len(bytecode.defaults) -
len_pre_args)
# [required args, optional args, splat arg, required args, keywords args, keyword rest, block]
# ^ ^
# pre post
# fill post-arguments from back.
actual_args_len = post
offset = len(bytecode.arg_pos) - post
for i in xrange(actual_args_len - 1, actual_args_len - len_post_arg - 1, -1):
self._set_arg(space, bytecode.arg_pos[i + offset], args_w[i])
post -= 1
# [required args, optional args, splat arg, required args, keywords args, keyword rest, block]
# ^ ^-------------
# pre post
# fill arguments from start as far as we can (until we hit post or the
# end of the default arguments + normal arguments
for i in xrange(min(post, len_pre_args + len(bytecode.defaults))):
self._set_arg(space, bytecode.arg_pos[i], args_w[i])
pre += 1
# [required args, optional args, splat arg, required args, keywords args, keyword rest, block]
# ------------------------^ ^
# pre post
given_defaults = pre - len_pre_args
# fill up remaining default arguments with their default values
for i in xrange(given_defaults, len(bytecode.defaults)):
bc = bytecode.defaults[i]
self.bytecode = bc
w_value = Interpreter().interpret(space, self, bc)
self._set_arg(space, bytecode.arg_pos[len_pre_args + i], w_value)
if bytecode.splat_arg_pos != -1:
if pre >= post:
splat_args_w = []
else:
splat_args_w = args_w[pre:post]
w_splat_args = space.newarray(splat_args_w)
self._set_arg(space, bytecode.splat_arg_pos, w_splat_args)
for i, name in enumerate(bytecode.kwarg_names):
w_key = space.newsymbol(name)
w_value = None
if keywords_hash is not None:
try:
w_value = keywords_hash.getitem(w_key)
keywords_hash.delete(w_key)
except KeyError:
pass
# kword arguments with defaults come first, so if we get an
# index error here, we're missing a required keyword argument
if w_value is None:
try:
bc = bytecode.kw_defaults[i]
except IndexError:
raise space.error(
space.w_ArgumentError,
"missing keyword: %s" % name)
self.bytecode = bc
w_value = Interpreter().interpret(space, self, bc)
self._set_arg(space, bytecode.cellvars.index(name), w_value)
self.bytecode = bytecode
if bytecode.kwrest_pos != -1:
if keywords_hash is not None:
self._set_arg(space, bytecode.kwrest_pos, keywords_hash)
else:
self._set_arg(space, bytecode.kwrest_pos, space.newhash())
elif keywords_hash is not None:
if keywords_hash.size() > 0:
raise space.error(
space.w_ArgumentError,
"unknown keywords: %s" % space.str_w(
space.send(space.send(keywords_hash, "keys"), "to_s")
))
if bytecode.block_arg_pos != -1:
if block is None:
w_block = space.w_nil
else:
w_block = block.copy(space)
self._set_arg(space, bytecode.block_arg_pos, w_block)
def push(self, w_obj):
stackpos = jit.promote(self.stackpos)
self.localsstack_w[stackpos] = w_obj
self.stackpos = stackpos + 1
def pop(self):
stackpos = jit.promote(self.stackpos) - 1
assert stackpos >= 0
w_res = self.localsstack_w[stackpos]
self.localsstack_w[stackpos] = None
self.stackpos = stackpos
return w_res
@jit.unroll_safe
def popitemsreverse(self, n):
items_w = [None] * n
for i in xrange(n - 1, -1, -1):
items_w[i] = self.pop()
return items_w
def peek(self):
stackpos = jit.promote(self.stackpos) - 1
assert stackpos >= 0
return self.localsstack_w[stackpos]
def popblock(self):
lastblock = self.lastblock
if lastblock is not None:
self.lastblock = lastblock.lastblock
return lastblock
@jit.unroll_safe
def unrollstack(self, kind):
while self.lastblock is not None:
block = self.popblock()
if block.handling_mask & kind:
return block
block.cleanupstack(self)
def unrollstack_and_jump(self, space, unroller):
block = self.unrollstack(unroller.kind)
return block.handle(space, self, unroller)
def has_contents(self):
return True
def get_filename(self):
return self.bytecode.filepath
def get_lineno(self, prev_frame):
if prev_frame is None:
instr = self.last_instr
else:
instr = prev_frame.back_last_instr - 1
try:
return self.bytecode.lineno_table[instr]
except IndexError:
return self.last_instr
def get_code_name(self):
return self.bytecode.name
class BuiltinFrame(BaseFrame):
def __init__(self, name):
BaseFrame.__init__(self)
self.name = name
def has_contents(self):
return self.backref() is not None
def get_filename(self):
return self.backref().get_filename()
def get_lineno(self, prev_frame):
return self.backref().get_lineno(self)
def get_code_name(self):
return self.name