def COMPARE_OP(ctx: _VSContext, instruction: dis.Instruction): """ Implements comparison operators. """ # TODO: Rewrite COMPARE_OP into Vanstein-ran function calls. # TODO: Add all of the comparison functions. if instruction.arg == 10: # Pop the too match off. to_match = ctx.pop() # This is what we check. raised = ctx.pop() from collections import Iterable if not isinstance(to_match, Iterable): to_match = (to_match, ) for e in to_match: # PyType_IsSubType if issubclass(type(raised), e): ctx.push(True) break else: ctx.push(False) return ctx
def DUP_TOP(ctx: _VSContext, instruction: dis.Instruction): """ Duplicates the top-most item on the stack. """ item = ctx.pop() ctx.push(item) ctx.push(item) return ctx
def LOAD_FAST(ctx: _VSContext, instruction: dis.Instruction): """ Loads from VARNAMES. """ item = ctx.varnames[instruction.arg] if item == NO_RESULT: safe_raise( ctx, NameError("name '{}' is not defined".format( ctx.co_varnames[instruction.arg]))) return ctx ctx.push(item) return ctx
def LOAD_GLOBAL(ctx: _VSContext, instruction: dis.Instruction): """ Loads a global from `ctx.__globals__`. """ name = ctx.co_names[instruction.arg] try: item = ctx.get_global(name) except KeyError: # todo: safe_raise return safe_raise(ctx, NameError("name '{}' is not defined".format(name))) ctx.push(item) return ctx
def run_context(self, context: _VSContext) -> _VSContext: """ Runs the current bytecode for a context. This will instructions off of the instruction stack, until it reaches a context switch. """ # Welcome to the main bulk of Vanstein. # Enjoy your stay! # Switch to running state for this context. context.state = VSCtxState.RUNNING self.current_context = context while True: if context.state is VSCtxState.FINISHED: # Done after a successful RETURN_VALUE. # Break the loop, and return the context. context.finish() return context if context.state is VSCtxState.ERRORED: return context next_instruction = context.next_instruction() self.current_instruction = next_instruction # First, we check if we need to context switch. # Check if it's CALL_FUNCTION. if next_instruction.opname == "CALL_FUNCTION": # This is the instruction for CALL_FUNCTION. No specialized one exists in the instructions.py file. # We need to context switch, so suspend this current one. context.state = VSCtxState.SUSPENDED # Get STACK[-arg] # CALL_FUNCTION(arg) => arg is number of positional arguments to use, so pop that off of the stack. bottom_of_stack = context.stack[-(next_instruction.arg + 1)] # method wrappers die if type(bottom_of_stack) is type: bottom_of_stack = bottom_of_stack.__new__ # Here's some context switching. # First, check if it's a builtin or is a native invoke. # Also, check if we should even do context switching. if inspect.isbuiltin(bottom_of_stack) or hasattr(bottom_of_stack, "_native_invoke")\ or self.do_context_switching is False: # Run it! result = self.__run_natively(context, next_instruction) # Set the result on the context. context.state = VSCtxState.RUNNING # Push the result onto the stack. context.push(result) # Continue the loop to the next instruction. continue if isinstance(bottom_of_stack, VSWrappedFunction): # Call the VSWrappedFunction to get a new context. # We'll manually fill these args. new_ctx = bottom_of_stack() else: # Wrap the function in a context. new_ctx = _VSContext(bottom_of_stack) # Set the previous context, for stack frame chaining. new_ctx.prev_ctx = context # Doubly linked list! context.next_ctx = new_ctx # Set the new state to PENDING so it knows to run it on the next run. new_ctx.state = VSCtxState.PENDING # Add a callback to the new context. # This is so the loop can schedule execution of the new context soon. new_ctx.add_done_callback(context._on_result_cb) new_ctx.add_exception_callback(context._on_exception_cb) # Fill the number of arguments the function call requests. args = [] for _ in range(0, next_instruction.arg): args.append(context.pop()) args = reversed(args) new_ctx.fill_args(*args) # Pop the function object off, too. context.pop() return new_ctx # Else, we run the respective instruction. try: i = getattr(instructions, next_instruction.opname) except AttributeError: raise NotImplementedError(next_instruction.opname) # Call the instruction handler. i(context, next_instruction)
def LOAD_CONST(ctx: _VSContext, instruction: dis.Instruction): """ Loads a const from `ctx.co_consts`. """ ctx.push(ctx.co_consts[instruction.arg]) return ctx