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