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