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, get_builtin_module, get_builtin_type 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_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 # Produce two structures where a method is involved. 276 277 parent, name = path.rsplit(".", 1) 278 parent_ref = self.importer.get_object(parent) 279 parent_kind = parent_ref and parent_ref.get_kind() 280 281 # Populate and write each structure. 282 283 if parent_kind == "<class>": 284 285 # A bound version of a method. 286 287 structure = self.populate_function(path, function_instance_attrs, False) 288 self.write_structure(f_decls, f_defs, encode_bound_reference(path), table_name, structure) 289 290 # An unbound version of a method. 291 292 structure = self.populate_function(path, function_instance_attrs, True) 293 self.write_structure(f_decls, f_defs, path, table_name, structure) 294 295 else: 296 # A normal function. 297 298 structure = self.populate_function(path, function_instance_attrs, False) 299 self.write_structure(f_decls, f_defs, path, table_name, structure) 300 301 # Functions with defaults need to declare instance structures. 302 303 if self.importer.function_defaults.get(path): 304 self.write_instance_structure(f_decls, path, structure_size) 305 extra_function_instances.append(path) 306 307 # Write function declarations. 308 # Signature: __attr <name>(__attr[]); 309 310 print >>f_signatures, "__attr %s(__attr args[]);" % encode_function_pointer(path) 311 312 # Consolidate parameter tables for instantiators and functions. 313 314 parameter_tables = set() 315 316 for path, function_path in self.callables.items(): 317 parameters = self.optimiser.parameters[function_path] 318 if not parameters: 319 parameters = () 320 else: 321 parameters = tuple(parameters) 322 parameter_tables.add(parameters) 323 324 # Generate parameter tables for distinct function signatures. 325 326 for parameters in parameter_tables: 327 self.make_parameter_table(f_decls, f_defs, parameters) 328 329 # Generate predefined constants. 330 331 for path, name in self.predefined_constant_members: 332 self.make_predefined_constant(f_decls, f_defs, path, name) 333 334 # Generate literal constants. 335 336 for constant, n in self.optimiser.constants.items(): 337 self.make_literal_constant(f_decls, f_defs, n, constant) 338 339 # Finish the main source file. 340 341 self.write_main_program(f_code, f_signatures) 342 343 # Record size information for certain function instances as well as 344 # for classes, modules and other instances. 345 346 size_tables = {} 347 348 for kind in ["<class>", "<module>", "<instance>"]: 349 size_tables[kind] = {} 350 351 # Generate structure size data. 352 353 for ref, structure in self.optimiser.structures.items(): 354 size_tables[ref.get_kind()][ref.get_origin()] = len(structure) 355 356 for path in extra_function_instances: 357 defaults = self.importer.function_defaults[path] 358 size_tables["<instance>"][path] = size_tables["<instance>"][self.function_type] + len(defaults) 359 360 size_tables = size_tables.items() 361 size_tables.sort() 362 363 for kind, sizes in size_tables: 364 self.write_size_constants(f_consts, kind, sizes, 0) 365 366 # Generate parameter table size data. 367 368 min_sizes = {} 369 max_sizes = {} 370 371 # Determine the minimum number of parameters for each 372 373 for path in self.optimiser.parameters.keys(): 374 argmin, argmax = self.get_argument_limits(path) 375 min_sizes[path] = argmin 376 377 # Use the parameter table details to define the maximum number. 378 # The context is already present in the collection. 379 380 for parameters in parameter_tables: 381 signature = self.get_parameter_signature(parameters) 382 max_sizes[signature] = len(parameters) 383 384 self.write_size_constants(f_consts, "pmin", min_sizes, 0) 385 self.write_size_constants(f_consts, "pmax", max_sizes, 0) 386 387 # Generate parameter codes. 388 389 self.write_code_constants(f_consts, self.optimiser.all_paramnames, self.optimiser.arg_locations, "pcode", "ppos") 390 391 # Generate attribute codes. 392 393 self.write_code_constants(f_consts, self.optimiser.all_attrnames, self.optimiser.locations, "code", "pos") 394 395 # Output more boilerplate. 396 397 print >>f_consts, """\ 398 399 #endif /* __PROGCONSTS_H__ */""" 400 401 print >>f_decls, """\ 402 403 #define __FUNCTION_TYPE %s 404 #define __FUNCTION_INSTANCE_SIZE %s 405 #define __TYPE_CLASS_TYPE %s 406 #define __TYPE_CLASS_POS %s 407 #define __TYPE_CLASS_CODE %s 408 409 #endif /* __PROGTYPES_H__ */""" % ( 410 encode_path(self.function_type), 411 encode_size("<instance>", self.function_type), 412 encode_path(self.type_type), 413 encode_symbol("pos", encode_type_attribute(self.type_type)), 414 encode_symbol("code", encode_type_attribute(self.type_type)), 415 ) 416 417 print >>f_signatures, """\ 418 419 #endif /* __MAIN_H__ */""" 420 421 finally: 422 f_consts.close() 423 f_defs.close() 424 f_decls.close() 425 f_signatures.close() 426 f_code.close() 427 428 def write_scripts(self, debug): 429 430 "Write scripts used to build the program." 431 432 f_native = open(join(self.output, "native.mk"), "w") 433 f_options = open(join(self.output, "options.mk"), "w") 434 try: 435 if debug: 436 print >>f_options, "CFLAGS = -g" 437 438 # Identify native modules used by the program. 439 440 native_modules = ["native/common.c"] 441 442 for name in self.importer.modules.keys(): 443 parts = name.split(".", 1) 444 445 # Identify source files to be built. 446 447 if parts[0] == "native": 448 native_modules.append("native/%s.c" % parts[1]) 449 450 print >>f_native, "SRC =", " ".join(native_modules) 451 452 finally: 453 f_native.close() 454 f_options.close() 455 456 def make_literal_constant(self, f_decls, f_defs, n, constant): 457 458 """ 459 Write literal constant details to 'f_decls' (to declare a structure) and 460 to 'f_defs' (to define the contents) for the constant with the number 461 'n' with the given 'constant'. 462 """ 463 464 value, value_type, encoding = constant 465 466 const_path = encode_literal_constant(n) 467 structure_name = encode_literal_reference(n) 468 469 ref = Reference("<instance>", value_type) 470 self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value, encoding) 471 472 def make_predefined_constant(self, f_decls, f_defs, path, name): 473 474 """ 475 Write predefined constant details to 'f_decls' (to declare a structure) 476 and to 'f_defs' (to define the contents) for the constant located in 477 'path' with the given 'name'. 478 """ 479 480 # Determine the details of the constant. 481 482 attr_path = "%s.%s" % (path, name) 483 structure_name = encode_predefined_reference(attr_path) 484 ref = self.importer.get_object(attr_path) 485 486 self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) 487 488 def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None, encoding=None): 489 490 """ 491 Write constant details to 'f_decls' (to declare a structure) and to 492 'f_defs' (to define the contents) for the constant described by 'ref' 493 having the given 'path' and 'structure_name' (for the constant structure 494 itself). 495 496 The additional 'data' and 'encoding' are used to describe specific 497 values. 498 """ 499 500 # Obtain the attributes. 501 502 cls = ref.get_origin() 503 indexes = self.optimiser.attr_table[ref] 504 attrnames = self.get_attribute_names(indexes) 505 attrs = self.get_instance_attributes(cls, attrnames) 506 507 # Set the data, if provided. 508 509 if data is not None: 510 attrs["__data__"] = data 511 512 # Also set a key for dynamic attribute lookup, if a string. 513 514 if attrs.has_key("__key__"): 515 if data in self.optimiser.all_attrnames: 516 attrs["__key__"] = data 517 else: 518 attrs["__key__"] = None 519 520 # Define Unicode constant encoding details. 521 522 if cls == self.unicode_type: 523 524 # Reference the encoding's own constant value. 525 526 if encoding: 527 n = self.optimiser.constants[(encoding, self.string_type, None)] 528 529 # Employ a special alias that will be tested specifically in 530 # encode_member. 531 532 encoding_ref = Reference("<instance>", self.string_type, "$c%d" % n) 533 534 # Use None where no encoding was indicated. 535 536 else: 537 encoding_ref = Reference("<instance>", self.none_type) 538 539 attrs["encoding"] = encoding_ref 540 541 # Define the structure details. An object is created for the constant, 542 # but an attribute is provided, referring to the object, for access to 543 # the constant in the program. 544 545 structure = [] 546 table_name = encode_tablename("<instance>", cls) 547 self.populate_structure(ref, attrs, ref.get_kind(), structure) 548 self.write_structure(f_decls, f_defs, structure_name, table_name, structure) 549 550 # Define a macro for the constant. 551 552 attr_name = encode_path(const_path) 553 print >>f_decls, "#define %s ((__attr) {.context=&%s, .value=&%s})" % (attr_name, structure_name, structure_name) 554 555 def make_parameter_table(self, f_decls, f_defs, parameters): 556 557 """ 558 Write parameter table details to 'f_decls' (to declare a table) and to 559 'f_defs' (to define the contents) for the given 'parameters'. 560 """ 561 562 # Use a signature for the table name instead of a separate name for each 563 # function. 564 565 signature = self.get_parameter_signature(parameters) 566 table_name = encode_tablename("<function>", signature) 567 structure_size = encode_size("pmax", signature) 568 569 table = [] 570 self.populate_parameter_table(parameters, table) 571 self.write_parameter_table(f_decls, f_defs, table_name, structure_size, table) 572 573 def get_parameter_signature(self, parameters): 574 575 "Return a signature for the given 'parameters'." 576 577 l = [] 578 for parameter in parameters: 579 if parameter is None: 580 l.append("") 581 else: 582 name, pos = parameter 583 l.append("%s_%s" % (name, pos)) 584 return l and "__".join(l) or "__void" 585 586 def get_signature_for_callable(self, path): 587 588 "Return the signature for the callable with the given 'path'." 589 590 function_path = self.callables[path] 591 parameters = self.optimiser.parameters[function_path] 592 return self.get_parameter_signature(parameters) 593 594 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 595 596 """ 597 Write size constants to 'f_consts' for the given 'size_prefix', using 598 the 'sizes' dictionary to populate the definition, adding the given 599 'padding' to the basic sizes. 600 """ 601 602 print >>f_consts, "enum %s {" % encode_size(size_prefix) 603 first = True 604 for path, size in sizes.items(): 605 if not first: 606 print >>f_consts, "," 607 else: 608 first = False 609 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 610 print >>f_consts, "\n };" 611 612 def write_code_constants(self, f_consts, attrnames, locations, code_prefix, pos_prefix): 613 614 """ 615 Write code constants to 'f_consts' for the given 'attrnames' and 616 attribute 'locations'. 617 """ 618 619 print >>f_consts, "enum %s {" % encode_symbol(code_prefix) 620 first = True 621 for i, attrname in enumerate(attrnames): 622 if not first: 623 print >>f_consts, "," 624 else: 625 first = False 626 f_consts.write(" %s = %d" % (encode_symbol(code_prefix, attrname), i)) 627 print >>f_consts, "\n };" 628 629 print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) 630 first = True 631 for i, attrnames in enumerate(locations): 632 for attrname in attrnames: 633 if not first: 634 print >>f_consts, "," 635 else: 636 first = False 637 f_consts.write(" %s = %d" % (encode_symbol(pos_prefix, attrname), i)) 638 print >>f_consts, "\n };" 639 640 def write_table(self, f_decls, f_defs, table_name, structure_size, table): 641 642 """ 643 Write the declarations to 'f_decls' and definitions to 'f_defs' for 644 the object having the given 'table_name' and the given 'structure_size', 645 with 'table' details used to populate the definition. 646 """ 647 648 print >>f_decls, "extern const __table %s;\n" % table_name 649 650 # Write the corresponding definition. 651 652 print >>f_defs, "const __table %s = {\n %s,\n {\n %s\n }\n };\n" % ( 653 table_name, structure_size, 654 ",\n ".join(table)) 655 656 def write_parameter_table(self, f_decls, f_defs, table_name, structure_size, table): 657 658 """ 659 Write the declarations to 'f_decls' and definitions to 'f_defs' for 660 the object having the given 'table_name' and the given 'structure_size', 661 with 'table' details used to populate the definition. 662 """ 663 664 print >>f_decls, "extern const __ptable %s;\n" % table_name 665 666 # Write the corresponding definition. 667 668 print >>f_defs, "const __ptable %s = {\n %s,\n {\n %s\n }\n };\n" % ( 669 table_name, structure_size, 670 ",\n ".join([("{%s, %s}" % t) for t in table])) 671 672 def write_instance_structure(self, f_decls, path, structure_size): 673 674 """ 675 Write a declaration to 'f_decls' for the object having the given 'path' 676 and the given 'structure_size'. 677 """ 678 679 # Write an instance-specific type definition for instances of classes. 680 # See: templates/types.h 681 682 print >>f_decls, """\ 683 typedef struct { 684 const __table * table; 685 unsigned int pos; 686 __attr attrs[%s]; 687 } %s; 688 """ % (structure_size, encode_symbol("obj", path)) 689 690 def write_structure(self, f_decls, f_defs, structure_name, table_name, structure, path=None): 691 692 """ 693 Write the declarations to 'f_decls' and definitions to 'f_defs' for 694 the object having the given 'structure_name', the given 'table_name', 695 and the given 'structure' details used to populate the definition. 696 """ 697 698 if f_decls: 699 print >>f_decls, "extern __obj %s;\n" % encode_path(structure_name) 700 701 is_class = path and self.importer.get_object(path).has_kind("<class>") 702 pos = is_class and encode_symbol("pos", encode_type_attribute(path)) or "0" 703 704 print >>f_defs, """\ 705 __obj %s = { 706 &%s, 707 %s, 708 { 709 %s 710 }}; 711 """ % ( 712 encode_path(structure_name), table_name, pos, 713 ",\n ".join(structure)) 714 715 def get_argument_limits(self, path): 716 717 """ 718 Return the argument minimum and maximum for the callable at 'path', 719 adding an argument position for a universal context. 720 """ 721 722 parameters = self.importer.function_parameters[path] 723 defaults = self.importer.function_defaults.get(path) 724 num_parameters = len(parameters) + 1 725 return num_parameters - (defaults and len(defaults) or 0), num_parameters 726 727 def get_attribute_names(self, indexes): 728 729 """ 730 Given a list of attribute table 'indexes', return a list of attribute 731 names. 732 """ 733 734 all_attrnames = self.optimiser.all_attrnames 735 attrnames = [] 736 for i in indexes: 737 if i is None: 738 attrnames.append(None) 739 else: 740 attrnames.append(all_attrnames[i]) 741 return attrnames 742 743 def get_static_attributes(self, kind, name, attrnames): 744 745 """ 746 Return a mapping of attribute names to paths for attributes belonging 747 to objects of the given 'kind' (being "<class>" or "<module>") with 748 the given 'name' and supporting the given 'attrnames'. 749 """ 750 751 attrs = {} 752 753 for attrname in attrnames: 754 if attrname is None: 755 continue 756 if kind == "<class>": 757 path = self.importer.all_class_attrs[name][attrname] 758 elif kind == "<module>": 759 path = "%s.%s" % (name, attrname) 760 else: 761 continue 762 763 # The module may be hidden. 764 765 attr = self.importer.get_object(path) 766 if not attr: 767 module = self.importer.hidden.get(path) 768 if module: 769 attr = Reference(module.name, "<module>") 770 attrs[attrname] = attr 771 772 return attrs 773 774 def get_instance_attributes(self, name, attrnames): 775 776 """ 777 Return a mapping of attribute names to references for attributes 778 belonging to instances of the class with the given 'name', where the 779 given 'attrnames' are supported. 780 """ 781 782 consts = self.importer.all_instance_attr_constants[name] 783 attrs = {} 784 for attrname in attrnames: 785 if attrname is None: 786 continue 787 const = consts.get(attrname) 788 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 789 return attrs 790 791 def populate_table(self, path, table): 792 793 """ 794 Traverse the attributes in the determined order for the structure having 795 the given 'path', adding entries to the attribute 'table'. 796 """ 797 798 for attrname in self.optimiser.structures[path]: 799 800 # Handle gaps in the structure. 801 802 if attrname is None: 803 table.append("0") 804 else: 805 table.append(encode_symbol("code", attrname)) 806 807 def populate_parameter_table(self, parameters, table): 808 809 """ 810 Traverse the 'parameters' in the determined order, adding entries to the 811 attribute 'table'. 812 """ 813 814 for value in parameters: 815 816 # Handle gaps in the structure. 817 818 if value is None: 819 table.append(("0", "0")) 820 else: 821 name, pos = value 822 table.append((encode_symbol("pcode", name), pos)) 823 824 def populate_function(self, path, function_instance_attrs, unbound=False): 825 826 """ 827 Populate a structure for the function with the given 'path'. The given 828 'attrs' provide the instance attributes, and if 'unbound' is set to a 829 true value, an unbound method structure is produced (as opposed to a 830 callable bound method structure). 831 """ 832 833 structure = [] 834 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure, unbound) 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, unbound=False): 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 If 'unbound' is set to a true value, an unbound method function pointer 848 will be employed, with a reference to the bound method incorporated into 849 the special __fn__ attribute. 850 """ 851 852 # Populate function instance structures for functions. 853 854 if ref.has_kind("<function>"): 855 origin = self.function_type 856 structure_ref = Reference("<instance>", self.function_type) 857 858 # Otherwise, just populate the appropriate structures. 859 860 else: 861 origin = ref.get_origin() 862 structure_ref = ref 863 864 for attrname in self.optimiser.structures[structure_ref]: 865 866 # Handle gaps in the structure. 867 868 if attrname is None: 869 structure.append("__NULL") 870 871 # Handle non-constant and constant members. 872 873 else: 874 attr = attrs[attrname] 875 876 # Special function pointer member. 877 878 if attrname == "__fn__": 879 880 # Provide bound method references and the unbound function 881 # pointer if populating methods in a class. 882 883 bound_attr = None 884 885 # Classes offer instantiators. 886 887 if kind == "<class>": 888 attr = encode_instantiator_pointer(attr) 889 890 # Methods offers references to bound versions and an unbound 891 # method function. 892 893 elif unbound: 894 bound_attr = encode_bound_reference(attr) 895 attr = "__unbound_method" 896 897 # Other functions just offer function pointers. 898 899 else: 900 attr = encode_function_pointer(attr) 901 902 structure.append("{.b=%s, .fn=%s}" % (bound_attr and "&%s" % bound_attr or "0", attr)) 903 continue 904 905 # Special argument specification member. 906 907 elif attrname == "__args__": 908 signature = self.get_signature_for_callable(ref.get_origin()) 909 ptable = encode_tablename("<function>", signature) 910 911 structure.append("{.min=%s, .ptable=&%s}" % (attr, ptable)) 912 continue 913 914 # Special internal data member. 915 916 elif attrname == "__data__": 917 structure.append("{.size=%d, .%s=%s}" % ( 918 encode_literal_constant_size(attr), 919 encode_literal_constant_member(attr), 920 encode_literal_constant_value(attr))) 921 continue 922 923 # Special internal key member. 924 925 elif attrname == "__key__": 926 structure.append("{.code=%s, .pos=%s}" % (attr and encode_symbol("code", attr) or "0", 927 attr and encode_symbol("pos", attr) or "0")) 928 continue 929 930 # Special cases. 931 932 elif attrname in ("__file__", "__name__"): 933 path = ref.get_origin() 934 value_type = self.string_type 935 936 # Provide constant values. These must match the values 937 # originally recorded during inspection. 938 939 if attrname == "__file__": 940 module = self.importer.get_module(path) 941 value = module.filename 942 943 # Function and class names are leafnames. 944 945 elif attrname == "__name__" and not ref.has_kind("<module>"): 946 value = path.rsplit(".", 1)[-1] 947 948 # All other names just use the object path information. 949 950 else: 951 value = path 952 953 encoding = None 954 955 local_number = self.importer.all_constants[path][(value, value_type, encoding)] 956 constant_name = "$c%d" % local_number 957 attr_path = "%s.%s" % (path, constant_name) 958 constant_number = self.optimiser.constant_numbers[attr_path] 959 constant_value = "__const%d" % constant_number 960 structure.append("%s /* %s */" % (constant_value, attrname)) 961 continue 962 963 elif attrname == "__parent__": 964 path = ref.get_origin() 965 966 # Parents of classes and functions are derived from their 967 # object paths. 968 969 value = path.rsplit(".", 1)[0] 970 structure.append("{.context=0, .value=&%s}" % encode_path(value)) 971 continue 972 973 # Special class relationship attributes. 974 975 elif is_type_attribute(attrname): 976 structure.append("{.context=0, .value=&%s}" % encode_path(decode_type_attribute(attrname))) 977 continue 978 979 # All other kinds of members. 980 981 structure.append(self.encode_member(origin, attrname, attr, kind)) 982 983 def encode_member(self, path, name, ref, structure_type): 984 985 """ 986 Encode within the structure provided by 'path', the member whose 'name' 987 provides 'ref', within the given 'structure_type'. 988 """ 989 990 kind = ref.get_kind() 991 origin = ref.get_origin() 992 993 # References to constant literals. 994 995 if kind == "<instance>" and ref.is_constant_alias(): 996 alias = ref.get_name() 997 998 # Use the alias directly if appropriate. 999 1000 if alias.startswith("$c"): 1001 constant_value = encode_literal_constant(int(alias[2:])) 1002 return "%s /* %s */" % (constant_value, name) 1003 1004 # Obtain a constant value directly assigned to the attribute. 1005 1006 if self.optimiser.constant_numbers.has_key(alias): 1007 constant_number = self.optimiser.constant_numbers[alias] 1008 constant_value = encode_literal_constant(constant_number) 1009 return "%s /* %s */" % (constant_value, name) 1010 1011 # Usage of predefined constants, currently only None supported. 1012 1013 if kind == "<instance>" and origin == self.none_type: 1014 attr_path = encode_predefined_reference(self.none_value) 1015 return "{.context=&%s, .value=&%s} /* %s */" % (attr_path, attr_path, name) 1016 1017 # Predefined constant members. 1018 1019 if (path, name) in self.predefined_constant_members: 1020 attr_path = encode_predefined_reference("%s.%s" % (path, name)) 1021 return "{.context=&%s, .value=&%s} /* %s */" % (attr_path, attr_path, name) 1022 1023 # General undetermined members. 1024 1025 if kind in ("<var>", "<instance>"): 1026 attr_path = encode_predefined_reference(self.none_value) 1027 return "{.context=&%s, .value=&%s} /* %s */" % (attr_path, attr_path, name) 1028 1029 # Set the context depending on the kind of attribute. 1030 # For methods: {&<parent>, &<attr>} 1031 # For other attributes: {&<attr>, &<attr>} 1032 1033 else: 1034 if kind == "<function>" and structure_type == "<class>": 1035 parent = origin.rsplit(".", 1)[0] 1036 context = "&%s" % encode_path(parent) 1037 elif kind == "<instance>": 1038 context = "&%s" % encode_path(origin) 1039 else: 1040 context = "0" 1041 return "{.context=%s, .value=&%s}" % (context, encode_path(origin)) 1042 1043 def append_defaults(self, path, structure): 1044 1045 """ 1046 For the given 'path', append default parameter members to the given 1047 'structure'. 1048 """ 1049 1050 for name, default in self.importer.function_defaults.get(path): 1051 structure.append(self.encode_member(path, name, default, "<instance>")) 1052 1053 def write_instantiator(self, f_code, f_signatures, path, init_ref): 1054 1055 """ 1056 Write an instantiator to 'f_code', with a signature to 'f_signatures', 1057 for instances of the class with the given 'path', with 'init_ref' as the 1058 initialiser function reference. 1059 1060 NOTE: This also needs to initialise any __fn__ and __args__ members 1061 NOTE: where __call__ is provided by the class. 1062 """ 1063 1064 parameters = self.importer.function_parameters[init_ref.get_origin()] 1065 1066 print >>f_code, """\ 1067 __attr %s(__attr __args[]) 1068 { 1069 /* Allocate the structure. */ 1070 __args[0] = __new(&%s, &%s, sizeof(%s)); 1071 1072 /* Call the initialiser. */ 1073 %s(__args); 1074 1075 /* Return the allocated object details. */ 1076 return __args[0]; 1077 } 1078 """ % ( 1079 encode_instantiator_pointer(path), 1080 encode_tablename("<instance>", path), 1081 encode_path(path), 1082 encode_symbol("obj", path), 1083 encode_function_pointer(init_ref.get_origin()) 1084 ) 1085 1086 print >>f_signatures, "#define __HAVE_%s" % encode_path(path) 1087 print >>f_signatures, "__attr %s(__attr[]);" % encode_instantiator_pointer(path) 1088 1089 # Write additional literal instantiators. These do not call the 1090 # initialisers but instead populate the structures directly. 1091 1092 if path in self.literal_instantiator_types: 1093 if path in self.literal_mapping_types: 1094 style = "mapping" 1095 else: 1096 style = "sequence" 1097 1098 print >>f_code, """\ 1099 __attr %s(__attr __args[], unsigned int number) 1100 { 1101 /* Allocate the structure. */ 1102 __args[0] = __new(&%s, &%s, sizeof(%s)); 1103 1104 /* Allocate a structure for the data and set it on the __data__ attribute. */ 1105 %s(__args, number); 1106 1107 /* Return the allocated object details. */ 1108 return __args[0]; 1109 } 1110 """ % ( 1111 encode_literal_instantiator(path), 1112 encode_tablename("<instance>", path), 1113 encode_path(path), 1114 encode_symbol("obj", path), 1115 encode_literal_data_initialiser(style) 1116 ) 1117 1118 print >>f_signatures, "__attr %s(__attr[], unsigned int);" % encode_literal_instantiator(path) 1119 1120 def write_main_program(self, f_code, f_signatures): 1121 1122 """ 1123 Write the main program to 'f_code', invoking the program's modules. Also 1124 write declarations for module main functions to 'f_signatures'. 1125 """ 1126 1127 print >>f_code, """\ 1128 int main(int argc, char *argv[]) 1129 { 1130 __exc __tmp_exc; 1131 1132 GC_INIT(); 1133 1134 __Try 1135 {""" 1136 1137 for name in self.importer.order_modules(): 1138 function_name = "__main_%s" % encode_path(name) 1139 print >>f_signatures, "void %s();" % function_name 1140 1141 # Omit the native modules. 1142 1143 parts = name.split(".") 1144 1145 if parts[0] != "native": 1146 print >>f_code, """\ 1147 %s();""" % function_name 1148 1149 print >>f_code, """\ 1150 } 1151 __Catch(__tmp_exc) 1152 { 1153 if (__ISINSTANCE(__tmp_exc.arg, ((__attr) {.context=0, .value=&__builtins___exception_system_SystemExit}))) 1154 return __load_via_object( 1155 __load_via_object(__tmp_exc.arg.value, %s).value, 1156 %s).intvalue; 1157 1158 fprintf(stderr, "Program terminated due to exception: %%s.\\n", 1159 __load_via_object( 1160 %s((__attr[]) {__NULL, __tmp_exc.arg}).value, 1161 %s).strvalue); 1162 return 1; 1163 } 1164 1165 return 0; 1166 } 1167 """ % ( 1168 encode_symbol("pos", "value"), 1169 encode_symbol("pos", "__data__"), 1170 encode_function_pointer("__builtins__.str.str"), 1171 encode_symbol("pos", "__data__") 1172 ) 1173 1174 # vim: tabstop=4 expandtab shiftwidth=4