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