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