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