micropython

rsvp.py

630:947aa6f5a86f
2013-01-13 Paul Boddie Added a test of the first form of B.
     1 #!/usr/bin/env python     2      3 """     4 A really simple virtual processor employing a simple set of instructions which     5 ignore low-level operations and merely concentrate on variable access, structure     6 access, structure allocation and function invocations.     7      8 Copyright (C) 2007, 2008, 2009, 2010, 2011 Paul Boddie <paul@boddie.org.uk>     9     10 This program is free software; you can redistribute it and/or modify it under    11 the terms of the GNU General Public License as published by the Free Software    12 Foundation; either version 3 of the License, or (at your option) any later    13 version.    14     15 This program is distributed in the hope that it will be useful, but WITHOUT    16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    17 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    18 details.    19     20 You should have received a copy of the GNU General Public License along with    21 this program.  If not, see <http://www.gnu.org/licenses/>.    22     23 --------    24     25 The execution model of the virtual processor involves the following things:    26     27   * Memory                          contains constants, global variable    28                                     references and program code    29     30   * PC (program counter) stack      contains the return address associated    31                                     with each function invocation    32     33   * Frame stack                     contains invocation frames in use and in    34                                     preparation plus temporary storage    35     36   * Local frame pointer stack       refers to the frames in the frame stack    37     38   * Invocation frame pointer stack    39     40   * Exception handler stack    41     42   * Exception handler locals stack  refers to the state of the local frame    43                                     pointer stack    44     45   * Exception handler PC stack      refers to the state of the PC stack    46     47   * Registers: working context/value,    48                assignment source context/value,    49                current exception value,    50                boolean status value,    51                object list start (constant),    52                parameter list start (constant)    53 """    54     55 from micropython.program import DataValue, ReplaceableContext, PlaceholderContext, FragmentObject    56 from rsvplib import Library    57     58 class IllegalInstruction(Exception):    59     pass    60     61 class IllegalAddress(Exception):    62     def __init__(self, address):    63         self.address = address    64     def __repr__(self):    65         return "IllegalAddress(%r)" % self.address    66     def __str__(self):    67         return repr(self)    68     69 class EmptyPCStack(Exception):    70     pass    71     72 class EmptyFrameStack(Exception):    73     pass    74     75 class BreakpointReached(Exception):    76     pass    77     78 class RSVPMachine:    79     80     "A really simple virtual processor."    81     82     def __init__(self, memory, objlist, paramlist, pc=None, debug=0, abort_upon_exception=0):    83     84         """    85         Initialise the processor with a 'memory' (a list of values containing    86         instructions and data), the object and parameter lists 'objlist' and    87         'paramlist', and the optional program counter 'pc'.    88         """    89     90         self.memory = memory    91         self._objlist = objlist    92         self._paramlist = paramlist    93         end_of_code = len(self.memory)    94     95         # Add the object list.    96     97         objlist_start = len(self.memory)    98         self.memory += objlist.as_raw()    99    100         # Add the parameter list.   101    102         paramlist_start = len(self.memory)   103         self.memory += paramlist.as_raw()   104    105         # A reference to the native library.   106    107         self.library = None   108    109         # Program counter and machine configuration.   110    111         self.pc = pc or 0   112         self.debug = debug   113         self.abort_upon_exception = abort_upon_exception   114    115         # Profiling.   116    117         self.coverage = [0] * end_of_code   118         self.counter = 0   119         self.cost = 0   120    121         # Stacks.   122    123         self.pc_stack = []   124         self.frame_stack = []   125         self.local_sp_stack = [0]   126         self.invocation_sp_stack = []   127    128         # Exception handler stacks are used to reset the state of the main   129         # stacks.   130    131         self.handler_stack = [end_of_code - 1] # final handler is the end of the code   132         self.handler_local_sp_stack = []   133         self.handler_invocation_sp_stack = []   134         self.handler_pc_stack = []   135    136         # Registers.   137    138         self.register_names = (   139             "working",   140             "working_context",   141             "source",   142             "source_context",   143             "exception",   144             "status",   145             "objlist",   146             "paramlist"   147             )   148         self.registers = {}   149    150         for name in self.register_names:   151             self.registers[name] = None   152    153         self.registers["objlist"] = objlist_start   154         self.registers["paramlist"] = paramlist_start   155    156         self.instruction = None   157    158         # Constants.   159         # NOTE: These should eventually be removed once the code using them has   160         # NOTE: been migrated to the native code library.   161    162         cls = self._get_class("__builtins__", "AttributeError")   163         self.attr_error = cls.location   164         self.attr_error_instance = cls.instance_template_location   165         cls = self._get_class("__builtins__", "TypeError")   166         self.type_error = cls.location   167         self.type_error_instance = cls.instance_template_location   168         cls = self._get_class("__builtins__", "tuple")   169         self.tuple_class = cls.location   170         self.tuple_instance = cls.instance_template_location   171    172         # Debugging attributes.   173    174         self.breakpoints = set()   175    176     def get_program_size(self):   177         return self.registers["objlist"]   178    179     def _get_class(self, module, name):   180         attr = self._objlist.access(module, name)   181         if attr is not None:   182             return attr.get_value()   183         else:   184             return None   185    186     # Debugging methods.   187    188     def dump(self):   189         print "PC", self.pc, "->", self.load(self.pc)   190         print "PC stack", self.pc_stack   191         print "Frame stack:"   192         if self.local_sp_stack:   193             start = self.local_sp_stack[0]   194             for end in self.local_sp_stack[1:]:   195                 print "  %2d" % start, self.frame_stack[start:end]   196                 start = end   197             else:   198                 print "  %2d" % start, self.frame_stack[start:]   199         print   200         print "Local stack pointers", self.local_sp_stack   201         print "Invocation stack pointers", self.invocation_sp_stack   202         print "Handler stack", self.handler_stack   203         print "Handler frame stack", self.handler_local_sp_stack   204         print "Handler PC stack", self.handler_pc_stack   205         print   206         print "Registers:"   207         print   208         for name in self.register_names:   209             print "%s: %s" % (name, self.registers[name])   210         print   211         print "Instruction", self.instruction   212    213     def show(self, start=None, end=None):   214         self.show_memory(self.memory[start:end], self.coverage[start:end], start or 0)   215    216     def show_pc(self, run_in=10):   217         start = max(0, self.pc - run_in)   218         end = self.pc + run_in   219         memory = self.memory[start:end]   220         coverage = self.coverage[start:end]   221         self.show_memory(memory, coverage, start)   222    223     def show_object(self, start):   224         obj = self.memory[start]   225         end = start + obj.size   226         self.show_memory(self.memory[start:end], self.coverage[start:end], start)   227    228     def show_memory(self, memory, coverage, start):   229         for i, (c, x) in enumerate(map(None, coverage, memory)):   230             location = start + i   231             if location == self.pc:   232                 print "->",   233             elif location in self.pc_stack:   234                 print "..",   235             else:   236                 print "  ",   237             print "%-5s %5d %r" % (c or "", location, x)   238    239     def step(self, dump=0):   240         self.execute()   241         self.show_pc()   242         if dump:   243             self.dump()   244    245     def set_break(self, location):   246         self.breakpoints.add(location)   247    248     def up(self):   249         retaddr = self.pc_stack[-1]   250         self.set_break(retaddr)   251         self.run()   252    253     # Internal operations.   254    255     def load(self, address):   256    257         "Return the value at the given 'address'."   258    259         try:   260             return self.memory[address]   261         except IndexError:   262             raise IllegalAddress(address)   263         except TypeError:   264             raise IllegalAddress(address)   265    266     def save(self, address, value):   267    268         "Save to the given 'address' the specified 'value'."   269    270         try:   271             self.memory[address] = value   272         except IndexError:   273             raise IllegalAddress(address)   274         except TypeError:   275             raise IllegalAddress(address)   276    277     def new(self, size):   278    279         """   280         Allocate space of the given 'size', returning the address of the space.   281         """   282    283         addr = len(self.memory)   284         for i in range(0, size):   285             self.memory.append(None)   286         return addr   287    288     def push_pc(self, data):   289    290         "Push 'data' onto the PC stack."   291    292         self.pc_stack.append(data)   293    294     def pull_pc(self):   295    296         "Pull a value from the PC stack and return it."   297    298         try:   299             return self.pc_stack.pop()   300         except IndexError:   301             raise EmptyPCStack   302    303     def run(self):   304    305         "Execute code in the memory, starting from the current PC address."   306    307         breakpoint = 0   308    309         try:   310             while 1:   311                 self.execute()   312         except EmptyPCStack:   313             pass   314         except BreakpointReached:   315             breakpoint = 1   316    317         print "Execution terminated",   318         if breakpoint:   319             print "with breakpoint."   320             print "At address", self.pc   321         elif self.registers["exception"] is not None:   322             ref = self.registers["exception"]   323             addr = self.load(ref + Library.instance_data_offset)   324             print "with exception:", self.load(ref)   325             print "At address %d: %r" % (addr, self.load(addr))   326         else:   327             print "successfully."   328         print "After", self.counter, "instructions at cost", self.cost, "units."   329    330     def test(self, module):   331    332         """   333         Test the code in the memory by running the code and investigating the   334         contents of variables. Use 'module' to identify result variables.   335         """   336    337         self.run()   338         success = 1   339    340         if self.registers["exception"] is None:   341             for name in module.keys():   342                 if name.startswith("result"):   343                     label, expected = name.split("_")   344                     attr = module[name]   345    346                     # Need to skip the header to get at the members.   347    348                     attr_location = module.location + Library.instance_data_offset + attr.position   349                     value = self.load(attr_location)   350    351                     if value is not None:   352                         content = self.load(value.ref + Library.instance_data_offset)   353                         print label, expected, content   354                         success = success and (int(expected) == content)   355                     else:   356                         print label, expected, "missing"   357                         success = 0   358    359             return success   360         else:   361             return 0   362    363     def execute(self):   364    365         "Execute code in the memory at the current PC address."   366    367         if self.pc in self.breakpoints:   368             self.breakpoints.remove(self.pc)   369             raise BreakpointReached   370    371         self.instruction = self.load(self.pc)   372    373         # Perform the instruction itself.   374    375         next_pc = self.perform(self.instruction)   376    377         # Update the coverage.   378    379         self.coverage[self.pc] += 1   380    381         # Update the program counter.   382    383         if next_pc is None:   384             self.pc += 1   385         else:   386             self.pc = next_pc   387    388     def get_method(self, instruction):   389    390         "Return the handler method for the given 'instruction'."   391    392         instruction_name = instruction.__class__.__name__   393         if self.debug:   394             print "%8d %s" % (self.pc, instruction_name)   395         method = getattr(self, instruction_name, None)   396         if method is None:   397             raise IllegalInstruction, (self.pc, instruction_name)   398         return method   399    400     def perform(self, instruction):   401    402         "Perform the 'instruction', returning the next PC value or None."   403    404         self.counter += 1   405         self.cost += instruction.cost   406         operand = instruction.get_operand()   407         method = self.get_method(instruction)   408         return method(operand, instruction)   409    410     def jump(self, addr, next):   411    412         """   413         Jump to the subroutine at (or identified by) 'addr'. If 'addr'   414         identifies a library function then invoke the library function and set   415         PC to 'next' afterwards; otherwise, set PC to 'addr'.   416         """   417    418         # Trap library functions introduced through the use of strings instead   419         # of proper locations.   420    421         if isinstance(addr, str):   422             handler = self.library and self.library.native_functions[addr](self.library)   423             if handler is None:   424                 return next   425             else:   426                 return handler   427         else:   428             self.push_pc(self.pc + 1)   429             return addr   430    431     # Helper methods.   432    433     def context_of(self, register):   434         return "%s_context" % register   435    436     def load_from_frame(self, operand):   437         frame = self.local_sp_stack[-1]   438         return self.frame_stack[frame + operand]   439    440     # Low-level instructions.   441    442     def LoadImmediate(self, operand, target):   443         self.registers[target] = operand   444    445     def LoadMemory(self, operand, target, source):   446         self.registers[target] = self.load(   447             self.registers[source] + (operand is not None and operand or 0)   448             )   449    450     # Instructions.   451    452     def Transfer(self, operand, instruction):   453         self.registers[instruction.target] = self.registers[instruction.source]   454    455     def LoadConst(self, operand, instruction):   456         self.LoadImmediate(operand, self.context_of(instruction.target))   457         self.LoadImmediate(operand, instruction.target)   458    459     def LoadClass(self, operand, instruction):   460         self.LoadImmediate(PlaceholderContext, self.context_of(instruction.target))   461         self.LoadImmediate(operand, instruction.target)   462    463     def LoadFunction(self, operand, instruction):   464         self.LoadImmediate(ReplaceableContext, self.context_of(instruction.target))   465         self.LoadImmediate(operand, instruction.target)   466    467     def LoadName(self, operand, instruction):   468         data = self.load_from_frame(operand)   469         self.LoadImmediate(data.context, self.context_of(instruction.target))   470         self.LoadImmediate(data.ref, instruction.target)   471    472     def StoreName(self, operand, instruction):   473         frame = self.local_sp_stack[-1]   474         self.frame_stack[frame + operand] = DataValue(   475             self.registers[self.context_of(instruction.source)],   476             self.registers[instruction.source])   477    478     LoadTemp = LoadName   479    480     def StoreTemp(self, operand, instruction):   481         frame = self.local_sp_stack[-1]   482         self.frame_stack[frame + operand] = DataValue(   483             self.registers[self.context_of(instruction.working)],   484             self.registers[instruction.working])   485    486     def LoadAddress(self, operand, instruction):   487         # Preserve context (potentially null).   488         data = self.load(operand)   489         self.LoadImmediate(data.context, self.context_of(instruction.target))   490         self.LoadImmediate(data.ref, instruction.target)   491    492     def LoadAddressContext(self, operand, instruction):   493         # Value becomes context.   494         data = self.load(operand)   495         self.LoadImmediate(self.registers[instruction.working], self.context_of(instruction.target))   496         self.LoadImmediate(data.ref, instruction.target)   497    498     def LoadAddressContextCond(self, operand, instruction):   499         data = self.load(operand)   500         data = self._LoadAddressContextCond(data.context, data.ref, self.registers[instruction.working])   501         self.LoadImmediate(data.context, self.context_of(instruction.target))   502         self.LoadImmediate(data.ref, instruction.target)   503    504     def StoreAddress(self, operand, instruction):   505         # Preserve context.   506         self.save(operand, DataValue(   507             self.registers[self.context_of(instruction.source)],   508             self.registers[instruction.source]))   509    510     def StoreAddressContext(self, operand, instruction):   511         # Overwrite context if null.   512         self._StoreAddressContext(operand,   513             self.registers[self.context_of(instruction.working)],   514             self.registers[instruction.working],   515             self.registers[self.context_of(instruction.source)],   516             self.registers[instruction.source])   517    518     def MakeInstance(self, size, instruction):   519         # NOTE: Referencing the instance template.   520         addr = self._MakeObject(size, self.registers[instruction.working] - Library.instance_template_size)   521         # Introduce object as context for the new object.   522         self.LoadImmediate(addr, self.context_of(instruction.target))   523         self.LoadImmediate(addr, instruction.target)   524    525     def MakeFragment(self, size, instruction):   526         # Reserve twice the amount of space.   527         addr = self._MakeFragment(size, size * 2)   528         # NOTE: Context is not relevant for fragments.   529         self.LoadImmediate(None, self.context_of(instruction.target))   530         self.LoadImmediate(addr, instruction.target)   531    532     def LoadAttr(self, pos, instruction):   533         # Retrieved context should already be appropriate for the instance.   534         # Skip any header.   535         data = self.load(self.registers[instruction.working] + Library.instance_data_offset + pos)   536         self.LoadImmediate(data.context, self.context_of(instruction.target))   537         self.LoadImmediate(data.ref, instruction.target)   538    539     def StoreAttr(self, pos, instruction):   540         # Target should already be an instance.   541         # Skip any header.   542         self.save(self.registers[instruction.working] + Library.instance_data_offset + pos , DataValue(   543             self.registers[self.context_of(instruction.source)],   544             self.registers[instruction.source]))   545    546     def LoadAttrIndex(self, index, instruction):   547         data = self.load(self.registers[instruction.working])   548         element = self.load(self.registers["objlist"] + data.classcode + index)   549    550         if element is not None:   551             attr_index, static_attr, offset = element   552             if attr_index == index:   553                 if static_attr:   554                     data = self.load(offset) # offset is address of class/module attribute   555                 else:   556                     data = self.load(self.registers[instruction.working] + offset)   557                 self.LoadImmediate(data.context, self.context_of(instruction.target))   558                 self.LoadImmediate(data.ref, instruction.target)   559                 return   560    561         self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance)   562         return self.RaiseException()   563    564     # LoadAttrIndexContext not defined.   565    566     def LoadAttrIndexContextCond(self, index, instruction):   567         data = self.load(self.registers[instruction.working])   568         element = self.load(self.registers["objlist"] + data.classcode + index)   569    570         if element is not None:   571             attr_index, static_attr, offset = element   572             if attr_index == index:   573                 if static_attr:   574                     loaded_data = self.load(offset) # offset is address of class/module attribute   575                     # Absent attrcode == class/module.   576                     if data.attrcode is not None:   577                         loaded_data = self._LoadAddressContextCond(   578                             loaded_data.context, loaded_data.ref,   579                             self.registers[instruction.working])   580                 else:   581                     loaded_data = self.load(self.registers[instruction.working] + offset)   582                 self.LoadImmediate(loaded_data.context, self.context_of(instruction.target))   583                 self.LoadImmediate(loaded_data.ref, instruction.target)   584                 return   585    586         self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance)   587         return self.RaiseException()   588    589     def StoreAttrIndex(self, index, instruction):   590         data = self.load(self.registers[instruction.working])   591         element = self.load(self.registers["objlist"] + data.classcode + index)   592    593         if element is not None:   594             attr_index, static_attr, offset = element   595             if attr_index == index:   596                 if static_attr:   597                     self._StoreAddressContext(offset,   598                         self.registers[self.context_of(instruction.working)],   599                         self.registers[instruction.working],   600                         self.registers[self.context_of(instruction.source)],   601                         self.registers[instruction.source])   602                     return   603                 else:   604                     self.save(self.registers[instruction.working] + offset, DataValue(   605                         self.registers[self.context_of(instruction.source)],   606                         self.registers[instruction.source]))   607                     return   608    609         self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance)   610         return self.RaiseException()   611    612     # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden.   613    614     def MakeFrame(self, size, instruction):   615         self.invocation_sp_stack.append(len(self.frame_stack))   616         self.frame_stack.extend([None] * size)   617    618     def DropFrame(self, operand, instruction):   619         self.local_sp_stack.pop()   620         frame = self.invocation_sp_stack.pop()   621         del self.frame_stack[frame:] # reset stack before call   622    623     def StoreFrame(self, pos, instruction):   624         frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame   625         self.frame_stack[frame + pos] = DataValue(   626             self.registers[self.context_of(instruction.working)],   627             self.registers[instruction.working])   628    629     def StoreFrameIndex(self, index, instruction):   630         frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame   631         data = self.load(self.registers[instruction.working])   632         element = self.load(self.registers["paramlist"] + data.funccode + index)   633    634         if element is not None:   635             # NOTE: Need to ensure correct positioning where a context has been generated.   636             param_index, offset = element   637             if param_index == index:   638                 self.frame_stack[frame + offset] = DataValue(   639                     self.registers[self.context_of(instruction.source)],   640                     self.registers[instruction.source])   641                 return   642    643         self.registers["exception"] = self._MakeObject(Library.instance_size, self.type_error_instance)   644         return self.RaiseException()   645    646     def LoadCallable(self, operand, instruction):   647         data = self.load(self.registers[instruction.working])   648         self.registers[instruction.target] = data.codeaddr   649    650     def StoreCallable(self, operand, instruction):   651         # NOTE: Should improve the representation and permit direct saving.   652         data = self.load(self.registers[instruction.working])   653         self.save(self.registers[instruction.working], data.with_callable(self.registers[instruction.source]))   654    655     def CheckContext(self, operand, instruction):   656         self.registers[instruction.target] = self.registers[instruction.working] is not ReplaceableContext   657    658     def CheckClass(self, operand, instruction):   659         if self.registers[instruction.working] in (ReplaceableContext, PlaceholderContext):   660             self.registers[instruction.target] = 0   661             return   662    663         data = self.load(self.registers[instruction.working])   664    665         # Classes are not themselves usable as the self argument.   666         # NOTE: This may change at some point.   667         # However, where classes appear as the context, instance   668         # compatibility is required in the first argument.   669    670         self.registers[instruction.target] = data.attrcode is None # absent attrcode == class   671    672     def CheckFrame(self, operand, instruction):   673         (nargs, ndefaults) = operand   674    675         # The frame is actually installed as the locals.   676         # Retrieve the context from the first local.   677    678         frame = self.local_sp_stack[-1]   679         nlocals = len(self.frame_stack[frame:])   680    681         if not ((nargs - ndefaults) <= nlocals):   682             raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (operand, nargs - ndefaults, nlocals, nargs)   683             #self.registers["exception"] = self._MakeObject(Library.instance_size, self.type_error_instance)   684             #return self.RaiseException()   685    686     def CheckExtra(self, nargs, instruction):   687    688         # The frame is actually installed as the locals.   689         # Retrieve the context from the first local.   690    691         frame = self.local_sp_stack[-1]   692         nlocals = len(self.frame_stack[frame:])   693    694         # Provide the extra star parameter if necessary.   695    696         if nlocals == nargs:   697             self.frame_stack.extend([None]) # ExtendFrame(1)   698    699     def FillDefaults(self, operand, instruction):   700         (nargs, ndefaults) = operand   701    702         # The frame is actually installed as the locals.   703    704         frame = self.local_sp_stack[-1]   705         nlocals = len(self.frame_stack[frame:])   706    707         # Support population of defaults.   708         # This involves copying the "attributes" of a function into the frame.   709    710         default = nlocals - (nargs - ndefaults)   711         self.frame_stack.extend([None] * (nargs - nlocals))   712         pos = nlocals   713    714         while pos < nargs:   715    716             # Skip any header.   717    718             self.frame_stack[frame + pos] = self.load(self.registers[instruction.working] + Library.instance_data_offset + default)   719             default += 1   720             pos += 1   721    722     def CopyExtra(self, start, instruction):   723    724         # The frame is the source of the extra arguments.   725    726         frame = self.local_sp_stack[-1]   727         nlocals = len(self.frame_stack[frame:])   728    729         # Make a tuple to hold the arguments.   730    731         ref = self._MakeObject(nlocals - start + 1, self.tuple_instance)   732    733         extra = 0   734         pos = start   735    736         while pos < nlocals:   737    738             # Skip any header.   739    740             self.save(ref + Library.instance_data_offset + extra, self.frame_stack[frame + pos])   741             extra += 1   742             pos += 1   743    744         self.LoadImmediate(ref, self.context_of(instruction.working))   745         self.LoadImmediate(ref, instruction.working)   746    747     def CheckInstance(self, operand, instruction):   748         # For the 'self' parameter in an invoked function, the proposed context   749         # ('self') is checked against the target's context.   750         self.registers[instruction.target] = self._CheckInstance(   751             self.registers[instruction.working],   752             self.registers[instruction.source])   753    754     def JumpInFrame(self, operand, instruction):   755         codeaddr = self.registers[instruction.working]   756         return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one   757    758     def JumpInFrameDirect(self, addr, instruction):   759         return self.jump(addr, self.pc + 1) # return to the instruction after this one   760    761     def JumpWithFrame(self, operand, instruction):   762         codeaddr = self.registers[instruction.working]   763         self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame   764         return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one   765    766     def JumpWithFrameDirect(self, addr, instruction):   767         self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame   768         return self.jump(addr, self.pc + 1) # return to the instruction after this one   769    770     def ExtendFrame(self, size, instruction):   771         self.frame_stack.extend([None] * size)   772    773     def AdjustFrame(self, size, instruction):   774         self.invocation_sp_stack[-1] += size   775    776     def Return(self, operand, instruction):   777         return self.pull_pc()   778    779     def Jump(self, addr, instruction):   780         return addr   781    782     def JumpIfTrue(self, addr, instruction):   783         if self.registers[instruction.working]:   784             return addr   785    786     def JumpIfFalse(self, addr, instruction):   787         if not self.registers[instruction.working]:   788             return addr   789    790     def ClearException(self, operand, instruction):   791         self.LoadImmediate(None, instruction.target)   792    793     def RaiseException(self, operand=None, instruction=None):   794         # NOTE: Adding the program counter as the first attribute after __class__.   795         self.save(self.registers["exception"] + Library.instance_data_offset, self.pc)   796         # Jumping to the current handler.   797         if self.abort_upon_exception:   798             raise Exception   799         return self.handler_stack[-1]   800    801     def PushHandler(self, addr, instruction):   802         self.handler_stack.append(addr)   803         self.handler_local_sp_stack.append(len(self.local_sp_stack))   804         self.handler_invocation_sp_stack.append(len(self.invocation_sp_stack))   805         self.handler_pc_stack.append(len(self.pc_stack))   806    807     def PopHandler(self, nframes, instruction):   808         # Get the new local frame pointer and PC stack references.   809         local_sp_top = self.handler_local_sp_stack[-nframes]   810         invocation_sp_top = self.handler_invocation_sp_stack[-nframes]   811         pc_top = self.handler_pc_stack[-nframes]   812         # Reduce the local frame pointer stack to refer to the handler's frame.   813         del self.local_sp_stack[local_sp_top:]   814         # Reduce the invocation frame pointer stack to refer to an outer frame.   815         del self.invocation_sp_stack[invocation_sp_top:]   816         # Reduce the PC stack to discard all superfluous return addresses.   817         del self.pc_stack[pc_top:]   818         # Remove elements from the handler stacks.   819         del self.handler_pc_stack[-nframes:]   820         del self.handler_local_sp_stack[-nframes:]   821         del self.handler_invocation_sp_stack[-nframes:]   822         del self.handler_stack[-nframes:]   823    824     def CheckException(self, operand, instruction):   825         self.registers[instruction.target] = self.registers["exception"] is not None and \   826             self._CheckInstance(self.registers["exception"], self.registers[instruction.working])   827    828     def TestIdentity(self, operand, instruction):   829         self.registers[instruction.target] = self.registers[instruction.working] == self.registers[instruction.source]   830    831     def TestIdentityAddress(self, addr, instruction):   832         self.registers[instruction.target] = self.registers[instruction.working] == addr   833    834     def InvertBoolean(self, operand, instruction):   835         self.registers[instruction.target] = not self.registers[instruction.source]   836    837     # Common implementation details.   838    839     def _CheckInstance(self, ref, cls):   840         data = self.load(ref)   841         target_data = self.load(cls)   842    843         # Insist on instance vs. class.   844    845         if data.attrcode is None: # absent attrcode == class/module   846             return 0   847    848         if target_data.attrcode is not None: # present attrcode == instance   849             return 0   850    851         # Find the table entry for the descendant.   852    853         element = self.load(self.registers["objlist"] + target_data.classcode + data.attrcode)   854    855         if element is not None:   856             attr_index, static_attr, offset = element   857             return attr_index == data.attrcode   858         else:   859             return 0   860    861     def _MakeObject(self, size, ref):   862         # Load the template.   863         data = self.load(ref)   864         addr = self.new(size)   865         # Save the header, overriding the size.   866         self.save(addr, data.with_size(size))   867         return addr   868    869     def _MakeFragment(self, occupied, size):   870         addr = self.new(size)   871         # Save the header, overriding the size.   872         self.save(addr, FragmentObject(occupied, size))   873         return addr   874    875     def _LoadAddressContextCond(self, context, value, inst_value):   876         # Check the instance context against the target's context.   877         # This provides the context overriding for methods.   878         if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_value, context):   879             # Replace the context with the instance.   880             return DataValue(inst_value, value)   881         else:   882             return DataValue(context, value)   883    884     def _StoreAddressContext(self, location, context, value, source_context, source_value):   885         if source_context is ReplaceableContext:   886             context = value   887         else:   888             context = source_context   889         self.save(location, DataValue(context, source_value))   890    891 # Convenience functions.   892    893 def machine(program, with_builtins=0, debug=0, abort_upon_exception=0):   894     print "Making the image..."   895     code = program.get_image(with_builtins)   896     print "Getting raw structures..."   897     ot = program.get_object_table()   898     pt = program.get_parameter_table()   899     objlist = ot.as_list()   900     paramlist = pt.as_list()   901     print "Getting raw image..."   902     rc = program.get_raw_image()   903     print "Initialising the machine..."   904     importer = program.get_importer()   905     constants = {}   906     for x in (True, False, NotImplemented):   907         constants[x] = importer.get_constant(x).location   908     rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception)   909     library = Library(rm, constants)   910     rm.library = library   911     rm.pc = program.code_location   912     print "Returning program occupying %d locations." % rm.get_program_size()   913     return rm   914    915 # vim: tabstop=4 expandtab shiftwidth=4