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