1 | """Implementations of Python fundamental objects for Byterun."""
|
2 | from __future__ import print_function
|
3 |
|
4 | import collections
|
5 | import sys
|
6 | import types
|
7 |
|
8 | from opy.lib import dis
|
9 | from opy.lib import inspect
|
10 |
|
11 |
|
12 | def debug1(msg, *args):
|
13 | if args:
|
14 | msg = msg % args
|
15 | print(msg, file=sys.stderr)
|
16 |
|
17 |
|
18 | def make_cell(value):
|
19 | # Thanks to Alex Gaynor for help with this bit of twistiness.
|
20 | # Construct an actual cell object by creating a closure right here,
|
21 | # and grabbing the cell object out of the function we create.
|
22 | fn = (lambda x: lambda: x)(value)
|
23 | return fn.func_closure[0]
|
24 |
|
25 |
|
26 | class Function(object):
|
27 | """
|
28 | CPython equivalent:
|
29 |
|
30 | x = PyFunction_New(v, f->f_globals);
|
31 | PyFunction_SetClosure(x, v)
|
32 | """
|
33 |
|
34 | __slots__ = [
|
35 | 'func_code', 'func_name', 'func_defaults', 'func_globals',
|
36 | 'func_dict', 'func_closure',
|
37 | '__name__', '__dict__', '__doc__',
|
38 | '_vm', '_func',
|
39 | ]
|
40 |
|
41 | def __init__(self, name, code, globs, defaults, closure, vm):
|
42 | self._vm = vm
|
43 | self.func_code = code
|
44 | self.func_name = self.__name__ = name or code.co_name
|
45 | self.func_defaults = tuple(defaults)
|
46 | self.func_globals = globs
|
47 | self.__dict__ = {}
|
48 | self.func_closure = closure
|
49 | self.__doc__ = code.co_consts[0] if code.co_consts else None
|
50 |
|
51 | # Sometimes, we need a real Python function. This is for that.
|
52 | # For some reason types.FunctionType doesn't accept normal keyword
|
53 | # args? Like args=, closure= ?
|
54 | # Is this only used for inspect.getcallargs?
|
55 |
|
56 | kw = {'argdefs': self.func_defaults}
|
57 | if closure:
|
58 | kw['closure'] = tuple(make_cell(0) for _ in closure)
|
59 | self._func = types.FunctionType(code, globs, **kw)
|
60 |
|
61 | def __repr__(self): # pragma: no cover
|
62 | return '<byterun Function %s at 0x%08x>' % (self.func_name, id(self))
|
63 |
|
64 | def __get__(self, instance, owner):
|
65 | # used in unit tests? But I don't see it triggered in real code.
|
66 | #raise AssertionError('Function.__get__')
|
67 | m = Method(instance, owner, self)
|
68 | #debug1('*** METHOD %s', m)
|
69 | return m
|
70 |
|
71 | def __call__(self, *args, **kwargs):
|
72 | #if PY2 and self.func_name in ["<setcomp>", "<dictcomp>", "<genexpr>"]:
|
73 | # D'oh! http://bugs.python.org/issue19611 Py2 doesn't know how to
|
74 | # inspect set comprehensions, dict comprehensions, or generator
|
75 | # expressions properly. They are always functions of one argument,
|
76 | # so just do the right thing.
|
77 | #assert len(args) == 1 and not kwargs, "Surprising comprehension!"
|
78 | #callargs = {".0": args[0]}
|
79 |
|
80 | # Different workaround for issue 19611 that works with
|
81 | # compiler2-generated code. Note that byterun does not use fastlocals,
|
82 | # so the name matters. With fastlocals, the co_varnames entry is just
|
83 | # a comment; the index is used instead.
|
84 | code = self.func_code
|
85 | if code.co_argcount == 1 and code.co_varnames[0] == '.0':
|
86 | callargs = {".0": args[0]}
|
87 | else:
|
88 | # NOTE: Can get ValueError due to issue 19611
|
89 | callargs = inspect.getcallargs(self._func, *args, **kwargs)
|
90 | #print('-- func_name %s CALLS ARGS %s' % (self.func_name, callargs))
|
91 |
|
92 | frame = self._vm.make_frame(self.func_code, callargs,
|
93 | self.func_globals, {})
|
94 |
|
95 | CO_GENERATOR = 32 # flag for "this code uses yield"
|
96 | if self.func_code.co_flags & CO_GENERATOR:
|
97 | gen = Generator(frame, self._vm)
|
98 | frame.generator = gen
|
99 | retval = gen
|
100 | else:
|
101 | # NOTE: Can raise exceptions!
|
102 | retval = self._vm.run_frame(frame)
|
103 | return retval
|
104 |
|
105 |
|
106 | class Method(object):
|
107 | def __init__(self, obj, _class, func):
|
108 | self.im_self = obj
|
109 | self.im_class = _class
|
110 | self.im_func = func
|
111 |
|
112 | def __repr__(self): # pragma: no cover
|
113 | name = "%s.%s" % (self.im_class.__name__, self.im_func.func_name)
|
114 | if self.im_self is not None:
|
115 | return '<Bound Method %s of %s>' % (name, self.im_self)
|
116 | else:
|
117 | return '<Unbound Method %s>' % (name,)
|
118 |
|
119 | def __call__(self, *args, **kwargs):
|
120 | if self.im_self is not None:
|
121 | return self.im_func(self.im_self, *args, **kwargs)
|
122 | else:
|
123 | return self.im_func(*args, **kwargs)
|
124 |
|
125 |
|
126 | class Cell(object):
|
127 | """A fake cell for closures.
|
128 |
|
129 | Closures keep names in scope by storing them not in a frame, but in a
|
130 | separate object called a cell. Frames share references to cells, and
|
131 | the LOAD_DEREF and STORE_DEREF opcodes get and set the value from cells.
|
132 |
|
133 | This class acts as a cell, though it has to jump through two hoops to make
|
134 | the simulation complete:
|
135 |
|
136 | 1. In order to create actual FunctionType functions, we have to have
|
137 | actual cell objects, which are difficult to make. See the twisty
|
138 | double-lambda in __init__.
|
139 |
|
140 | 2. Actual cell objects can't be modified, so to implement STORE_DEREF,
|
141 | we store a one-element list in our cell, and then use [0] as the
|
142 | actual value.
|
143 |
|
144 | """
|
145 | def __init__(self, value):
|
146 | self.contents = value
|
147 |
|
148 | def get(self):
|
149 | return self.contents
|
150 |
|
151 | def set(self, value):
|
152 | self.contents = value
|
153 |
|
154 |
|
155 | # PyTryBlock in CPython, for SETUP_EXCEPT, etc.
|
156 | Block = collections.namedtuple("Block", "type, handler, level")
|
157 |
|
158 |
|
159 | class Frame(object):
|
160 | def __init__(self, f_code, f_globals, f_locals, f_back):
|
161 | self.f_code = f_code
|
162 | self.f_globals = f_globals
|
163 | self.f_locals = f_locals
|
164 | self.f_back = f_back
|
165 | self.stack = []
|
166 | if f_back:
|
167 | self.f_builtins = f_back.f_builtins
|
168 | else:
|
169 | self.f_builtins = f_locals['__builtins__']
|
170 | if hasattr(self.f_builtins, '__dict__'):
|
171 | self.f_builtins = self.f_builtins.__dict__
|
172 |
|
173 | self.f_lineno = f_code.co_firstlineno
|
174 | self.f_lasti = 0
|
175 |
|
176 | if f_code.co_cellvars:
|
177 | self.cells = {}
|
178 | if not f_back.cells:
|
179 | f_back.cells = {}
|
180 | for var in f_code.co_cellvars:
|
181 | # Make a cell for the variable in our locals, or None.
|
182 | cell = Cell(self.f_locals.get(var))
|
183 | f_back.cells[var] = self.cells[var] = cell
|
184 | else:
|
185 | self.cells = None
|
186 |
|
187 | if f_code.co_freevars:
|
188 | if not self.cells:
|
189 | self.cells = {}
|
190 | for var in f_code.co_freevars:
|
191 | assert self.cells is not None
|
192 | assert f_back.cells, "f_back.cells: %r" % (f_back.cells,)
|
193 | self.cells[var] = f_back.cells[var]
|
194 |
|
195 | self.block_stack = []
|
196 | self.generator = None
|
197 |
|
198 | def __repr__(self): # pragma: no cover
|
199 | return '<Frame at 0x%08x: %r @ %d>' % (
|
200 | id(self), self.f_code.co_filename, self.f_lineno
|
201 | )
|
202 |
|
203 | def top(self):
|
204 | """Return the value at the top of the stack, with no changes."""
|
205 | return self.stack[-1]
|
206 |
|
207 | def pop(self, i=0):
|
208 | """Pop a value from the stack.
|
209 |
|
210 | Default to the top of the stack, but `i` can be a count from the top
|
211 | instead.
|
212 |
|
213 | """
|
214 | return self.stack.pop(-1-i)
|
215 |
|
216 | def push(self, *vals):
|
217 | """Push values onto the value stack."""
|
218 | self.stack.extend(vals)
|
219 |
|
220 | def popn(self, n):
|
221 | """Pop a number of values from the value stack.
|
222 |
|
223 | A list of `n` values is returned, the deepest value first.
|
224 | """
|
225 | if n:
|
226 | ret = self.stack[-n:]
|
227 | del self.stack[-n:]
|
228 | return ret
|
229 | else:
|
230 | return []
|
231 |
|
232 | def peek(self, n):
|
233 | """Get a value `n` entries down in the stack, without changing the stack."""
|
234 | return self.stack[-n]
|
235 |
|
236 | def jump(self, offset):
|
237 | """Move the bytecode pointer to `offset`, so it will execute next."""
|
238 | self.f_lasti = offset
|
239 |
|
240 | def push_block(self, type, handler=None, level=None):
|
241 | """Used for SETUP_{LOOP,EXCEPT,FINALLY,WITH}."""
|
242 | if level is None:
|
243 | level = len(self.stack)
|
244 | self.block_stack.append(Block(type, handler, level))
|
245 |
|
246 | def pop_block(self):
|
247 | return self.block_stack.pop()
|
248 |
|
249 | def _unwind_block(self, block, vm):
|
250 | """
|
251 | Args:
|
252 | vm: VirtualMachineError to possibly mutate
|
253 | """
|
254 | if block.type == 'except-handler':
|
255 | offset = 3
|
256 | else:
|
257 | offset = 0
|
258 |
|
259 | while len(self.stack) > block.level + offset:
|
260 | self.pop()
|
261 |
|
262 | if block.type == 'except-handler':
|
263 | tb, value, exctype = self.popn(3)
|
264 | vm.last_exception = exctype, value, tb
|
265 |
|
266 | def handle_block_stack(self, why, vm):
|
267 | """
|
268 | After every bytecode that returns why != None, handle everything on the
|
269 | block stack.
|
270 |
|
271 | The block stack and data stack are shuffled for looping, exception
|
272 | handling, or returning.
|
273 | """
|
274 | assert why != 'yield'
|
275 |
|
276 | block = self.block_stack[-1]
|
277 | if block.type == 'loop' and why == 'continue':
|
278 | self.jump(vm.return_value)
|
279 | why = None
|
280 | return why
|
281 |
|
282 | self.pop_block()
|
283 | self._unwind_block(block, vm)
|
284 |
|
285 | if block.type == 'loop' and why == 'break':
|
286 | why = None
|
287 | self.jump(block.handler)
|
288 | return why
|
289 |
|
290 | if (block.type in ('finally', 'with') or
|
291 | block.type == 'setup-except' and why == 'exception'):
|
292 | if why == 'exception':
|
293 | exctype, value, tb = vm.last_exception
|
294 | self.push(tb, value, exctype)
|
295 | else:
|
296 | if why in ('return', 'continue'):
|
297 | self.push(vm.return_value)
|
298 | self.push(why)
|
299 |
|
300 | why = None
|
301 | self.jump(block.handler)
|
302 | return why
|
303 |
|
304 | return why
|
305 |
|
306 | def decode_next(self):
|
307 | """
|
308 | Parse 1 - 3 bytes of bytecode into an instruction and maybe arguments.
|
309 | """
|
310 | byteCode = ord(self.f_code.co_code[self.f_lasti])
|
311 | self.f_lasti += 1
|
312 |
|
313 | arguments = []
|
314 | if byteCode >= dis.HAVE_ARGUMENT:
|
315 | arg = self.f_code.co_code[self.f_lasti : self.f_lasti+2]
|
316 | self.f_lasti += 2
|
317 | intArg = ord(arg[0]) + (ord(arg[1]) << 8)
|
318 | if byteCode in dis.hasconst:
|
319 | arg = self.f_code.co_consts[intArg]
|
320 | elif byteCode in dis.hasfree:
|
321 | if intArg < len(self.f_code.co_cellvars):
|
322 | arg = self.f_code.co_cellvars[intArg]
|
323 | else:
|
324 | var_idx = intArg - len(self.f_code.co_cellvars)
|
325 | arg = self.f_code.co_freevars[var_idx]
|
326 | elif byteCode in dis.hasname:
|
327 | arg = self.f_code.co_names[intArg]
|
328 | elif byteCode in dis.hasjrel:
|
329 | arg = self.f_lasti + intArg
|
330 | elif byteCode in dis.hasjabs:
|
331 | arg = intArg
|
332 | elif byteCode in dis.haslocal:
|
333 | arg = self.f_code.co_varnames[intArg]
|
334 | else:
|
335 | arg = intArg
|
336 | arguments = [arg]
|
337 |
|
338 | byteName = dis.opname[byteCode]
|
339 | return byteName, arguments
|
340 |
|
341 | def line_number(self):
|
342 | """Get the current line number the frame is executing."""
|
343 | # We don't keep f_lineno up to date, so calculate it based on the
|
344 | # instruction address and the line number table.
|
345 | lnotab = self.f_code.co_lnotab
|
346 | byte_increments = lnotab[0::2]
|
347 | line_increments = lnotab[1::2]
|
348 |
|
349 | byte_num = 0
|
350 | line_num = self.f_code.co_firstlineno
|
351 |
|
352 | for byte_incr, line_incr in zip(byte_increments, line_increments):
|
353 | byte_incr = ord(byte_incr)
|
354 | line_incr = ord(line_incr)
|
355 |
|
356 | byte_num += byte_incr
|
357 | if byte_num > self.f_lasti:
|
358 | break
|
359 | line_num += line_incr
|
360 |
|
361 | return line_num
|
362 |
|
363 |
|
364 | class Generator(object):
|
365 | """A wrapper around a Frame that can run for one generator tick."""
|
366 |
|
367 | def __init__(self, g_frame, vm):
|
368 | self.g_frame = g_frame
|
369 | self._vm = vm
|
370 | self.started = False
|
371 | self.finished = False
|
372 |
|
373 | # Part of the iterator protocol.
|
374 | def __iter__(self):
|
375 | """DO_NOT_INTERPRET"""
|
376 | return self
|
377 |
|
378 | # Part of the iterator protocol.
|
379 | def next(self):
|
380 | """DO_NOT_INTERPRET"""
|
381 | # Docstring is a hack for pyvm2 ! Is there a better way?
|
382 | # This is a THIRD path for a function.
|
383 |
|
384 | return self.send(None)
|
385 |
|
386 | # Part of the iterator protocol.
|
387 | def send(self, value=None):
|
388 | """DO_NOT_INTERPRET"""
|
389 | if not self.started and value is not None:
|
390 | raise TypeError("Can't send non-None value to a just-started generator")
|
391 | self.g_frame.stack.append(value)
|
392 | self.started = True
|
393 | # NOTE: Can raise exceptions!
|
394 | val = self._vm.resume_frame(self.g_frame)
|
395 | if self.finished:
|
396 | raise StopIteration(val)
|
397 | return val
|