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