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 # Initialise the size, if a string. 504 505 if attrs.has_key("__size__"): 506 attrs["__size__"] = len(data) 507 508 # Define Unicode constant encoding details. 509 510 if cls == self.unicode_type: 511 512 # Reference the encoding's own constant value. 513 514 if encoding: 515 n = self.optimiser.constants[(encoding, self.string_type, None)] 516 517 # Employ a special alias that will be tested specifically in 518 # encode_member. 519 520 encoding_ref = Reference("<instance>", self.string_type, "$c%d" % n) 521 522 # Use None where no encoding was indicated. 523 524 else: 525 encoding_ref = Reference("<instance>", self.none_type) 526 527 attrs["encoding"] = encoding_ref 528 529 # Define the structure details. An object is created for the constant, 530 # but an attribute is provided, referring to the object, for access to 531 # the constant in the program. 532 533 structure = [] 534 table_name = encode_tablename("<instance>", cls) 535 self.populate_structure(ref, attrs, ref.get_kind(), structure) 536 self.write_structure(f_decls, f_defs, structure_name, table_name, structure) 537 538 # Define a macro for the constant. 539 540 attr_name = encode_path(const_path) 541 print >>f_decls, "#define %s ((__attr) {{.context=&%s, .value=&%s}})" % (attr_name, structure_name, structure_name) 542 543 def make_parameter_table(self, f_decls, f_defs, parameters): 544 545 """ 546 Write parameter table details to 'f_decls' (to declare a table) and to 547 'f_defs' (to define the contents) for the given 'parameters'. 548 """ 549 550 # Use a signature for the table name instead of a separate name for each 551 # function. 552 553 signature = self.get_parameter_signature(parameters) 554 table_name = encode_tablename("<function>", signature) 555 structure_size = encode_size("pmax", signature) 556 557 table = [] 558 self.populate_parameter_table(parameters, table) 559 self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) 560 561 def get_parameter_signature(self, parameters): 562 563 "Return a signature for the given 'parameters'." 564 565 l = [] 566 for parameter in parameters: 567 if parameter is None: 568 l.append("") 569 else: 570 name, pos = parameter 571 l.append("%s_%s" % (name, pos)) 572 return l and "__".join(l) or "__void" 573 574 def get_signature_for_callable(self, path): 575 576 "Return the signature for the callable with the given 'path'." 577 578 function_path = self.callables[path] 579 parameters = self.optimiser.parameters[function_path] 580 return self.get_parameter_signature(parameters) 581 582 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 583 584 """ 585 Write size constants to 'f_consts' for the given 'size_prefix', using 586 the 'sizes' dictionary to populate the definition, adding the given 587 'padding' to the basic sizes. 588 """ 589 590 print >>f_consts, "enum %s {" % encode_size(size_prefix) 591 first = True 592 for path, size in sizes.items(): 593 if not first: 594 print >>f_consts, "," 595 else: 596 first = False 597 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 598 print >>f_consts, "\n };" 599 600 def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix): 601 602 """ 603 Write code constants to 'f_consts' for the given 'attrnames' and 604 attribute 'locations'. 605 """ 606 607 print >>f_consts, "enum %s {" % encode_symbol(code_prefix) 608 first = True 609 for i, attrname in enumerate(attrnames): 610 if not first: 611 print >>f_consts, "," 612 else: 613 first = False 614 f_consts.write(" %s = %d" % (encode_symbol(code_prefix, attrname), i)) 615 print >>f_consts, "\n };" 616 617 print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) 618 first = True 619 for i, attrnames in enumerate(locations): 620 for attrname in attrnames: 621 if not first: 622 print >>f_consts, "," 623 else: 624 first = False 625 f_consts.write(" %s = %d" % (encode_symbol(pos_prefix, attrname), i)) 626 print >>f_consts, "\n };" 627 628 def write_table(self, f_decls, f_defs, table_name, structure_size, table): 629 630 """ 631 Write the declarations to 'f_decls' and definitions to 'f_defs' for 632 the object having the given 'table_name' and the given 'structure_size', 633 with 'table' details used to populate the definition. 634 """ 635 636 print >>f_decls, "extern const __table %s;\n" % table_name 637 638 # Write the corresponding definition. 639 640 print >>f_defs, """\ 641 const __table %s = { 642 %s, 643 { 644 %s 645 } 646 }; 647 """ % (table_name, structure_size, ",\n ".join(table)) 648 649 def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): 650 651 """ 652 Write the declarations to 'f_decls' and definitions to 'f_defs' for 653 the object having the given 'table_name' and the given 'structure_size', 654 with 'table' details used to populate the definition. 655 """ 656 657 members = [] 658 for t in table: 659 members.append("{%s, %s}" % t) 660 661 print >>f_decls, "extern const __ptable %s;\n" % table_name 662 663 # Write the corresponding definition. 664 665 print >>f_defs, """\ 666 const __ptable %s = { 667 %s, 668 { 669 %s 670 } 671 }; 672 """ % (table_name, structure_size, ",\n ".join(members)) 673 674 def write_instance_structure(self, f_decls, path, structure_size): 675 676 """ 677 Write a declaration to 'f_decls' for the object having the given 'path' 678 and the given 'structure_size'. 679 """ 680 681 # Write an instance-specific type definition for instances of classes. 682 # See: templates/types.h 683 684 print >>f_decls, """\ 685 typedef struct { 686 const __table * table; 687 __pos pos; 688 __attr attrs[%s]; 689 } %s; 690 """ % (structure_size, encode_symbol("obj", path)) 691 692 def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None): 693 694 """ 695 Write the declarations to 'f_decls' and definitions to 'f_defs' for 696 the object having the given 'structure_name', the given 'table_name', 697 and the given 'structure' details used to populate the definition. 698 """ 699 700 if f_decls: 701 print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) 702 703 is_class = path and self.importer.get_object(path).has_kind("<class>") 704 pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" 705 706 print >>f_defs, """\ 707 __obj %s = { 708 &%s, 709 %s, 710 { 711 %s 712 }}; 713 """ % ( 714 encode_path(structure_name), table_name, pos, 715 ",\n ".join(structure)) 716 717 def get_argument_limits(self, path): 718 719 """ 720 Return the argument minimum and maximum for the callable at 'path', 721 adding an argument position for a universal context. 722 """ 723 724 parameters = self.importer.function_parameters[path] 725 defaults = self.importer.function_defaults.get(path) 726 num_parameters = len(parameters) + 1 727 return num_parameters - (defaults and len(defaults) or 0), num_parameters 728 729 def get_attribute_names(self, indexes): 730 731 """ 732 Given a list of attribute table 'indexes', return a list of attribute 733 names. 734 """ 735 736 all_attrnames = self.optimiser.all_attrnames 737 attrnames = [] 738 for i in indexes: 739 if i is None: 740 attrnames.append(None) 741 else: 742 attrnames.append(all_attrnames[i]) 743 return attrnames 744 745 def get_static_attributes(self, kind, name, attrnames): 746 747 """ 748 Return a mapping of attribute names to paths for attributes belonging 749 to objects of the given 'kind' (being "<class>" or "<module>") with 750 the given 'name' and supporting the given 'attrnames'. 751 """ 752 753 attrs = {} 754 755 for attrname in attrnames: 756 if attrname is None: 757 continue 758 if kind == "<class>": 759 path = self.importer.all_class_attrs[name][attrname] 760 elif kind == "<module>": 761 path = "%s.%s" % (name, attrname) 762 else: 763 continue 764 765 # The module may be hidden. 766 767 attr = self.importer.get_object(path) 768 if not attr: 769 module = self.importer.hidden.get(path) 770 if module: 771 attr = Reference(module.name, "<module>") 772 attrs[attrname] = attr 773 774 return attrs 775 776 def get_instance_attributes(self, name, attrnames): 777 778 """ 779 Return a mapping of attribute names to references for attributes 780 belonging to instances of the class with the given 'name', where the 781 given 'attrnames' are supported. 782 """ 783 784 consts = self.importer.all_instance_attr_constants[name] 785 attrs = {} 786 for attrname in attrnames: 787 if attrname is None: 788 continue 789 const = consts.get(attrname) 790 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 791 return attrs 792 793 def populate_table(self, path, table): 794 795 """ 796 Traverse the attributes in the determined order for the structure having 797 the given 'path', adding entries to the attribute 'table'. 798 """ 799 800 for attrname in self.optimiser.structures[path]: 801 802 # Handle gaps in the structure. 803 804 if attrname is None: 805 table.append("0") 806 else: 807 table.append(encode_symbol("code", attrname)) 808 809 def populate_parameter_table(self, parameters, table): 810 811 """ 812 Traverse the 'parameters' in the determined order, adding entries to the 813 attribute 'table'. 814 """ 815 816 for value in parameters: 817 818 # Handle gaps in the structure. 819 820 if value is None: 821 table.append(("0", "0")) 822 else: 823 name, pos = value 824 table.append((encode_symbol("pcode", name), pos)) 825 826 def populate_function(self, path, function_instance_attrs): 827 828 """ 829 Populate a structure for the function with the given 'path'. The given 830 'attrs' provide the instance attributes. 831 """ 832 833 structure = [] 834 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure) 835 836 # Append default members. 837 838 self.append_defaults(path, structure) 839 return structure 840 841 def populate_structure(self, ref, attrs, kind, structure): 842 843 """ 844 Traverse the attributes in the determined order for the structure having 845 the given 'ref' whose members are provided by the 'attrs' mapping, in a 846 structure of the given 'kind', adding entries to the object 'structure'. 847 """ 848 849 # Populate function instance structures for functions. 850 851 if ref.has_kind("<function>"): 852 origin = self.function_type 853 structure_ref = Reference("<instance>", self.function_type) 854 855 # Otherwise, just populate the appropriate structures. 856 857 else: 858 origin = ref.get_origin() 859 structure_ref = ref 860 861 for attrname in self.optimiser.structures[structure_ref]: 862 863 # Handle gaps in the structure. 864 865 if attrname is None: 866 structure.append("__NULL") 867 868 # Handle non-constant and constant members. 869 870 else: 871 attr = attrs[attrname] 872 873 # Special function pointer member. 874 875 if attrname == "__fn__": 876 877 # Classes offer instantiators which can be called without a 878 # context. 879 880 if kind == "<class>": 881 attr = encode_instantiator_pointer(attr) 882 unbound_attr = attr 883 884 # Provide bound method and unbound function pointers if 885 # populating methods in a class. 886 887 else: 888 attr = encode_function_pointer(attr) 889 unbound_attr = "__unbound_method" 890 891 structure.append("{{.inv=%s, .fn=%s}}" % (unbound_attr, attr)) 892 continue 893 894 # Special argument specification member. 895 896 elif attrname == "__args__": 897 signature = self.get_signature_for_callable(ref.get_origin()) 898 ptable = encode_tablename("<function>", signature) 899 900 structure.append("{{.min=%s, .ptable=&%s}}" % (attr, ptable)) 901 continue 902 903 # Special internal data member. 904 905 elif attrname == "__data__": 906 structure.append("{.%s=%s}" % ( 907 encode_literal_constant_member(attr), 908 encode_literal_constant_value(attr))) 909 continue 910 911 # Special internal size member. 912 913 elif attrname == "__size__": 914 structure.append("{.intvalue=%d}" % attr) 915 continue 916 917 # Special internal key member. 918 919 elif attrname == "__key__": 920 structure.append("{{.code=%s, .pos=%s}}" % (attr and encode_symbol("code", attr) or "0", 921 attr and encode_symbol("pos", attr) or "0")) 922 continue 923 924 # Special cases. 925 926 elif attrname in ("__file__", "__name__"): 927 path = ref.get_origin() 928 value_type = self.string_type 929 930 # Provide constant values. These must match the values 931 # originally recorded during inspection. 932 933 if attrname == "__file__": 934 module = self.importer.get_module(path) 935 value = module.filename 936 937 # Function and class names are leafnames. 938 939 elif attrname == "__name__" and not ref.has_kind("<module>"): 940 value = path.rsplit(".", 1)[-1] 941 942 # All other names just use the object path information. 943 944 else: 945 value = path 946 947 encoding = None 948 949 local_number = self.importer.all_constants[path][(value, value_type, encoding)] 950 constant_name = "$c%d" % local_number 951 attr_path = "%s.%s" % (path, constant_name) 952 constant_number = self.optimiser.constant_numbers[attr_path] 953 constant_value = "__const%d" % constant_number 954 structure.append("%s /* %s */" % (constant_value, attrname)) 955 continue 956 957 elif attrname == "__parent__": 958 path = ref.get_origin() 959 960 # Parents of classes and functions are derived from their 961 # object paths. 962 963 value = path.rsplit(".", 1)[0] 964 structure.append("{{.context=0, .value=&%s}}" % encode_path(value)) 965 continue 966 967 # Special class relationship attributes. 968 969 elif is_type_attribute(attrname): 970 structure.append("{{.context=0, .value=&%s}}" % encode_path(decode_type_attribute(attrname))) 971 continue 972 973 # All other kinds of members. 974 975 structure.append(self.encode_member(origin, attrname, attr, kind)) 976 977 def encode_member(self, path, name, ref, structure_type): 978 979 """ 980 Encode within the structure provided by 'path', the member whose 'name' 981 provides 'ref', within the given 'structure_type'. 982 """ 983 984 kind = ref.get_kind() 985 origin = ref.get_origin() 986 987 # References to constant literals. 988 989 if kind == "<instance>" and ref.is_constant_alias(): 990 alias = ref.get_name() 991 992 # Use the alias directly if appropriate. 993 994 if alias.startswith("$c"): 995 constant_value = encode_literal_constant(int(alias[2:])) 996 return "%s /* %s */" % (constant_value, name) 997 998 # Obtain a constant value directly assigned to the attribute. 999 1000 if self.optimiser.constant_numbers.has_key(alias): 1001 constant_number = self.optimiser.constant_numbers[alias] 1002 constant_value = encode_literal_constant(constant_number) 1003 return "%s /* %s */" % (constant_value, name) 1004 1005 # Usage of predefined constants, currently only None supported. 1006 1007 if kind == "<instance>" and origin == self.none_type: 1008 attr_path = encode_predefined_reference(self.none_value) 1009 return "{{.context=&%s, .value=&%s}} /* %s */" % (attr_path, attr_path, name) 1010 1011 # Predefined constant members. 1012 1013 if (path, name) in self.predefined_constant_members: 1014 attr_path = encode_predefined_reference("%s.%s" % (path, name)) 1015 return "{{.context=&%s, .value=&%s}} /* %s */" % (attr_path, attr_path, name) 1016 1017 # General undetermined members. 1018 1019 if kind in ("<var>", "<instance>"): 1020 attr_path = encode_predefined_reference(self.none_value) 1021 return "{{.context=&%s, .value=&%s}} /* %s */" % (attr_path, attr_path, name) 1022 1023 # Set the context depending on the kind of attribute. 1024 # For methods: {&<parent>, &<attr>} 1025 # For other attributes: {&<attr>, &<attr>} 1026 1027 else: 1028 if kind == "<function>" and structure_type == "<class>": 1029 parent = origin.rsplit(".", 1)[0] 1030 context = "&%s" % encode_path(parent) 1031 elif kind == "<instance>": 1032 context = "&%s" % encode_path(origin) 1033 else: 1034 context = "0" 1035 return "{{.context=%s, .value=&%s}}" % (context, encode_path(origin)) 1036 1037 def append_defaults(self, path, structure): 1038 1039 """ 1040 For the given 'path', append default parameter members to the given 1041 'structure'. 1042 """ 1043 1044 for name, default in self.importer.function_defaults.get(path): 1045 structure.append(self.encode_member(path, name, default, "<instance>")) 1046 1047 def write_instantiator(self, f_code, f_signatures, path, init_ref): 1048 1049 """ 1050 Write an instantiator to 'f_code', with a signature to 'f_signatures', 1051 for instances of the class with the given 'path', with 'init_ref' as the 1052 initialiser function reference. 1053 1054 NOTE: This also needs to initialise any __fn__ and __args__ members 1055 NOTE: where __call__ is provided by the class. 1056 """ 1057 1058 parameters = self.importer.function_parameters[init_ref.get_origin()] 1059 1060 print >>f_code, """\ 1061 __attr %s(__attr __args[]) 1062 { 1063 /* Allocate the structure. */ 1064 __args[0] = __NEWINSTANCE(%s); 1065 1066 /* Call the initialiser. */ 1067 %s(__args); 1068 1069 /* Return the allocated object details. */ 1070 return __args[0]; 1071 } 1072 """ % ( 1073 encode_instantiator_pointer(path), 1074 encode_path(path), 1075 encode_function_pointer(init_ref.get_origin()) 1076 ) 1077 1078 print >>f_signatures, "#define __HAVE_%s" % encode_path(path) 1079 print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) 1080 1081 # Write additional literal instantiators. These do not call the 1082 # initialisers but instead populate the structures directly. 1083 1084 if path in self.literal_instantiator_types: 1085 if path in self.literal_mapping_types: 1086 style = "mapping" 1087 else: 1088 style = "sequence" 1089 1090 print >>f_code, """\ 1091 __attr %s(__attr __args[], __pos number) 1092 { 1093 /* Allocate the structure. */ 1094 __args[0] = __NEWINSTANCE(%s); 1095 1096 /* Allocate a structure for the data and set it on the __data__ attribute. */ 1097 %s(__args, number); 1098 1099 /* Return the allocated object details. */ 1100 return __args[0]; 1101 } 1102 """ % ( 1103 encode_literal_instantiator(path), 1104 encode_path(path), 1105 encode_literal_data_initialiser(style) 1106 ) 1107 1108 print >>f_signatures, "__attr %s(__attr[], __pos);" % encode_literal_instantiator(path) 1109 1110 def write_main_program(self, f_code, f_signatures): 1111 1112 """ 1113 Write the main program to 'f_code', invoking the program's modules. Also 1114 write declarations for module main functions to 'f_signatures'. 1115 """ 1116 1117 print >>f_code, """\ 1118 int main(int argc, char *argv[]) 1119 { 1120 __exc __tmp_exc; 1121 1122 GC_INIT(); 1123 1124 __Try 1125 {""" 1126 1127 for name in self.importer.order_modules(): 1128 function_name = "__main_%s" % encode_path(name) 1129 print >>f_signatures, "void %s();" % function_name 1130 1131 # Omit the native modules. 1132 1133 parts = name.split(".") 1134 1135 if parts[0] != "native": 1136 print >>f_code, """\ 1137 %s();""" % function_name 1138 1139 print >>f_code, """\ 1140 } 1141 __Catch(__tmp_exc) 1142 { 1143 if (__ISINSTANCE(__tmp_exc.arg, ((__attr) {{.context=0, .value=&__builtins___exception_system_SystemExit}}))) 1144 return __load_via_object( 1145 __load_via_object(__tmp_exc.arg.value, %s).value, 1146 %s).intvalue; 1147 1148 fprintf(stderr, "Program terminated due to exception: %%s.\\n", 1149 __load_via_object( 1150 %s((__attr[]) {__NULL, __tmp_exc.arg}).value, 1151 %s).strvalue); 1152 return 1; 1153 } 1154 1155 return 0; 1156 } 1157 """ % ( 1158 encode_symbol("pos", "value"), 1159 encode_symbol("pos", "__data__"), 1160 encode_function_pointer("__builtins__.str.str"), 1161 encode_symbol("pos", "__data__") 1162 ) 1163 1164 # vim: tabstop=4 expandtab shiftwidth=4