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