Permalink
Browse files

Simplify VirtualMachine public interface by pulling out run_code().

- Some logging to isolate the testdata/generator_exceptions.py bug.
  • Loading branch information...
Andy Chu
Andy Chu committed Apr 7, 2018
1 parent 82563d8 commit 7d8f6682195b510b99478611e942894f46d2c7f8
Showing with 46 additions and 29 deletions.
  1. +3 −3 opy/byterun/execfile.py
  2. +5 −3 opy/byterun/pyobj.py
  3. +34 −19 opy/byterun/pyvm2.py
  4. +4 −4 opy/byterun/vmtest.py
View
@@ -4,7 +4,7 @@
import os
import sys
from pyvm2 import VirtualMachine
import pyvm2
# In Py 2.x, the builtins were in __builtin__
@@ -30,10 +30,10 @@ def run_code_object(code, args, package=None):
else:
sys.path[0] = os.path.abspath(os.path.dirname(args[0]))
vm = VirtualMachine()
vm = pyvm2.VirtualMachine()
try:
# Execute the source file.
vm.run_code(code, f_globals=main_mod.__dict__)
pyvm2.run_code(vm, code, f_globals=main_mod.__dict__)
finally:
# Restore the old __main__
sys.modules['__main__'] = old_main_mod
View
@@ -36,9 +36,11 @@ def __init__(self, name, code, globs, locs, defaults, closure, vm):
self.__doc__ = code.co_consts[0] if code.co_consts else None
# Sometimes, we need a real Python function. This is for that.
kw = {
'argdefs': self.func_defaults,
}
# For some reason types.FunctionType doesn't accept normal keyword
# args? Like args=, closure= ?
# Is this only used for inspect.getcallargs?
kw = {'argdefs': self.func_defaults}
if closure:
kw['closure'] = tuple(make_cell(0) for _ in closure)
self._func = types.FunctionType(code, globs, **kw)
View
@@ -31,6 +31,10 @@ def debug(msg, *args):
if not VERBOSE:
return
debug1(msg, *args)
def debug1(msg, *args):
if args:
msg = msg % args
print(msg, file=sys.stderr)
@@ -74,6 +78,17 @@ def __str__(self):
return '\n'.join(parts) + '\n'
def run_code(vm, code, f_globals=None):
"""Main entry point.
Used by tests and by execfile.
"""
frame = vm.make_frame(code, f_globals=f_globals)
val = vm.run_frame(frame)
vm.check_invariants()
return val
class VirtualMachine(object):
def __init__(self, subset=False, verbose=VERBOSE):
@@ -146,18 +161,6 @@ def resume_frame(self, frame):
frame.f_back = None
return val
def run_code(self, code, f_globals=None, f_locals=None):
"""Main entry point."""
frame = self.make_frame(code, f_globals=f_globals, f_locals=f_locals)
val = self.run_frame(frame)
# Check some invariants
if self.frames: # pragma: no cover
raise VirtualMachineError("Frames left over!")
if self.frame and self.frame.stack: # pragma: no cover
raise VirtualMachineError("Data left on stack! %r" % self.frame.stack)
return val
def logTick(self, byteName, arguments, opoffset, linestarts):
""" Log arguments, block stack, and data stack for each opcode."""
indent = " " * (len(self.frames)-1)
@@ -224,10 +227,9 @@ def _pop_frame(self):
self.frame = None
def run_frame(self, frame):
"""Run a frame until it returns (somehow).
Exceptions are raised, the return value is returned.
"""Run a frame until it returns or raises an exception.
This function raises GuestException or returns the return value.
"""
# bytecode offset -> line number
#print('frame %s ' % frame)
@@ -282,6 +284,13 @@ def run_frame(self, frame):
#print('num_ticks: %d' % num_ticks)
return self.return_value
def check_invariants(self):
# Check some invariants
if self.frames: # pragma: no cover
raise VirtualMachineError("Frames left over!")
if self.frame and self.frame.stack: # pragma: no cover
raise VirtualMachineError("Data left on stack! %r" % self.frame.stack)
## Stack manipulation
def byte_LOAD_CONST(self, const):
@@ -762,6 +771,7 @@ def byte_WITH_CLEANUP(self):
## Functions
def byte_MAKE_FUNCTION(self, argc):
"""Make a runtime object from a types.CodeObject, typically in a .pyc file."""
name = None
code = self.pop()
defaults = self.popn(argc)
@@ -808,18 +818,22 @@ def call_function(self, arg, args, kwargs):
posargs.extend(args)
func = self.pop()
debug('*** call_function POPPED %s', func)
if getattr(func, 'func_name', None) == 'decode_next':
raise AssertionError('BAD: %s' % func)
frame = self.frame
if hasattr(func, 'im_func'):
# Methods get self as an implicit first parameter.
debug('')
debug('im_self %r', (func.im_self,))
debug('posargs %r', (posargs,))
#debug('')
#debug('im_self %r', (func.im_self,))
#debug('posargs %r', (posargs,))
if func.im_self:
posargs.insert(0, func.im_self)
debug('posargs AFTER %r', (posargs,))
#debug('posargs AFTER %r', (posargs,))
# TODO: We have the frame here, but I also want the location.
# dis has it!
@@ -862,6 +876,7 @@ def call_function(self, arg, args, kwargs):
# __import__, which yields a native function.
if isinstance(func, types.FunctionType):
debug1('*** WRAPPING %s', func)
defaults = func.func_defaults or ()
byterun_func = Function(
func.func_name, func.func_code, func.func_globals,
View
@@ -10,7 +10,7 @@
import unittest
from pyvm2 import VirtualMachine, VirtualMachineError
import pyvm2
# Make this false if you need to run the debugger inside a test.
CAPTURE_STDOUT = ('-s' not in sys.argv)
@@ -48,12 +48,12 @@ def assert_ok(self, code, raises=None):
vm_stdout = cStringIO.StringIO()
if CAPTURE_STDOUT: # pragma: no branch
sys.stdout = vm_stdout
vm = VirtualMachine()
vm = pyvm2.VirtualMachine()
vm_value = vm_exc = None
try:
vm_value = vm.run_code(code)
except VirtualMachineError: # pragma: no cover
vm_value = pyvm2.run_code(vm, code)
except pyvm2.VirtualMachineError: # pragma: no cover
# If the VM code raises an error, show it.
raise
except AssertionError: # pragma: no cover

0 comments on commit 7d8f668

Please sign in to comment.