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