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