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, 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, 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 + 2, 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()) + 1, # size 124 call_method_funccode # funccode 125 ), 126 127 # The __class__ attribute for instances. 128 129 DataValue( 130 PlaceholderContext, 131 item.location 132 ), 133 134 # Class... 135 136 DataObject( 137 classcode, 138 None, # is not instance 139 instantiator_code_location, 140 item.full_name(), 141 len(item.class_attributes()) + 1, # size 142 instantiator_funccode # funccode 143 ) 144 ] 145 146 class RSVPConst(RSVPObject): 147 148 "A wrapper for constants." 149 150 def set_location(self, location, with_builtins): 151 location = RSVPObject.set_location(self, location, with_builtins) 152 153 # Include the __class__ attribute in the size. 154 155 return location + 1 + len(self.raw_data()) 156 157 def as_raw(self, objtable, paramtable, with_builtins): 158 item = self.item 159 classcode = objtable.as_list().get_code(item.value_type_name()) 160 attrcode = objtable.get_index(item.value_type_name()) 161 name_parts = item.value_type_name_parts() 162 attr = objtable.access(*name_parts) 163 cls = attr.get_value() 164 165 return [ 166 DataObject( 167 classcode, 168 attrcode, # is instance 169 None, 170 item.value_type_name(), 171 3 # size (header plus __class__ plus data) 172 ), 173 174 # The __class__ attribute for instances. 175 176 DataValue( 177 PlaceholderContext, 178 cls.location 179 ) 180 181 # NOTE: The RSVP library needs changing if more attributes are added 182 # NOTE: here. 183 184 ] + self.raw_data() 185 186 def raw_data(self): 187 item = self.item 188 value = item.get_value() 189 # NOTE: Start simple and use single entries for most types. 190 if item.value_type_name() in ("__builtins__.tuple", "__builtins__.list"): 191 return [len(value)] + list(value) 192 else: 193 return [value] 194 195 class RSVPFunction(RSVPObject): 196 197 "A wrapper for functions." 198 199 def set_location(self, location, with_builtins): 200 item = self.item 201 location = RSVPObject.set_location(self, location, with_builtins) 202 203 # Set the code location only where the code has been 204 # generated. 205 206 if not self.is_generated(with_builtins): 207 item.code_location = item.full_name() 208 209 # Skip __class__ plus any defaults for static functions. 210 211 elif not item.is_dynamic(): 212 item.code_location = location + 1 + len(item.defaults) 213 214 # Skip __class__ plus any defaults for dynamic functions. 215 216 else: 217 item.code_location = location + 1 218 219 # Include the __class__ attribute. 220 221 return location + 1 222 223 def finalise_location(self, with_builtins): 224 self._finalise_location(with_builtins) 225 226 def as_raw(self, objtable, paramtable, with_builtins): 227 item = self.item 228 attr = objtable.access("__builtins__", "function") 229 cls = attr.get_value() 230 # NOTE: Need class and parameter details! Should arguably be an instance of types.FunctionType. 231 return [ 232 DataObject( 233 objtable.as_list().get_code("__builtins__.function"), 234 objtable.get_index("__builtins__.function"), # is instance 235 item.code_location, 236 "__builtins__.function", 237 len(item.defaults) + 1, # size (not accurate for lambda functions before instantiation) 238 paramtable.as_list().get_code(item.full_name()) # funccode 239 ), 240 241 # The __class__ attribute for functions. 242 243 DataValue( 244 PlaceholderContext, 245 cls.location 246 ) 247 ] 248 249 class RSVPModule(RSVPObject): 250 251 "A wrapper for modules." 252 253 def as_raw(self, objtable, paramtable, with_builtins): 254 item = self.item 255 return [ 256 DataObject( 257 objtable.as_list().get_code(item.full_name()), 258 None, # modules treated like classes 259 None, 260 item.full_name(), 261 len(item.module_attributes()) + 1 # size 262 ) 263 ] 264 265 # Serialisation-related data and functions. 266 267 def get_object(item): 268 if isinstance(item, Attr): 269 cls = RSVPAttr 270 elif isinstance(item, Block): 271 cls = RSVPBlock 272 elif isinstance(item, Class): 273 cls = RSVPClass 274 elif isinstance(item, Const): 275 cls = RSVPConst 276 elif isinstance(item, Function): 277 cls = RSVPFunction 278 elif isinstance(item, Module): 279 cls = RSVPModule 280 else: 281 cls = None 282 283 if cls is not None: 284 return cls(item) 285 else: 286 return None 287 288 # Instruction-related classes. 289 290 class Instruction: 291 292 "A generic instruction." 293 294 def __init__(self, attr=None): 295 self.attr = attr 296 self.input = None 297 self.source = None # for storage instructions 298 299 def copy(self): 300 return self.__class__(self.attr) 301 302 def __repr__(self): 303 if self.attr is not None: 304 return "%s(%r)%s" % (self.__class__.__name__, self.attr, self.show_input()) 305 else: 306 return "%s()%s" % (self.__class__.__name__, self.show_input()) 307 308 def show_input(self): 309 if self.input is not None: 310 if self.source is not None: 311 return " <- (%r, %r)" % (self.input, self.source) 312 else: 313 return " <- %r" % self.input 314 elif self.source is not None: 315 return " <-- %r" % self.source 316 else: 317 return "" 318 319 def get_operand(self): 320 return None 321 322 class FrameRelativeInstruction(Instruction): 323 324 "An instruction operating on the current frame." 325 326 def __repr__(self): 327 return "%s(%r)%s" % (self.__class__.__name__, self.get_operand(), self.show_input()) 328 329 def get_operand(self): 330 return self.attr.position 331 332 FR = FrameRelativeInstruction 333 334 class AddressRelativeInstruction(Instruction): 335 336 "An instruction accessing an object's attribute." 337 338 def __repr__(self): 339 position = self.get_operand() 340 if position is not None: 341 return "%s(%r)%s # %s" % (self.__class__.__name__, position, self.show_input(), name(self.attr)) 342 else: 343 return "%s(%r)%s" % (self.__class__.__name__, name(self.attr), self.show_input()) 344 345 def get_operand(self): 346 return self.attr.position 347 348 AR = AddressRelativeInstruction 349 350 class AddressInstruction(Instruction): 351 352 "An instruction loading an address directly." 353 354 def __repr__(self): 355 location, position, result = self.get_operands() 356 if location is not None: 357 return "%s(%r)%s # %r, %r (%s)" % ( 358 self.__class__.__name__, result, self.show_input(), location, position, name(self.attr)) 359 elif result is not None: 360 return "%s(%r)%s # %s" % ( 361 self.__class__.__name__, result, self.show_input(), name(self.attr)) 362 else: 363 return "%s(...)%s # %s" % ( 364 self.__class__.__name__, self.show_input(), name(self.attr)) 365 366 def get_operands(self): 367 if isinstance(self.attr, Attr): 368 position = self.attr.position 369 location = self.attr.parent.location 370 371 # NOTE: Unpositioned attributes are handled here. 372 373 if location is not None and position is not None: 374 result = location + position + 1 375 else: 376 location = self.attr.parent.name 377 position = self.attr.name 378 result = None 379 return location, position, result 380 else: 381 return None, None, self.attr.location 382 383 def get_operand(self): 384 return self.get_operands()[-1] 385 386 Address = AddressInstruction 387 388 class TargetInstruction(Instruction): 389 390 "An instruction loading the address of an invocation target." 391 392 def __repr__(self): 393 return "%s(%r) # %r" % (self.__class__.__name__, self.get_operand(), name(self.attr)) 394 395 def get_operand(self): 396 return self.attr.code_body_location 397 398 Target = TargetInstruction 399 400 class ImmediateInstruction(Instruction): 401 402 "An instruction employing a constant." 403 404 def __repr__(self): 405 return "%s(%r)%s" % (self.__class__.__name__, self.attr, self.show_input()) 406 407 def get_operand(self): 408 return self.attr 409 410 Immediate = ImmediateInstruction 411 412 # Access to stored constant data. 413 414 class LoadConst(Address): 415 "Load the constant or module from the specified location." 416 cost = 1 417 418 class LoadClass(Address): 419 "Load the class from the specified location." 420 cost = 1 421 422 class LoadFunction(Address): 423 "Load the function from the specified location." 424 cost = 1 425 426 # Access within an invocation frame. 427 428 class LoadName(FR): 429 "Load the current value from the given local attribute/variable." 430 cost = 2 431 432 class StoreName(FR): 433 "Store the source value into the given local attribute/variable." 434 cost = 2 435 436 class LoadTemp(Immediate): 437 "Load the current value from the given temporary location." 438 cost = 2 439 440 class StoreTemp(Immediate): 441 "Store the current value into the given temporary location." 442 cost = 2 443 444 # Access to static data. 445 446 class LoadAddress(Address): 447 "Load the current value from the given fixed attribute address." 448 cost = 1 449 450 class StoreAddress(Address): 451 "Store the source value into the given fixed attribute address." 452 cost = 1 453 454 class LoadAddressContext(Address): 455 "Load the current value from the given fixed attribute address, using the current value as context." 456 cost = 2 457 458 class StoreAddressContext(Address): 459 "Store the current value into the given fixed attribute address, using the current value as context." 460 cost = 2 461 462 class LoadAddressContextCond(Address): 463 """ 464 Load the current value from the given fixed attribute address, only using the current value as 465 context if the attribute is compatible. 466 """ 467 cost = 4 468 469 class MakeInstance(Immediate): 470 "Make a new instance using the current value as a reference to a template." 471 cost = 5 472 473 class MakeFragment(Immediate): 474 "Make a new list fragment." 475 cost = 5 476 477 # Access to address-relative data. (LoadAttrIndexContext not defined.) 478 479 class LoadAttr(AR): 480 "Load into the current value the given attribute of the object referenced by the current value." 481 cost = 2 482 483 class StoreAttr(AR): 484 "Store the source value into the given attribute of the object referenced by the current value." 485 cost = 2 486 487 class LoadAttrIndex(Immediate): 488 "Load into the current value the attribute of the current value with the given index." 489 cost = 6 490 491 class StoreAttrIndex(Immediate): 492 "Store the source value into the attribute of the current value with the given index." 493 cost = 6 494 495 class LoadAttrIndexContextCond(Immediate): 496 """ 497 Load into the current value the attribute of the current value with the given index, only making the 498 current value the context if the attribute is compatible. 499 """ 500 cost = 8 501 502 # Access to object details. 503 504 class LoadCallable(Instruction): 505 "Load the target of an invocation." 506 cost = 2 507 508 class StoreCallable(Instruction): 509 "Store the source value into the object referenced by the current value." 510 cost = 3 511 512 # Access to invocation frames in preparation. 513 514 class MakeFrame(Immediate): 515 "Make a new invocation frame." 516 cost = 2 517 518 class AdjustFrame(Immediate): 519 "Adjust the current invocation frame for corrected invocations." 520 cost = 2 521 522 class DropFrame(Instruction): 523 "Drop an invocation frame." 524 cost = 2 525 526 class StoreFrame(Immediate): 527 "Store the current value as an argument for the parameter with the given position." 528 cost = 2 529 530 class StoreFrameIndex(Immediate): 531 "Store the source value as an argument of the current value for the parameter with the given index." 532 cost = 6 533 534 class LoadContext(Instruction): 535 "Load the context of an invocation." 536 cost = 2 537 538 # Context-related tests. 539 540 class CheckContext(Instruction): 541 "Check to see if the context is valid." 542 cost = 2 543 544 class CheckClass(Instruction): 545 "Check the current value to determine whether it is a class." 546 cost = 2 547 548 class CheckInstance(Instruction): 549 """ 550 Check the current value as an instance of a class or its subclasses (used with 'self' in an 551 invocation). 552 """ 553 cost = 6 554 555 class CheckType(Instruction): 556 "Check the current value as an instance of a specific class only." 557 cost = 5 558 559 # Access to frames upon invocation. 560 561 class CheckFrame(Immediate): 562 "Check the frame for the correct number of arguments." 563 cost = 3 564 565 class CheckExtra(Immediate): 566 "Ensure that the frame can provide extra arguments." 567 cost = 3 568 569 class FillDefaults(Immediate): 570 "Fill frame positions with defaults, if appropriate." 571 cost = 8 # variable 572 573 class ExtendFrame(Immediate): 574 "Extend the current frame for temporary storage use." 575 cost = 1 576 577 class CopyExtra(Immediate): 578 "Copy extra arguments into a separate sequence, starting from the given position." 579 cost = 10 580 581 # Invocation-related instructions, using a special result "register". 582 583 class JumpInFrame(Instruction): 584 "Jump, using the current locals, to the current callable." 585 cost = 2 586 587 class JumpWithFrame(Instruction): 588 "Jump, adopting the invocation frame, to the current callable." 589 cost = 3 590 591 class JumpWithFrameDirect(Target): 592 "Jump to the specified address, adopting the invocation frame." 593 cost = 3 594 595 class Return(Instruction): 596 "Return from a subprogram." 597 cost = 2 598 599 class LoadResult(Instruction): 600 "Load into the current value a returned value." 601 cost = 1 602 603 class StoreResult(Instruction): 604 "Store the current value as a value to be returned." 605 cost = 1 606 607 # Branch-related instructions. 608 609 class Jump(Address): 610 "Jump unconditionally." 611 cost = 1 612 613 class JumpIfFalse(Address): 614 "Jump if the last evaluation gave a false result." 615 cost = 2 616 617 class JumpIfTrue(Address): 618 "Jump if the last evaluation gave a true result." 619 cost = 2 620 621 # Exception-related instructions, using a special exception "register". 622 623 class LoadException(Instruction): 624 "Load the raised exception." 625 cost = 1 626 627 class StoreException(Instruction): 628 "Store the current object in the exception register." 629 cost = 1 630 631 class ClearException(Instruction): 632 "Reset the exception register." 633 cost = 1 634 635 class RaiseException(Instruction): 636 "Raise an exception, jumping to the active handler." 637 cost = 2 638 639 class PushHandler(Address): 640 "Push an exception handler onto the handler stack." 641 cost = 3 642 643 class PopHandler(Immediate): 644 "Pop exception handlers from the handler stack." 645 cost = 3 646 647 class CheckException(Instruction): 648 "Check the raised exception against another." 649 cost = 6 650 651 # Test instructions, operating on the boolean status register. 652 653 class TestIdentity(Instruction): 654 "Test whether the current value is identical to the source value, setting the boolean status." 655 cost = 2 656 657 class TestIdentityAddress(Address): 658 "Test whether the current value is identical to the given address, setting the boolean status." 659 cost = 2 660 661 class InvertBoolean(Instruction): 662 "Invert the boolean status." 663 cost = 1 664 665 # Instructions which affect the current value. (LoadAttrIndexContext not defined.) 666 667 current_value_instructions = ( 668 LoadConst, LoadClass, LoadFunction, LoadName, LoadTemp, 669 LoadAddress, LoadAddressContext, LoadAddressContextCond, 670 LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond, 671 LoadCallable, LoadContext, LoadResult, 672 LoadException, MakeInstance, MakeFragment, 673 CopyExtra 674 ) 675 676 # Instructions which use the current value. (LoadAttrIndexContext not defined.) 677 678 simple_input_user_instructions = ( 679 StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored 680 LoadAddressContext, LoadAddressContextCond, # as the object being referenced 681 LoadAttr, LoadAttrIndex, LoadAttrIndexContextCond, # as the object being referenced 682 StoreAttr, StoreAttrIndex, StoreCallable, # as the object being referenced 683 StoreFrameIndex, # as the object being referenced 684 StoreAddressContext, # as the context 685 LoadCallable, 686 TestIdentity, TestIdentityAddress, CheckInstance, # as one of the operands 687 CheckException, CheckFrame, FillDefaults, 688 MakeInstance, 689 CheckContext, CheckClass, CheckType, 690 LoadContext # as the object providing the result 691 ) 692 693 # vim: tabstop=4 expandtab shiftwidth=4