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