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