micropython

rsvp.py

461:0c488107ea06
2011-08-30 Paul Boddie Added a method to the machine to indicate the size of a program.
     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_memory(self, memory, coverage, start):   224         for i, (c, x) in enumerate(map(None, coverage, memory)):   225             location = start + i   226             if location == self.pc:   227                 print "->",   228             elif location in self.pc_stack:   229                 print "..",   230             else:   231                 print "  ",   232             print "%-5s %5d %r" % (c or "", location, x)   233    234     def step(self, dump=0):   235         self.execute()   236         self.show_pc()   237         if dump:   238             self.dump()   239    240     def set_break(self, location):   241         self.breakpoints.add(location)   242    243     def up(self):   244         retaddr = self.pc_stack[-1]   245         self.set_break(retaddr)   246         self.run()   247    248     # Internal operations.   249    250     def load(self, address):   251    252         "Return the value at the given 'address'."   253    254         try:   255             return self.memory[address]   256         except IndexError:   257             raise IllegalAddress(address)   258         except TypeError:   259             raise IllegalAddress(address)   260    261     def save(self, address, value):   262    263         "Save to the given 'address' the specified 'value'."   264    265         try:   266             self.memory[address] = value   267         except IndexError:   268             raise IllegalAddress(address)   269         except TypeError:   270             raise IllegalAddress(address)   271    272     def new(self, size):   273    274         """   275         Allocate space of the given 'size', returning the address of the space.   276         """   277    278         addr = len(self.memory)   279         for i in range(0, size):   280             self.memory.append(None)   281         return addr   282    283     def push_pc(self, data):   284    285         "Push 'data' onto the PC stack."   286    287         self.pc_stack.append(data)   288    289     def pull_pc(self):   290    291         "Pull a value from the PC stack and return it."   292    293         try:   294             return self.pc_stack.pop()   295         except IndexError:   296             raise EmptyPCStack   297    298     def run(self):   299    300         "Execute code in the memory, starting from the current PC address."   301    302         breakpoint = 0   303    304         try:   305             while 1:   306                 self.execute()   307         except EmptyPCStack:   308             pass   309         except BreakpointReached:   310             breakpoint = 1   311    312         print "Execution terminated",   313         if breakpoint:   314             print "with breakpoint."   315             print "At address", self.pc   316         elif self.registers["exception"] is not None:   317             ref = self.registers["exception"]   318             addr = self.load(ref + Library.instance_data_offset)   319             print "with exception:", self.load(ref)   320             print "At address %d: %r" % (addr, self.load(addr))   321         else:   322             print "successfully."   323         print "After", self.counter, "instructions at cost", self.cost, "units."   324    325     def test(self, module):   326    327         """   328         Test the code in the memory by running the code and investigating the   329         contents of variables. Use 'module' to identify result variables.   330         """   331    332         self.run()   333         success = 1   334    335         if self.registers["exception"] is None:   336             for name in module.keys():   337                 if name.startswith("result"):   338                     label, expected = name.split("_")   339                     attr = module[name]   340    341                     # Need to skip the header to get at the members.   342    343                     attr_location = module.location + Library.instance_data_offset + attr.position   344                     value = self.load(attr_location)   345    346                     if value is not None:   347                         content = self.load(value.ref + Library.instance_data_offset)   348                         print label, expected, content   349                         success = success and (int(expected) == content)   350                     else:   351                         print label, expected, "missing"   352                         success = 0   353    354             return success   355         else:   356             return 0   357    358     def execute(self):   359    360         "Execute code in the memory at the current PC address."   361    362         if self.pc in self.breakpoints:   363             self.breakpoints.remove(self.pc)   364             raise BreakpointReached   365    366         self.instruction = self.load(self.pc)   367    368         # Perform the instruction itself.   369    370         next_pc = self.perform(self.instruction)   371    372         # Update the coverage.   373    374         self.coverage[self.pc] += 1   375    376         # Update the program counter.   377    378         if next_pc is None:   379             self.pc += 1   380         else:   381             self.pc = next_pc   382    383     def get_method(self, instruction):   384    385         "Return the handler method for the given 'instruction'."   386    387         instruction_name = instruction.__class__.__name__   388         if self.debug:   389             print "%8d %s" % (self.pc, instruction_name)   390         method = getattr(self, instruction_name, None)   391         if method is None:   392             raise IllegalInstruction, (self.pc, instruction_name)   393         return method   394    395     def perform(self, instruction):   396    397         "Perform the 'instruction', returning the next PC value or None."   398    399         self.counter += 1   400         self.cost += instruction.cost   401         operand = instruction.get_operand()   402         method = self.get_method(instruction)   403         return method(operand, instruction)   404    405     def jump(self, addr, next):   406    407         """   408         Jump to the subroutine at (or identified by) 'addr'. If 'addr'   409         identifies a library function then invoke the library function and set   410         PC to 'next' afterwards; otherwise, set PC to 'addr'.   411         """   412    413         # Trap library functions introduced through the use of strings instead   414         # of proper locations.   415    416         if isinstance(addr, str):   417             handler = self.library and self.library.native_functions[addr](self.library)   418             if handler is None:   419                 return next   420             else:   421                 return handler   422         else:   423             self.push_pc(self.pc + 1)   424             return addr   425    426     # Helper methods.   427    428     def context_of(self, register):   429         return "%s_context" % register   430    431     def load_from_frame(self, operand):   432         frame = self.local_sp_stack[-1]   433         return self.frame_stack[frame + operand]   434    435     # Low-level instructions.   436    437     def LoadImmediate(self, operand, target):   438         self.registers[target] = operand   439    440     def LoadMemory(self, operand, target, source):   441         self.registers[target] = self.load(   442             self.registers[source] + (operand is not None and operand or 0)   443             )   444    445     # Instructions.   446    447     def Transfer(self, operand, instruction):   448         self.registers[instruction.target] = self.registers[instruction.source]   449    450     def LoadConst(self, operand, instruction):   451         self.LoadImmediate(operand, self.context_of(instruction.target))   452         self.LoadImmediate(operand, instruction.target)   453    454     def LoadClass(self, operand, instruction):   455         self.LoadImmediate(PlaceholderContext, self.context_of(instruction.target))   456         self.LoadImmediate(operand, instruction.target)   457    458     def LoadFunction(self, operand, instruction):   459         self.LoadImmediate(ReplaceableContext, self.context_of(instruction.target))   460         self.LoadImmediate(operand, instruction.target)   461    462     def LoadName(self, operand, instruction):   463         data = self.load_from_frame(operand)   464         self.LoadImmediate(data.context, self.context_of(instruction.target))   465         self.LoadImmediate(data.ref, instruction.target)   466    467     def StoreName(self, operand, instruction):   468         frame = self.local_sp_stack[-1]   469         self.frame_stack[frame + operand] = DataValue(   470             self.registers[self.context_of(instruction.source)],   471             self.registers[instruction.source])   472    473     LoadTemp = LoadName   474    475     def StoreTemp(self, operand, instruction):   476         frame = self.local_sp_stack[-1]   477         self.frame_stack[frame + operand] = DataValue(   478             self.registers[self.context_of(instruction.working)],   479             self.registers[instruction.working])   480    481     def LoadAddress(self, operand, instruction):   482         # Preserve context (potentially null).   483         data = self.load(operand)   484         self.LoadImmediate(data.context, self.context_of(instruction.target))   485         self.LoadImmediate(data.ref, instruction.target)   486    487     def LoadAddressContext(self, operand, instruction):   488         # Value becomes context.   489         data = self.load(operand)   490         self.LoadImmediate(self.registers[instruction.working], self.context_of(instruction.target))   491         self.LoadImmediate(data.ref, instruction.target)   492    493     def LoadAddressContextCond(self, operand, instruction):   494         data = self.load(operand)   495         data = self._LoadAddressContextCond(data.context, data.ref, self.registers[instruction.working])   496         self.LoadImmediate(data.context, self.context_of(instruction.target))   497         self.LoadImmediate(data.ref, instruction.target)   498    499     def StoreAddress(self, operand, instruction):   500         # Preserve context.   501         self.save(operand, DataValue(   502             self.registers[self.context_of(instruction.source)],   503             self.registers[instruction.source]))   504    505     def StoreAddressContext(self, operand, instruction):   506         # Overwrite context if null.   507         self._StoreAddressContext(operand,   508             self.registers[self.context_of(instruction.working)],   509             self.registers[instruction.working],   510             self.registers[self.context_of(instruction.source)],   511             self.registers[instruction.source])   512    513     def MakeInstance(self, size, instruction):   514         # NOTE: Referencing the instance template.   515         addr = self._MakeObject(size, self.registers[instruction.working] - Library.instance_template_size)   516         # Introduce object as context for the new object.   517         self.LoadImmediate(addr, self.context_of(instruction.target))   518         self.LoadImmediate(addr, instruction.target)   519    520     def MakeFragment(self, size, instruction):   521         # Reserve twice the amount of space.   522         addr = self._MakeFragment(size, size * 2)   523         # NOTE: Context is not relevant for fragments.   524         self.LoadImmediate(None, self.context_of(instruction.target))   525         self.LoadImmediate(addr, instruction.target)   526    527     def LoadAttr(self, pos, instruction):   528         # Retrieved context should already be appropriate for the instance.   529         # Skip any header.   530         data = self.load(self.registers[instruction.working] + Library.instance_data_offset + pos)   531         self.LoadImmediate(data.context, self.context_of(instruction.target))   532         self.LoadImmediate(data.ref, instruction.target)   533    534     def StoreAttr(self, pos, instruction):   535         # Target should already be an instance.   536         # Skip any header.   537         self.save(self.registers[instruction.working] + Library.instance_data_offset + pos , DataValue(   538             self.registers[self.context_of(instruction.source)],   539             self.registers[instruction.source]))   540    541     def LoadAttrIndex(self, index, instruction):   542         data = self.load(self.registers[instruction.working])   543         element = self.load(self.registers["objlist"] + data.classcode + index)   544    545         if element is not None:   546             attr_index, static_attr, offset = element   547             if attr_index == index:   548                 if static_attr:   549                     data = self.load(offset) # offset is address of class/module attribute   550                 else:   551                     data = self.load(self.registers[instruction.working] + offset)   552                 self.LoadImmediate(data.context, self.context_of(instruction.target))   553                 self.LoadImmediate(data.ref, instruction.target)   554                 return   555    556         self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance)   557         return self.RaiseException()   558    559     # LoadAttrIndexContext not defined.   560    561     def LoadAttrIndexContextCond(self, index, instruction):   562         data = self.load(self.registers[instruction.working])   563         element = self.load(self.registers["objlist"] + data.classcode + index)   564    565         if element is not None:   566             attr_index, static_attr, offset = element   567             if attr_index == index:   568                 if static_attr:   569                     loaded_data = self.load(offset) # offset is address of class/module attribute   570                     # Absent attrcode == class/module.   571                     if data.attrcode is not None:   572                         loaded_data = self._LoadAddressContextCond(   573                             loaded_data.context, loaded_data.ref,   574                             self.registers[instruction.working])   575                 else:   576                     loaded_data = self.load(self.registers[instruction.working] + offset)   577                 self.LoadImmediate(loaded_data.context, self.context_of(instruction.target))   578                 self.LoadImmediate(loaded_data.ref, instruction.target)   579                 return   580    581         self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance)   582         return self.RaiseException()   583    584     def StoreAttrIndex(self, index, instruction):   585         data = self.load(self.registers[instruction.working])   586         element = self.load(self.registers["objlist"] + data.classcode + index)   587    588         if element is not None:   589             attr_index, static_attr, offset = element   590             if attr_index == index:   591                 if static_attr:   592                     self._StoreAddressContext(offset,   593                         self.registers[self.context_of(instruction.working)],   594                         self.registers[instruction.working],   595                         self.registers[self.context_of(instruction.source)],   596                         self.registers[instruction.source])   597                     return   598                 else:   599                     self.save(self.registers[instruction.working] + offset, DataValue(   600                         self.registers[self.context_of(instruction.source)],   601                         self.registers[instruction.source]))   602                     return   603    604         self.registers["exception"] = self._MakeObject(Library.instance_size, self.attr_error_instance)   605         return self.RaiseException()   606    607     # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden.   608    609     def MakeFrame(self, size, instruction):   610         self.invocation_sp_stack.append(len(self.frame_stack))   611         self.frame_stack.extend([None] * size)   612    613     def DropFrame(self, operand, instruction):   614         self.local_sp_stack.pop()   615         frame = self.invocation_sp_stack.pop()   616         del self.frame_stack[frame:] # reset stack before call   617    618     def StoreFrame(self, pos, instruction):   619         frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame   620         self.frame_stack[frame + pos] = DataValue(   621             self.registers[self.context_of(instruction.working)],   622             self.registers[instruction.working])   623    624     def StoreFrameIndex(self, index, instruction):   625         frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame   626         data = self.load(self.registers[instruction.working])   627         element = self.load(self.registers["paramlist"] + data.funccode + index)   628    629         if element is not None:   630             # NOTE: Need to ensure correct positioning where a context has been generated.   631             param_index, offset = element   632             if param_index == index:   633                 self.frame_stack[frame + offset] = DataValue(   634                     self.registers[self.context_of(instruction.source)],   635                     self.registers[instruction.source])   636                 return   637    638         self.registers["exception"] = self._MakeObject(Library.instance_size, self.type_error_instance)   639         return self.RaiseException()   640    641     def LoadCallable(self, operand, instruction):   642         data = self.load(self.registers[instruction.working])   643         self.registers[instruction.target] = data.codeaddr   644    645     def StoreCallable(self, operand, instruction):   646         # NOTE: Should improve the representation and permit direct saving.   647         data = self.load(self.registers[instruction.working])   648         self.save(self.registers[instruction.working], data.with_callable(self.registers[instruction.source]))   649    650     def CheckContext(self, operand, instruction):   651         self.registers[instruction.target] = self.registers[instruction.working] is not ReplaceableContext   652    653     def CheckClass(self, operand, instruction):   654         if self.registers[instruction.working] in (ReplaceableContext, PlaceholderContext):   655             self.registers[instruction.target] = 0   656             return   657    658         data = self.load(self.registers[instruction.working])   659    660         # Classes are not themselves usable as the self argument.   661         # NOTE: This may change at some point.   662         # However, where classes appear as the context, instance   663         # compatibility is required in the first argument.   664    665         self.registers[instruction.target] = data.attrcode is None # absent attrcode == class   666    667     def CheckFrame(self, operand, instruction):   668         (nargs, ndefaults) = operand   669    670         # The frame is actually installed as the locals.   671         # Retrieve the context from the first local.   672    673         frame = self.local_sp_stack[-1]   674         nlocals = len(self.frame_stack[frame:])   675    676         if not ((nargs - ndefaults) <= nlocals):   677             raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (operand, nargs - ndefaults, nlocals, nargs)   678             self.registers["exception"] = self._MakeObject(Library.instance_size, self.type_error_instance)   679             return self.RaiseException()   680    681     def CheckExtra(self, nargs, instruction):   682    683         # The frame is actually installed as the locals.   684         # Retrieve the context from the first local.   685    686         frame = self.local_sp_stack[-1]   687         nlocals = len(self.frame_stack[frame:])   688    689         # Provide the extra star parameter if necessary.   690    691         if nlocals == nargs:   692             self.frame_stack.extend([None]) # ExtendFrame(1)   693    694     def FillDefaults(self, operand, instruction):   695         (nargs, ndefaults) = operand   696    697         # The frame is actually installed as the locals.   698    699         frame = self.local_sp_stack[-1]   700         nlocals = len(self.frame_stack[frame:])   701    702         # Support population of defaults.   703         # This involves copying the "attributes" of a function into the frame.   704    705         default = nlocals - (nargs - ndefaults)   706         self.frame_stack.extend([None] * (nargs - nlocals))   707         pos = nlocals   708    709         while pos < nargs:   710    711             # Skip any header.   712    713             self.frame_stack[frame + pos] = self.load(self.registers[instruction.working] + Library.instance_data_offset + default)   714             default += 1   715             pos += 1   716    717     def CopyExtra(self, start, instruction):   718    719         # The frame is the source of the extra arguments.   720    721         frame = self.local_sp_stack[-1]   722         nlocals = len(self.frame_stack[frame:])   723    724         # Make a tuple to hold the arguments.   725    726         ref = self._MakeObject(nlocals - start + 1, self.tuple_instance)   727    728         extra = 0   729         pos = start   730    731         while pos < nlocals:   732    733             # Skip any header.   734    735             self.save(ref + Library.instance_data_offset + extra, self.frame_stack[frame + pos])   736             extra += 1   737             pos += 1   738    739         self.LoadImmediate(ref, self.context_of(instruction.working))   740         self.LoadImmediate(ref, instruction.working)   741    742     def CheckInstance(self, operand, instruction):   743         # For the 'self' parameter in an invoked function, the proposed context   744         # ('self') is checked against the target's context.   745         self.registers[instruction.target] = self._CheckInstance(   746             self.registers[instruction.working],   747             self.registers[instruction.source])   748    749     def JumpInFrame(self, operand, instruction):   750         codeaddr = self.registers[instruction.working]   751         return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one   752    753     def JumpWithFrame(self, operand, instruction):   754         codeaddr = self.registers[instruction.working]   755         self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame   756         return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one   757    758     def JumpWithFrameDirect(self, addr, instruction):   759         self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame   760         return self.jump(addr, self.pc + 1) # return to the instruction after this one   761    762     def ExtendFrame(self, size, instruction):   763         self.frame_stack.extend([None] * size)   764    765     def AdjustFrame(self, size, instruction):   766         self.invocation_sp_stack[-1] += size   767    768     def Return(self, operand, instruction):   769         return self.pull_pc()   770    771     def Jump(self, addr, instruction):   772         return addr   773    774     def JumpIfTrue(self, addr, instruction):   775         if self.registers[instruction.working]:   776             return addr   777    778     def JumpIfFalse(self, addr, instruction):   779         if not self.registers[instruction.working]:   780             return addr   781    782     def ClearException(self, operand, instruction):   783         self.LoadImmediate(None, instruction.target)   784    785     def RaiseException(self, operand=None, instruction=None):   786         # NOTE: Adding the program counter as the first attribute after __class__.   787         self.save(self.registers["exception"] + Library.instance_data_offset, self.pc)   788         # Jumping to the current handler.   789         if self.abort_upon_exception:   790             raise Exception   791         return self.handler_stack[-1]   792    793     def PushHandler(self, addr, instruction):   794         self.handler_stack.append(addr)   795         self.handler_local_sp_stack.append(len(self.local_sp_stack))   796         self.handler_invocation_sp_stack.append(len(self.invocation_sp_stack))   797         self.handler_pc_stack.append(len(self.pc_stack))   798    799     def PopHandler(self, nframes, instruction):   800         # Get the new local frame pointer and PC stack references.   801         local_sp_top = self.handler_local_sp_stack[-nframes]   802         invocation_sp_top = self.handler_invocation_sp_stack[-nframes]   803         pc_top = self.handler_pc_stack[-nframes]   804         # Reduce the local frame pointer stack to refer to the handler's frame.   805         del self.local_sp_stack[local_sp_top:]   806         # Reduce the invocation frame pointer stack to refer to an outer frame.   807         del self.invocation_sp_stack[invocation_sp_top:]   808         # Reduce the PC stack to discard all superfluous return addresses.   809         del self.pc_stack[pc_top:]   810         # Remove elements from the handler stacks.   811         del self.handler_pc_stack[-nframes:]   812         del self.handler_local_sp_stack[-nframes:]   813         del self.handler_invocation_sp_stack[-nframes:]   814         del self.handler_stack[-nframes:]   815    816     def CheckException(self, operand, instruction):   817         self.registers[instruction.target] = self.registers["exception"] is not None and \   818             self._CheckInstance(self.registers["exception"], self.registers[instruction.working])   819    820     def TestIdentity(self, operand, instruction):   821         self.registers[instruction.target] = self.registers[instruction.working] == self.registers[instruction.source]   822    823     def TestIdentityAddress(self, addr, instruction):   824         self.registers[instruction.target] = self.registers[instruction.working] == addr   825    826     def InvertBoolean(self, operand, instruction):   827         self.registers[instruction.target] = not self.registers[instruction.source]   828    829     # Common implementation details.   830    831     def _CheckInstance(self, ref, cls):   832         data = self.load(ref)   833         target_data = self.load(cls)   834    835         # Insist on instance vs. class.   836    837         if data.attrcode is None: # absent attrcode == class/module   838             return 0   839    840         if target_data.attrcode is not None: # present attrcode == instance   841             return 0   842    843         # Find the table entry for the descendant.   844    845         element = self.load(self.registers["objlist"] + target_data.classcode + data.attrcode)   846    847         if element is not None:   848             attr_index, static_attr, offset = element   849             return attr_index == data.attrcode   850         else:   851             return 0   852    853     def _MakeObject(self, size, ref):   854         # Load the template.   855         data = self.load(ref)   856         addr = self.new(size)   857         # Save the header, overriding the size.   858         self.save(addr, data.with_size(size))   859         return addr   860    861     def _MakeFragment(self, occupied, size):   862         addr = self.new(size)   863         # Save the header, overriding the size.   864         self.save(addr, FragmentObject(occupied, size))   865         return addr   866    867     def _LoadAddressContextCond(self, context, value, inst_value):   868         # Check the instance context against the target's context.   869         # This provides the context overriding for methods.   870         if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_value, context):   871             # Replace the context with the instance.   872             return DataValue(inst_value, value)   873         else:   874             return DataValue(context, value)   875    876     def _StoreAddressContext(self, location, context, value, source_context, source_value):   877         if source_context is ReplaceableContext:   878             context = value   879         else:   880             context = source_context   881         self.save(location, DataValue(context, source_value))   882    883 # Convenience functions.   884    885 def machine(program, with_builtins=0, debug=0, abort_upon_exception=0):   886     print "Making the image..."   887     code = program.get_image(with_builtins)   888     print "Getting raw structures..."   889     ot = program.get_object_table()   890     pt = program.get_parameter_table()   891     objlist = ot.as_list()   892     paramlist = pt.as_list()   893     print "Getting raw image..."   894     rc = program.get_raw_image()   895     print "Initialising the machine..."   896     importer = program.get_importer()   897     constants = {}   898     for x in (True, False, NotImplemented):   899         constants[x] = importer.get_constant(x).location   900     rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception)   901     library = Library(rm, constants)   902     rm.library = library   903     rm.pc = program.code_location   904     print "Returning program occupying %d locations." % rm.get_program_size()   905     return rm   906    907 # vim: tabstop=4 expandtab shiftwidth=4