1 #!/usr/bin/env python 2 3 """ 4 RSVP instruction and serialisation classes. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from micropython.data import Attr, Const, Class, Function, Module 23 from micropython.program import Block, DataObject, DataValue, PlaceholderContext 24 from micropython.raw import RawObject 25 26 def name(attr): 27 if isinstance(attr, Attr): 28 return attr.name or "<unnamed>" 29 elif isinstance(attr, (Block, Const)): 30 return attr 31 else: 32 return attr.full_name() or "<unnamed>" 33 34 # Serialisation-related classes. 35 36 class RSVPObject(RawObject): 37 38 "A generic data object wrapper." 39 40 def _finalise_location(self, with_builtins): 41 42 """ 43 Set the code body location for items now that the code blocks have been 44 positioned. 45 """ 46 47 item = self.item 48 49 if not self.is_generated(with_builtins): 50 item.code_body_location = item.full_name() 51 else: 52 item.code_body_location = item.get_body_block().location 53 54 class RSVPAttr(RSVPObject): 55 56 "A wrapper for attributes/values." 57 58 def as_raw(self, objtable, paramtable, with_builtins): 59 item = self.item 60 return [ 61 DataValue( 62 item.get_context() and item.get_context().location, 63 item.get_value() and item.get_value().location 64 ) 65 ] 66 67 class RSVPBlock(RSVPObject): 68 69 "A wrapper for blocks." 70 71 def set_location(self, location, objtable, with_builtins): 72 item = self.item 73 item.location = location 74 return location + len(item.code) 75 76 def as_raw(self, objtable, paramtable, with_builtins): 77 return self.item.code 78 79 class RSVPClass(RSVPObject): 80 81 "A wrapper for classes." 82 83 def set_location(self, location, objtable, with_builtins): 84 self.item.instance_template_location = location 85 86 # Include the instance template and __class__ attribute in the size. 87 88 return RSVPObject.set_location(self, location + 1, objtable, with_builtins) 89 90 def finalise_body_location(self, with_builtins): 91 self._finalise_location(with_builtins) 92 93 def as_raw(self, objtable, paramtable, with_builtins): 94 item = self.item 95 classcode = objtable.as_list().get_code(item.full_name()) 96 attrcode = objtable.get_index("#" + item.full_name()) 97 98 # Include a template of an instance for use when instantiating classes. 99 100 call_method = item.all_class_attributes().get("__call__") 101 call_method_value = call_method and call_method.get_value() 102 call_method_code_location = call_method_value and call_method_value.code_location 103 call_method_funccode = call_method_value and paramtable.as_list().get_code(call_method_value.full_name()) 104 105 instantiator_funccode = paramtable.as_list().get_code(item.get_instantiator().full_name()) 106 107 # NOTE: The instantiator code is the first block of the class. 108 109 if not self.is_generated(with_builtins): 110 instantiator_code_location = item.full_name() 111 else: 112 instantiator_code_location = item.get_instantiator().blocks[0].location 113 114 return [ 115 116 # Template instance... 117 118 DataObject( 119 classcode, 120 attrcode, # is instance 121 call_method_code_location, 122 item.full_name(), 123 len(item.instance_attributes()), # size (excluding __class__) 124 call_method_funccode # funccode 125 ), 126 127 # Class... 128 129 DataObject( 130 classcode, 131 None, # is not instance 132 instantiator_code_location, 133 item.full_name(), 134 len(item.class_attributes()) + 1, # size 135 instantiator_funccode # funccode 136 ) 137 ] 138 139 class RSVPConst(RSVPObject): 140 141 "A wrapper for constants." 142 143 def set_location(self, location, objtable, with_builtins): 144 item = self.item 145 value = item.get_value() 146 type_name = self.type_name(item.value_type_name(), value, objtable) 147 148 location = RSVPObject.set_location(self, location, objtable, with_builtins) 149 return location + self.raw_size(type_name, value) 150 151 def as_raw(self, objtable, paramtable, with_builtins): 152 item = self.item 153 value = item.get_value() 154 type_name = self.type_name(item.value_type_name(), value, objtable) 155 raw_data = self.raw_data(type_name, value, objtable) 156 size = self.raw_size(type_name, value) + 1 157 158 # Generate the footprint of the constant. 159 160 classcode = objtable.as_list().get_code(type_name) 161 attrcode = objtable.get_index("#" + type_name) 162 163 return [ 164 DataObject( 165 classcode, 166 attrcode, # is instance 167 None, 168 type_name, 169 size # header plus data 170 ) 171 172 # NOTE: The RSVP library needs changing if more attributes are added 173 # NOTE: here (such as a __class__ attribute for all instances). 174 175 ] + raw_data 176 177 def type_name(self, type_name, value, objtable): 178 179 # Change the type of string constants if they might be used as attribute 180 # accessors. 181 182 accessor_cls = "__builtins__._accessor" 183 184 if type_name == "__builtins__.str" and value in objtable.attribute_names() and \ 185 accessor_cls in objtable.object_names(): 186 187 return accessor_cls 188 else: 189 return type_name 190 191 def raw_data(self, type_name, value, objtable): 192 193 # NOTE: Start simple and use single entries for most types, even though 194 # NOTE: a concrete representation may use multiple memory words to 195 # NOTE: represent the data. 196 197 if type_name in ("__builtins__.tuple", "__builtins__.list"): 198 return [len(value)] + list(value) 199 elif type_name == "__builtins__._accessor": 200 return [value, objtable.get_index(value)] 201 else: 202 return [value] 203 204 def raw_size(self, type_name, value): 205 if type_name in ("__builtins__.tuple", "__builtins__.list"): 206 return 1 + len(value) 207 elif type_name == "__builtins__._accessor": 208 return 2 209 else: 210 return 1 211 212 class RSVPFunction(RSVPObject): 213 214 "A wrapper for functions." 215 216 def set_location(self, location, objtable, with_builtins): 217 item = self.item 218 location = RSVPObject.set_location(self, location, objtable, with_builtins) 219 220 # Set the code location only where the code has been 221 # generated. 222 223 if not self.is_generated(with_builtins): 224 item.code_location = item.full_name() 225 226 # Skip any defaults for static functions. 227 228 elif not item.is_dynamic(): 229 item.code_location = location + len(item.defaults) 230 231 # Skip any defaults for dynamic functions. 232 233 else: 234 item.code_location = location 235 236 return location 237 238 def finalise_body_location(self, with_builtins): 239 self._finalise_location(with_builtins) 240 241 def as_raw(self, objtable, paramtable, with_builtins): 242 item = self.item 243 # NOTE: Need class and parameter details! Should arguably be an instance of types.FunctionType. 244 return [ 245 DataObject( 246 objtable.as_list().get_code("__builtins__.function"), 247 objtable.get_index("#__builtins__.function"), # is instance 248 item.code_location, 249 "__builtins__.function", 250 len(item.defaults) + 1, # size (not accurate for lambda functions before instantiation) 251 paramtable.as_list().get_code(item.full_name()) # funccode 252 ) 253 ] 254 255 class RSVPModule(RSVPObject): 256 257 "A wrapper for modules." 258 259 def finalise_location(self, with_builtins): 260 item = self.item 261 if item.blocks: 262 item.code_location = item.blocks[0].location 263 264 def as_raw(self, objtable, paramtable, with_builtins): 265 item = self.item 266 return [ 267 DataObject( 268 objtable.as_list().get_code(item.full_name()), 269 None, # modules treated like classes 270 item.code_location, 271 item.full_name(), 272 len(item.module_attributes()) + 1 # size 273 ) 274 ] 275 276 # Serialisation-related data and functions. 277 278 def get_object(item): 279 if isinstance(item, Attr): 280 cls = RSVPAttr 281 elif isinstance(item, Block): 282 cls = RSVPBlock 283 elif isinstance(item, Class): 284 cls = RSVPClass 285 elif isinstance(item, Const): 286 cls = RSVPConst 287 elif isinstance(item, Function): 288 cls = RSVPFunction 289 elif isinstance(item, Module): 290 cls = RSVPModule 291 else: 292 cls = None 293 294 if cls is not None: 295 return cls(item) 296 else: 297 return None 298 299 # Instruction-related classes. 300 301 class Instruction: 302 303 "A generic instruction." 304 305 default_working = "working" 306 default_target = "working" 307 default_source = None 308 309 # NOTE: Ultimately, instructions apart from Transfer will use specific 310 # NOTE: registers such as "working_value" and "working_context". 311 312 def __init__(self, attr=None, working=None, target=None, source=None): 313 self.attr = attr 314 self.working = working or self.default_working 315 self.target = target or self.default_target 316 self.source = source or self.default_source 317 318 def get_details(self): 319 return self.__class__, self.attr, self.working, self.target, self.source 320 321 def copy(self): 322 return self.__class__(self.attr, self.working, self.target, self.source) 323 324 def __repr__(self): 325 return "%s(%s)" % (self.__class__.__name__, 326 ", ".join( 327 self.format_operand() + 328 self.format_working() + 329 self.format_source() + 330 self.format_target() 331 )) 332 333 def __hash__(self): 334 return hash(self.get_details()) 335 336 def __eq__(self, other): 337 return self.get_details() == other.get_details() 338 339 def __ne__(self, other): 340 return not self.__eq__(other) 341 342 def format_operand(self): 343 operand = self.get_operand() 344 return operand is not None and [repr(operand)] or [] 345 346 def format_working(self): 347 return self.working != self.default_working and ["working=%r" % self.working] or [] 348 349 def format_source(self): 350 return self.source != self.default_source and ["source=%r" % self.source] or [] 351 352 def format_target(self): 353 return self.target != self.default_target and ["target=%r" % self.target] or [] 354 355 def get_operand(self): 356 return None 357 358 class FrameRelativeInstruction(Instruction): 359 360 "An instruction operating on the current frame." 361 362 def get_operand(self): 363 return self.attr.position 364 365 FR = FrameRelativeInstruction 366 367 class AddressRelativeInstruction(Instruction): 368 369 "An instruction accessing an object's attribute." 370 371 def format_operand(self): 372 return Instruction.format_operand(self) + ["name=%r" % name(self.attr)] 373 374 def get_operand(self): 375 return self.attr.position 376 377 AR = AddressRelativeInstruction 378 379 class AddressInstruction(Instruction): 380 381 "An instruction loading an address directly." 382 383 def format_operand(self): 384 location, position, result = self.get_operands() 385 if location is not None: 386 return ["%s" % result, "location=%s" % location, "position=%s" % position, 387 "name=%s" % name(self.attr)] 388 elif result is not None: 389 return ["%s" % result, "name=%s" % name(self.attr)] 390 else: 391 return ["name=%s" % name(self.attr)] 392 393 def get_operands(self): 394 if isinstance(self.attr, Attr): 395 position = self.attr.position 396 location = self.attr.parent.location 397 398 # NOTE: Unpositioned attributes are handled here. 399 400 if location is not None and position is not None: 401 result = location + position + 1 402 else: 403 location = self.attr.parent.name 404 position = self.attr.name 405 result = None 406 return location, position, result 407 else: 408 return None, None, self.attr.location 409 410 def get_operand(self): 411 return self.get_operands()[-1] 412 413 Address = AddressInstruction 414 415 class TargetInstruction(Instruction): 416 417 "An instruction loading the address of a direct invocation target." 418 419 def format_operand(self): 420 return Instruction.format_operand(self) + ["name=%r" % name(self.attr)] 421 422 def get_operand(self): 423 return self.attr.code_body_location 424 425 Target = TargetInstruction 426 427 class ImmediateInstruction(Instruction): 428 429 "An instruction employing a constant." 430 431 def get_operand(self): 432 return self.attr 433 434 Immediate = ImmediateInstruction 435 436 # Low-level instructions. 437 438 class Transfer(Instruction): 439 "Transfer a register's contents into another." 440 cost = 1 441 442 class LoadMemory(Instruction): 443 "Load a value from a memory address given by a register, optionally adding the operand." 444 cost = 1 445 446 class Add(Instruction): 447 "Add the value given by the working register to the operand." 448 cost = 1 449 450 class Multiply(Instruction): 451 "Multiply the value given by the working register by the operand." 452 cost = 1 453 454 # Access to stored constant data. 455 456 class LoadConst(Address): 457 "Load the constant or module from the specified location." 458 cost = 1 459 460 class LoadClass(Address): 461 "Load the class from the specified location." 462 cost = 1 463 464 class LoadFunction(Address): 465 "Load the function from the specified location." 466 cost = 1 467 468 # Access within an invocation frame. 469 470 class LoadName(FR): 471 "Load the current value from the given local attribute/variable." 472 cost = 2 473 474 class StoreName(FR): 475 "Store the source value into the given local attribute/variable." 476 cost = 2 477 default_target = None 478 479 class LoadTemp(Immediate): 480 "Load the current value from the given temporary location." 481 cost = 2 482 483 class StoreTemp(Immediate): 484 "Store the current value into the given temporary location." 485 cost = 2 486 default_target = None 487 488 # Access to static data. 489 490 class LoadAddress(Address): 491 "Load the current value from the given fixed attribute address." 492 cost = 1 493 494 class StoreAddress(Address): 495 "Store the source value into the given fixed attribute address." 496 cost = 1 497 default_working = None 498 default_target = None 499 500 class LoadAddressContext(Address): 501 "Load the current value from the given fixed attribute address, using the current value as context." 502 cost = 2 503 504 class StoreAddressContext(Address): 505 "Store the current value into the given fixed attribute address, using the current value as context." 506 cost = 2 507 default_target = None 508 509 class LoadAddressContextCond(Address): 510 """ 511 Load the current value from the given fixed attribute address, only using the current value as 512 context if the attribute is compatible. 513 """ 514 cost = 4 515 516 class MakeInstance(Immediate): 517 "Make a new instance using the current value as a reference to a template." 518 cost = 5 519 520 class MakeFragment(Immediate): 521 "Make a new list fragment." 522 cost = 5 523 524 # Access to address-relative data. (LoadAttrIndexContext not defined.) 525 526 class LoadAttr(AR): 527 "Load into the current value the given attribute of the object referenced by the current value." 528 cost = 2 529 530 class StoreAttr(AR): 531 "Store the source value into the given attribute of the object referenced by the current value." 532 cost = 2 533 default_target = None 534 535 class LoadAttrIndex(Immediate): 536 "Load into the current value the attribute of the current value with the given index." 537 cost = 6 538 539 class StoreAttrIndex(Immediate): 540 "Store the source value into the attribute of the current value with the given index." 541 cost = 6 542 default_target = None 543 544 class LoadAttrIndexContextCond(Immediate): 545 """ 546 Load into the current value the attribute of the current value with the given index, only making the 547 current value the context if the attribute is compatible. 548 """ 549 cost = 8 550 551 # Access to object details. 552 553 class LoadCallable(Instruction): 554 "Load the target of an invocation." 555 cost = 2 556 557 class StoreCallable(Instruction): 558 "Store the source value into the object referenced by the current value." 559 cost = 3 560 default_target = None 561 562 # Access to invocation frames in preparation. 563 564 class MakeFrame(Immediate): 565 "Make a new invocation frame." 566 cost = 2 567 default_target = None 568 569 class AdjustFrame(Immediate): 570 "Adjust the current invocation frame for corrected invocations." 571 cost = 2 572 default_target = None 573 574 class DropFrame(Instruction): 575 "Drop an invocation frame." 576 cost = 2 577 default_target = None 578 579 class StoreFrame(Immediate): 580 "Store the current value as an argument for the parameter with the given position." 581 cost = 2 582 default_target = None 583 584 class StoreFrameIndex(Immediate): 585 "Store the source value as an argument of the current value for the parameter with the given index." 586 cost = 6 587 default_target = None 588 589 # Context-related tests. 590 591 class CheckContext(Instruction): 592 "Check to see if the context is valid." 593 cost = 2 594 595 class CheckClass(Instruction): 596 "Check the current value to determine whether it is a class." 597 cost = 2 598 599 class CheckInstance(Instruction): 600 """ 601 Check the current value as an instance of a class or its subclasses (used with 'self' in an 602 invocation). 603 """ 604 cost = 6 605 606 # Access to frames upon invocation. 607 608 class CheckFrame(Immediate): 609 "Check the frame for the correct number of arguments." 610 cost = 3 611 612 class CheckExtra(Immediate): 613 "Ensure that the frame can provide extra arguments." 614 cost = 3 615 616 class FillDefaults(Immediate): 617 "Fill frame positions with defaults, if appropriate." 618 cost = 8 # variable 619 default_target = None 620 621 class ExtendFrame(Immediate): 622 "Extend the current frame for temporary storage use." 623 cost = 1 624 default_target = None 625 626 class CopyExtra(Immediate): 627 "Copy extra arguments into a separate sequence, starting from the given position." 628 cost = 10 629 630 # Invocation-related instructions, using a special result "register". 631 632 class JumpInFrame(Instruction): 633 "Jump, using the current locals, to the current callable." 634 cost = 2 635 default_target = None 636 637 class JumpInFrameDirect(Address): 638 "Jump, using the current locals, to the specified address." 639 cost = 2 640 default_target = None 641 642 class JumpWithFrame(Instruction): 643 "Jump, adopting the invocation frame, to the current callable." 644 cost = 3 645 default_target = None 646 647 class JumpWithFrameDirect(Target): 648 "Jump to the specified address, adopting the invocation frame." 649 cost = 3 650 default_target = None 651 652 class Return(Instruction): 653 "Return from a subprogram." 654 cost = 2 655 default_target = None 656 657 # Branch-related instructions. 658 659 class Jump(Address): 660 "Jump unconditionally." 661 cost = 1 662 default_target = None 663 664 class JumpIfFalse(Address): 665 "Jump if the last evaluation gave a false result." 666 cost = 2 667 default_target = None 668 669 class JumpIfTrue(Address): 670 "Jump if the last evaluation gave a true result." 671 cost = 2 672 default_target = None 673 674 # Exception-related instructions, using a special exception "register". 675 676 class RaiseException(Instruction): 677 "Raise an exception, jumping to the active handler." 678 cost = 2 679 default_target = None 680 681 class PushHandler(Address): 682 "Push an exception handler onto the handler stack." 683 cost = 3 684 default_target = None 685 686 class PopHandler(Immediate): 687 "Pop exception handlers from the handler stack." 688 cost = 3 689 default_target = None 690 691 class CheckException(Instruction): 692 "Check the raised exception against another." 693 cost = 6 694 695 class ClearException(Instruction): 696 "Clear any raised exception." 697 cost = 1 698 699 # Test instructions, operating on the boolean status register. 700 701 class TestIdentity(Instruction): 702 "Test whether the current value is identical to the source value, setting the boolean status." 703 cost = 2 704 705 class TestIdentityAddress(Address): 706 "Test whether the current value is identical to the given address, setting the boolean status." 707 cost = 2 708 709 class InvertBoolean(Instruction): 710 "Invert the boolean status." 711 cost = 1 712 713 # Instructions which can affect the current value. (LoadAttrIndexContext not defined.) 714 715 def affects_register(instruction, register): 716 717 """ 718 Returns whether 'instruction' affects the given 'register', either directly 719 or as a consequence of its execution. 720 """ 721 722 return instruction.target == register or isinstance(instruction, ( 723 JumpInFrame, JumpInFrameDirect, 724 JumpWithFrame, JumpWithFrameDirect, 725 JumpIfTrue, JumpIfFalse, 726 Jump 727 )) 728 729 # vim: tabstop=4 expandtab shiftwidth=4