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