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 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 value, 48 boolean status value, 49 source value, 50 current result, 51 current exception, 52 current callable 53 """ 54 55 from micropython.program import ReplaceableContext, PlaceholderContext, FragmentObject 56 import operator 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 # Stacks. 102 103 self.pc_stack = [] 104 self.frame_stack = [] 105 self.local_sp_stack = [0] 106 self.invocation_sp_stack = [] 107 self.handler_stack = [len(self.memory) - 1] # final handler is the end of the code 108 self.handler_local_sp_stack = [] 109 self.handler_pc_stack = [] 110 111 # Registers. 112 113 self.instruction = None 114 self.operand = None 115 self.value = None 116 self.status = None 117 self.source = None 118 self.callable = None 119 self.result = None 120 self.exception = None 121 122 # Constants. 123 124 cls = self._get_class("__builtins__", "AttributeError") 125 self.attr_error = cls.location 126 self.attr_error_instance = cls.instance_template_location 127 cls = self._get_class("__builtins__", "TypeError") 128 self.type_error = cls.location 129 self.type_error_instance = cls.instance_template_location 130 131 # Debugging attributes. 132 133 self.breakpoints = set() 134 135 def _get_class(self, module, name): 136 return self._objlist.access(module, name).get_value() 137 138 # Debugging methods. 139 140 def dump(self): 141 print "PC", self.pc, "->", self.load(self.pc) 142 print "PC stack", self.pc_stack 143 print "Frame stack", self.frame_stack 144 print "Local stack pointers", self.local_sp_stack 145 print "Invocation stack pointers", self.invocation_sp_stack 146 print "Handler stack", self.handler_stack 147 print "Handler frame stack", self.handler_local_sp_stack 148 print "Handler PC stack", self.handler_pc_stack 149 print 150 print "Instruction", self.instruction 151 print "Operand", self.operand 152 print "Value", self.value 153 print "Status", self.status 154 print "Source", self.source 155 print "Callable", self.callable 156 print "Result", self.result 157 print "Exception", self.exception 158 159 def show(self): 160 self.show_memory(self.memory, 0) 161 162 def show_pc(self, run_in=10): 163 start = max(0, self.pc - run_in) 164 end = self.pc + run_in 165 memory = self.memory[start:end] 166 self.show_memory(memory, start) 167 168 def show_memory(self, memory, start): 169 for i, x in enumerate(memory): 170 location = start + i 171 if location == self.pc: 172 print "->", 173 else: 174 print " ", 175 print "%5d %r" % (location, x) 176 177 def step(self, dump=0): 178 self.execute() 179 self.show_pc() 180 if dump: 181 self.dump() 182 183 def set_break(self, location): 184 self.breakpoints.add(location) 185 186 # Internal operations. 187 188 def load(self, address): 189 190 "Return the value at the given 'address'." 191 192 try: 193 return self.memory[address] 194 except IndexError: 195 raise IllegalAddress(address) 196 except TypeError: 197 raise IllegalAddress(address) 198 199 def save(self, address, value): 200 201 "Save to the given 'address' the specified 'value'." 202 203 try: 204 self.memory[address] = value 205 except IndexError: 206 raise IllegalAddress(address) 207 except TypeError: 208 raise IllegalAddress(address) 209 210 def new(self, size): 211 212 """ 213 Allocate space of the given 'size', returning the address of the space. 214 """ 215 216 addr = len(self.memory) 217 for i in range(0, size): 218 self.memory.append(None) 219 return addr 220 221 def push_pc(self, data): 222 223 "Push 'data' onto the PC stack." 224 225 self.pc_stack.append(data) 226 227 def pull_pc(self): 228 229 "Pull a value from the PC stack and return it." 230 231 try: 232 return self.pc_stack.pop() 233 except IndexError: 234 raise EmptyPCStack 235 236 def run(self): 237 238 "Execute code in the memory, starting from the current PC address." 239 240 breakpoint = 0 241 242 try: 243 while 1: 244 self.execute() 245 except EmptyPCStack: 246 pass 247 except BreakpointReached: 248 breakpoint = 1 249 250 print "Execution terminated", 251 if self.exception is not None: 252 ref = self.exception 253 addr = self.load(ref + 1) 254 print "with exception:", self.load(ref) 255 print "At address %d: %r" % (addr, self.load(addr)) 256 elif breakpoint: 257 print "with breakpoint." 258 print "At address", self.pc 259 else: 260 print "successfully." 261 262 def test(self, module): 263 264 """ 265 Test the code in the memory by running the code and investigating the 266 contents of variables. Use 'module' to identify result variables. 267 """ 268 269 self.run() 270 success = 1 271 272 if self.exception is None: 273 for name in module.keys(): 274 if name.startswith("result"): 275 label, expected = name.split("_") 276 attr = module[name] 277 278 # NOTE: Assumptions about headers and content made. 279 280 attr_location = module.location + 1 + attr.position 281 context, ref = self.load(attr_location) 282 283 if ref is not None: 284 content = self.load(ref + 1) 285 print label, expected, content 286 success = success and (int(expected) == content) 287 else: 288 print label, expected, "missing" 289 success = 0 290 291 return success 292 else: 293 return 0 294 295 def execute(self): 296 297 "Execute code in the memory at the current PC address." 298 299 if self.pc in self.breakpoints: 300 self.breakpoints.remove(self.pc) 301 raise BreakpointReached 302 303 self.instruction = self.load(self.pc) 304 305 # Process any inputs of the instruction. 306 307 self.process_inputs() 308 309 # Perform the instruction itself. 310 311 next_pc = self.perform(self.instruction) 312 313 # Update the program counter. 314 315 if next_pc is None: 316 self.pc += 1 317 else: 318 self.pc = next_pc 319 320 def get_method(self, instruction): 321 322 "Return the handler method for the given 'instruction'." 323 324 instruction_name = instruction.__class__.__name__ 325 if self.debug: 326 print "%8d %s" % (self.pc, instruction_name) 327 method = getattr(self, instruction_name, None) 328 if method is None: 329 raise IllegalInstruction, (self.pc, instruction_name) 330 return method 331 332 def perform(self, instruction): 333 334 "Perform the 'instruction', returning the next PC value or None." 335 336 self.operand = instruction.get_operand() 337 method = self.get_method(instruction) 338 return method() 339 340 def process_inputs(self): 341 342 """ 343 Process any inputs of the current instruction. This permits any directly 344 connected sub-instructions to produce the effects that separate 345 instructions would otherwise have. 346 """ 347 348 value = self.value 349 if self.instruction.source is not None: 350 self.perform(self.instruction.source) 351 self.source = self.value 352 self.value = value 353 if self.instruction.input is not None: 354 self.perform(self.instruction.input) 355 356 def jump(self, addr, next): 357 358 """ 359 Jump to the subroutine at (or identified by) 'addr'. If 'addr' 360 identifies a library function then invoke the library function and set 361 PC to 'next' afterwards; otherwise, set PC to 'addr'. 362 """ 363 364 # Trap library functions introduced through the use of strings instead 365 # of proper locations. 366 367 if isinstance(addr, str): 368 handler = self.library and self.library.native_functions[addr](self.library) 369 if handler is None: 370 return next 371 else: 372 return handler 373 else: 374 self.push_pc(self.pc + 1) 375 return addr 376 377 # Instructions. 378 379 def LoadConst(self): 380 self.value = self.operand, self.operand 381 382 def LoadClass(self): 383 self.value = PlaceholderContext, self.operand 384 385 def LoadFunction(self): 386 self.value = ReplaceableContext, self.operand 387 388 def LoadName(self): 389 frame = self.local_sp_stack[-1] 390 self.value = self.frame_stack[frame + self.operand] 391 392 def StoreName(self): 393 frame = self.local_sp_stack[-1] 394 self.frame_stack[frame + self.operand] = self.source # uses the source value 395 396 LoadTemp = LoadName 397 398 def StoreTemp(self): 399 frame = self.local_sp_stack[-1] 400 self.frame_stack[frame + self.operand] = self.value 401 402 def LoadAddress(self): 403 # Preserve context (potentially null). 404 self.value = self.load(self.operand) 405 406 def LoadAddressContext(self): 407 context, ref = self.load(self.operand) 408 inst_context, inst_ref = self.value 409 self.value = inst_ref, ref 410 411 def LoadAddressContextCond(self): 412 context, ref = self.load(self.operand) 413 inst_context, inst_ref = self.value 414 self.value = self._LoadAddressContextCond(context, ref, inst_ref) 415 416 def StoreAddress(self): 417 # Preserve context. 418 self.save(self.operand, self.source) 419 420 def StoreAddressContext(self): 421 # Overwrite context if null. 422 context_context, context_ref = self.value 423 source_context, source_ref = self.source 424 if source_context is ReplaceableContext: 425 context = context_ref 426 else: 427 context = source_context 428 self.save(self.operand, (context, source_ref)) 429 430 def MakeInstance(self): 431 size = self.operand 432 context, ref = self.value 433 # NOTE: Referencing the instance template. 434 addr = self._MakeObject(size, ref - 1) 435 # Introduce object as context for the new object. 436 self.value = addr, addr 437 438 def MakeFragment(self): 439 size = self.operand 440 addr = self._MakeFragment(size) 441 # NOTE: Context is not relevant for fragments. 442 self.value = None, addr 443 444 def LoadAttr(self): 445 context, ref = self.value 446 # Retrieved context should already be appropriate for the instance. 447 # NOTE: Adding 1 to skip any header. 448 self.value = self.load(ref + self.operand + 1) 449 450 def StoreAttr(self): 451 context, ref = self.value 452 # Target should already be an instance. 453 # NOTE: Adding 1 to skip any header. 454 self.save(ref + self.operand + 1, self.source) 455 456 def LoadAttrIndex(self): 457 context, ref = self.value 458 data = self.load(ref) 459 element = self.objlist[data.classcode + self.operand] 460 461 if element is not None: 462 attr_index, static_attr, offset = element 463 if attr_index == self.operand: 464 if static_attr: 465 self.value = self.load(offset) # offset is address of class/module attribute 466 else: 467 self.value = self.load(ref + offset) 468 return 469 470 self.exception = self._MakeObject(2, self.attr_error_instance) 471 return self.RaiseException() 472 473 # LoadAttrIndexContext not defined. 474 475 def LoadAttrIndexContextCond(self): 476 inst_context, inst_ref = self.value 477 data = self.load(inst_ref) 478 element = self.objlist[data.classcode + self.operand] 479 480 if element is not None: 481 attr_index, static_attr, offset = element 482 if attr_index == self.operand: 483 if static_attr: 484 loaded_context, loaded_ref = self.load(offset) # offset is address of class/module attribute 485 if data.attrcode is None: # absent attrcode == class/module 486 self.value = loaded_context, loaded_ref 487 else: 488 self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, inst_ref) 489 else: 490 self.value = self.load(inst_ref + offset) 491 return 492 493 self.exception = self._MakeObject(2, self.attr_error_instance) 494 return self.RaiseException() 495 496 def StoreAttrIndex(self): 497 context, ref = self.value 498 data = self.load(ref) 499 element = self.objlist[data.classcode + self.operand] 500 501 if element is not None: 502 attr_index, static_attr, offset = element 503 if attr_index == self.operand: 504 if static_attr: 505 self.exception = self._MakeObject(2, self.type_error_instance) 506 return self.RaiseException() 507 else: 508 self.save(ref + offset, self.source) 509 return 510 511 self.exception = self._MakeObject(2, self.attr_error_instance) 512 return self.RaiseException() 513 514 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 515 516 def MakeFrame(self): 517 self.invocation_sp_stack.append(len(self.frame_stack)) 518 self.frame_stack.extend([None] * self.operand) 519 520 def DropFrame(self): 521 self.local_sp_stack.pop() 522 frame = self.invocation_sp_stack.pop() 523 del self.frame_stack[frame:] # reset stack before call 524 525 def StoreFrame(self): 526 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 527 self.frame_stack[frame + self.operand] = self.value 528 529 def StoreFrameIndex(self): 530 context, ref = self.value 531 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 532 data = self.load(ref) 533 element = self.paramlist[data.funccode + self.operand] 534 535 if element is not None: 536 # NOTE: Need to ensure correct positioning where a context has been generated. 537 param_index, offset = element 538 if param_index == self.operand: 539 self.frame_stack[frame + offset] = self.source 540 return 541 542 self.exception = self._MakeObject(2, self.type_error_instance) 543 return self.RaiseException() 544 545 def LoadCallable(self): 546 context, ref = self.value 547 data = self.load(ref) 548 self.callable = data.codeaddr 549 550 def StoreCallable(self): 551 context, ref = self.value 552 # NOTE: Should improve the representation and permit direct saving. 553 data = self.load(ref) 554 self.save(ref, data.with_callable(self.callable)) 555 556 def LoadContext(self): 557 context, ref = self.value 558 # NOTE: Omission of the context of the context would make things like 559 # NOTE: self() inside methods impossible. 560 self.value = context, context 561 562 def CheckContext(self): 563 self.status = self.value[1] is not ReplaceableContext 564 565 def CheckClass(self): 566 context, ref = self.value 567 if ref in (ReplaceableContext, PlaceholderContext): 568 self.status = 0 569 return 570 571 data = self.load(ref) 572 573 # Classes are not themselves usable as the self argument. 574 # NOTE: This may change at some point. 575 # However, where classes appear as the context, instance 576 # compatibility is required in the first argument. 577 578 self.status = data.attrcode is None # absent attrcode == class 579 580 def CheckFrame(self): 581 (nargs, ndefaults, has_star) = self.operand 582 583 # The frame is actually installed as the locals. 584 # Retrieve the context from the first local. 585 586 frame = self.local_sp_stack[-1] 587 nlocals = len(self.frame_stack[frame:]) 588 589 # NOTE: Not testing (nlocals <= nargs or has_star) due to imprecise 590 # NOTE: invocation frame removal (after frame adjustment). 591 592 if not ((nargs - ndefaults) <= nlocals): 593 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 594 self.exception = self._MakeObject(2, self.type_error_instance) 595 return self.RaiseException() 596 597 def FillDefaults(self): 598 context, ref = self.value 599 (nargs, ndefaults) = self.operand 600 601 # The frame is actually installed as the locals. 602 603 frame = self.local_sp_stack[-1] 604 nlocals = len(self.frame_stack[frame:]) 605 606 # Support population of defaults. 607 # This involves copying the "attributes" of a function into the frame. 608 609 default = nlocals - (nargs - ndefaults) 610 self.frame_stack.extend([None] * (nargs - nlocals)) 611 pos = nlocals 612 613 while pos < nargs: 614 self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header 615 default += 1 616 pos += 1 617 618 def CheckSelf(self): 619 context, ref = self.value 620 context_context, context_ref = self.source 621 622 # Check the details of the proposed context and the target's context. 623 624 self.status = self._CheckInstance(ref, context_ref) 625 626 def JumpInFrame(self): 627 codeaddr = self.callable 628 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 629 630 def JumpWithFrame(self): 631 codeaddr = self.callable 632 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 633 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 634 635 def JumpWithFrameDirect(self): 636 operand = self.operand 637 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 638 return self.jump(operand, self.pc + 1) # return to the instruction after this one 639 640 def ExtendFrame(self): 641 self.frame_stack.extend([None] * self.operand) 642 643 def AdjustFrame(self): 644 self.invocation_sp_stack[-1] += self.operand 645 646 def Return(self): 647 return self.pull_pc() 648 649 def LoadResult(self): 650 self.value = self.result 651 652 def StoreResult(self): 653 self.result = self.value 654 655 def Jump(self): 656 return self.operand 657 658 def JumpIfTrue(self): 659 if self.status: 660 return self.operand 661 662 def JumpIfFalse(self): 663 if not self.status: 664 return self.operand 665 666 def LoadException(self): 667 self.value = self.exception, self.exception 668 669 def StoreException(self): 670 self.exception = self.value[1] 671 672 def ClearException(self): 673 self.exception = None 674 675 def RaiseException(self): 676 # NOTE: Adding the program counter as the first attribute. 677 self.save(self.exception + 1, self.pc) 678 # Jumping to the current handler. 679 if self.abort_upon_exception: 680 raise Exception 681 return self.handler_stack[-1] 682 683 def PushHandler(self): 684 self.handler_stack.append(self.operand) 685 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 686 self.handler_pc_stack.append(len(self.pc_stack)) 687 688 def PopHandler(self): 689 # Reduce the local frame pointer stack to refer to the handler's frame. 690 del self.local_sp_stack[self.handler_local_sp_stack.pop():] 691 # Reduce the PC stack to discard all superfluous return addresses. 692 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 693 self.handler_stack.pop() 694 695 def CheckException(self): 696 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1]) 697 698 def TestIdentity(self): 699 self.status = self.value[1] == self.source[1] 700 701 def TestIdentityAddress(self): 702 self.status = self.value[1] == self.operand 703 704 # LoadBoolean is implemented in the generated code. 705 # StoreBoolean is implemented by testing against the True value. 706 707 def InvertBoolean(self): 708 self.status = not self.status 709 710 # Common implementation details. 711 712 def _CheckInstance(self, ref, cls): 713 data = self.load(ref) 714 target_data = self.load(cls) 715 716 # Insist on instance vs. class. 717 718 if data.attrcode is None: # absent attrcode == class/module 719 return 0 720 721 if target_data.attrcode is not None: # present attrcode == instance 722 return 0 723 724 # Find the table entry for the descendant. 725 726 element = self.objlist[target_data.classcode + data.attrcode] 727 728 if element is not None: 729 attr_index, static_attr, offset = element 730 return attr_index == data.attrcode 731 else: 732 return 0 733 734 def _MakeObject(self, size, ref): 735 # Load the template. 736 data = self.load(ref) 737 addr = self.new(size) 738 # Save the header, overriding the size. 739 self.save(addr, data.with_size(size)) 740 return addr 741 742 def _MakeFragment(self, size): 743 # Reserve twice the amount of space. 744 addr = self.new(size * 2) 745 # Save the header, overriding the size. 746 self.save(addr, FragmentObject(size, size * 2)) 747 return addr 748 749 def _LoadAddressContextCond(self, context, ref, inst_ref): 750 # Check the instance context against the target's context. 751 # This provides the context overriding for methods. 752 if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context): 753 # Replace the context with the instance. 754 return inst_ref, ref 755 else: 756 return context, ref 757 758 class Library: 759 760 "Native function implementations." 761 762 def __init__(self, machine, true_constant, false_constant): 763 764 """ 765 Initialise the library with the 'machine' and the addresses 766 'true_constant' and 'false_constant'. 767 """ 768 769 self.machine = machine 770 self.true_constant = true_constant 771 self.false_constant = false_constant 772 773 # Native class constants. 774 775 cls = self.machine._get_class("__builtins__", "int") 776 self.int_class = cls.location 777 self.int_instance = cls.instance_template_location 778 cls = self.machine._get_class("__builtins__", "list") 779 self.list_class = cls.location 780 self.list_instance = cls.instance_template_location 781 cls = self.machine._get_class("__builtins__", "IndexError") 782 self.index_error = cls.location 783 self.index_error_instance = cls.instance_template_location 784 cls = self.machine._get_class("__builtins__", "tuple") 785 self.tuple_class = cls.location 786 787 self.type_error_instance = self.machine.type_error_instance 788 self.frame_stack = self.machine.frame_stack 789 self.local_sp_stack = self.machine.local_sp_stack 790 791 def builtins_int_arithmetic_op(self, op): 792 frame = self.local_sp_stack[-1] 793 794 # Get operands addresses. 795 796 left_context, left = self.frame_stack[frame] 797 right_context, right = self.frame_stack[frame + 1] 798 799 # Test operand suitability. 800 # NOTE: Support other types. 801 802 if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)): 803 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 804 return self.machine.RaiseException() 805 806 # NOTE: Assume single location for data. 807 808 left_data = left + 1 809 right_data = right + 1 810 811 # Make a new object. 812 813 addr = self.machine._MakeObject(2, self.int_instance) 814 815 # Store the result. 816 # NOTE: The data is considered ready to use. 817 818 self.machine.save(addr + 1, op(self.machine.load(left_data), self.machine.load(right_data))) 819 820 # Return the new object. 821 # Introduce object as context for the new object. 822 823 self.machine.result = addr, addr 824 825 def builtins_int_add(self): 826 return self.builtins_int_arithmetic_op(operator.add) 827 828 def builtins_int_sub(self): 829 return self.builtins_int_arithmetic_op(operator.sub) 830 831 def builtins_int_bool(self): 832 frame = self.local_sp_stack[-1] 833 834 # Get operands addresses. 835 836 left_context, left = self.frame_stack[frame] 837 838 # Test operand suitability. 839 840 if not self.machine._CheckInstance(left, self.int_class): 841 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 842 return self.machine.RaiseException() 843 844 # NOTE: Assume single location for data. 845 846 left_data = left + 1 847 848 # Test the data. 849 # NOTE: The data is considered ready to use. 850 851 if self.machine.load(left_data) != 0: 852 self.machine.result = self.true_constant, self.true_constant 853 else: 854 self.machine.result = self.false_constant, self.false_constant 855 856 def builtins_int_neg(self): 857 frame = self.local_sp_stack[-1] 858 859 # Get operands addresses. 860 861 left_context, left = self.frame_stack[frame] 862 863 # Test operand suitability. 864 865 if not self.machine._CheckInstance(left, self.int_class): 866 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 867 return self.machine.RaiseException() 868 869 # NOTE: Assume single location for data. 870 871 left_data = left + 1 872 873 # Make a new object. 874 875 addr = self.machine._MakeObject(2, self.int_instance) 876 877 # Store the result. 878 # NOTE: The data is considered ready to use. 879 880 self.machine.save(addr + 1, -self.machine.load(left_data)) 881 882 # Return the new object. 883 # Introduce object as context for the new object. 884 885 self.machine.result = addr, addr 886 887 def builtins_int_op(self, op, true_if_incompatible): 888 frame = self.local_sp_stack[-1] 889 890 # Get operands addresses. 891 892 left_context, left = self.frame_stack[frame] 893 right_context, right = self.frame_stack[frame + 1] 894 895 # Test operand suitability. 896 # NOTE: Support other types. 897 # NOTE: Handle comparisons of incompatible types more appropriately. 898 899 if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)): 900 if true_if_incompatible: 901 self.machine.result = self.true_constant, self.true_constant 902 else: 903 self.machine.result = self.false_constant, self.false_constant 904 return 905 906 # NOTE: Assume single location for data. 907 908 left_data = left + 1 909 right_data = right + 1 910 911 # Test the data. 912 # NOTE: The data is considered ready to use. 913 914 if op(self.machine.load(left_data), self.machine.load(right_data)): 915 self.machine.result = self.true_constant, self.true_constant 916 else: 917 self.machine.result = self.false_constant, self.false_constant 918 919 def builtins_int_lt(self): 920 return self.builtins_int_op(operator.lt, 0) 921 922 def builtins_int_le(self): 923 return self.builtins_int_op(operator.le, 0) 924 925 def builtins_int_gt(self): 926 return self.builtins_int_op(operator.gt, 0) 927 928 def builtins_int_ge(self): 929 return self.builtins_int_op(operator.ge, 0) 930 931 def builtins_int_eq(self): 932 return self.builtins_int_op(operator.eq, 0) 933 934 def builtins_int_ne(self): 935 return self.builtins_int_op(operator.ne, 1) 936 937 def builtins_bool_bool(self): 938 frame = self.local_sp_stack[-1] 939 940 # Get operands addresses. 941 942 left_context, left = self.frame_stack[frame] 943 self.machine.result = left, left 944 945 def builtins_list_new(self): 946 frame = self.local_sp_stack[-1] 947 948 # The first parameter should be empty. 949 # NOTE: Specific copying of tuples/lists. 950 951 args_context, args = self.frame_stack[frame + 1] 952 953 # Test operand suitability. 954 955 if self.machine._CheckInstance(args, self.list_class): 956 _x, sequence = self.machine.load(args + 1) 957 header = self.machine.load(sequence) 958 size = header.occupied_size 959 elif self.machine._CheckInstance(args, self.tuple_class): 960 sequence = args 961 header = self.machine.load(sequence) 962 size = header.size 963 else: 964 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 965 return self.machine.RaiseException() 966 967 # Copy the sequence contents. 968 969 new_fragment = self.machine._MakeFragment(size) 970 for i in range(1, size): 971 self.machine.save(new_fragment + i, self.machine.load(sequence + i)) 972 973 # Make the list instance. 974 975 addr = self.machine._MakeObject(2, self.list_instance) 976 self.machine.save(addr + 1, (None, new_fragment)) 977 978 self.machine.result = addr, addr 979 980 def builtins_list_getitem(self): 981 frame = self.local_sp_stack[-1] 982 983 # Get the operand address. 984 985 item_context, item = self.frame_stack[frame + 1] 986 987 # Get the list address. 988 989 obj_context, obj = self.frame_stack[frame] 990 991 # Get the fragment address. 992 # NOTE: Assume single location for header. 993 994 _x, fragment = self.machine.load(obj + 1) 995 996 # Get the fragment header. 997 998 header = self.machine.load(fragment) 999 nelements = header.occupied_size - 1 1000 1001 # NOTE: Assume single location for data and header. 1002 1003 item_pos = self.machine.load(item + 1) 1004 1005 if item_pos >= 0 and item_pos < nelements: 1006 pass 1007 elif item_pos < 0 and item_pos >= -nelements: 1008 item_pos = nelements + item_pos 1009 else: 1010 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 1011 return self.machine.RaiseException() 1012 1013 # NOTE: Assume single location for header. 1014 1015 self.machine.result = self.machine.load(fragment + 1 + item_pos) 1016 1017 def builtins_list_len(self): 1018 frame = self.local_sp_stack[-1] 1019 1020 # Get the list address. 1021 1022 obj_context, obj = self.frame_stack[frame] 1023 1024 # Get the fragment address. 1025 # NOTE: Assume single location for header. 1026 1027 _x, fragment = self.machine.load(obj + 1) 1028 1029 # Get the fragment header. 1030 1031 header = self.machine.load(fragment) 1032 nelements = header.occupied_size - 1 1033 1034 # Make a new object. 1035 1036 addr = self.machine._MakeObject(2, self.int_instance) 1037 1038 # Store the result. 1039 # NOTE: The data is considered ready to use. 1040 1041 self.machine.save(addr + 1, nelements) 1042 1043 # Return the new object. 1044 # Introduce object as context for the new object. 1045 1046 self.machine.result = addr, addr 1047 1048 def builtins_list_append(self): 1049 frame = self.local_sp_stack[-1] 1050 1051 # Get operand address. 1052 1053 arg_context, arg = self.frame_stack[frame + 1] 1054 1055 # Get the list address. 1056 1057 obj_context, obj = self.frame_stack[frame] 1058 1059 # Get the fragment address. 1060 # NOTE: Assume single location for header. 1061 1062 _x, fragment = self.machine.load(obj + 1) 1063 1064 # Get the fragment header. 1065 1066 header = self.machine.load(fragment) 1067 1068 # Attempt to add the reference. 1069 1070 if header.occupied_size < header.allocated_size: 1071 self.machine.save(fragment + header.occupied_size, (arg_context, arg)) 1072 header.occupied_size += 1 1073 else: 1074 1075 # Make a new fragment, maintaining more space than currently 1076 # occupied in order to avoid reallocation. 1077 1078 new_fragment = self.machine._MakeFragment(header.allocated_size + 1) 1079 1080 # Copy existing elements. 1081 1082 for i in range(1, header.allocated_size): 1083 self.machine.save(new_fragment + i, self.machine.load(fragment + i)) 1084 1085 self.machine.save(new_fragment + header.allocated_size, (arg_context, arg)) 1086 1087 # Set the new fragment in the object. 1088 # NOTE: The old fragment could be deallocated. 1089 1090 self.machine.save(obj + 1, (None, new_fragment)) 1091 1092 def builtins_tuple_len(self): 1093 frame = self.local_sp_stack[-1] 1094 1095 # Get the tuple address. 1096 1097 obj_context, obj = self.frame_stack[frame] 1098 1099 # Get the header. 1100 # NOTE: Assume single location for header. 1101 1102 header = self.machine.load(obj) 1103 nelements = header.size - 1 1104 1105 # Make a new object. 1106 1107 addr = self.machine._MakeObject(2, self.int_instance) 1108 1109 # Store the result. 1110 # NOTE: The data is considered ready to use. 1111 1112 self.machine.save(addr + 1, nelements) 1113 1114 # Return the new object. 1115 # Introduce object as context for the new object. 1116 1117 self.machine.result = addr, addr 1118 1119 def builtins_tuple_getitem(self): 1120 frame = self.local_sp_stack[-1] 1121 1122 # Get the operand address. 1123 1124 item_context, item = self.frame_stack[frame + 1] 1125 1126 # Get the tuple address. 1127 1128 obj_context, obj = self.frame_stack[frame] 1129 1130 # Get the header. 1131 # NOTE: Assume single location for header. 1132 1133 header = self.machine.load(obj) 1134 nelements = header.size - 1 1135 1136 # NOTE: Assume single location for data and header. 1137 1138 item_pos = self.machine.load(item + 1) 1139 1140 if item_pos >= 0 and item_pos < nelements: 1141 pass 1142 elif item_pos < 0 and item_pos >= -nelements: 1143 item_pos = nelements + item_pos 1144 else: 1145 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 1146 return self.machine.RaiseException() 1147 1148 # NOTE: Assume single location for header. 1149 1150 self.machine.result = self.machine.load(obj + 1 + item_pos) 1151 1152 def builtins_object_init(self): 1153 pass 1154 1155 native_functions = { 1156 1157 # Native method implementations: 1158 1159 "__builtins__.int.__add__" : builtins_int_add, 1160 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 1161 "__builtins__.int.__sub__" : builtins_int_sub, 1162 "__builtins__.int.__iadd__" : builtins_int_add, 1163 "__builtins__.int.__bool__" : builtins_int_bool, 1164 "__builtins__.int.__neg__" : builtins_int_neg, 1165 "__builtins__.int.__lt__" : builtins_int_lt, 1166 "__builtins__.int.__le__" : builtins_int_le, 1167 "__builtins__.int.__gt__" : builtins_int_gt, 1168 "__builtins__.int.__ge__" : builtins_int_ge, 1169 "__builtins__.int.__eq__" : builtins_int_eq, 1170 "__builtins__.int.__ne__" : builtins_int_ne, 1171 "__builtins__.bool.__bool__" : builtins_bool_bool, 1172 "__builtins__.list.__getitem__" : builtins_list_getitem, 1173 "__builtins__.list.__len__" : builtins_list_len, 1174 "__builtins__.list.append" : builtins_list_append, 1175 "__builtins__.tuple.__len__" : builtins_tuple_len, 1176 "__builtins__.tuple.__getitem__" : builtins_tuple_getitem, 1177 1178 # Native initialisers: 1179 1180 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 1181 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 1182 1183 # Native instantiators: 1184 1185 "__builtins__.list" : builtins_list_new, 1186 } 1187 1188 # Convenience functions. 1189 1190 def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): 1191 print "Making the image..." 1192 code = program.get_image(with_builtins) 1193 print "Getting raw structures..." 1194 ot = program.get_object_table() 1195 pt = program.get_parameter_table() 1196 objlist = ot.as_list() 1197 paramlist = pt.as_list() 1198 print "Getting raw image..." 1199 rc = program.get_raw_image() 1200 print "Initialising the machine..." 1201 importer = program.get_importer() 1202 true_constant = importer.get_constant(True).location 1203 false_constant = importer.get_constant(False).location 1204 rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception) 1205 library = Library(rm, true_constant, false_constant) 1206 rm.library = library 1207 rm.pc = program.code_location 1208 print "Returning program occupying %d locations." % len(rm.memory) 1209 return rm 1210 1211 # vim: tabstop=4 expandtab shiftwidth=4