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