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