micropython

rsvp.py

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