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