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