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