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