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