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