Author: Anton Gulenko <anton.gule...@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 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit