[pypy-commit] lang-smalltalk storage-interpreter-refactoring: Intermediate commit.
anton_gulenko
noreply at buildbot.pypy.org
Sun Jul 27 12:22:11 CEST 2014
Author: Anton Gulenko <anton.gulenko at googlemail.com>
Branch: storage-interpreter-refactoring
Changeset: r948:3d889c32a173
Date: 2014-07-25 08:55 +0200
http://bitbucket.org/pypy/lang-smalltalk/changeset/3d889c32a173/
Log: Intermediate commit. Added message to all SmalltalkExceptions,
catching and printing at toplevel. Extracted
interpreter_debugging.py and interpreter_bytecodes.py.
diff too long, truncating to 2000 out of 2010 lines
diff --git a/spyvm/error.py b/spyvm/error.py
--- a/spyvm/error.py
+++ b/spyvm/error.py
@@ -1,30 +1,42 @@
-# some exception classes for the Smalltalk VM
+
+# Some exception classes for the Smalltalk VM
class SmalltalkException(Exception):
"""Base class for Smalltalk exception hierarchy"""
+ exception_type = "SmalltalkException"
+ _attrs_ = ["msg"]
+ def __init__(self, msg="<no message>"):
+ self.msg = msg
class PrimitiveFailedError(SmalltalkException):
- pass
+ exception_type = "PrimitiveFailedError"
class PrimitiveNotYetWrittenError(PrimitiveFailedError):
- pass
+ exception_type = "PrimitiveNotYetWrittenError"
class UnwrappingError(PrimitiveFailedError):
- pass
+ exception_type = "UnwrappingError"
class WrappingError(PrimitiveFailedError):
- pass
+ exception_type = "WrappingError"
class WrapperException(SmalltalkException):
- def __init__(self, msg):
- self.msg = msg
+ exception_type = "WrapperException"
class FatalError(SmalltalkException):
- def __init__(self, msg):
- self.msg = msg
+ exception_type = "FatalError"
class BlockCannotReturnError(SmalltalkException):
- pass
+ exception_type = "BlockCannotReturnError"
+
+class MethodNotFound(SmalltalkException):
+ exception_type = "MethodNotFound"
+
+class MissingBytecode(SmalltalkException):
+ """Bytecode not implemented yet."""
+ exception_type = "MissingBytecode"
+ def __init__(self, bytecodename):
+ SmalltalkException.__init__(self, "Missing bytecode encountered: %s" % bytecodename)
class Exit(Exception):
_attrs_ = ["msg"]
diff --git a/spyvm/interpreter.py b/spyvm/interpreter.py
--- a/spyvm/interpreter.py
+++ b/spyvm/interpreter.py
@@ -1,26 +1,60 @@
-import py
import os
-from spyvm.shadow import ContextPartShadow, MethodContextShadow, BlockContextShadow, MethodNotFound
-from spyvm import model, constants, primitives, conftest, wrapper, objspace
-from spyvm.tool.bitmanipulation import splitter
-from rpython.rlib import jit, rstackovf
-from rpython.rlib import objectmodel, unroll
+from spyvm.shadow import MethodContextShadow
+from spyvm import model, constants, wrapper, objspace, interpreter_bytecodes
-class MissingBytecode(Exception):
- """Bytecode not implemented yet."""
- def __init__(self, bytecodename):
- self.bytecodename = bytecodename
- print "MissingBytecode:", bytecodename # hack for debugging
+from rpython.rlib import jit, rstackovf, unroll
-class IllegalStoreError(Exception):
- """Illegal Store."""
+class ReturnFromTopLevel(Exception):
+ _attrs_ = ["object"]
+ def __init__(self, object):
+ self.object = object
+
+class Return(Exception):
+ _attrs_ = ["value", "s_target_context", "is_local"]
+ def __init__(self, s_target_context, w_result):
+ self.value = w_result
+ self.s_target_context = s_target_context
+ self.is_local = False
+
+class ContextSwitchException(Exception):
+ """General Exception that causes the interpreter to leave
+ the current context."""
+
+ _attrs_ = ["s_new_context"]
+ type = "ContextSwitch"
+ def __init__(self, s_new_context):
+ self.s_new_context = s_new_context
+
+ def print_trace(self, old_context):
+ print "====== %s, contexts forced to heap at: %s" % (self.type, self.s_new_context.short_str())
+
+class StackOverflow(ContextSwitchException):
+ """This causes the current jit-loop to be left, dumping all virtualized objects to the heap.
+ This breaks performance, so it should rarely happen.
+ In case of severe performance problems, execute with -t and check if this occurrs."""
+ type = "Stack Overflow"
+
+class ProcessSwitch(ContextSwitchException):
+ """This causes the interpreter to switch the executed context.
+ Triggered when switching the process."""
+
+ def print_trace(self, old_context):
+ print "====== Switched process from: %s" % old_context.short_str()
+ print "====== to: %s " % self.s_new_context.short_str()
+
+class SenderChainManipulation(ContextSwitchException):
+ """Manipulation of the sender chain can invalidate the jitted C stack.
+ We have to dump all virtual objects and rebuild the stack.
+ We try to raise this as rarely as possible and as late as possible."""
+ type = "Sender Manipulation"
+
+UNROLLING_BYTECODE_RANGES = unroll.unrolling_iterable(interpreter_bytecodes.BYTECODE_RANGES)
def get_printable_location(pc, self, method):
bc = ord(method.bytes[pc])
name = method.safe_identifier_string()
- return '(%s) [%d]: <%s>%s' % (name, pc, hex(bc), BYTECODE_NAMES[bc])
-
+ return '(%s) [%d]: <%s>%s' % (name, pc, hex(bc), interpreter_bytecodes.BYTECODE_NAMES[bc])
class Interpreter(object):
_immutable_fields_ = ["space", "image",
@@ -218,7 +252,7 @@
s_frame.push_all(list(w_arguments))
return s_frame
- # ============== Methods for tracing, printing and debugging ==============
+ # ============== Methods for tracing and printing ==============
def is_tracing(self):
return jit.promote(self.trace)
@@ -226,875 +260,6 @@
def print_padded(self, str):
assert self.is_tracing()
print (' ' * self.stack_depth) + str
-
- def activate_debug_bytecode(self):
- "NOT_RPYTHON"
- def do_break(self):
- import pdb
- if self.break_on_bytecodes:
- pdb.set_trace()
- Interpreter.debug_bytecode = do_break
- self.break_on_bytecodes = True
-
- def debug_bytecode(self):
- # This is for debugging. In a pdb console, execute the following:
- # self.activate_debug_bytecode()
- pass
-class ReturnFromTopLevel(Exception):
- _attrs_ = ["object"]
- def __init__(self, object):
- self.object = object
-
-class Return(Exception):
- _attrs_ = ["value", "s_target_context", "is_local"]
- def __init__(self, s_target_context, w_result):
- self.value = w_result
- self.s_target_context = s_target_context
- self.is_local = False
-
-class ContextSwitchException(Exception):
- """General Exception that causes the interpreter to leave
- the current context."""
-
- _attrs_ = ["s_new_context"]
- type = "ContextSwitch"
- def __init__(self, s_new_context):
- self.s_new_context = s_new_context
-
- def print_trace(self, old_context):
- print "====== %s, contexts forced to heap at: %s" % (self.type, self.s_new_context.short_str())
-
-class StackOverflow(ContextSwitchException):
- """This causes the current jit-loop to be left, dumping all virtualized objects to the heap.
- This breaks performance, so it should rarely happen.
- In case of severe performance problems, execute with -t and check if this occurrs."""
- type = "Stack Overflow"
-
-class ProcessSwitch(ContextSwitchException):
- """This causes the interpreter to switch the executed context.
- Triggered when switching the process."""
-
- def print_trace(self, old_context):
- print "====== Switched process from: %s" % old_context.short_str()
- print "====== to: %s " % self.s_new_context.short_str()
-
-class SenderChainManipulation(ContextSwitchException):
- """Manipulation of the sender chain can invalidate the jitted C stack.
- We have to dump all virtual objects and rebuild the stack.
- We try to raise this as rarely as possible and as late as possible."""
- type = "Sender Manipulation"
-
-import rpython.rlib.unroll
-if hasattr(unroll, "unrolling_zero"):
- unrolling_zero = unroll.unrolling_zero
-else:
- class unrolling_int(int, unroll.SpecTag):
- def __add__(self, other):
- return unrolling_int(int.__add__(self, other))
- __radd__ = __add__
- def __sub__(self, other):
- return unrolling_int(int.__sub__(self, other))
- def __rsub__(self, other):
- return unrolling_int(int.__rsub__(self, other))
- unrolling_zero = unrolling_int(0)
-
-
-# This is a decorator for bytecode implementation methods.
-# parameter_bytes=N means N additional bytes are fetched as parameters.
-def bytecode_implementation(parameter_bytes=0):
- def bytecode_implementation_decorator(actual_implementation_method):
- @jit.unroll_safe
- def bytecode_implementation_wrapper(self, interp, current_bytecode):
- parameters = ()
- i = unrolling_zero
- while i < parameter_bytes:
- parameters += (self.fetch_next_bytecode(), )
- i = i + 1
- # This is a good place to step through bytecodes.
- interp.debug_bytecode()
- return actual_implementation_method(self, interp, current_bytecode, *parameters)
- bytecode_implementation_wrapper.func_name = actual_implementation_method.func_name
- return bytecode_implementation_wrapper
- return bytecode_implementation_decorator
-
-def make_call_primitive_bytecode(primitive, selector, argcount, store_pc=False):
- func = primitives.prim_table[primitive]
- @bytecode_implementation()
- def callPrimitive(self, interp, current_bytecode):
- # WARNING: this is used for bytecodes for which it is safe to
- # directly call the primitive. In general, it is not safe: for
- # example, depending on the type of the receiver, bytecodePrimAt
- # may invoke primitives.AT, primitives.STRING_AT, or anything
- # else that the user put in a class in an 'at:' method.
- # The rule of thumb is that primitives with only int and float
- # in their unwrap_spec are safe.
- try:
- return func(interp, self, argcount)
- except primitives.PrimitiveFailedError:
- pass
- return self._sendSelfSelectorSpecial(selector, argcount, interp)
- callPrimitive.func_name = "callPrimitive_%s" % func.func_name
- return callPrimitive
-
-def make_call_primitive_bytecode_classbased(a_class_name, a_primitive, alternative_class_name, alternative_primitive, selector, argcount):
- @bytecode_implementation()
- def callClassbasedPrimitive(self, interp, current_bytecode):
- rcvr = self.peek(argcount)
- receiver_class = rcvr.getclass(self.space)
- try:
- if receiver_class is getattr(self.space, a_class_name):
- func = primitives.prim_table[a_primitive]
- return func(interp, self, argcount)
- elif receiver_class is getattr(self.space, alternative_class_name):
- func = primitives.prim_table[alternative_primitive]
- return func(interp, self, argcount)
- except primitives.PrimitiveFailedError:
- pass
- return self._sendSelfSelectorSpecial(selector, argcount, interp)
- callClassbasedPrimitive.func_name = "callClassbasedPrimitive_%s" % selector
- return callClassbasedPrimitive
-
-# Some selectors cannot be overwritten, therefore no need to handle PrimitiveFailed.
-def make_quick_call_primitive_bytecode(primitive_index, argcount):
- func = primitives.prim_table[primitive_index]
- @bytecode_implementation()
- def quick_call_primitive_bytecode(self, interp, current_bytecode):
- return func(interp, self, argcount)
- return quick_call_primitive_bytecode
-
-# This is for bytecodes that actually implement a simple message-send.
-# We do not optimize anything for these cases.
-def make_send_selector_bytecode(selector, argcount):
- @bytecode_implementation()
- def selector_bytecode(self, interp, current_bytecode):
- return self._sendSelfSelectorSpecial(selector, argcount, interp)
- selector_bytecode.func_name = "selector_bytecode_%s" % selector
- return selector_bytecode
-
-# ___________________________________________________________________________
-# Bytecode Implementations:
-#
-# "self" is always a ContextPartShadow instance.
-
-# __extend__ adds new methods to the ContextPartShadow class
-class __extend__(ContextPartShadow):
-
- # ====== Push/Pop bytecodes ======
-
- @bytecode_implementation()
- def pushReceiverVariableBytecode(self, interp, current_bytecode):
- index = current_bytecode & 15
- self.push(self.w_receiver().fetch(self.space, index))
-
- @bytecode_implementation()
- def pushTemporaryVariableBytecode(self, interp, current_bytecode):
- index = current_bytecode & 15
- self.push(self.gettemp(index))
-
- @bytecode_implementation()
- def pushLiteralConstantBytecode(self, interp, current_bytecode):
- index = current_bytecode & 31
- self.push(self.w_method().getliteral(index))
-
- @bytecode_implementation()
- def pushLiteralVariableBytecode(self, interp, current_bytecode):
- # this bytecode assumes that literals[index] is an Association
- # which is an object with two named vars, and fetches the second
- # named var (the value).
- index = current_bytecode & 31
- w_association = self.w_method().getliteral(index)
- association = wrapper.AssociationWrapper(self.space, w_association)
- self.push(association.value())
-
- @bytecode_implementation()
- def storeAndPopReceiverVariableBytecode(self, interp, current_bytecode):
- index = current_bytecode & 7
- self.w_receiver().store(self.space, index, self.pop())
-
- @bytecode_implementation()
- def storeAndPopTemporaryVariableBytecode(self, interp, current_bytecode):
- index = current_bytecode & 7
- self.settemp(index, self.pop())
-
- @bytecode_implementation()
- def pushReceiverBytecode(self, interp, current_bytecode):
- self.push(self.w_receiver())
-
- @bytecode_implementation()
- def pushConstantTrueBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_true)
-
- @bytecode_implementation()
- def pushConstantFalseBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_false)
-
- @bytecode_implementation()
- def pushConstantNilBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_nil)
-
- @bytecode_implementation()
- def pushConstantMinusOneBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_minus_one)
-
- @bytecode_implementation()
- def pushConstantZeroBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_zero)
-
- @bytecode_implementation()
- def pushConstantOneBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_one)
-
- @bytecode_implementation()
- def pushConstantTwoBytecode(self, interp, current_bytecode):
- self.push(interp.space.w_two)
-
- @bytecode_implementation()
- def pushActiveContextBytecode(self, interp, current_bytecode):
- self.push(self.w_self())
-
- @bytecode_implementation()
- def duplicateTopBytecode(self, interp, current_bytecode):
- self.push(self.top())
-
- @bytecode_implementation()
- def popStackBytecode(self, interp, current_bytecode):
- self.pop()
-
- @bytecode_implementation(parameter_bytes=1)
- def pushNewArrayBytecode(self, interp, current_bytecode, descriptor):
- arraySize, popIntoArray = splitter[7, 1](descriptor)
- newArray = None
- if popIntoArray == 1:
- newArray = interp.space.wrap_list(self.pop_and_return_n(arraySize))
- else:
- newArray = interp.space.w_Array.as_class_get_shadow(interp.space).new(arraySize)
- self.push(newArray)
-
- # ====== Extended Push/Pop bytecodes ======
-
- def _extendedVariableTypeAndIndex(self, descriptor):
- return ((descriptor >> 6) & 3), (descriptor & 63)
-
- @bytecode_implementation(parameter_bytes=1)
- def extendedPushBytecode(self, interp, current_bytecode, descriptor):
- variableType, variableIndex = self._extendedVariableTypeAndIndex(descriptor)
- if variableType == 0:
- self.push(self.w_receiver().fetch(self.space, variableIndex))
- elif variableType == 1:
- self.push(self.gettemp(variableIndex))
- elif variableType == 2:
- self.push(self.w_method().getliteral(variableIndex))
- elif variableType == 3:
- w_association = self.w_method().getliteral(variableIndex)
- association = wrapper.AssociationWrapper(self.space, w_association)
- self.push(association.value())
- else:
- assert 0
-
- def _extendedStoreBytecode(self, interp, current_bytecode, descriptor):
- variableType, variableIndex = self._extendedVariableTypeAndIndex(descriptor)
- if variableType == 0:
- self.w_receiver().store(self.space, variableIndex, self.top())
- elif variableType == 1:
- self.settemp(variableIndex, self.top())
- elif variableType == 2:
- raise IllegalStoreError
- elif variableType == 3:
- w_association = self.w_method().getliteral(variableIndex)
- association = wrapper.AssociationWrapper(self.space, w_association)
- association.store_value(self.top())
-
- @bytecode_implementation(parameter_bytes=1)
- def extendedStoreBytecode(self, interp, current_bytecode, descriptor):
- return self._extendedStoreBytecode(interp, current_bytecode, descriptor)
-
- @bytecode_implementation(parameter_bytes=1)
- def extendedStoreAndPopBytecode(self, interp, current_bytecode, descriptor):
- self._extendedStoreBytecode(interp, current_bytecode, descriptor)
- self.pop()
-
- def _extract_index_and_temps(self, index_in_array, index_of_array):
- w_indirectTemps = self.gettemp(index_of_array)
- return index_in_array, w_indirectTemps
-
- @bytecode_implementation(parameter_bytes=2)
- def pushRemoteTempLongBytecode(self, interp, current_bytecode, index_in_array, index_of_array):
- index_in_array, w_indirectTemps = self._extract_index_and_temps(index_in_array, index_of_array)
- self.push(w_indirectTemps.at0(self.space, index_in_array))
-
- @bytecode_implementation(parameter_bytes=2)
- def storeRemoteTempLongBytecode(self, interp, current_bytecode, index_in_array, index_of_array):
- index_in_array, w_indirectTemps = self._extract_index_and_temps(index_in_array, index_of_array)
- w_indirectTemps.atput0(self.space, index_in_array, self.top())
-
- @bytecode_implementation(parameter_bytes=2)
- def storeAndPopRemoteTempLongBytecode(self, interp, current_bytecode, index_in_array, index_of_array):
- index_in_array, w_indirectTemps = self._extract_index_and_temps(index_in_array, index_of_array)
- w_indirectTemps.atput0(self.space, index_in_array, self.pop())
-
- @bytecode_implementation(parameter_bytes=3)
- def pushClosureCopyCopiedValuesBytecode(self, interp, current_bytecode, descriptor, j, i):
- """ Copied from Blogpost: http://www.mirandabanda.org/cogblog/2008/07/22/closures-part-ii-the-bytecodes/
- ContextPart>>pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize
- "Simulate the action of a 'closure copy' bytecode whose result is the
- new BlockClosure for the following code"
- | copiedValues |
- numCopied > 0
- ifTrue:
- [copiedValues := Array new: numCopied.
- numCopied to: 1 by: -1 do:
- [:i|
- copiedValues at: i put: self pop]]
- ifFalse:
- [copiedValues := nil].
- self push: (BlockClosure new
- outerContext: self
- startpc: pc
- numArgs: numArgs
- copiedValues: copiedValues).
- self jump: blockSize
- """
-
- space = self.space
- numArgs, numCopied = splitter[4, 4](descriptor)
- blockSize = (j << 8) | i
- # Create new instance of BlockClosure
- w_closure = space.newClosure(self.w_self(), self.pc(), numArgs,
- self.pop_and_return_n(numCopied))
- self.push(w_closure)
- self._jump(blockSize)
-
- # ====== Helpers for send/return bytecodes ======
-
- def _sendSelfSelector(self, w_selector, argcount, interp):
- receiver = self.peek(argcount)
- return self._sendSelector(w_selector, argcount, interp,
- receiver, receiver.class_shadow(self.space))
-
- def _sendSuperSelector(self, w_selector, argcount, interp):
- compiledin_class = self.w_method().compiled_in()
- assert isinstance(compiledin_class, model.W_PointersObject)
- s_compiledin = compiledin_class.as_class_get_shadow(self.space)
- return self._sendSelector(w_selector, argcount, interp, self.w_receiver(),
- s_compiledin.s_superclass())
-
- def _sendSelector(self, w_selector, argcount, interp,
- receiver, receiverclassshadow, w_arguments=None):
- assert argcount >= 0
- try:
- w_method = receiverclassshadow.lookup(w_selector)
- except MethodNotFound:
- return self._doesNotUnderstand(w_selector, argcount, interp, receiver)
-
- code = w_method.primitive()
- if code:
- if w_arguments:
- self.push_all(w_arguments)
- try:
- return self._call_primitive(code, interp, argcount, w_method, w_selector)
- except primitives.PrimitiveFailedError:
- pass # ignore this error and fall back to the Smalltalk version
- if not w_arguments:
- w_arguments = self.pop_and_return_n(argcount)
- s_frame = w_method.create_frame(interp.space, receiver, w_arguments)
- self.pop() # receiver
-
- # ######################################################################
- if interp.is_tracing():
- interp.print_padded('-> ' + s_frame.short_str())
-
- return interp.stack_frame(s_frame, self)
-
- @objectmodel.specialize.arg(1)
- def _sendSelfSelectorSpecial(self, selector, numargs, interp):
- w_selector = self.space.get_special_selector(selector)
- return self._sendSelfSelector(w_selector, numargs, interp)
-
- def _sendSpecialSelector(self, interp, receiver, special_selector, w_args=[]):
- w_special_selector = self.space.objtable["w_" + special_selector]
- s_class = receiver.class_shadow(self.space)
- w_method = s_class.lookup(w_special_selector)
- s_frame = w_method.create_frame(interp.space, receiver, w_args)
-
- # ######################################################################
- if interp.is_tracing():
- interp.print_padded('-> %s %s' % (special_selector, s_frame.short_str()))
- if not objectmodel.we_are_translated():
- import pdb; pdb.set_trace()
-
- return interp.stack_frame(s_frame, self)
-
- def _doesNotUnderstand(self, w_selector, argcount, interp, receiver):
- arguments = self.pop_and_return_n(argcount)
- w_message_class = self.space.classtable["w_Message"]
- assert isinstance(w_message_class, model.W_PointersObject)
- s_message_class = w_message_class.as_class_get_shadow(self.space)
- w_message = s_message_class.new()
- w_message.store(self.space, 0, w_selector)
- w_message.store(self.space, 1, self.space.wrap_list(arguments))
- self.pop() # The receiver, already known.
-
- try:
- if interp.space.headless.is_set():
- primitives.exitFromHeadlessExecution(self, "doesNotUnderstand:", w_message)
- return self._sendSpecialSelector(interp, receiver, "doesNotUnderstand", [w_message])
- except MethodNotFound:
- from spyvm.shadow import ClassShadow
- s_class = receiver.class_shadow(self.space)
- assert isinstance(s_class, ClassShadow)
- from spyvm import error
- raise error.Exit("Missing doesNotUnderstand in hierarchy of %s" % s_class.getname())
-
- def _mustBeBoolean(self, interp, receiver):
- return self._sendSpecialSelector(interp, receiver, "mustBeBoolean")
-
- def _call_primitive(self, code, interp, argcount, w_method, w_selector):
- # ##################################################################
- if interp.is_tracing():
- interp.print_padded("-> primitive %d \t(in %s, named %s)" % (
- code, self.w_method().get_identifier_string(),
- w_selector.selector_string()))
- func = primitives.prim_holder.prim_table[code]
- try:
- # note: argcount does not include rcvr
- # the primitive pushes the result (if any) onto the stack itself
- return func(interp, self, argcount, w_method)
- except primitives.PrimitiveFailedError, e:
- if interp.is_tracing():
- interp.print_padded("-- primitive %d FAILED\t (in %s, named %s)" % (
- code, w_method.safe_identifier_string(), w_selector.selector_string()))
- raise e
-
- def _return(self, return_value, interp, local_return=False):
- # unfortunately, this assert is not true for some tests. TODO fix this.
- # assert self._stack_ptr == self.tempsize()
-
- # ##################################################################
- if interp.is_tracing():
- interp.print_padded('<- ' + return_value.as_repr_string())
-
- if self.home_is_self() or local_return:
- # a local return just needs to go up the stack once. there
- # it will find the sender as a local, and we don't have to
- # force the reference
- s_return_to = None
- return_from_top = self.s_sender() is None
- else:
- s_return_to = self.s_home().s_sender()
- return_from_top = s_return_to is None
-
- if return_from_top:
- # This should never happen while executing a normal image.
- raise ReturnFromTopLevel(return_value)
- else:
- raise Return(s_return_to, return_value)
-
- # ====== Send/Return bytecodes ======
-
- @bytecode_implementation()
- def returnReceiverBytecode(self, interp, current_bytecode):
- return self._return(self.w_receiver(), interp)
-
- @bytecode_implementation()
- def returnTrueBytecode(self, interp, current_bytecode):
- return self._return(interp.space.w_true, interp)
-
- @bytecode_implementation()
- def returnFalseBytecode(self, interp, current_bytecode):
- return self._return(interp.space.w_false, interp)
-
- @bytecode_implementation()
- def returnNilBytecode(self, interp, current_bytecode):
- return self._return(interp.space.w_nil, interp)
-
- @bytecode_implementation()
- def returnTopFromMethodBytecode(self, interp, current_bytecode):
- return self._return(self.pop(), interp)
-
- @bytecode_implementation()
- def returnTopFromBlockBytecode(self, interp, current_bytecode):
- return self._return(self.pop(), interp, local_return=True)
-
- @bytecode_implementation()
- def sendLiteralSelectorBytecode(self, interp, current_bytecode):
- w_selector = self.w_method().getliteral(current_bytecode & 15)
- argcount = ((current_bytecode >> 4) & 3) - 1
- return self._sendSelfSelector(w_selector, argcount, interp)
-
- def _getExtendedSelectorArgcount(self, descriptor):
- return ((self.w_method().getliteral(descriptor & 31)),
- (descriptor >> 5))
-
- @bytecode_implementation(parameter_bytes=1)
- def singleExtendedSendBytecode(self, interp, current_bytecode, descriptor):
- w_selector, argcount = self._getExtendedSelectorArgcount(descriptor)
- return self._sendSelfSelector(w_selector, argcount, interp)
-
- @bytecode_implementation(parameter_bytes=2)
- def doubleExtendedDoAnythingBytecode(self, interp, current_bytecode, second, third):
- from spyvm import error
- opType = second >> 5
- if opType == 0:
- # selfsend
- return self._sendSelfSelector(self.w_method().getliteral(third),
- second & 31, interp)
- elif opType == 1:
- # supersend
- return self._sendSuperSelector(self.w_method().getliteral(third),
- second & 31, interp)
- elif opType == 2:
- # pushReceiver
- self.push(self.w_receiver().fetch(self.space, third))
- elif opType == 3:
- # pushLiteralConstant
- self.push(self.w_method().getliteral(third))
- elif opType == 4:
- # pushLiteralVariable
- w_association = self.w_method().getliteral(third)
- association = wrapper.AssociationWrapper(self.space, w_association)
- self.push(association.value())
- elif opType == 5:
- # TODO - the following two special cases should not be necessary
- try:
- self.w_receiver().store(self.space, third, self.top())
- except SenderChainManipulation, e:
- raise SenderChainManipulation(self)
- elif opType == 6:
- try:
- self.w_receiver().store(self.space, third, self.pop())
- except SenderChainManipulation, e:
- raise SenderChainManipulation(self)
- elif opType == 7:
- w_association = self.w_method().getliteral(third)
- association = wrapper.AssociationWrapper(self.space, w_association)
- association.store_value(self.top())
-
- @bytecode_implementation(parameter_bytes=1)
- def singleExtendedSuperBytecode(self, interp, current_bytecode, descriptor):
- w_selector, argcount = self._getExtendedSelectorArgcount(descriptor)
- return self._sendSuperSelector(w_selector, argcount, interp)
-
- @bytecode_implementation(parameter_bytes=1)
- def secondExtendedSendBytecode(self, interp, current_bytecode, descriptor):
- w_selector = self.w_method().getliteral(descriptor & 63)
- argcount = descriptor >> 6
- return self._sendSelfSelector(w_selector, argcount, interp)
-
- # ====== Misc ======
-
- def _activate_unwind_context(self, interp):
- if self.is_closure_context() or not self.is_BlockClosure_ensure():
- self.mark_returned()
- return
- # The first temp is executed flag for both #ensure: and #ifCurtailed:
- if self.gettemp(1).is_nil(self.space):
- self.settemp(1, self.space.w_true) # mark unwound
- self.push(self.gettemp(0)) # push the first argument
- try:
- self.bytecodePrimValue(interp, 0)
- except Return, nlr:
- assert nlr.s_target_context or nlr.is_local
- if self is not nlr.s_target_context and not nlr.is_local:
- raise nlr
- finally:
- self.mark_returned()
-
- @bytecode_implementation()
- def unknownBytecode(self, interp, current_bytecode):
- raise MissingBytecode("unknownBytecode")
-
- @bytecode_implementation()
- def experimentalBytecode(self, interp, current_bytecode):
- raise MissingBytecode("experimentalBytecode")
-
- # ====== Jump bytecodes ======
-
- def _jump(self, offset):
- self.store_pc(self.pc() + offset)
-
- def _jumpConditional(self, interp, expecting_true, position):
- if expecting_true:
- w_expected = interp.space.w_true
- w_alternative = interp.space.w_false
- else:
- w_alternative = interp.space.w_true
- w_expected = interp.space.w_false
-
- # Don't check the class, just compare with only two Boolean instances.
- w_bool = self.pop()
- if w_expected.is_same_object(w_bool):
- self._jump(position)
- elif not w_alternative.is_same_object(w_bool):
- self._mustBeBoolean(interp, w_bool)
-
- def _shortJumpOffset(self, current_bytecode):
- return (current_bytecode & 7) + 1
-
- def _longJumpOffset(self, current_bytecode, parameter):
- return ((current_bytecode & 3) << 8) + parameter
-
- @bytecode_implementation()
- def shortUnconditionalJumpBytecode(self, interp, current_bytecode):
- self._jump(self._shortJumpOffset(current_bytecode))
-
- @bytecode_implementation()
- def shortConditionalJumpBytecode(self, interp, current_bytecode):
- # The conditional jump is "jump on false"
- self._jumpConditional(interp, False, self._shortJumpOffset(current_bytecode))
-
- @bytecode_implementation(parameter_bytes=1)
- def longUnconditionalJumpBytecode(self, interp, current_bytecode, parameter):
- offset = (((current_bytecode & 7) - 4) << 8) + parameter
- self._jump(offset)
-
- @bytecode_implementation(parameter_bytes=1)
- def longJumpIfTrueBytecode(self, interp, current_bytecode, parameter):
- self._jumpConditional(interp, True, self._longJumpOffset(current_bytecode, parameter))
-
- @bytecode_implementation(parameter_bytes=1)
- def longJumpIfFalseBytecode(self, interp, current_bytecode, parameter):
- self._jumpConditional(interp, False, self._longJumpOffset(current_bytecode, parameter))
-
- # ====== Bytecodes implemented with primitives and message sends ======
-
- bytecodePrimAdd = make_call_primitive_bytecode(primitives.ADD, "+", 1)
- bytecodePrimSubtract = make_call_primitive_bytecode(primitives.SUBTRACT, "-", 1)
- bytecodePrimLessThan = make_call_primitive_bytecode (primitives.LESSTHAN, "<", 1)
- bytecodePrimGreaterThan = make_call_primitive_bytecode(primitives.GREATERTHAN, ">", 1)
- bytecodePrimLessOrEqual = make_call_primitive_bytecode(primitives.LESSOREQUAL, "<=", 1)
- bytecodePrimGreaterOrEqual = make_call_primitive_bytecode(primitives.GREATEROREQUAL, ">=", 1)
- bytecodePrimEqual = make_call_primitive_bytecode(primitives.EQUAL, "=", 1)
- bytecodePrimNotEqual = make_call_primitive_bytecode(primitives.NOTEQUAL, "~=", 1)
- bytecodePrimMultiply = make_call_primitive_bytecode(primitives.MULTIPLY, "*", 1)
- bytecodePrimDivide = make_call_primitive_bytecode(primitives.DIVIDE, "/", 1)
- bytecodePrimMod = make_call_primitive_bytecode(primitives.MOD, "\\\\", 1)
- bytecodePrimMakePoint = make_call_primitive_bytecode(primitives.MAKE_POINT, "@", 1)
- bytecodePrimBitShift = make_call_primitive_bytecode(primitives.BIT_SHIFT, "bitShift:", 1)
- bytecodePrimDiv = make_call_primitive_bytecode(primitives.DIV, "//", 1)
- bytecodePrimBitAnd = make_call_primitive_bytecode(primitives.BIT_AND, "bitAnd:", 1)
- bytecodePrimBitOr = make_call_primitive_bytecode(primitives.BIT_OR, "bitOr:", 1)
-
- bytecodePrimAt = make_send_selector_bytecode("at:", 1)
- bytecodePrimAtPut = make_send_selector_bytecode("at:put:", 2)
- bytecodePrimSize = make_send_selector_bytecode("size", 0)
- bytecodePrimNext = make_send_selector_bytecode("next", 0)
- bytecodePrimNextPut = make_send_selector_bytecode("nextPut:", 1)
- bytecodePrimAtEnd = make_send_selector_bytecode("atEnd", 0)
-
- bytecodePrimEquivalent = make_quick_call_primitive_bytecode(primitives.EQUIVALENT, 1)
- bytecodePrimClass = make_quick_call_primitive_bytecode(primitives.CLASS, 0)
-
- bytecodePrimBlockCopy = make_call_primitive_bytecode(primitives.BLOCK_COPY, "blockCopy:", 1)
- bytecodePrimValue = make_call_primitive_bytecode_classbased("w_BlockContext", primitives.VALUE, "w_BlockClosure", primitives.CLOSURE_VALUE, "value", 0)
- bytecodePrimValueWithArg = make_call_primitive_bytecode_classbased("w_BlockContext", primitives.VALUE, "w_BlockClosure", primitives.CLOSURE_VALUE_, "value:", 1)
-
- bytecodePrimDo = make_send_selector_bytecode("do:", 1)
- bytecodePrimNew = make_send_selector_bytecode("new", 0)
- bytecodePrimNewWithArg = make_send_selector_bytecode("new:", 1)
- bytecodePrimPointX = make_send_selector_bytecode("x", 0)
- bytecodePrimPointY = make_send_selector_bytecode("y", 0)
-
-BYTECODE_RANGES = [
- ( 0, 15, "pushReceiverVariableBytecode"),
- ( 16, 31, "pushTemporaryVariableBytecode"),
- ( 32, 63, "pushLiteralConstantBytecode"),
- ( 64, 95, "pushLiteralVariableBytecode"),
- ( 96, 103, "storeAndPopReceiverVariableBytecode"),
- (104, 111, "storeAndPopTemporaryVariableBytecode"),
- (112, "pushReceiverBytecode"),
- (113, "pushConstantTrueBytecode"),
- (114, "pushConstantFalseBytecode"),
- (115, "pushConstantNilBytecode"),
- (116, "pushConstantMinusOneBytecode"),
- (117, "pushConstantZeroBytecode"),
- (118, "pushConstantOneBytecode"),
- (119, "pushConstantTwoBytecode"),
- (120, "returnReceiverBytecode"),
- (121, "returnTrueBytecode"),
- (122, "returnFalseBytecode"),
- (123, "returnNilBytecode"),
- (124, "returnTopFromMethodBytecode"),
- (125, "returnTopFromBlockBytecode"),
- (126, "unknownBytecode"),
- (127, "unknownBytecode"),
- (128, "extendedPushBytecode"),
- (129, "extendedStoreBytecode"),
- (130, "extendedStoreAndPopBytecode"),
- (131, "singleExtendedSendBytecode"),
- (132, "doubleExtendedDoAnythingBytecode"),
- (133, "singleExtendedSuperBytecode"),
- (134, "secondExtendedSendBytecode"),
- (135, "popStackBytecode"),
- (136, "duplicateTopBytecode"),
- (137, "pushActiveContextBytecode"),
- (138, "pushNewArrayBytecode"),
- (139, "experimentalBytecode"),
- (140, "pushRemoteTempLongBytecode"),
- (141, "storeRemoteTempLongBytecode"),
- (142, "storeAndPopRemoteTempLongBytecode"),
- (143, "pushClosureCopyCopiedValuesBytecode"),
- (144, 151, "shortUnconditionalJumpBytecode"),
- (152, 159, "shortConditionalJumpBytecode"),
- (160, 167, "longUnconditionalJumpBytecode"),
- (168, 171, "longJumpIfTrueBytecode"),
- (172, 175, "longJumpIfFalseBytecode"),
- (176, "bytecodePrimAdd"),
- (177, "bytecodePrimSubtract"),
- (178, "bytecodePrimLessThan"),
- (179, "bytecodePrimGreaterThan"),
- (180, "bytecodePrimLessOrEqual"),
- (181, "bytecodePrimGreaterOrEqual"),
- (182, "bytecodePrimEqual"),
- (183, "bytecodePrimNotEqual"),
- (184, "bytecodePrimMultiply"),
- (185, "bytecodePrimDivide"),
- (186, "bytecodePrimMod"),
- (187, "bytecodePrimMakePoint"),
- (188, "bytecodePrimBitShift"),
- (189, "bytecodePrimDiv"),
- (190, "bytecodePrimBitAnd"),
- (191, "bytecodePrimBitOr"),
- (192, "bytecodePrimAt"),
- (193, "bytecodePrimAtPut"),
- (194, "bytecodePrimSize"),
- (195, "bytecodePrimNext"),
- (196, "bytecodePrimNextPut"),
- (197, "bytecodePrimAtEnd"),
- (198, "bytecodePrimEquivalent"),
- (199, "bytecodePrimClass"),
- (200, "bytecodePrimBlockCopy"),
- (201, "bytecodePrimValue"),
- (202, "bytecodePrimValueWithArg"),
- (203, "bytecodePrimDo"),
- (204, "bytecodePrimNew"),
- (205, "bytecodePrimNewWithArg"),
- (206, "bytecodePrimPointX"),
- (207, "bytecodePrimPointY"),
- (208, 255, "sendLiteralSelectorBytecode"),
- ]
-
-from rpython.rlib.unroll import unrolling_iterable
-UNROLLING_BYTECODE_RANGES = unrolling_iterable(BYTECODE_RANGES)
-
-def initialize_bytecode_names():
- result = [None] * 256
- for entry in BYTECODE_RANGES:
- if len(entry) == 2:
- result[entry[0]] = entry[1]
- else:
- for arg, pos in enumerate(range(entry[0], entry[1]+1)):
- result[pos] = "%s(%s)" % (entry[2], arg)
- assert None not in result
- return result
-
-BYTECODE_NAMES = initialize_bytecode_names()
-
-def initialize_bytecode_table():
- result = [None] * 256
- for entry in BYTECODE_RANGES:
- if len(entry) == 2:
- positions = [entry[0]]
- else:
- positions = range(entry[0], entry[1]+1)
- for pos in positions:
- result[pos] = getattr(ContextPartShadow, entry[-1])
- assert None not in result
- return result
-
-# this table is only used for creating named bytecodes in tests and printing
-BYTECODE_TABLE = initialize_bytecode_table()
-
-# Smalltalk debugging facilities, patching Interpreter and ContextPartShadow
-# in order to enable tracing/jumping for message sends etc.
-def debugging():
- def stepping_debugger_init(original):
- def meth(self, space, image=None, trace=False):
- return_value = original(self, space, image=image, trace=trace)
- # ##############################################################
-
- self.message_stepping = False
- self.halt_on_failing_primitives = False
-
- # ##############################################################
- return return_value
- return meth
-
- Interpreter.__init__ = stepping_debugger_init(Interpreter.__init__)
-
- def stepping_debugger_send(original):
- """When interp.message_stepping is True, we halt on every call of ContextPartShadow._sendSelector.
- The method is not called for bytecode message sends (see constants.SPECIAL_SELECTORS)"""
- def meth(s_context, w_selector, argcount, interp,
- receiver, receiverclassshadow):
- options = [False]
- def next(): interp.message_stepping = True; print 'Now continue (c).'
- def over(): options[0] = True; print 'Skipping #%s. You still need to continue(c).' % w_selector.str_content()
- def pstack(): print s_context.print_stack()
- if interp.message_stepping:
- if argcount == 0:
- print "-> %s #%s" % (receiver.as_repr_string(),
- w_selector.str_content())
- elif argcount == 1:
- print "-> %s #%s %s" % (receiver.as_repr_string(),
- w_selector.str_content(),
- s_context.peek(0).as_repr_string())
- else:
- print "-> %s #%s %r" % (receiver.as_repr_string(),
- w_selector.str_content(),
- [s_context.peek(argcount-1-i) for i in range(argcount)])
- import pdb; pdb.set_trace()
- if options[0]:
- m_s = interp.message_stepping
- interp.message_stepping = False
- try:
- return original(s_context, w_selector, argcount, interp, receiver, receiverclassshadow)
- finally:
- interp.message_stepping = m_s
- else:
- return original(s_context, w_selector, argcount, interp, receiver, receiverclassshadow)
- return meth
-
- ContextPartShadow._sendSelector = stepping_debugger_send(ContextPartShadow._sendSelector)
-
- def stepping_debugger_failed_primitive_halt(original):
- def meth(self, code, interp, argcount, w_method, w_selector):
- try:
- original(self, code, interp, argcount, w_method, w_selector)
- except primitives.PrimitiveFailedError, e:
- if interp.halt_on_failing_primitives:
- func = primitives.prim_holder.prim_table[code]
- if func.func_name != 'raise_failing_default' and code != 83:
- import pdb; pdb.set_trace()
- try:
- func(interp, self, argcount, w_method) # will fail again
- except primitives.PrimitiveFailedError:
- pass
- raise e
- return meth
-
- ContextPartShadow._call_primitive = stepping_debugger_failed_primitive_halt(ContextPartShadow._call_primitive)
-
- def trace_missing_named_primitives(original):
- def meth(interp, s_frame, argcount, w_method=None):
- try:
- return original(interp, s_frame, argcount, w_method=w_method)
- except primitives.PrimitiveFailedError, e:
- space = interp.space
- w_description = w_method.literalat0(space, 1)
- if not isinstance(w_description, model.W_PointersObject) or w_description.size() < 2:
- raise e
- w_modulename = w_description.at0(space, 0)
- w_functionname = w_description.at0(space, 1)
- if not (isinstance(w_modulename, model.W_BytesObject) and
- isinstance(w_functionname, model.W_BytesObject)):
- raise e
- signature = (w_modulename.as_string(), w_functionname.as_string())
- debugging.missing_named_primitives.add(signature)
- raise e
- return meth
-
- primitives.prim_table[primitives.EXTERNAL_CALL] = trace_missing_named_primitives(primitives.prim_table[primitives.EXTERNAL_CALL])
- debugging.missing_named_primitives = set()
-
-# debugging()
+# Uncomment this to load debugging facilities at startup.
+#from spyvm import interpreter_debugging; Interpreter.__init__ = interpreter_debugging.activating_init(Interpreter.__init__)
diff --git a/spyvm/interpreter_bytecodes.py b/spyvm/interpreter_bytecodes.py
new file mode 100644
--- /dev/null
+++ b/spyvm/interpreter_bytecodes.py
@@ -0,0 +1,725 @@
+
+from spyvm.shadow import ContextPartShadow
+from spyvm import model, primitives, wrapper, error
+from spyvm.tool.bitmanipulation import splitter
+from rpython.rlib import objectmodel, unroll, jit
+
+# unrolling_zero has been removed from rlib at some point.
+if hasattr(unroll, "unrolling_zero"):
+ unrolling_zero = unroll.unrolling_zero
+else:
+ class unrolling_int(int, unroll.SpecTag):
+ def __add__(self, other):
+ return unrolling_int(int.__add__(self, other))
+ __radd__ = __add__
+ def __sub__(self, other):
+ return unrolling_int(int.__sub__(self, other))
+ def __rsub__(self, other):
+ return unrolling_int(int.__rsub__(self, other))
+ unrolling_zero = unrolling_int(0)
+
+# This is a decorator for bytecode implementation methods.
+# parameter_bytes=N means N additional bytes are fetched as parameters.
+def bytecode_implementation(parameter_bytes=0):
+ def bytecode_implementation_decorator(actual_implementation_method):
+ @jit.unroll_safe
+ def bytecode_implementation_wrapper(self, interp, current_bytecode):
+ parameters = ()
+ i = unrolling_zero
+ while i < parameter_bytes:
+ parameters += (self.fetch_next_bytecode(), )
+ i = i + 1
+ # This is a good place to step through bytecodes.
+ self.debug_bytecode()
+ return actual_implementation_method(self, interp, current_bytecode, *parameters)
+ bytecode_implementation_wrapper.func_name = actual_implementation_method.func_name
+ return bytecode_implementation_wrapper
+ return bytecode_implementation_decorator
+
+def make_call_primitive_bytecode(primitive, selector, argcount, store_pc=False):
+ func = primitives.prim_table[primitive]
+ @bytecode_implementation()
+ def callPrimitive(self, interp, current_bytecode):
+ # WARNING: this is used for bytecodes for which it is safe to
+ # directly call the primitive. In general, it is not safe: for
+ # example, depending on the type of the receiver, bytecodePrimAt
+ # may invoke primitives.AT, primitives.STRING_AT, or anything
+ # else that the user put in a class in an 'at:' method.
+ # The rule of thumb is that primitives with only int and float
+ # in their unwrap_spec are safe.
+ try:
+ return func(interp, self, argcount)
+ except error.PrimitiveFailedError:
+ pass
+ return self._sendSelfSelectorSpecial(selector, argcount, interp)
+ callPrimitive.func_name = "callPrimitive_%s" % func.func_name
+ return callPrimitive
+
+def make_call_primitive_bytecode_classbased(a_class_name, a_primitive, alternative_class_name, alternative_primitive, selector, argcount):
+ @bytecode_implementation()
+ def callClassbasedPrimitive(self, interp, current_bytecode):
+ rcvr = self.peek(argcount)
+ receiver_class = rcvr.getclass(self.space)
+ try:
+ if receiver_class is getattr(self.space, a_class_name):
+ func = primitives.prim_table[a_primitive]
+ return func(interp, self, argcount)
+ elif receiver_class is getattr(self.space, alternative_class_name):
+ func = primitives.prim_table[alternative_primitive]
+ return func(interp, self, argcount)
+ except error.PrimitiveFailedError:
+ pass
+ return self._sendSelfSelectorSpecial(selector, argcount, interp)
+ callClassbasedPrimitive.func_name = "callClassbasedPrimitive_%s" % selector
+ return callClassbasedPrimitive
+
+# Some selectors cannot be overwritten, therefore no need to handle PrimitiveFailed.
+def make_quick_call_primitive_bytecode(primitive_index, argcount):
+ func = primitives.prim_table[primitive_index]
+ @bytecode_implementation()
+ def quick_call_primitive_bytecode(self, interp, current_bytecode):
+ return func(interp, self, argcount)
+ return quick_call_primitive_bytecode
+
+# This is for bytecodes that actually implement a simple message-send.
+# We do not optimize anything for these cases.
+def make_send_selector_bytecode(selector, argcount):
+ @bytecode_implementation()
+ def selector_bytecode(self, interp, current_bytecode):
+ return self._sendSelfSelectorSpecial(selector, argcount, interp)
+ selector_bytecode.func_name = "selector_bytecode_%s" % selector
+ return selector_bytecode
+
+# ___________________________________________________________________________
+# Bytecode Implementations:
+#
+# "self" is always a ContextPartShadow instance.
+# __extend__ adds new methods to the ContextPartShadow class
+class __extend__(ContextPartShadow):
+
+ # ====== Push/Pop bytecodes ======
+
+ @bytecode_implementation()
+ def pushReceiverVariableBytecode(self, interp, current_bytecode):
+ index = current_bytecode & 15
+ self.push(self.w_receiver().fetch(self.space, index))
+
+ @bytecode_implementation()
+ def pushTemporaryVariableBytecode(self, interp, current_bytecode):
+ index = current_bytecode & 15
+ self.push(self.gettemp(index))
+
+ @bytecode_implementation()
+ def pushLiteralConstantBytecode(self, interp, current_bytecode):
+ index = current_bytecode & 31
+ self.push(self.w_method().getliteral(index))
+
+ @bytecode_implementation()
+ def pushLiteralVariableBytecode(self, interp, current_bytecode):
+ # this bytecode assumes that literals[index] is an Association
+ # which is an object with two named vars, and fetches the second
+ # named var (the value).
+ index = current_bytecode & 31
+ w_association = self.w_method().getliteral(index)
+ association = wrapper.AssociationWrapper(self.space, w_association)
+ self.push(association.value())
+
+ @bytecode_implementation()
+ def storeAndPopReceiverVariableBytecode(self, interp, current_bytecode):
+ index = current_bytecode & 7
+ self.w_receiver().store(self.space, index, self.pop())
+
+ @bytecode_implementation()
+ def storeAndPopTemporaryVariableBytecode(self, interp, current_bytecode):
+ index = current_bytecode & 7
+ self.settemp(index, self.pop())
+
+ @bytecode_implementation()
+ def pushReceiverBytecode(self, interp, current_bytecode):
+ self.push(self.w_receiver())
+
+ @bytecode_implementation()
+ def pushConstantTrueBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_true)
+
+ @bytecode_implementation()
+ def pushConstantFalseBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_false)
+
+ @bytecode_implementation()
+ def pushConstantNilBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_nil)
+
+ @bytecode_implementation()
+ def pushConstantMinusOneBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_minus_one)
+
+ @bytecode_implementation()
+ def pushConstantZeroBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_zero)
+
+ @bytecode_implementation()
+ def pushConstantOneBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_one)
+
+ @bytecode_implementation()
+ def pushConstantTwoBytecode(self, interp, current_bytecode):
+ self.push(interp.space.w_two)
+
+ @bytecode_implementation()
+ def pushActiveContextBytecode(self, interp, current_bytecode):
+ self.push(self.w_self())
+
+ @bytecode_implementation()
+ def duplicateTopBytecode(self, interp, current_bytecode):
+ self.push(self.top())
+
+ @bytecode_implementation()
+ def popStackBytecode(self, interp, current_bytecode):
+ self.pop()
+
+ @bytecode_implementation(parameter_bytes=1)
+ def pushNewArrayBytecode(self, interp, current_bytecode, descriptor):
+ arraySize, popIntoArray = splitter[7, 1](descriptor)
+ newArray = None
+ if popIntoArray == 1:
+ newArray = interp.space.wrap_list(self.pop_and_return_n(arraySize))
+ else:
+ newArray = interp.space.w_Array.as_class_get_shadow(interp.space).new(arraySize)
+ self.push(newArray)
+
+ # ====== Extended Push/Pop bytecodes ======
+
+ def _extendedVariableTypeAndIndex(self, descriptor):
+ return ((descriptor >> 6) & 3), (descriptor & 63)
+
+ @bytecode_implementation(parameter_bytes=1)
+ def extendedPushBytecode(self, interp, current_bytecode, descriptor):
+ variableType, variableIndex = self._extendedVariableTypeAndIndex(descriptor)
+ if variableType == 0:
+ self.push(self.w_receiver().fetch(self.space, variableIndex))
+ elif variableType == 1:
+ self.push(self.gettemp(variableIndex))
+ elif variableType == 2:
+ self.push(self.w_method().getliteral(variableIndex))
+ elif variableType == 3:
+ w_association = self.w_method().getliteral(variableIndex)
+ association = wrapper.AssociationWrapper(self.space, w_association)
+ self.push(association.value())
+ else:
+ assert 0
+
+ def _extendedStoreBytecode(self, interp, current_bytecode, descriptor):
+ variableType, variableIndex = self._extendedVariableTypeAndIndex(descriptor)
+ if variableType == 0:
+ self.w_receiver().store(self.space, variableIndex, self.top())
+ elif variableType == 1:
+ self.settemp(variableIndex, self.top())
+ elif variableType == 2:
+ raise error.FatalError("Illegal ExtendedStoreBytecode. veriableType 2.")
+ elif variableType == 3:
+ w_association = self.w_method().getliteral(variableIndex)
+ association = wrapper.AssociationWrapper(self.space, w_association)
+ association.store_value(self.top())
+
+ @bytecode_implementation(parameter_bytes=1)
+ def extendedStoreBytecode(self, interp, current_bytecode, descriptor):
+ return self._extendedStoreBytecode(interp, current_bytecode, descriptor)
+
+ @bytecode_implementation(parameter_bytes=1)
+ def extendedStoreAndPopBytecode(self, interp, current_bytecode, descriptor):
+ self._extendedStoreBytecode(interp, current_bytecode, descriptor)
+ self.pop()
+
+ def _extract_index_and_temps(self, index_in_array, index_of_array):
+ w_indirectTemps = self.gettemp(index_of_array)
+ return index_in_array, w_indirectTemps
+
+ @bytecode_implementation(parameter_bytes=2)
+ def pushRemoteTempLongBytecode(self, interp, current_bytecode, index_in_array, index_of_array):
+ index_in_array, w_indirectTemps = self._extract_index_and_temps(index_in_array, index_of_array)
+ self.push(w_indirectTemps.at0(self.space, index_in_array))
+
+ @bytecode_implementation(parameter_bytes=2)
+ def storeRemoteTempLongBytecode(self, interp, current_bytecode, index_in_array, index_of_array):
+ index_in_array, w_indirectTemps = self._extract_index_and_temps(index_in_array, index_of_array)
+ w_indirectTemps.atput0(self.space, index_in_array, self.top())
+
+ @bytecode_implementation(parameter_bytes=2)
+ def storeAndPopRemoteTempLongBytecode(self, interp, current_bytecode, index_in_array, index_of_array):
+ index_in_array, w_indirectTemps = self._extract_index_and_temps(index_in_array, index_of_array)
+ w_indirectTemps.atput0(self.space, index_in_array, self.pop())
+
+ @bytecode_implementation(parameter_bytes=3)
+ def pushClosureCopyCopiedValuesBytecode(self, interp, current_bytecode, descriptor, j, i):
+ """ Copied from Blogpost: http://www.mirandabanda.org/cogblog/2008/07/22/closures-part-ii-the-bytecodes/
+ ContextPart>>pushClosureCopyNumCopiedValues: numCopied numArgs: numArgs blockSize: blockSize
+ "Simulate the action of a 'closure copy' bytecode whose result is the
+ new BlockClosure for the following code"
+ | copiedValues |
+ numCopied > 0
+ ifTrue:
+ [copiedValues := Array new: numCopied.
+ numCopied to: 1 by: -1 do:
+ [:i|
+ copiedValues at: i put: self pop]]
+ ifFalse:
+ [copiedValues := nil].
+ self push: (BlockClosure new
+ outerContext: self
+ startpc: pc
+ numArgs: numArgs
+ copiedValues: copiedValues).
+ self jump: blockSize
+ """
+
+ space = self.space
+ numArgs, numCopied = splitter[4, 4](descriptor)
+ blockSize = (j << 8) | i
+ # Create new instance of BlockClosure
+ w_closure = space.newClosure(self.w_self(), self.pc(), numArgs,
+ self.pop_and_return_n(numCopied))
+ self.push(w_closure)
+ self._jump(blockSize)
+
+ # ====== Helpers for send/return bytecodes ======
+
+ def _sendSelfSelector(self, w_selector, argcount, interp):
+ receiver = self.peek(argcount)
+ return self._sendSelector(w_selector, argcount, interp,
+ receiver, receiver.class_shadow(self.space))
+
+ def _sendSuperSelector(self, w_selector, argcount, interp):
+ compiledin_class = self.w_method().compiled_in()
+ assert isinstance(compiledin_class, model.W_PointersObject)
+ s_compiledin = compiledin_class.as_class_get_shadow(self.space)
+ return self._sendSelector(w_selector, argcount, interp, self.w_receiver(),
+ s_compiledin.s_superclass())
+
+ def _sendSelector(self, w_selector, argcount, interp,
+ receiver, receiverclassshadow, w_arguments=None):
+ assert argcount >= 0
+ try:
+ w_method = receiverclassshadow.lookup(w_selector)
+ except error.MethodNotFound:
+ return self._doesNotUnderstand(w_selector, argcount, interp, receiver)
+
+ code = w_method.primitive()
+ if code:
+ if w_arguments:
+ self.push_all(w_arguments)
+ try:
+ return self._call_primitive(code, interp, argcount, w_method, w_selector)
+ except error.PrimitiveFailedError:
+ pass # ignore this error and fall back to the Smalltalk version
+ if not w_arguments:
+ w_arguments = self.pop_and_return_n(argcount)
+ s_frame = w_method.create_frame(interp.space, receiver, w_arguments)
+ self.pop() # receiver
+
+ # ######################################################################
+ if interp.is_tracing():
+ interp.print_padded('-> ' + s_frame.short_str())
+
+ return interp.stack_frame(s_frame, self)
+
+ @objectmodel.specialize.arg(1)
+ def _sendSelfSelectorSpecial(self, selector, numargs, interp):
+ w_selector = self.space.get_special_selector(selector)
+ return self._sendSelfSelector(w_selector, numargs, interp)
+
+ def _sendSpecialSelector(self, interp, receiver, special_selector, w_args=[]):
+ w_special_selector = self.space.objtable["w_" + special_selector]
+ s_class = receiver.class_shadow(self.space)
+ w_method = s_class.lookup(w_special_selector)
+ s_frame = w_method.create_frame(interp.space, receiver, w_args)
+
+ # ######################################################################
+ if interp.is_tracing():
+ interp.print_padded('-> %s %s' % (special_selector, s_frame.short_str()))
+ if not objectmodel.we_are_translated():
+ import pdb; pdb.set_trace()
+
+ return interp.stack_frame(s_frame, self)
+
+ def _doesNotUnderstand(self, w_selector, argcount, interp, receiver):
+ arguments = self.pop_and_return_n(argcount)
+ w_message_class = self.space.classtable["w_Message"]
+ assert isinstance(w_message_class, model.W_PointersObject)
+ s_message_class = w_message_class.as_class_get_shadow(self.space)
+ w_message = s_message_class.new()
+ w_message.store(self.space, 0, w_selector)
+ w_message.store(self.space, 1, self.space.wrap_list(arguments))
+ self.pop() # The receiver, already known.
+
+ try:
+ if interp.space.headless.is_set():
+ primitives.exitFromHeadlessExecution(self, "doesNotUnderstand:", w_message)
+ return self._sendSpecialSelector(interp, receiver, "doesNotUnderstand", [w_message])
+ except error.MethodNotFound:
+ from spyvm.shadow import ClassShadow
+ s_class = receiver.class_shadow(self.space)
+ assert isinstance(s_class, ClassShadow)
+ raise error.Exit("Missing doesNotUnderstand in hierarchy of %s" % s_class.getname())
+
+ def _mustBeBoolean(self, interp, receiver):
+ return self._sendSpecialSelector(interp, receiver, "mustBeBoolean")
+
+ def _call_primitive(self, code, interp, argcount, w_method, w_selector):
+ # ##################################################################
+ if interp.is_tracing():
+ interp.print_padded("-> primitive %d \t(in %s, named %s)" % (
+ code, self.w_method().get_identifier_string(),
+ w_selector.selector_string()))
+ func = primitives.prim_holder.prim_table[code]
+ try:
+ # note: argcount does not include rcvr
+ # the primitive pushes the result (if any) onto the stack itself
+ return func(interp, self, argcount, w_method)
+ except error.PrimitiveFailedError, e:
+ if interp.is_tracing():
+ interp.print_padded("-- primitive %d FAILED\t (in %s, named %s)" % (
+ code, w_method.safe_identifier_string(), w_selector.selector_string()))
+ raise e
+
+ def _return(self, return_value, interp, local_return=False):
+ # unfortunately, this assert is not true for some tests. TODO fix this.
+ # assert self._stack_ptr == self.tempsize()
+
+ # ##################################################################
+ if interp.is_tracing():
+ interp.print_padded('<- ' + return_value.as_repr_string())
+
+ if self.home_is_self() or local_return:
+ # a local return just needs to go up the stack once. there
+ # it will find the sender as a local, and we don't have to
+ # force the reference
+ s_return_to = None
+ return_from_top = self.s_sender() is None
+ else:
+ s_return_to = self.s_home().s_sender()
+ return_from_top = s_return_to is None
+
+ if return_from_top:
+ # This should never happen while executing a normal image.
+ from spyvm.interpreter import ReturnFromTopLevel
+ raise ReturnFromTopLevel(return_value)
+ else:
+ from spyvm.interpreter import Return
+ raise Return(s_return_to, return_value)
+
+ # ====== Send/Return bytecodes ======
+
+ @bytecode_implementation()
+ def returnReceiverBytecode(self, interp, current_bytecode):
+ return self._return(self.w_receiver(), interp)
+
+ @bytecode_implementation()
+ def returnTrueBytecode(self, interp, current_bytecode):
+ return self._return(interp.space.w_true, interp)
+
+ @bytecode_implementation()
+ def returnFalseBytecode(self, interp, current_bytecode):
+ return self._return(interp.space.w_false, interp)
+
+ @bytecode_implementation()
+ def returnNilBytecode(self, interp, current_bytecode):
+ return self._return(interp.space.w_nil, interp)
+
+ @bytecode_implementation()
+ def returnTopFromMethodBytecode(self, interp, current_bytecode):
+ return self._return(self.pop(), interp)
+
+ @bytecode_implementation()
+ def returnTopFromBlockBytecode(self, interp, current_bytecode):
+ return self._return(self.pop(), interp, local_return=True)
+
+ @bytecode_implementation()
+ def sendLiteralSelectorBytecode(self, interp, current_bytecode):
+ w_selector = self.w_method().getliteral(current_bytecode & 15)
+ argcount = ((current_bytecode >> 4) & 3) - 1
+ return self._sendSelfSelector(w_selector, argcount, interp)
+
+ def _getExtendedSelectorArgcount(self, descriptor):
+ return ((self.w_method().getliteral(descriptor & 31)),
+ (descriptor >> 5))
+
+ @bytecode_implementation(parameter_bytes=1)
+ def singleExtendedSendBytecode(self, interp, current_bytecode, descriptor):
+ w_selector, argcount = self._getExtendedSelectorArgcount(descriptor)
+ return self._sendSelfSelector(w_selector, argcount, interp)
+
+ @bytecode_implementation(parameter_bytes=2)
+ def doubleExtendedDoAnythingBytecode(self, interp, current_bytecode, second, third):
+ from spyvm.interpreter import SenderChainManipulation
+ opType = second >> 5
+ if opType == 0:
+ # selfsend
+ return self._sendSelfSelector(self.w_method().getliteral(third),
+ second & 31, interp)
+ elif opType == 1:
+ # supersend
+ return self._sendSuperSelector(self.w_method().getliteral(third),
+ second & 31, interp)
+ elif opType == 2:
+ # pushReceiver
+ self.push(self.w_receiver().fetch(self.space, third))
+ elif opType == 3:
+ # pushLiteralConstant
+ self.push(self.w_method().getliteral(third))
+ elif opType == 4:
+ # pushLiteralVariable
+ w_association = self.w_method().getliteral(third)
+ association = wrapper.AssociationWrapper(self.space, w_association)
+ self.push(association.value())
+ elif opType == 5:
+ # TODO - the following two special cases should not be necessary
+ try:
+ self.w_receiver().store(self.space, third, self.top())
+ except SenderChainManipulation, e:
+ raise SenderChainManipulation(self)
+ elif opType == 6:
+ try:
+ self.w_receiver().store(self.space, third, self.pop())
+ except SenderChainManipulation, e:
+ raise SenderChainManipulation(self)
+ elif opType == 7:
+ w_association = self.w_method().getliteral(third)
+ association = wrapper.AssociationWrapper(self.space, w_association)
+ association.store_value(self.top())
+
+ @bytecode_implementation(parameter_bytes=1)
+ def singleExtendedSuperBytecode(self, interp, current_bytecode, descriptor):
+ w_selector, argcount = self._getExtendedSelectorArgcount(descriptor)
+ return self._sendSuperSelector(w_selector, argcount, interp)
+
+ @bytecode_implementation(parameter_bytes=1)
+ def secondExtendedSendBytecode(self, interp, current_bytecode, descriptor):
+ w_selector = self.w_method().getliteral(descriptor & 63)
+ argcount = descriptor >> 6
+ return self._sendSelfSelector(w_selector, argcount, interp)
+
+ # ====== Misc ======
+
+ def _activate_unwind_context(self, interp):
+ if self.is_closure_context() or not self.is_BlockClosure_ensure():
+ self.mark_returned()
+ return
+ # The first temp is executed flag for both #ensure: and #ifCurtailed:
+ if self.gettemp(1).is_nil(self.space):
+ self.settemp(1, self.space.w_true) # mark unwound
+ self.push(self.gettemp(0)) # push the first argument
+ try:
+ self.bytecodePrimValue(interp, 0)
+ except Return, nlr:
+ assert nlr.s_target_context or nlr.is_local
+ if self is not nlr.s_target_context and not nlr.is_local:
+ raise nlr
+ finally:
+ self.mark_returned()
+
+ @bytecode_implementation()
+ def unknownBytecode(self, interp, current_bytecode):
+ raise error.MissingBytecode("unknownBytecode")
+
+ @bytecode_implementation()
+ def experimentalBytecode(self, interp, current_bytecode):
+ raise error.MissingBytecode("experimentalBytecode")
+
+ # ====== Jump bytecodes ======
+
+ def _jump(self, offset):
+ self.store_pc(self.pc() + offset)
+
+ def _jumpConditional(self, interp, expecting_true, position):
+ if expecting_true:
+ w_expected = interp.space.w_true
+ w_alternative = interp.space.w_false
+ else:
+ w_alternative = interp.space.w_true
+ w_expected = interp.space.w_false
+
+ # Don't check the class, just compare with only two Boolean instances.
+ w_bool = self.pop()
+ if w_expected.is_same_object(w_bool):
+ self._jump(position)
+ elif not w_alternative.is_same_object(w_bool):
+ self._mustBeBoolean(interp, w_bool)
+
+ def _shortJumpOffset(self, current_bytecode):
+ return (current_bytecode & 7) + 1
+
+ def _longJumpOffset(self, current_bytecode, parameter):
+ return ((current_bytecode & 3) << 8) + parameter
+
+ @bytecode_implementation()
+ def shortUnconditionalJumpBytecode(self, interp, current_bytecode):
+ self._jump(self._shortJumpOffset(current_bytecode))
+
+ @bytecode_implementation()
+ def shortConditionalJumpBytecode(self, interp, current_bytecode):
+ # The conditional jump is "jump on false"
+ self._jumpConditional(interp, False, self._shortJumpOffset(current_bytecode))
+
+ @bytecode_implementation(parameter_bytes=1)
+ def longUnconditionalJumpBytecode(self, interp, current_bytecode, parameter):
+ offset = (((current_bytecode & 7) - 4) << 8) + parameter
+ self._jump(offset)
+
+ @bytecode_implementation(parameter_bytes=1)
+ def longJumpIfTrueBytecode(self, interp, current_bytecode, parameter):
+ self._jumpConditional(interp, True, self._longJumpOffset(current_bytecode, parameter))
+
+ @bytecode_implementation(parameter_bytes=1)
+ def longJumpIfFalseBytecode(self, interp, current_bytecode, parameter):
+ self._jumpConditional(interp, False, self._longJumpOffset(current_bytecode, parameter))
+
+ # ====== Bytecodes implemented with primitives and message sends ======
+
+ bytecodePrimAdd = make_call_primitive_bytecode(primitives.ADD, "+", 1)
+ bytecodePrimSubtract = make_call_primitive_bytecode(primitives.SUBTRACT, "-", 1)
+ bytecodePrimLessThan = make_call_primitive_bytecode (primitives.LESSTHAN, "<", 1)
+ bytecodePrimGreaterThan = make_call_primitive_bytecode(primitives.GREATERTHAN, ">", 1)
+ bytecodePrimLessOrEqual = make_call_primitive_bytecode(primitives.LESSOREQUAL, "<=", 1)
+ bytecodePrimGreaterOrEqual = make_call_primitive_bytecode(primitives.GREATEROREQUAL, ">=", 1)
+ bytecodePrimEqual = make_call_primitive_bytecode(primitives.EQUAL, "=", 1)
+ bytecodePrimNotEqual = make_call_primitive_bytecode(primitives.NOTEQUAL, "~=", 1)
+ bytecodePrimMultiply = make_call_primitive_bytecode(primitives.MULTIPLY, "*", 1)
+ bytecodePrimDivide = make_call_primitive_bytecode(primitives.DIVIDE, "/", 1)
+ bytecodePrimMod = make_call_primitive_bytecode(primitives.MOD, "\\\\", 1)
+ bytecodePrimMakePoint = make_call_primitive_bytecode(primitives.MAKE_POINT, "@", 1)
+ bytecodePrimBitShift = make_call_primitive_bytecode(primitives.BIT_SHIFT, "bitShift:", 1)
+ bytecodePrimDiv = make_call_primitive_bytecode(primitives.DIV, "//", 1)
+ bytecodePrimBitAnd = make_call_primitive_bytecode(primitives.BIT_AND, "bitAnd:", 1)
+ bytecodePrimBitOr = make_call_primitive_bytecode(primitives.BIT_OR, "bitOr:", 1)
+
+ bytecodePrimAt = make_send_selector_bytecode("at:", 1)
+ bytecodePrimAtPut = make_send_selector_bytecode("at:put:", 2)
+ bytecodePrimSize = make_send_selector_bytecode("size", 0)
+ bytecodePrimNext = make_send_selector_bytecode("next", 0)
+ bytecodePrimNextPut = make_send_selector_bytecode("nextPut:", 1)
+ bytecodePrimAtEnd = make_send_selector_bytecode("atEnd", 0)
+
+ bytecodePrimEquivalent = make_quick_call_primitive_bytecode(primitives.EQUIVALENT, 1)
+ bytecodePrimClass = make_quick_call_primitive_bytecode(primitives.CLASS, 0)
+
+ bytecodePrimBlockCopy = make_call_primitive_bytecode(primitives.BLOCK_COPY, "blockCopy:", 1)
+ bytecodePrimValue = make_call_primitive_bytecode_classbased("w_BlockContext", primitives.VALUE, "w_BlockClosure", primitives.CLOSURE_VALUE, "value", 0)
+ bytecodePrimValueWithArg = make_call_primitive_bytecode_classbased("w_BlockContext", primitives.VALUE, "w_BlockClosure", primitives.CLOSURE_VALUE_, "value:", 1)
+
+ bytecodePrimDo = make_send_selector_bytecode("do:", 1)
+ bytecodePrimNew = make_send_selector_bytecode("new", 0)
+ bytecodePrimNewWithArg = make_send_selector_bytecode("new:", 1)
+ bytecodePrimPointX = make_send_selector_bytecode("x", 0)
+ bytecodePrimPointY = make_send_selector_bytecode("y", 0)
+
+ def debug_bytecode(self):
+ # Hook used in interpreter_debugging
+ pass
+
+BYTECODE_RANGES = [
+ ( 0, 15, "pushReceiverVariableBytecode"),
+ ( 16, 31, "pushTemporaryVariableBytecode"),
+ ( 32, 63, "pushLiteralConstantBytecode"),
+ ( 64, 95, "pushLiteralVariableBytecode"),
+ ( 96, 103, "storeAndPopReceiverVariableBytecode"),
+ (104, 111, "storeAndPopTemporaryVariableBytecode"),
+ (112, "pushReceiverBytecode"),
+ (113, "pushConstantTrueBytecode"),
+ (114, "pushConstantFalseBytecode"),
+ (115, "pushConstantNilBytecode"),
+ (116, "pushConstantMinusOneBytecode"),
+ (117, "pushConstantZeroBytecode"),
+ (118, "pushConstantOneBytecode"),
+ (119, "pushConstantTwoBytecode"),
+ (120, "returnReceiverBytecode"),
+ (121, "returnTrueBytecode"),
+ (122, "returnFalseBytecode"),
+ (123, "returnNilBytecode"),
+ (124, "returnTopFromMethodBytecode"),
+ (125, "returnTopFromBlockBytecode"),
+ (126, "unknownBytecode"),
+ (127, "unknownBytecode"),
+ (128, "extendedPushBytecode"),
+ (129, "extendedStoreBytecode"),
+ (130, "extendedStoreAndPopBytecode"),
+ (131, "singleExtendedSendBytecode"),
+ (132, "doubleExtendedDoAnythingBytecode"),
+ (133, "singleExtendedSuperBytecode"),
+ (134, "secondExtendedSendBytecode"),
+ (135, "popStackBytecode"),
+ (136, "duplicateTopBytecode"),
+ (137, "pushActiveContextBytecode"),
+ (138, "pushNewArrayBytecode"),
+ (139, "experimentalBytecode"),
+ (140, "pushRemoteTempLongBytecode"),
+ (141, "storeRemoteTempLongBytecode"),
+ (142, "storeAndPopRemoteTempLongBytecode"),
+ (143, "pushClosureCopyCopiedValuesBytecode"),
+ (144, 151, "shortUnconditionalJumpBytecode"),
+ (152, 159, "shortConditionalJumpBytecode"),
+ (160, 167, "longUnconditionalJumpBytecode"),
+ (168, 171, "longJumpIfTrueBytecode"),
+ (172, 175, "longJumpIfFalseBytecode"),
+ (176, "bytecodePrimAdd"),
+ (177, "bytecodePrimSubtract"),
+ (178, "bytecodePrimLessThan"),
+ (179, "bytecodePrimGreaterThan"),
+ (180, "bytecodePrimLessOrEqual"),
+ (181, "bytecodePrimGreaterOrEqual"),
+ (182, "bytecodePrimEqual"),
+ (183, "bytecodePrimNotEqual"),
+ (184, "bytecodePrimMultiply"),
+ (185, "bytecodePrimDivide"),
+ (186, "bytecodePrimMod"),
+ (187, "bytecodePrimMakePoint"),
+ (188, "bytecodePrimBitShift"),
+ (189, "bytecodePrimDiv"),
+ (190, "bytecodePrimBitAnd"),
+ (191, "bytecodePrimBitOr"),
+ (192, "bytecodePrimAt"),
+ (193, "bytecodePrimAtPut"),
+ (194, "bytecodePrimSize"),
+ (195, "bytecodePrimNext"),
+ (196, "bytecodePrimNextPut"),
+ (197, "bytecodePrimAtEnd"),
+ (198, "bytecodePrimEquivalent"),
+ (199, "bytecodePrimClass"),
+ (200, "bytecodePrimBlockCopy"),
+ (201, "bytecodePrimValue"),
+ (202, "bytecodePrimValueWithArg"),
+ (203, "bytecodePrimDo"),
+ (204, "bytecodePrimNew"),
+ (205, "bytecodePrimNewWithArg"),
+ (206, "bytecodePrimPointX"),
+ (207, "bytecodePrimPointY"),
+ (208, 255, "sendLiteralSelectorBytecode"),
+ ]
+
+def initialize_bytecode_names():
+ result = [None] * 256
+ for entry in BYTECODE_RANGES:
+ if len(entry) == 2:
+ result[entry[0]] = entry[1]
+ else:
+ for arg, pos in enumerate(range(entry[0], entry[1]+1)):
+ result[pos] = "%s(%s)" % (entry[2], arg)
+ assert None not in result
+ return result
+
+BYTECODE_NAMES = initialize_bytecode_names()
+
+def initialize_bytecode_table():
+ result = [None] * 256
+ for entry in BYTECODE_RANGES:
+ if len(entry) == 2:
+ positions = [entry[0]]
+ else:
+ positions = range(entry[0], entry[1]+1)
+ for pos in positions:
+ result[pos] = getattr(ContextPartShadow, entry[-1])
+ assert None not in result
+ return result
+
+# this table is only used for creating named bytecodes in tests and printing
+BYTECODE_TABLE = initialize_bytecode_table()
diff --git a/spyvm/interpreter_debugging.py b/spyvm/interpreter_debugging.py
new file mode 100644
--- /dev/null
+++ b/spyvm/interpreter_debugging.py
@@ -0,0 +1,109 @@
+
+import pdb
+from spyvm.shadow import ContextPartShadow
+from spyvm import model, constants, primitives
+
+# This module patches up the interpreter and adds breakpoints at certain execution points.
+# Only usable in interpreted mode due to pdb.
+# To use, execute one of following after interpreter.py is loaded:
+# from spyvm import interpreter_debugging; interpreter_debugging.activate_debugging()
+# or, before Interpreter instance is created:
+# Interpreter.__init__ = interpreter_debugging.activating_init(Interpreter.__init__)
+
+# After this, following flags control whether the interpreter breaks at the respective locations:
+# <interp> can be an interpreter instance or the Interpreter class
+# interp.step_bytecodes
+# interp.step_sends
+# interp.step_returns
+# interp.step_primitives
+# interp.step_failed_primitives
+# interp.step_failed_named_primitives
+
+def activating_init(original):
+ def meth(*args):
+ activate_debugging()
+ return original(*args)
+ return meth
+
+def activate_debugging():
+ from spyvm.interpreter import Interpreter
+ Interpreter.step_bytecodes = False
+ Interpreter.step_sends = False
+ Interpreter.step_returns = False
+ Interpreter.step_primitives = False
+ Interpreter.step_failed_primitives = False
+
+ _break = pdb.set_trace
+
+ def patch(obj):
+ def do_patch(meth):
+ name = meth.__name__
+ original = getattr(obj, name)
+ assert original, "Object %r does not have a method named %s" % (obj, name)
+ replacement = meth(original)
+ setattr(obj, name, replacement)
+ return meth
+ return do_patch
+
+ patch_context = patch(ContextPartShadow)
+
+ @patch_context
+ def debug_bytecode(original):
+ def meth(self):
+ if self.step_bytecodes:
+ _break() # Continue stepping from here to get to the current bytecode execution
+ return meth
+
+ @patch_context
+ def _sendSelector(original):
+ def meth(self, w_selector, argcount, interp, receiver, receiverclassshadow, w_arguments=None):
+ if interp.step_sends:
+ _break() # Continue stepping from here to get to the current message send
+ return original(self, w_selector, argcount, interp, receiver, receiverclassshadow, w_arguments=w_arguments)
+ return meth
+
+ @patch_context
+ def _return(original):
+ def meth(self, return_value, interp, local_return=False):
+ if interp.step_returns:
+ _break() # Continue stepping from here to get to the current return
+ return original(self, return_value, interp, local_return=local_return)
+ return meth
+
+ @patch_context
+ def _call_primitive(original):
+ def meth(self, code, interp, argcount, w_method, w_selector):
+ if interp.step_primitives:
+ _break() # Continue stepping from here to get to the current primitive
+ try:
+ return original(self, code, interp, argcount, w_method, w_selector)
+ except error.PrimitiveFailedError, e:
+ if interp.step_failed_primitives:
+ _break() # Continue stepping from here to get to the current failed primitive.
+
+ # Should fail again.
+ original(self, code, interp, argcount, w_method, w_selector)
+ return meth
+
+ def failed_named_primitive(original):
+ def meth(interp, s_frame, argcount, w_method=None):
+ try:
+ return original(interp, s_frame, argcount, w_method=w_method)
+ except error.PrimitiveFailedError, e:
+ if interp.step_failed_named_primitives:
+ _break() # Continue from here to get to the current failed named primitive.
+
+ space = interp.space
+ w_description = w_method.literalat0(space, 1)
+ if isinstance(w_description, model.W_PointersObject) and w_description.size() >= 2:
+ w_modulename = w_description.at0(space, 0)
+ w_functionname = w_description.at0(space, 1)
+ print "Failed named primitive. Module: %s, Function: %s" % (w_modulename, w_functionname)
+
+ # Should fail again.
+ original(interp, s_frame, argcount, w_method=w_method)
+ raise e
+ return meth
+
+ primitives.prim_table[primitives.EXTERNAL_CALL] = failed_named_primitive(primitives.prim_table[primitives.EXTERNAL_CALL])
+
\ No newline at end of file
diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -1280,7 +1280,7 @@
return self.get_identifier_string()
def bytecode_string(self, markBytecode=0):
- from spyvm.interpreter import BYTECODE_TABLE
+ from spyvm.interpreter_bytecodes import BYTECODE_TABLE
retval = "Bytecode:------------"
j = 1
for i in self.bytes:
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -679,7 +679,7 @@
assert isinstance(w_bitmap, model_display.W_DisplayBitmap)
w_bitmap.flush_to_screen()
return w_rcvr
- except shadow.MethodNotFound:
+ except error.MethodNotFound:
from spyvm.plugins.bitblt import BitBltPlugin
BitBltPlugin.call("primitiveCopyBits", interp, s_frame, argcount, w_method)
return w_rcvr
@@ -1358,9 +1358,7 @@
unwrap_spec=[object, object, list],
no_result=True, clean_stack=False)
def func(interp, s_frame, w_rcvr, w_selector, w_arguments):
- from spyvm.shadow import MethodNotFound
s_frame.pop_n(2) # removing our arguments
-
return s_frame._sendSelector(w_selector, len(w_arguments), interp, w_rcvr,
w_rcvr.class_shadow(interp.space), w_arguments=w_arguments)
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -286,11 +286,8 @@
FLOAT = 5
LARGE_POSITIVE_INTEGER = 6
-class MethodNotFound(error.SmalltalkException):
- pass
-
class ClassShadowError(error.SmalltalkException):
- pass
+ exception_type = "ClassShadowError"
class ClassShadow(AbstractCachingShadow):
"""A shadow for Smalltalk objects that are classes
@@ -505,7 +502,7 @@
if w_method is not None:
return w_method
look_in_shadow = look_in_shadow._s_superclass
- raise MethodNotFound(self, w_selector)
+ raise error.MethodNotFound()
def changed(self):
self.superclass_changed(version.Version())
diff --git a/spyvm/test/test_interpreter.py b/spyvm/test/test_interpreter.py
--- a/spyvm/test/test_interpreter.py
+++ b/spyvm/test/test_interpreter.py
@@ -1,5 +1,5 @@
import py, operator, sys
-from spyvm import model, interpreter, primitives, shadow, objspace, wrapper, constants
+from spyvm import model, interpreter, primitives, shadow, objspace, wrapper, constants, error
from .util import create_space_interp, copy_to_module, cleanup_module, import_bytecodes, TestInterpreter
from spyvm.wrapper import PointWrapper
from spyvm.conftest import option
@@ -139,7 +139,7 @@
def test_unknownBytecode():
w_frame, s_frame = new_frame(unknownBytecode)
- py.test.raises(interpreter.MissingBytecode, step_in_interp, s_frame)
+ py.test.raises(error.MissingBytecode, step_in_interp, s_frame)
# push bytecodes
def test_pushReceiverBytecode():
@@ -579,7 +579,7 @@
test_storeAndPopTemporaryVariableBytecode(lambda index: extendedStoreAndPopBytecode + chr((1<<6) + index))
- py.test.raises(interpreter.IllegalStoreError,
+ py.test.raises(error.FatalError,
test_storeAndPopTemporaryVariableBytecode,
lambda index: extendedStoreAndPopBytecode + chr((2<<6) + index))
diff --git a/spyvm/test/test_model.py b/spyvm/test/test_model.py
--- a/spyvm/test/test_model.py
+++ b/spyvm/test/test_model.py
@@ -1,6 +1,7 @@
import py, math, socket
from spyvm import model, model_display, shadow, objspace, error, display
-from spyvm.shadow import MethodNotFound, WEAK_POINTERS
+from spyvm.error import MethodNotFound
+from spyvm.shadow import WEAK_POINTERS
from rpython.rlib.rarithmetic import intmask, r_uint
from rpython.rtyper.lltypesystem import lltype, rffi
from .util import create_space, copy_to_module, cleanup_module
diff --git a/spyvm/test/util.py b/spyvm/test/util.py
--- a/spyvm/test/util.py
+++ b/spyvm/test/util.py
@@ -1,5 +1,5 @@
import sys
-from spyvm import model, shadow, objspace, version, constants, squeakimage, interpreter
+from spyvm import model, shadow, objspace, version, constants, squeakimage, interpreter, interpreter_bytecodes
from rpython.rlib.objectmodel import instantiate
# Most tests don't need a bootstrapped objspace. Those that do, indicate so explicitely.
@@ -65,7 +65,7 @@
assert entry[0] <= opcode <= entry[1]
return chr(opcode)
setattr(mod, name, get_opcode_chr)
- for entry in interpreter.BYTECODE_RANGES:
+ for entry in interpreter_bytecodes.BYTECODE_RANGES:
name = entry[-1]
if len(entry) == 2: # no range
setattr(mod, name, chr(entry[0]))
diff --git a/spyvm/tool/analyseimage.py b/spyvm/tool/analyseimage.py
--- a/spyvm/tool/analyseimage.py
+++ b/spyvm/tool/analyseimage.py
@@ -59,7 +59,7 @@
w_frame = w_method.create_frame(interp.space, w_object)
interp.store_w_active_context(w_frame)
- from spyvm.interpreter import BYTECODE_TABLE
+ from spyvm.interpreter_bytecodes import BYTECODE_TABLE
while True:
try:
interp.step()
diff --git a/targetimageloadingsmalltalk.py b/targetimageloadingsmalltalk.py
--- a/targetimageloadingsmalltalk.py
+++ b/targetimageloadingsmalltalk.py
More information about the pypy-commit
mailing list