1 #!/usr/bin/env python 2 3 """ 4 RSVP instruction and serialisation classes. 5 6 Copyright (C) 2007, 2008, 2009, 2010, 2011 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_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_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 as_raw(self, objtable, paramtable, with_builtins): 260 item = self.item 261 return [ 262 DataObject( 263 objtable.as_list().get_code(item.full_name()), 264 None, # modules treated like classes 265 None, 266 item.full_name(), 267 len(item.module_attributes()) + 1 # size 268 ) 269 ] 270 271 # Serialisation-related data and functions. 272 273 def get_object(item): 274 if isinstance(item, Attr): 275 cls = RSVPAttr 276 elif isinstance(item, Block): 277 cls = RSVPBlock 278 elif isinstance(item, Class): 279 cls = RSVPClass 280 elif isinstance(item, Const): 281 cls = RSVPConst 282 elif isinstance(item, Function): 283 cls = RSVPFunction 284 elif isinstance(item, Module): 285 cls = RSVPModule 286 else: 287 cls = None 288 289 if cls is not None: 290 return cls(item) 291 else: 292 return None 293 294 # Instruction-related classes. 295 296 class Instruction: 297 298 "A generic instruction." 299 300 default_working = "working" 301 default_target = "working" 302 default_source = None 303 304 # NOTE: Ultimately, instructions apart from Transfer will use specific 305 # NOTE: registers such as "working_value" and "working_context". 306 307 def __init__(self, attr=None, working=None, target=None, source=None): 308 self.attr = attr 309 self.working = working or self.default_working 310 self.target = target or self.default_target 311 self.source = source or self.default_source 312 313 def get_details(self): 314 return self.__class__, self.attr, self.working, self.target, self.source 315 316 def copy(self): 317 return self.__class__(self.attr, self.working, self.target, self.source) 318 319 def __repr__(self): 320 return "%s(%s)" % (self.__class__.__name__, 321 ", ".join( 322 self.format_operand() + 323 self.format_working() + 324 self.format_source() + 325 self.format_target() 326 )) 327 328 def __hash__(self): 329 return hash(self.get_details()) 330 331 def __eq__(self, other): 332 return self.get_details() == other.get_details() 333 334 def __ne__(self, other): 335 return not self.__eq__(other) 336 337 def format_operand(self): 338 operand = self.get_operand() 339 return operand is not None and [repr(operand)] or [] 340 341 def format_working(self): 342 return self.working != self.default_working and ["working=%r" % self.working] or [] 343 344 def format_source(self): 345 return self.source != self.default_source and ["source=%r" % self.source] or [] 346 347 def format_target(self): 348 return self.target != self.default_target and ["target=%r" % self.target] or [] 349 350 def get_operand(self): 351 return None 352 353 class FrameRelativeInstruction(Instruction): 354 355 "An instruction operating on the current frame." 356 357 def get_operand(self): 358 return self.attr.position 359 360 FR = FrameRelativeInstruction 361 362 class AddressRelativeInstruction(Instruction): 363 364 "An instruction accessing an object's attribute." 365 366 def format_operand(self): 367 return Instruction.format_operand(self) + ["name=%r" % name(self.attr)] 368 369 def get_operand(self): 370 return self.attr.position 371 372 AR = AddressRelativeInstruction 373 374 class AddressInstruction(Instruction): 375 376 "An instruction loading an address directly." 377 378 def format_operand(self): 379 location, position, result = self.get_operands() 380 if location is not None: 381 return ["%s" % result, "location=%s" % location, "position=%s" % position, 382 "name=%s" % name(self.attr)] 383 elif result is not None: 384 return ["%s" % result, "name=%s" % name(self.attr)] 385 else: 386 return ["name=%s" % name(self.attr)] 387 388 def get_operands(self): 389 if isinstance(self.attr, Attr): 390 position = self.attr.position 391 location = self.attr.parent.location 392 393 # NOTE: Unpositioned attributes are handled here. 394 395 if location is not None and position is not None: 396 result = location + position + 1 397 else: 398 location = self.attr.parent.name 399 position = self.attr.name 400 result = None 401 return location, position, result 402 else: 403 return None, None, self.attr.location 404 405 def get_operand(self): 406 return self.get_operands()[-1] 407 408 Address = AddressInstruction 409 410 class TargetInstruction(Instruction): 411 412 "An instruction loading the address of an invocation target." 413 414 def format_operand(self): 415 return Instruction.format_operand(self) + ["name=%r" % name(self.attr)] 416 417 def get_operand(self): 418 return self.attr.code_body_location 419 420 Target = TargetInstruction 421 422 class ImmediateInstruction(Instruction): 423 424 "An instruction employing a constant." 425 426 def get_operand(self): 427 return self.attr 428 429 Immediate = ImmediateInstruction 430 431 # Low-level instructions. 432 433 class Transfer(Instruction): 434 "Transfer a register's contents into another." 435 cost = 1 436 437 class LoadMemory(Instruction): 438 "Load a value from a memory address given by a register, optionally adding the operand." 439 cost = 1 440 441 class Add(Instruction): 442 "Add the value given by the working register to the operand." 443 cost = 1 444 445 class Multiply(Instruction): 446 "Multiply the value given by the working register by the operand." 447 cost = 1 448 449 # Access to stored constant data. 450 451 class LoadConst(Address): 452 "Load the constant or module from the specified location." 453 cost = 1 454 455 class LoadClass(Address): 456 "Load the class from the specified location." 457 cost = 1 458 459 class LoadFunction(Address): 460 "Load the function from the specified location." 461 cost = 1 462 463 # Access within an invocation frame. 464 465 class LoadName(FR): 466 "Load the current value from the given local attribute/variable." 467 cost = 2 468 469 class StoreName(FR): 470 "Store the source value into the given local attribute/variable." 471 cost = 2 472 default_target = None 473 474 class LoadTemp(Immediate): 475 "Load the current value from the given temporary location." 476 cost = 2 477 478 class StoreTemp(Immediate): 479 "Store the current value into the given temporary location." 480 cost = 2 481 default_target = None 482 483 # Access to static data. 484 485 class LoadAddress(Address): 486 "Load the current value from the given fixed attribute address." 487 cost = 1 488 489 class StoreAddress(Address): 490 "Store the source value into the given fixed attribute address." 491 cost = 1 492 default_working = None 493 default_target = None 494 495 class LoadAddressContext(Address): 496 "Load the current value from the given fixed attribute address, using the current value as context." 497 cost = 2 498 499 class StoreAddressContext(Address): 500 "Store the current value into the given fixed attribute address, using the current value as context." 501 cost = 2 502 default_target = None 503 504 class LoadAddressContextCond(Address): 505 """ 506 Load the current value from the given fixed attribute address, only using the current value as 507 context if the attribute is compatible. 508 """ 509 cost = 4 510 511 class MakeInstance(Immediate): 512 "Make a new instance using the current value as a reference to a template." 513 cost = 5 514 515 class MakeFragment(Immediate): 516 "Make a new list fragment." 517 cost = 5 518 519 # Access to address-relative data. (LoadAttrIndexContext not defined.) 520 521 class LoadAttr(AR): 522 "Load into the current value the given attribute of the object referenced by the current value." 523 cost = 2 524 525 class StoreAttr(AR): 526 "Store the source value into the given attribute of the object referenced by the current value." 527 cost = 2 528 default_target = None 529 530 class LoadAttrIndex(Immediate): 531 "Load into the current value the attribute of the current value with the given index." 532 cost = 6 533 534 class StoreAttrIndex(Immediate): 535 "Store the source value into the attribute of the current value with the given index." 536 cost = 6 537 default_target = None 538 539 class LoadAttrIndexContextCond(Immediate): 540 """ 541 Load into the current value the attribute of the current value with the given index, only making the 542 current value the context if the attribute is compatible. 543 """ 544 cost = 8 545 546 # Access to object details. 547 548 class LoadCallable(Instruction): 549 "Load the target of an invocation." 550 cost = 2 551 552 class StoreCallable(Instruction): 553 "Store the source value into the object referenced by the current value." 554 cost = 3 555 default_target = None 556 557 # Access to invocation frames in preparation. 558 559 class MakeFrame(Immediate): 560 "Make a new invocation frame." 561 cost = 2 562 default_target = None 563 564 class AdjustFrame(Immediate): 565 "Adjust the current invocation frame for corrected invocations." 566 cost = 2 567 default_target = None 568 569 class DropFrame(Instruction): 570 "Drop an invocation frame." 571 cost = 2 572 default_target = None 573 574 class StoreFrame(Immediate): 575 "Store the current value as an argument for the parameter with the given position." 576 cost = 2 577 default_target = None 578 579 class StoreFrameIndex(Immediate): 580 "Store the source value as an argument of the current value for the parameter with the given index." 581 cost = 6 582 default_target = None 583 584 # Context-related tests. 585 586 class CheckContext(Instruction): 587 "Check to see if the context is valid." 588 cost = 2 589 590 class CheckClass(Instruction): 591 "Check the current value to determine whether it is a class." 592 cost = 2 593 594 class CheckInstance(Instruction): 595 """ 596 Check the current value as an instance of a class or its subclasses (used with 'self' in an 597 invocation). 598 """ 599 cost = 6 600 601 # Access to frames upon invocation. 602 603 class CheckFrame(Immediate): 604 "Check the frame for the correct number of arguments." 605 cost = 3 606 607 class CheckExtra(Immediate): 608 "Ensure that the frame can provide extra arguments." 609 cost = 3 610 611 class FillDefaults(Immediate): 612 "Fill frame positions with defaults, if appropriate." 613 cost = 8 # variable 614 default_target = None 615 616 class ExtendFrame(Immediate): 617 "Extend the current frame for temporary storage use." 618 cost = 1 619 default_target = None 620 621 class CopyExtra(Immediate): 622 "Copy extra arguments into a separate sequence, starting from the given position." 623 cost = 10 624 625 # Invocation-related instructions, using a special result "register". 626 627 class JumpInFrame(Instruction): 628 "Jump, using the current locals, to the current callable." 629 cost = 2 630 default_target = None 631 632 class JumpInFrameDirect(Address): 633 "Jump, using the current locals, to the specified address." 634 cost = 2 635 default_target = None 636 637 class JumpWithFrame(Instruction): 638 "Jump, adopting the invocation frame, to the current callable." 639 cost = 3 640 default_target = None 641 642 class JumpWithFrameDirect(Target): 643 "Jump to the specified address, adopting the invocation frame." 644 cost = 3 645 default_target = None 646 647 class Return(Instruction): 648 "Return from a subprogram." 649 cost = 2 650 default_target = None 651 652 # Branch-related instructions. 653 654 class Jump(Address): 655 "Jump unconditionally." 656 cost = 1 657 default_target = None 658 659 class JumpIfFalse(Address): 660 "Jump if the last evaluation gave a false result." 661 cost = 2 662 default_target = None 663 664 class JumpIfTrue(Address): 665 "Jump if the last evaluation gave a true result." 666 cost = 2 667 default_target = None 668 669 # Exception-related instructions, using a special exception "register". 670 671 class RaiseException(Instruction): 672 "Raise an exception, jumping to the active handler." 673 cost = 2 674 default_target = None 675 676 class PushHandler(Address): 677 "Push an exception handler onto the handler stack." 678 cost = 3 679 default_target = None 680 681 class PopHandler(Immediate): 682 "Pop exception handlers from the handler stack." 683 cost = 3 684 default_target = None 685 686 class CheckException(Instruction): 687 "Check the raised exception against another." 688 cost = 6 689 690 class ClearException(Instruction): 691 "Clear any raised exception." 692 cost = 1 693 694 # Test instructions, operating on the boolean status register. 695 696 class TestIdentity(Instruction): 697 "Test whether the current value is identical to the source value, setting the boolean status." 698 cost = 2 699 700 class TestIdentityAddress(Address): 701 "Test whether the current value is identical to the given address, setting the boolean status." 702 cost = 2 703 704 class InvertBoolean(Instruction): 705 "Invert the boolean status." 706 cost = 1 707 708 # Instructions which can affect the current value. (LoadAttrIndexContext not defined.) 709 710 def affects_register(instruction, register): 711 712 """ 713 Returns whether 'instruction' affects the given 'register', either directly 714 or as a consequence of its execution. 715 """ 716 717 return instruction.target == register or isinstance(instruction, ( 718 JumpInFrame, JumpInFrameDirect, 719 JumpWithFrame, JumpWithFrameDirect, 720 JumpIfTrue, JumpIfFalse, 721 Jump 722 )) 723 724 # vim: tabstop=4 expandtab shiftwidth=4