| 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
 |