1 #!/usr/bin/env python 2 3 """ 4 Generate C code from object layouts and other deduced information. 5 6 Copyright (C) 2015, 2016 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 common import CommonOutput 23 from encoders import encode_bound_reference, encode_function_pointer, \ 24 encode_instantiator_pointer, \ 25 encode_literal_constant, encode_literal_constant_member, \ 26 encode_literal_constant_value, \ 27 encode_literal_data_initialiser, \ 28 encode_literal_instantiator, encode_literal_reference, \ 29 encode_path, \ 30 encode_predefined_reference, encode_size, \ 31 encode_symbol, encode_tablename, \ 32 encode_type_attribute, decode_type_attribute, \ 33 is_type_attribute 34 from os import listdir, mkdir 35 from os.path import exists, isdir, join, split 36 from referencing import Reference 37 38 def copy(source, target): 39 40 "Copy a text file from 'source' to 'target'." 41 42 if isdir(target): 43 target = join(target, split(source)[-1]) 44 infile = open(source) 45 outfile = open(target, "w") 46 try: 47 outfile.write(infile.read()) 48 finally: 49 outfile.close() 50 infile.close() 51 52 class Generator(CommonOutput): 53 54 "A code generator." 55 56 # NOTE: These must be synchronised with the library. 57 58 function_type = "__builtins__.core.function" 59 string_type = "__builtins__.str.string" 60 type_type = "__builtins__.core.type" 61 62 predefined_constant_members = ( 63 ("__builtins__.boolean", "False"), 64 ("__builtins__.boolean", "True"), 65 ("__builtins__.none", "None"), 66 ("__builtins__.notimplemented", "NotImplemented"), 67 ) 68 69 literal_mapping_types = ( 70 "__builtins__.dict.dict", 71 ) 72 73 literal_sequence_types = ( 74 "__builtins__.list.list", 75 "__builtins__.tuple.tuple", 76 ) 77 78 literal_instantiator_types = literal_mapping_types + literal_sequence_types 79 80 def __init__(self, importer, optimiser, output): 81 82 """ 83 Initialise the generator with the given 'importer', 'optimiser' and 84 'output' directory. 85 """ 86 87 self.importer = importer 88 self.optimiser = optimiser 89 self.output = output 90 91 def to_output(self, debug=False): 92 93 "Write the generated code." 94 95 self.check_output() 96 self.write_structures() 97 self.copy_templates(debug) 98 99 def copy_templates(self, debug=False): 100 101 "Copy template files to the generated output directory." 102 103 templates = join(split(__file__)[0], "templates") 104 105 for filename in listdir(templates): 106 target = self.output 107 108 # Handle debug resources. 109 110 if filename.endswith("-debug"): 111 if debug: 112 target = join(self.output, filename[:-len("-debug")]) 113 else: 114 continue 115 116 # Handle non-debug resources. 117 118 if debug and exists(join(templates, "%s-debug" % filename)): 119 continue 120 121 pathname = join(templates, filename) 122 123 # Copy files into the target directory. 124 125 if not isdir(pathname): 126 copy(pathname, target) 127 128 # Copy directories (such as the native code directory). 129 130 else: 131 target = join(self.output, filename) 132 133 if not exists(target): 134 mkdir(target) 135 136 for filename in listdir(pathname): 137 copy(join(pathname, filename), target) 138 139 def write_structures(self): 140 141 "Write structures used by the program." 142 143 f_consts = open(join(self.output, "progconsts.h"), "w") 144 f_defs = open(join(self.output, "progtypes.c"), "w") 145 f_decls = open(join(self.output, "progtypes.h"), "w") 146 f_signatures = open(join(self.output, "main.h"), "w") 147 f_code = open(join(self.output, "main.c"), "w") 148 149 try: 150 # Output boilerplate. 151 152 print >>f_consts, """\ 153 #ifndef __PROGCONSTS_H__ 154 #define __PROGCONSTS_H__ 155 """ 156 print >>f_decls, """\ 157 #ifndef __PROGTYPES_H__ 158 #define __PROGTYPES_H__ 159 160 #include "progconsts.h" 161 #include "types.h" 162 """ 163 print >>f_defs, """\ 164 #include "progtypes.h" 165 #include "progops.h" 166 #include "main.h" 167 """ 168 print >>f_signatures, """\ 169 #ifndef __MAIN_H__ 170 #define __MAIN_H__ 171 172 #include "types.h" 173 """ 174 print >>f_code, """\ 175 #include <string.h> 176 #include <stdio.h> 177 #include "types.h" 178 #include "exceptions.h" 179 #include "ops.h" 180 #include "progconsts.h" 181 #include "progtypes.h" 182 #include "progops.h" 183 #include "main.h" 184 """ 185 186 # Generate table and structure data. 187 188 function_instance_attrs = None 189 objects = self.optimiser.attr_table.items() 190 objects.sort() 191 192 self.callables = {} 193 194 for ref, indexes in objects: 195 attrnames = self.get_attribute_names(indexes) 196 197 kind = ref.get_kind() 198 path = ref.get_origin() 199 table_name = encode_tablename(kind, path) 200 structure_size = encode_size(kind, path) 201 202 # Generate structures for classes and modules. 203 204 if kind != "<instance>": 205 structure = [] 206 attrs = self.get_static_attributes(kind, path, attrnames) 207 208 # Set a special instantiator on the class. 209 210 if kind == "<class>": 211 212 # Write instantiator declarations based on the 213 # applicable initialiser. 214 215 init_ref = attrs["__init__"] 216 217 # Write instantiator definitions. 218 219 self.write_instantiator(f_code, f_signatures, path, init_ref) 220 221 # Record the callable for parameter table generation. 222 223 self.callables[path] = init_ref.get_origin() 224 225 # Define special attributes. 226 227 signature = self.get_signature_for_callable(path) 228 attrs["__fn__"] = path 229 attrs["__args__"] = encode_size("pmin", signature) 230 231 self.populate_structure(Reference(kind, path), attrs, kind, structure) 232 233 if kind == "<class>": 234 self.write_instance_structure(f_decls, path, structure_size) 235 236 self.write_structure(f_decls, f_defs, path, table_name, structure, 237 kind == "<class>" and path) 238 239 # Record function instance details for function generation below. 240 241 else: 242 attrs = self.get_instance_attributes(path, attrnames) 243 if path == self.function_type: 244 function_instance_attrs = attrs 245 246 # Record the callable for parameter table generation. 247 248 self.callables[path] = path 249 250 # Write a table for all objects. 251 252 table = [] 253 self.populate_table(Reference(kind, path), table) 254 self.write_table(f_decls, f_defs, table_name, structure_size, table) 255 256 # Generate function instances. 257 258 functions = self.importer.function_parameters.keys() 259 functions.sort() 260 extra_function_instances = [] 261 262 for path in functions: 263 264 # Instantiators are generated above. 265 266 if self.importer.classes.has_key(path) or not self.importer.get_object(path): 267 continue 268 269 # Record the callable for parameter table generation. 270 271 self.callables[path] = path 272 273 # Define the structure details. 274 275 cls = self.function_type 276 table_name = encode_tablename("<instance>", cls) 277 structure_size = encode_size("<instance>", path) 278 279 # Set a special callable attribute on the instance. 280 281 signature = self.get_signature_for_callable(path) 282 function_instance_attrs["__fn__"] = path 283 function_instance_attrs["__args__"] = encode_size("pmin", signature) 284 285 # Produce two structures where a method is involved. 286 287 parent, name = path.rsplit(".", 1) 288 parent_ref = self.importer.get_object(parent) 289 parent_kind = parent_ref and parent_ref.get_kind() 290 291 # Populate and write each structure. 292 293 if parent_kind == "<class>": 294 295 # A bound version of a method. 296 297 structure = self.populate_function(path, function_instance_attrs, False) 298 self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure) 299 300 # An unbound version of a method. 301 302 structure = self.populate_function(path, function_instance_attrs, True) 303 self.write_structure(f_decls, f_defs, path, table_name, structure) 304 305 else: 306 # A normal function. 307 308 structure = self.populate_function(path, function_instance_attrs, False) 309 self.write_structure(f_decls, f_defs, path, table_name, structure) 310 311 # Functions with defaults need to declare instance structures. 312 313 if self.importer.function_defaults.get(path): 314 self.write_instance_structure(f_decls, path, structure_size) 315 extra_function_instances.append(path) 316 317 # Write function declarations. 318 # Signature: __attr <name>(__attr[]); 319 320 print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path) 321 322 # Consolidate parameter tables for instantiators and functions. 323 324 parameter_tables = set() 325 326 for path, function_path in self.callables.items(): 327 parameters = self.optimiser.parameters[function_path] 328 if not parameters: 329 parameters = () 330 else: 331 parameters = tuple(parameters) 332 parameter_tables.add(parameters) 333 334 # Generate parameter tables for distinct function signatures. 335 336 for parameters in parameter_tables: 337 self.make_parameter_table(f_decls, f_defs, parameters) 338 339 # Generate predefined constants. 340 341 for path, name in self.predefined_constant_members: 342 self.make_predefined_constant(f_decls, f_defs, path, name) 343 344 # Generate literal constants. 345 346 for value, n in self.optimiser.constants.items(): 347 self.make_literal_constant(f_decls, f_defs, n, value) 348 349 # Finish the main source file. 350 351 self.write_main_program(f_code, f_signatures) 352 353 # Record size information for certain function instances as well as 354 # for classes, modules and other instances. 355 356 size_tables = {} 357 358 for kind in ["<class>", "<module>", "<instance>"]: 359 size_tables[kind] = {} 360 361 # Generate structure size data. 362 363 for ref, structure in self.optimiser.structures.items(): 364 size_tables[ref.get_kind()][ref.get_origin()] = len(structure) 365 366 for path in extra_function_instances: 367 defaults = self.importer.function_defaults[path] 368 size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults) 369 370 size_tables = size_tables.items() 371 size_tables.sort() 372 373 for kind, sizes in size_tables: 374 self.write_size_constants(f_consts, kind, sizes, 0) 375 376 # Generate parameter table size data. 377 378 min_sizes = {} 379 max_sizes = {} 380 381 for path, parameters in self.optimiser.parameters.items(): 382 argmin, argmax = self.get_argument_limits(path) 383 384 # Use the parameter signature in the constant names. 385 386 signature = self.get_parameter_signature(parameters) 387 min_sizes[signature] = argmin 388 max_sizes[signature] = argmax 389 390 # Record instantiator limits. 391 392 if path.endswith(".__init__"): 393 path = path[:-len(".__init__")] 394 395 self.write_size_constants(f_consts, "pmin", min_sizes, 0) 396 self.write_size_constants(f_consts, "pmax", max_sizes, 0) 397 398 # Generate parameter codes. 399 400 self.write_code_constants(f_consts, self.optimiser.all_paramnames, self.optimiser.arg_locations, "pcode", "ppos") 401 402 # Generate attribute codes. 403 404 self.write_code_constants(f_consts, self.optimiser.all_attrnames, self.optimiser.locations, "code", "pos") 405 406 # Output more boilerplate. 407 408 print >>f_consts, """\ 409 410 #endif /* __PROGCONSTS_H__ */""" 411 412 print >>f_decls, """\ 413 414 #define __FUNCTION_TYPE %s 415 #define __FUNCTION_INSTANCE_SIZE %s 416 #define __TYPE_CLASS_TYPE %s 417 #define __TYPE_CLASS_POS %s 418 #define __TYPE_CLASS_CODE %s 419 420 #endif /* __PROGTYPES_H__ */""" % ( 421 encode_path(self.function_type), 422 encode_size("<instance>", self.function_type), 423 encode_path(self.type_type), 424 encode_symbol("pos", encode_type_attribute(self.type_type)), 425 encode_symbol("code", encode_type_attribute(self.type_type)), 426 ) 427 428 print >>f_signatures, """\ 429 430 #endif /* __MAIN_H__ */""" 431 432 finally: 433 f_consts.close() 434 f_defs.close() 435 f_decls.close() 436 f_signatures.close() 437 f_code.close() 438 439 def make_literal_constant(self, f_decls, f_defs, n, value): 440 441 """ 442 Write literal constant details to 'f_decls' (to declare a structure) and 443 to 'f_defs' (to define the contents) for the constant with the number 444 'n' with the given literal 'value'. 445 """ 446 447 const_path = encode_literal_constant(n) 448 structure_name = encode_literal_reference(n) 449 450 # NOTE: This makes assumptions about the __builtins__ structure. 451 452 modname = value.__class__.__name__ 453 typename = modname == "str" and "string" or modname 454 ref = Reference("<instance>", "__builtins__.%s.%s" % (modname, typename)) 455 456 self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value) 457 458 def make_predefined_constant(self, f_decls, f_defs, path, name): 459 460 """ 461 Write predefined constant details to 'f_decls' (to declare a structure) 462 and to 'f_defs' (to define the contents) for the constant located in 463 'path' with the given 'name'. 464 """ 465 466 # Determine the details of the constant. 467 468 attr_path = "%s.%s" % (path, name) 469 structure_name = encode_predefined_reference(attr_path) 470 ref = self.importer.get_object(attr_path) 471 472 self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) 473 474 def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None): 475 476 """ 477 Write constant details to 'f_decls' (to declare a structure) and to 478 'f_defs' (to define the contents) for the constant described by 'ref' 479 having the given 'path' and 'structure_name' (for the constant structure 480 itself). 481 """ 482 483 # Obtain the attributes. 484 485 cls = ref.get_origin() 486 indexes = self.optimiser.attr_table[ref] 487 attrnames = self.get_attribute_names(indexes) 488 attrs = self.get_instance_attributes(cls, attrnames) 489 490 # Set the data, if provided. 491 492 if data is not None: 493 attrs["__data__"] = data 494 495 # Also set a key for dynamic attribute lookup, if a string. 496 497 if cls == self.string_type: 498 if data in self.optimiser.all_attrnames: 499 attrs["__key__"] = data 500 else: 501 attrs["__key__"] = None 502 503 # Define the structure details. An object is created for the constant, 504 # but an attribute is provided, referring to the object, for access to 505 # the constant in the program. 506 507 structure = [] 508 table_name = encode_tablename("<instance>", cls) 509 self.populate_structure(ref, attrs, ref.get_kind(), structure) 510 self.write_structure(f_decls, f_defs, structure_name, table_name, structure) 511 512 # Define a macro for the constant. 513 514 attr_name = encode_path(const_path) 515 print >>f_decls, "#define %s ((__attr) {&%s, &%s})" % (attr_name, structure_name, structure_name) 516 517 def make_parameter_table(self, f_decls, f_defs, parameters): 518 519 """ 520 Write parameter table details to 'f_decls' (to declare a table) and to 521 'f_defs' (to define the contents) for the given 'parameters'. 522 """ 523 524 # Use a signature for the table name instead of a separate name for each 525 # function. 526 527 signature = self.get_parameter_signature(parameters) 528 table_name = encode_tablename("<function>", signature) 529 structure_size = encode_size("pmax", signature) 530 531 table = [] 532 self.populate_parameter_table(parameters, table) 533 self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) 534 535 def get_parameter_signature(self, parameters): 536 537 "Return a signature for the given 'parameters'." 538 539 l = [] 540 for parameter in parameters: 541 if parameter is None: 542 l.append("") 543 else: 544 name, pos = parameter 545 l.append("%s_%s" % (name, pos)) 546 return l and "__".join(l) or "__void" 547 548 def get_signature_for_callable(self, path): 549 550 "Return the signature for the callable with the given 'path'." 551 552 function_path = self.callables[path] 553 parameters = self.optimiser.parameters[function_path] 554 return self.get_parameter_signature(parameters) 555 556 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 557 558 """ 559 Write size constants to 'f_consts' for the given 'size_prefix', using 560 the 'sizes' dictionary to populate the definition, adding the given 561 'padding' to the basic sizes. 562 """ 563 564 print >>f_consts, "enum %s {" % encode_size(size_prefix) 565 first = True 566 for path, size in sizes.items(): 567 if not first: 568 print >>f_consts, "," 569 else: 570 first = False 571 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 572 print >>f_consts, "\n };" 573 574 def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix): 575 576 """ 577 Write code constants to 'f_consts' for the given 'attrnames' and 578 attribute 'locations'. 579 """ 580 581 print >>f_consts, "enum %s {" % encode_symbol(code_prefix) 582 first = True 583 for i, attrname in enumerate(attrnames): 584 if not first: 585 print >>f_consts, "," 586 else: 587 first = False 588 f_consts.write(" %s = %d" % (encode_symbol(code_prefix, attrname), i)) 589 print >>f_consts, "\n };" 590 591 print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) 592 first = True 593 for i, attrnames in enumerate(locations): 594 for attrname in attrnames: 595 if not first: 596 print >>f_consts, "," 597 else: 598 first = False 599 f_consts.write(" %s = %d" % (encode_symbol(pos_prefix, attrname), i)) 600 print >>f_consts, "\n };" 601 602 def write_table(self, f_decls, f_defs, table_name, structure_size, table): 603 604 """ 605 Write the declarations to 'f_decls' and definitions to 'f_defs' for 606 the object having the given 'table_name' and the given 'structure_size', 607 with 'table' details used to populate the definition. 608 """ 609 610 print >>f_decls, "extern const __table %s;\n" % table_name 611 612 # Write the corresponding definition. 613 614 print >>f_defs, "const __table %s = {\n %s,\n {\n %s\n }\n };\n" % ( 615 table_name, structure_size, 616 ",\n ".join(table)) 617 618 def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): 619 620 """ 621 Write the declarations to 'f_decls' and definitions to 'f_defs' for 622 the object having the given 'table_name' and the given 'structure_size', 623 with 'table' details used to populate the definition. 624 """ 625 626 print >>f_decls, "extern const __ptable %s;\n" % table_name 627 628 # Write the corresponding definition. 629 630 print >>f_defs, "const __ptable %s = {\n %s,\n {\n %s\n }\n };\n" % ( 631 table_name, structure_size, 632 ",\n ".join([("{%s, %s}" % t) for t in table])) 633 634 def write_instance_structure(self, f_decls, path, structure_size): 635 636 """ 637 Write a declaration to 'f_decls' for the object having the given 'path' 638 and the given 'structure_size'. 639 """ 640 641 # Write an instance-specific type definition for instances of classes. 642 # See: templates/types.h 643 644 print >>f_decls, """\ 645 typedef struct { 646 const __table * table; 647 unsigned int pos; 648 __attr attrs[%s]; 649 } %s; 650 """ % (structure_size, encode_symbol("obj", path)) 651 652 def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None): 653 654 """ 655 Write the declarations to 'f_decls' and definitions to 'f_defs' for 656 the object having the given 'structure_name', the given 'table_name', 657 and the given 'structure' details used to populate the definition. 658 """ 659 660 if f_decls: 661 print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) 662 663 is_class = path and self.importer.get_object(path).has_kind("<class>") 664 pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" 665 666 print >>f_defs, """\ 667 __obj %s = { 668 &%s, 669 %s, 670 { 671 %s 672 }}; 673 """ % ( 674 encode_path(structure_name), table_name, pos, 675 ",\n ".join(structure)) 676 677 def get_argument_limits(self, path): 678 679 """ 680 Return the argument minimum and maximum for the callable at 'path', 681 adding an argument position for a universal context. 682 """ 683 684 parameters = self.importer.function_parameters[path] 685 defaults = self.importer.function_defaults.get(path) 686 num_parameters = len(parameters) + 1 687 return num_parameters - (defaults and len(defaults) or 0), num_parameters 688 689 def get_attribute_names(self, indexes): 690 691 """ 692 Given a list of attribute table 'indexes', return a list of attribute 693 names. 694 """ 695 696 all_attrnames = self.optimiser.all_attrnames 697 attrnames = [] 698 for i in indexes: 699 if i is None: 700 attrnames.append(None) 701 else: 702 attrnames.append(all_attrnames[i]) 703 return attrnames 704 705 def get_static_attributes(self, kind, name, attrnames): 706 707 """ 708 Return a mapping of attribute names to paths for attributes belonging 709 to objects of the given 'kind' (being "<class>" or "<module>") with 710 the given 'name' and supporting the given 'attrnames'. 711 """ 712 713 attrs = {} 714 715 for attrname in attrnames: 716 if attrname is None: 717 continue 718 if kind == "<class>": 719 path = self.importer.all_class_attrs[name][attrname] 720 elif kind == "<module>": 721 path = "%s.%s" % (name, attrname) 722 else: 723 continue 724 725 # The module may be hidden. 726 727 attr = self.importer.get_object(path) 728 if not attr: 729 module = self.importer.hidden.get(path) 730 if module: 731 attr = Reference(module.name, "<module>") 732 attrs[attrname] = attr 733 734 return attrs 735 736 def get_instance_attributes(self, name, attrnames): 737 738 """ 739 Return a mapping of attribute names to references for attributes 740 belonging to instances of the class with the given 'name', where the 741 given 'attrnames' are supported. 742 """ 743 744 consts = self.importer.all_instance_attr_constants[name] 745 attrs = {} 746 for attrname in attrnames: 747 if attrname is None: 748 continue 749 const = consts.get(attrname) 750 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 751 return attrs 752 753 def populate_table(self, path, table): 754 755 """ 756 Traverse the attributes in the determined order for the structure having 757 the given 'path', adding entries to the attribute 'table'. 758 """ 759 760 for attrname in self.optimiser.structures[path]: 761 762 # Handle gaps in the structure. 763 764 if attrname is None: 765 table.append("0") 766 else: 767 table.append(encode_symbol("code", attrname)) 768 769 def populate_parameter_table(self, parameters, table): 770 771 """ 772 Traverse the 'parameters' in the determined order, adding entries to the 773 attribute 'table'. 774 """ 775 776 for value in parameters: 777 778 # Handle gaps in the structure. 779 780 if value is None: 781 table.append(("0", "0")) 782 else: 783 name, pos = value 784 table.append((encode_symbol("pcode", name), pos)) 785 786 def populate_function(self, path, function_instance_attrs, unbound=False): 787 788 """ 789 Populate a structure for the function with the given 'path'. The given 790 'attrs' provide the instance attributes, and if 'unbound' is set to a 791 true value, an unbound method structure is produced (as opposed to a 792 callable bound method structure). 793 """ 794 795 structure = [] 796 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure, unbound) 797 798 # Append default members. 799 800 self.append_defaults(path, structure) 801 return structure 802 803 def populate_structure(self, ref, attrs, kind, structure, unbound=False): 804 805 """ 806 Traverse the attributes in the determined order for the structure having 807 the given 'ref' whose members are provided by the 'attrs' mapping, in a 808 structure of the given 'kind', adding entries to the object 'structure'. 809 If 'unbound' is set to a true value, an unbound method function pointer 810 will be employed, with a reference to the bound method incorporated into 811 the special __fn__ attribute. 812 """ 813 814 # Populate function instance structures for functions. 815 816 if ref.has_kind("<function>"): 817 origin = self.function_type 818 structure_ref = Reference("<instance>", self.function_type) 819 820 # Otherwise, just populate the appropriate structures. 821 822 else: 823 origin = ref.get_origin() 824 structure_ref = ref 825 826 for attrname in self.optimiser.structures[structure_ref]: 827 828 # Handle gaps in the structure. 829 830 if attrname is None: 831 structure.append("{0, 0}") 832 833 # Handle non-constant and constant members. 834 835 else: 836 attr = attrs[attrname] 837 838 # Special function pointer member. 839 840 if attrname == "__fn__": 841 842 # Provide bound method references and the unbound function 843 # pointer if populating methods in a class. 844 845 bound_attr = None 846 847 # Classes offer instantiators. 848 849 if kind == "<class>": 850 attr = encode_instantiator_pointer(attr) 851 852 # Methods offers references to bound versions and an unbound 853 # method function. 854 855 elif unbound: 856 bound_attr = encode_bound_reference(attr) 857 attr = "__unbound_method" 858 859 # Other functions just offer function pointers. 860 861 else: 862 attr = encode_function_pointer(attr) 863 864 structure.append("{%s, .fn=%s}" % (bound_attr and ".b=&%s" % bound_attr or "0", attr)) 865 continue 866 867 # Special argument specification member. 868 869 elif attrname == "__args__": 870 signature = self.get_signature_for_callable(ref.get_origin()) 871 ptable = encode_tablename("<function>", signature) 872 873 structure.append("{.min=%s, .ptable=&%s}" % (attr, ptable)) 874 continue 875 876 # Special internal data member. 877 878 elif attrname == "__data__": 879 structure.append("{0, .%s=%s}" % (encode_literal_constant_member(attr), 880 encode_literal_constant_value(attr))) 881 continue 882 883 # Special internal key member. 884 885 elif attrname == "__key__": 886 structure.append("{.code=%s, .pos=%s}" % (attr and encode_symbol("code", attr) or "0", 887 attr and encode_symbol("pos", attr) or "0")) 888 continue 889 890 # Special cases. 891 892 elif attrname in ("__file__", "__fname__", "__mname__", "__name__"): 893 path = ref.get_origin() 894 895 if attrname == "__file__": 896 module = self.importer.get_module(path) 897 value = module.filename 898 else: 899 value = path 900 901 local_number = self.importer.all_constants[path][value] 902 constant_name = "$c%d" % local_number 903 attr_path = "%s.%s" % (path, constant_name) 904 constant_number = self.optimiser.constant_numbers[attr_path] 905 constant_value = "__const%d" % constant_number 906 structure.append("%s /* %s */" % (constant_value, attrname)) 907 continue 908 909 # Special class relationship attributes. 910 911 elif is_type_attribute(attrname): 912 structure.append("{0, &%s}" % encode_path(decode_type_attribute(attrname))) 913 continue 914 915 structure.append(self.encode_member(origin, attrname, attr, kind)) 916 917 def encode_member(self, path, name, ref, structure_type): 918 919 """ 920 Encode within the structure provided by 'path', the member whose 'name' 921 provides 'ref', within the given 'structure_type'. 922 """ 923 924 kind = ref.get_kind() 925 origin = ref.get_origin() 926 927 # References to constant literals. 928 929 if kind == "<instance>" and ref.is_constant_alias(): 930 alias = ref.get_name() 931 932 # Obtain a constant value directly assigned to the attribute. 933 934 if self.optimiser.constant_numbers.has_key(alias): 935 constant_number = self.optimiser.constant_numbers[alias] 936 constant_value = "__const%d" % constant_number 937 return "%s /* %s */" % (constant_value, name) 938 939 # Predefined constant references. 940 941 if (path, name) in self.predefined_constant_members: 942 attr_path = encode_predefined_reference("%s.%s" % (path, name)) 943 return "{&%s, &%s} /* %s */" % (attr_path, attr_path, name) 944 945 # General undetermined members. 946 947 if kind in ("<var>", "<instance>"): 948 return "{0, 0} /* %s */" % name 949 950 # Set the context depending on the kind of attribute. 951 # For methods: {&<parent>, &<attr>} 952 # For other attributes: {&<attr>, &<attr>} 953 954 else: 955 if kind == "<function>" and structure_type == "<class>": 956 parent = origin.rsplit(".", 1)[0] 957 context = "&%s" % encode_path(parent) 958 elif kind == "<instance>": 959 context = "&%s" % encode_path(origin) 960 else: 961 context = "0" 962 return "{%s, &%s}" % (context, encode_path(origin)) 963 964 def append_defaults(self, path, structure): 965 966 """ 967 For the given 'path', append default parameter members to the given 968 'structure'. 969 """ 970 971 for name, default in self.importer.function_defaults.get(path): 972 structure.append(self.encode_member(path, name, default, "<instance>")) 973 974 def write_instantiator(self, f_code, f_signatures, path, init_ref): 975 976 """ 977 Write an instantiator to 'f_code', with a signature to 'f_signatures', 978 for instances of the class with the given 'path', with 'init_ref' as the 979 initialiser function reference. 980 981 NOTE: This also needs to initialise any __fn__ and __args__ members 982 NOTE: where __call__ is provided by the class. 983 """ 984 985 parameters = self.importer.function_parameters[init_ref.get_origin()] 986 987 print >>f_code, """\ 988 __attr %s(__attr __args[]) 989 { 990 /* Allocate the structure. */ 991 __args[0] = __new(&%s, &%s, sizeof(%s)); 992 993 /* Call the initialiser. */ 994 %s(__args); 995 996 /* Return the allocated object details. */ 997 return __args[0]; 998 } 999 """ % ( 1000 encode_instantiator_pointer(path), 1001 encode_tablename("<instance>", path), 1002 encode_path(path), 1003 encode_symbol("obj", path), 1004 encode_function_pointer(init_ref.get_origin()) 1005 ) 1006 1007 print >>f_signatures, "#define __HAVE_%s" % encode_path(path) 1008 print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) 1009 1010 # Write additional literal instantiators. These do not call the 1011 # initialisers but instead populate the structures directly. 1012 1013 if path in self.literal_instantiator_types: 1014 if path in self.literal_mapping_types: 1015 style = "mapping" 1016 else: 1017 style = "sequence" 1018 1019 print >>f_code, """\ 1020 __attr %s(__attr __args[], unsigned int number) 1021 { 1022 /* Allocate the structure. */ 1023 __args[0] = __new(&%s, &%s, sizeof(%s)); 1024 1025 /* Allocate a structure for the data and set it on the __data__ attribute. */ 1026 %s(__args, number); 1027 1028 /* Return the allocated object details. */ 1029 return __args[0]; 1030 } 1031 """ % ( 1032 encode_literal_instantiator(path), 1033 encode_tablename("<instance>", path), 1034 encode_path(path), 1035 encode_symbol("obj", path), 1036 encode_literal_data_initialiser(style) 1037 ) 1038 1039 print >>f_signatures, "__attr %s(__attr[], unsigned int);" % encode_literal_instantiator(path) 1040 1041 def write_main_program(self, f_code, f_signatures): 1042 1043 """ 1044 Write the main program to 'f_code', invoking the program's modules. Also 1045 write declarations for module main functions to 'f_signatures'. 1046 """ 1047 1048 print >>f_code, """\ 1049 int main(int argc, char *argv[]) 1050 { 1051 __exc __tmp_exc; 1052 1053 __Try 1054 {""" 1055 1056 for name in self.importer.order_modules(): 1057 function_name = "__main_%s" % encode_path(name) 1058 print >>f_signatures, "void %s();" % function_name 1059 1060 # Omit the native modules. 1061 1062 parts = name.split(".") 1063 1064 if parts[0] != "native": 1065 print >>f_code, """\ 1066 %s();""" % function_name 1067 1068 print >>f_code, """\ 1069 return 0; 1070 } 1071 __Catch(__tmp_exc) 1072 { 1073 fprintf(stderr, "Program terminated due to exception: %%s.\\n", 1074 __load_via_object( 1075 %s((__attr[]) {{0, 0}, __tmp_exc.arg}).value, 1076 %s).strvalue); 1077 return 1; 1078 } 1079 } 1080 """ % ( 1081 encode_function_pointer("__builtins__.str.str"), 1082 encode_symbol("pos", "__data__") 1083 ) 1084 1085 # vim: tabstop=4 expandtab shiftwidth=4