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.machine._get_class("__builtins__", "tuple") 131 self.tuple_class = cls.location 132 self.tuple_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 CopyExtra(self): 622 start = self.operand 623 624 # The frame is the source of the extra arguments. 625 626 frame = self.local_sp_stack[-1] 627 nlocals = len(self.frame_stack[frame:]) 628 629 # Make a tuple to hold the arguments. 630 631 ref = self._MakeObject(nlocals - start + 1, self.tuple_instance) 632 633 extra = 0 634 pos = start 635 636 while pos < nlocals: 637 self.save(ref + extra + 1, self.frame_stack[frame + pos]) # skip header when storing 638 extra += 1 639 pos += 1 640 641 self.value = ref, ref 642 643 def CheckSelf(self): 644 context, ref = self.value 645 context_context, context_ref = self.source 646 647 # Check the details of the proposed context and the target's context. 648 649 self.status = self._CheckInstance(ref, context_ref) 650 651 def JumpInFrame(self): 652 codeaddr = self.callable 653 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 654 655 def JumpWithFrame(self): 656 codeaddr = self.callable 657 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 658 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 659 660 def JumpWithFrameDirect(self): 661 operand = self.operand 662 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 663 return self.jump(operand, self.pc + 1) # return to the instruction after this one 664 665 def ExtendFrame(self): 666 self.frame_stack.extend([None] * self.operand) 667 668 def AdjustFrame(self): 669 self.invocation_sp_stack[-1] += self.operand 670 671 def Return(self): 672 return self.pull_pc() 673 674 def LoadResult(self): 675 self.value = self.result 676 677 def StoreResult(self): 678 self.result = self.value 679 680 def Jump(self): 681 return self.operand 682 683 def JumpIfTrue(self): 684 if self.status: 685 return self.operand 686 687 def JumpIfFalse(self): 688 if not self.status: 689 return self.operand 690 691 def LoadException(self): 692 self.value = self.exception, self.exception 693 694 def StoreException(self): 695 self.exception = self.value[1] 696 697 def ClearException(self): 698 self.exception = None 699 700 def RaiseException(self): 701 # NOTE: Adding the program counter as the first attribute. 702 self.save(self.exception + 1, self.pc) 703 # Jumping to the current handler. 704 if self.abort_upon_exception: 705 raise Exception 706 return self.handler_stack[-1] 707 708 def PushHandler(self): 709 self.handler_stack.append(self.operand) 710 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 711 self.handler_pc_stack.append(len(self.pc_stack)) 712 713 def PopHandler(self): 714 # Reduce the local frame pointer stack to refer to the handler's frame. 715 del self.local_sp_stack[self.handler_local_sp_stack.pop():] 716 # Reduce the PC stack to discard all superfluous return addresses. 717 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 718 self.handler_stack.pop() 719 720 def CheckException(self): 721 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1]) 722 723 def TestIdentity(self): 724 self.status = self.value[1] == self.source[1] 725 726 def TestIdentityAddress(self): 727 self.status = self.value[1] == self.operand 728 729 # LoadBoolean is implemented in the generated code. 730 # StoreBoolean is implemented by testing against the True value. 731 732 def InvertBoolean(self): 733 self.status = not self.status 734 735 # Common implementation details. 736 737 def _CheckInstance(self, ref, cls): 738 data = self.load(ref) 739 target_data = self.load(cls) 740 741 # Insist on instance vs. class. 742 743 if data.attrcode is None: # absent attrcode == class/module 744 return 0 745 746 if target_data.attrcode is not None: # present attrcode == instance 747 return 0 748 749 # Find the table entry for the descendant. 750 751 element = self.objlist[target_data.classcode + data.attrcode] 752 753 if element is not None: 754 attr_index, static_attr, offset = element 755 return attr_index == data.attrcode 756 else: 757 return 0 758 759 def _MakeObject(self, size, ref): 760 # Load the template. 761 data = self.load(ref) 762 addr = self.new(size) 763 # Save the header, overriding the size. 764 self.save(addr, data.with_size(size)) 765 return addr 766 767 def _MakeFragment(self, size): 768 # Reserve twice the amount of space. 769 addr = self.new(size * 2) 770 # Save the header, overriding the size. 771 self.save(addr, FragmentObject(size, size * 2)) 772 return addr 773 774 def _LoadAddressContextCond(self, context, ref, inst_ref): 775 # Check the instance context against the target's context. 776 # This provides the context overriding for methods. 777 if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context): 778 # Replace the context with the instance. 779 return inst_ref, ref 780 else: 781 return context, ref 782 783 class Library: 784 785 "Native function implementations." 786 787 def __init__(self, machine, true_constant, false_constant): 788 789 """ 790 Initialise the library with the 'machine' and the addresses 791 'true_constant' and 'false_constant'. 792 """ 793 794 self.machine = machine 795 self.true_constant = true_constant 796 self.false_constant = false_constant 797 798 # Native class constants. 799 800 cls = self.machine._get_class("__builtins__", "int") 801 self.int_class = cls.location 802 self.int_instance = cls.instance_template_location 803 cls = self.machine._get_class("__builtins__", "list") 804 self.list_class = cls.location 805 self.list_instance = cls.instance_template_location 806 cls = self.machine._get_class("__builtins__", "IndexError") 807 self.index_error = cls.location 808 self.index_error_instance = cls.instance_template_location 809 810 self.tuple_class = self.machine.tuple_class 811 self.type_error_instance = self.machine.type_error_instance 812 813 self.frame_stack = self.machine.frame_stack 814 self.local_sp_stack = self.machine.local_sp_stack 815 816 def builtins_int_arithmetic_op(self, op): 817 frame = self.local_sp_stack[-1] 818 819 # Get operands addresses. 820 821 left_context, left = self.frame_stack[frame] 822 right_context, right = self.frame_stack[frame + 1] 823 824 # Test operand suitability. 825 # NOTE: Support other types. 826 827 if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)): 828 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 829 return self.machine.RaiseException() 830 831 # NOTE: Assume single location for data. 832 833 left_data = left + 1 834 right_data = right + 1 835 836 # Make a new object. 837 838 addr = self.machine._MakeObject(2, self.int_instance) 839 840 # Store the result. 841 # NOTE: The data is considered ready to use. 842 843 self.machine.save(addr + 1, op(self.machine.load(left_data), self.machine.load(right_data))) 844 845 # Return the new object. 846 # Introduce object as context for the new object. 847 848 self.machine.result = addr, addr 849 850 def builtins_int_add(self): 851 return self.builtins_int_arithmetic_op(operator.add) 852 853 def builtins_int_sub(self): 854 return self.builtins_int_arithmetic_op(operator.sub) 855 856 def builtins_int_bool(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 # Test the data. 874 # NOTE: The data is considered ready to use. 875 876 if self.machine.load(left_data) != 0: 877 self.machine.result = self.true_constant, self.true_constant 878 else: 879 self.machine.result = self.false_constant, self.false_constant 880 881 def builtins_int_neg(self): 882 frame = self.local_sp_stack[-1] 883 884 # Get operands addresses. 885 886 left_context, left = self.frame_stack[frame] 887 888 # Test operand suitability. 889 890 if not self.machine._CheckInstance(left, self.int_class): 891 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 892 return self.machine.RaiseException() 893 894 # NOTE: Assume single location for data. 895 896 left_data = left + 1 897 898 # Make a new object. 899 900 addr = self.machine._MakeObject(2, self.int_instance) 901 902 # Store the result. 903 # NOTE: The data is considered ready to use. 904 905 self.machine.save(addr + 1, -self.machine.load(left_data)) 906 907 # Return the new object. 908 # Introduce object as context for the new object. 909 910 self.machine.result = addr, addr 911 912 def builtins_int_op(self, op, true_if_incompatible): 913 frame = self.local_sp_stack[-1] 914 915 # Get operands addresses. 916 917 left_context, left = self.frame_stack[frame] 918 right_context, right = self.frame_stack[frame + 1] 919 920 # Test operand suitability. 921 # NOTE: Support other types. 922 # NOTE: Handle comparisons of incompatible types more appropriately. 923 924 if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)): 925 if true_if_incompatible: 926 self.machine.result = self.true_constant, self.true_constant 927 else: 928 self.machine.result = self.false_constant, self.false_constant 929 return 930 931 # NOTE: Assume single location for data. 932 933 left_data = left + 1 934 right_data = right + 1 935 936 # Test the data. 937 # NOTE: The data is considered ready to use. 938 939 if op(self.machine.load(left_data), self.machine.load(right_data)): 940 self.machine.result = self.true_constant, self.true_constant 941 else: 942 self.machine.result = self.false_constant, self.false_constant 943 944 def builtins_int_lt(self): 945 return self.builtins_int_op(operator.lt, 0) 946 947 def builtins_int_le(self): 948 return self.builtins_int_op(operator.le, 0) 949 950 def builtins_int_gt(self): 951 return self.builtins_int_op(operator.gt, 0) 952 953 def builtins_int_ge(self): 954 return self.builtins_int_op(operator.ge, 0) 955 956 def builtins_int_eq(self): 957 return self.builtins_int_op(operator.eq, 0) 958 959 def builtins_int_ne(self): 960 return self.builtins_int_op(operator.ne, 1) 961 962 def builtins_bool_bool(self): 963 frame = self.local_sp_stack[-1] 964 965 # Get operands addresses. 966 967 left_context, left = self.frame_stack[frame] 968 self.machine.result = left, left 969 970 def builtins_list_new(self): 971 frame = self.local_sp_stack[-1] 972 973 # The first parameter should be empty. 974 # NOTE: Specific copying of tuples/lists. 975 976 args_context, args = self.frame_stack[frame + 1] 977 978 # Test operand suitability. 979 980 if self.machine._CheckInstance(args, self.list_class): 981 _x, sequence = self.machine.load(args + 1) 982 header = self.machine.load(sequence) 983 size = header.occupied_size 984 elif self.machine._CheckInstance(args, self.tuple_class): 985 sequence = args 986 header = self.machine.load(sequence) 987 size = header.size 988 else: 989 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 990 return self.machine.RaiseException() 991 992 # Copy the sequence contents. 993 994 new_fragment = self.machine._MakeFragment(size) 995 for i in range(1, size): 996 self.machine.save(new_fragment + i, self.machine.load(sequence + i)) 997 998 # Make the list instance. 999 1000 addr = self.machine._MakeObject(2, self.list_instance) 1001 self.machine.save(addr + 1, (None, new_fragment)) 1002 1003 self.machine.result = addr, addr 1004 1005 def builtins_list_getitem(self): 1006 frame = self.local_sp_stack[-1] 1007 1008 # Get the operand address. 1009 1010 item_context, item = self.frame_stack[frame + 1] 1011 1012 # Get the list address. 1013 1014 obj_context, obj = self.frame_stack[frame] 1015 1016 # Get the fragment address. 1017 # NOTE: Assume single location for header. 1018 1019 _x, fragment = self.machine.load(obj + 1) 1020 1021 # Get the fragment header. 1022 1023 header = self.machine.load(fragment) 1024 nelements = header.occupied_size - 1 1025 1026 # NOTE: Assume single location for data and header. 1027 1028 item_pos = self.machine.load(item + 1) 1029 1030 if item_pos >= 0 and item_pos < nelements: 1031 pass 1032 elif item_pos < 0 and item_pos >= -nelements: 1033 item_pos = nelements + item_pos 1034 else: 1035 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 1036 return self.machine.RaiseException() 1037 1038 # NOTE: Assume single location for header. 1039 1040 self.machine.result = self.machine.load(fragment + 1 + item_pos) 1041 1042 def builtins_list_len(self): 1043 frame = self.local_sp_stack[-1] 1044 1045 # Get the list address. 1046 1047 obj_context, obj = self.frame_stack[frame] 1048 1049 # Get the fragment address. 1050 # NOTE: Assume single location for header. 1051 1052 _x, fragment = self.machine.load(obj + 1) 1053 1054 # Get the fragment header. 1055 1056 header = self.machine.load(fragment) 1057 nelements = header.occupied_size - 1 1058 1059 # Make a new object. 1060 1061 addr = self.machine._MakeObject(2, self.int_instance) 1062 1063 # Store the result. 1064 # NOTE: The data is considered ready to use. 1065 1066 self.machine.save(addr + 1, nelements) 1067 1068 # Return the new object. 1069 # Introduce object as context for the new object. 1070 1071 self.machine.result = addr, addr 1072 1073 def builtins_list_append(self): 1074 frame = self.local_sp_stack[-1] 1075 1076 # Get operand address. 1077 1078 arg_context, arg = self.frame_stack[frame + 1] 1079 1080 # Get the list address. 1081 1082 obj_context, obj = self.frame_stack[frame] 1083 1084 # Get the fragment address. 1085 # NOTE: Assume single location for header. 1086 1087 _x, fragment = self.machine.load(obj + 1) 1088 1089 # Get the fragment header. 1090 1091 header = self.machine.load(fragment) 1092 1093 # Attempt to add the reference. 1094 1095 if header.occupied_size < header.allocated_size: 1096 self.machine.save(fragment + header.occupied_size, (arg_context, arg)) 1097 header.occupied_size += 1 1098 else: 1099 1100 # Make a new fragment, maintaining more space than currently 1101 # occupied in order to avoid reallocation. 1102 1103 new_fragment = self.machine._MakeFragment(header.allocated_size + 1) 1104 1105 # Copy existing elements. 1106 1107 for i in range(1, header.allocated_size): 1108 self.machine.save(new_fragment + i, self.machine.load(fragment + i)) 1109 1110 self.machine.save(new_fragment + header.allocated_size, (arg_context, arg)) 1111 1112 # Set the new fragment in the object. 1113 # NOTE: The old fragment could be deallocated. 1114 1115 self.machine.save(obj + 1, (None, new_fragment)) 1116 1117 def builtins_tuple_len(self): 1118 frame = self.local_sp_stack[-1] 1119 1120 # Get the tuple address. 1121 1122 obj_context, obj = self.frame_stack[frame] 1123 1124 # Get the header. 1125 # NOTE: Assume single location for header. 1126 1127 header = self.machine.load(obj) 1128 nelements = header.size - 1 1129 1130 # Make a new object. 1131 1132 addr = self.machine._MakeObject(2, self.int_instance) 1133 1134 # Store the result. 1135 # NOTE: The data is considered ready to use. 1136 1137 self.machine.save(addr + 1, nelements) 1138 1139 # Return the new object. 1140 # Introduce object as context for the new object. 1141 1142 self.machine.result = addr, addr 1143 1144 def builtins_tuple_getitem(self): 1145 frame = self.local_sp_stack[-1] 1146 1147 # Get the operand address. 1148 1149 item_context, item = self.frame_stack[frame + 1] 1150 1151 # Get the tuple address. 1152 1153 obj_context, obj = self.frame_stack[frame] 1154 1155 # Get the header. 1156 # NOTE: Assume single location for header. 1157 1158 header = self.machine.load(obj) 1159 nelements = header.size - 1 1160 1161 # NOTE: Assume single location for data and header. 1162 1163 item_pos = self.machine.load(item + 1) 1164 1165 if item_pos >= 0 and item_pos < nelements: 1166 pass 1167 elif item_pos < 0 and item_pos >= -nelements: 1168 item_pos = nelements + item_pos 1169 else: 1170 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 1171 return self.machine.RaiseException() 1172 1173 # NOTE: Assume single location for header. 1174 1175 self.machine.result = self.machine.load(obj + 1 + item_pos) 1176 1177 def builtins_object_init(self): 1178 pass 1179 1180 native_functions = { 1181 1182 # Native method implementations: 1183 1184 "__builtins__.int.__add__" : builtins_int_add, 1185 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 1186 "__builtins__.int.__sub__" : builtins_int_sub, 1187 "__builtins__.int.__iadd__" : builtins_int_add, 1188 "__builtins__.int.__bool__" : builtins_int_bool, 1189 "__builtins__.int.__neg__" : builtins_int_neg, 1190 "__builtins__.int.__lt__" : builtins_int_lt, 1191 "__builtins__.int.__le__" : builtins_int_le, 1192 "__builtins__.int.__gt__" : builtins_int_gt, 1193 "__builtins__.int.__ge__" : builtins_int_ge, 1194 "__builtins__.int.__eq__" : builtins_int_eq, 1195 "__builtins__.int.__ne__" : builtins_int_ne, 1196 "__builtins__.bool.__bool__" : builtins_bool_bool, 1197 "__builtins__.list.__getitem__" : builtins_list_getitem, 1198 "__builtins__.list.__len__" : builtins_list_len, 1199 "__builtins__.list.append" : builtins_list_append, 1200 "__builtins__.tuple.__len__" : builtins_tuple_len, 1201 "__builtins__.tuple.__getitem__" : builtins_tuple_getitem, 1202 1203 # Native initialisers: 1204 1205 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 1206 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 1207 1208 # Native instantiators: 1209 1210 "__builtins__.list" : builtins_list_new, 1211 } 1212 1213 # Convenience functions. 1214 1215 def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): 1216 print "Making the image..." 1217 code = program.get_image(with_builtins) 1218 print "Getting raw structures..." 1219 ot = program.get_object_table() 1220 pt = program.get_parameter_table() 1221 objlist = ot.as_list() 1222 paramlist = pt.as_list() 1223 print "Getting raw image..." 1224 rc = program.get_raw_image() 1225 print "Initialising the machine..." 1226 importer = program.get_importer() 1227 true_constant = importer.get_constant(True).location 1228 false_constant = importer.get_constant(False).location 1229 rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception) 1230 library = Library(rm, true_constant, false_constant) 1231 rm.library = library 1232 rm.pc = program.code_location 1233 print "Returning program occupying %d locations." % len(rm.memory) 1234 return rm 1235 1236 # vim: tabstop=4 expandtab shiftwidth=4