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