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