1 #!/usr/bin/env python 2 3 """ 4 Generate C code from object layouts and other deduced information. 5 6 Copyright (C) 2015-2019, 2021 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.str" 52 tuple_type = "__builtins__.tuple.tuple" 53 type_type = "__builtins__.core.type" 54 unicode_type = "__builtins__.unicode.unicode" 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 : "__float", 73 int_type : "__int", 74 } 75 76 def __init__(self, importer, optimiser, output): 77 78 """ 79 Initialise the generator with the given 'importer', 'optimiser' and 80 'output' directory. 81 """ 82 83 self.importer = importer 84 self.optimiser = optimiser 85 self.output = output 86 87 # The special instance indicator. 88 89 self.instancepos = self.optimiser.attr_locations["__class__"] 90 91 def to_output(self, reset=False, debug=False, gc_sections=False): 92 93 "Write the generated code." 94 95 self.check_output("debug=%r gc_sections=%r" % (debug, gc_sections)) 96 self.write_structures() 97 self.write_scripts(debug, gc_sections) 98 self.copy_templates(reset) 99 100 def copy_templates(self, reset=False): 101 102 "Copy template files to the generated output directory." 103 104 templates = join(split(__file__)[0], "templates") 105 106 only_if_newer = not reset 107 108 for filename in listdir(templates): 109 target = self.output 110 pathname = join(templates, filename) 111 112 # Copy files into the target directory. 113 114 if not isdir(pathname): 115 copy(pathname, target, only_if_newer) 116 117 # Copy directories (such as the native code directory). 118 119 else: 120 target = join(self.output, filename) 121 122 if not exists(target): 123 mkdir(target) 124 125 existing = listdir(target) 126 needed = listdir(pathname) 127 128 # Determine which files are superfluous by comparing their 129 # basenames (without extensions) to those of the needed 130 # filenames. This should preserve object files for needed source 131 # files, only discarding completely superfluous files from the 132 # target directory. 133 134 needed_basenames = set() 135 for filename in needed: 136 needed_basenames.add(splitext(filename)[0]) 137 138 superfluous = [] 139 for filename in existing: 140 if splitext(filename)[0] not in needed_basenames: 141 superfluous.append(filename) 142 143 # Copy needed files. 144 145 for filename in needed: 146 copy(join(pathname, filename), target, only_if_newer) 147 148 # Remove superfluous files. 149 150 for filename in superfluous: 151 remove(join(target, filename)) 152 153 def write_structures(self): 154 155 "Write structures used by the program." 156 157 f_consts = open(join(self.output, "progconsts.h"), "w") 158 f_defs = open(join(self.output, "progtypes.c"), "w") 159 f_decls = open(join(self.output, "progtypes.h"), "w") 160 f_signatures = open(join(self.output, "main.h"), "w") 161 f_code = open(join(self.output, "main.c"), "w") 162 f_calls = open(join(self.output, "calls.c"), "w") 163 f_call_macros = open(join(self.output, "calls.h"), "w") 164 165 try: 166 # Output boilerplate. 167 168 print >>f_consts, """\ 169 #ifndef __PROGCONSTS_H__ 170 #define __PROGCONSTS_H__ 171 172 #include "types.h" 173 """ 174 print >>f_decls, """\ 175 #ifndef __PROGTYPES_H__ 176 #define __PROGTYPES_H__ 177 178 #include "progconsts.h" 179 #include "types.h" 180 """ 181 print >>f_defs, """\ 182 #include "progtypes.h" 183 #include "progops.h" 184 #include "main.h" 185 """ 186 print >>f_signatures, """\ 187 #ifndef __MAIN_H__ 188 #define __MAIN_H__ 189 190 #include "types.h" 191 """ 192 print >>f_code, """\ 193 #include <string.h> 194 #include <stdio.h> 195 #include <threads.h> 196 #include "gc.h" 197 #include "signals.h" 198 #include "types.h" 199 #include "exceptions.h" 200 #include "ops.h" 201 #include "progconsts.h" 202 #include "progtypes.h" 203 #include "main.h" 204 #include "progops.h" 205 #include "calls.h" 206 """ 207 208 print >>f_call_macros, """\ 209 #ifndef __CALLS_H__ 210 #define __CALLS_H__ 211 212 #include "types.h" 213 """ 214 215 # Generate table and structure data. 216 217 function_instance_attrs = None 218 objects = self.optimiser.all_attrs.items() 219 objects.sort() 220 221 self.callables = {} 222 223 for ref, attrnames in objects: 224 kind = ref.get_kind() 225 path = ref.get_origin() 226 table_name = encode_tablename(kind, path) 227 structure_size = encode_size(kind, path) 228 229 # Generate structures for classes and modules. 230 231 if kind != "<instance>": 232 structure = [] 233 trailing = [] 234 attrs = self.get_static_attributes(kind, path, attrnames) 235 236 # Set a special instantiator on the class. 237 238 if kind == "<class>": 239 240 # Write instantiator declarations based on the 241 # applicable initialiser. 242 243 init_ref = attrs["__init__"] 244 245 # Write instantiator definitions. 246 247 self.write_instantiator(f_code, f_signatures, path, init_ref) 248 249 # Record the callable for parameter table generation. 250 251 self.callables[path] = init_ref.get_origin() 252 253 # Define special attributes. 254 255 attrs["__fn__"] = path 256 attrs["__args__"] = path 257 258 self.populate_structure(Reference(kind, path), attrs, kind, structure) 259 self.populate_trailing(Reference(kind, path), attrs, trailing) 260 261 if kind == "<class>": 262 self.write_instance_structure(f_decls, path) 263 264 self.write_structure(f_decls, f_defs, path, table_name, 265 structure, trailing, ref) 266 267 # Record function instance details for function generation below. 268 269 else: 270 attrs = self.get_instance_attributes(path, attrnames) 271 if path == self.function_type: 272 function_instance_attrs = attrs 273 274 # Record the callable for parameter table generation. 275 276 self.callables[path] = path 277 278 # Write a table for all objects. 279 280 table = [] 281 self.populate_table(ref, table) 282 self.write_table(f_decls, f_defs, table_name, structure_size, 283 table, ref) 284 285 # Generate function instances. 286 287 functions = self.importer.function_parameters.keys() 288 functions.sort() 289 extra_function_instances = [] 290 291 for path in functions: 292 293 # Instantiators are generated above. 294 295 if self.importer.classes.has_key(path) or not self.importer.get_object(path): 296 continue 297 298 # Record the callable for parameter table generation. 299 300 self.callables[path] = path 301 302 # Define the structure details. 303 304 cls = self.function_type 305 table_name = encode_tablename("<instance>", cls) 306 307 # Set a special callable attribute on the instance. 308 309 function_instance_attrs["__fn__"] = path 310 function_instance_attrs["__args__"] = path 311 312 structure = self.populate_function(path, function_instance_attrs) 313 self.write_structure(f_decls, f_defs, path, table_name, structure, [], Reference("<function>", path)) 314 315 # Functions with defaults need to declare instance structures. 316 317 if self.importer.function_defaults.get(path): 318 self.write_instance_structure(f_decls, path) 319 extra_function_instances.append(path) 320 321 # Write function declarations. 322 # Signature: __attr <name>(...); 323 324 parameters = self.importer.function_parameters[path] 325 326 # Include the context plus the original parameters. 327 328 l = ["__attr"] * (len(parameters) + 1) 329 print >>f_signatures, "__attr %s(%s);" % (encode_function_pointer(path), ", ".join(l)) 330 331 # Generate parameter table size data. 332 333 min_parameters = {} 334 max_parameters = {} 335 size_parameters = {} 336 all_max_parameters = set() 337 338 # Consolidate parameter tables for instantiators and functions. 339 340 parameter_tables = set() 341 342 for path, function_path in self.callables.items(): 343 argmin, argmax = self.get_argument_limits(function_path) 344 345 # Obtain the parameter table members. 346 347 parameters = self.optimiser.parameters[function_path] 348 if not parameters: 349 parameters = () 350 else: 351 parameters = tuple(parameters) 352 353 # Define each table in terms of the members and the minimum 354 # number of arguments. 355 356 parameter_tables.add((argmin, parameters)) 357 signature = self.get_parameter_signature(argmin, parameters) 358 359 # Record the minimum number of arguments, the maximum number, 360 # and the size of the table. 361 362 min_parameters[signature] = argmin 363 max_parameters[signature] = argmax 364 size_parameters[signature] = len(parameters) 365 all_max_parameters.add(argmax) 366 367 self.write_size_constants(f_consts, "pmin", min_parameters, 0) 368 self.write_size_constants(f_consts, "pmax", max_parameters, 0) 369 self.write_size_constants(f_consts, "psize", size_parameters, 0) 370 371 # Generate parameter tables for distinct function signatures. 372 373 for argmin, parameters in parameter_tables: 374 self.make_parameter_table(f_decls, f_defs, argmin, parameters) 375 376 # Generate predefined constants. 377 378 for path, name in self.predefined_constant_members: 379 self.make_predefined_constant(f_decls, f_defs, path, name) 380 381 # Generate literal constants. 382 383 for constant, n in self.optimiser.constants.items(): 384 self.make_literal_constant(f_decls, f_defs, n, constant) 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 const_path = encode_literal_constant(n) 580 structure_name = encode_literal_reference(n) 581 582 ref = Reference("<instance>", value_type) 583 self.make_constant(f_decls, f_defs, ref, const_path, structure_name, value, encoding) 584 585 def make_predefined_constant(self, f_decls, f_defs, path, name): 586 587 """ 588 Write predefined constant details to 'f_decls' (to declare a structure) 589 and to 'f_defs' (to define the contents) for the constant located in 590 'path' with the given 'name'. 591 """ 592 593 # Determine the details of the constant. 594 595 attr_path = "%s.%s" % (path, name) 596 structure_name = encode_predefined_reference(attr_path) 597 ref = self.importer.get_object(attr_path) 598 599 self.make_constant(f_decls, f_defs, ref, attr_path, structure_name) 600 601 def make_constant(self, f_decls, f_defs, ref, const_path, structure_name, data=None, encoding=None): 602 603 """ 604 Write constant details to 'f_decls' (to declare a structure) and to 605 'f_defs' (to define the contents) for the constant described by 'ref' 606 having the given 'const_path' (providing an attribute for the constant) 607 and 'structure_name' (for the constant structure itself). 608 609 The additional 'data' and 'encoding' are used to describe specific 610 values. 611 """ 612 613 # Obtain the attributes. 614 615 cls = ref.get_origin() 616 attrnames = self.optimiser.all_attrs[ref] 617 attrs = self.get_instance_attributes(cls, attrnames) 618 619 # Set the data, if provided. 620 621 if data is not None: 622 623 # Data retained by special attribute. 624 625 if attrs.has_key("__data__"): 626 attrs["__data__"] = data 627 628 # Data retained by a trailing data area. 629 630 elif attrs.has_key("__trailing__"): 631 attrs["__trailing__"] = data 632 633 # Also set a key for dynamic attribute lookup, if a string. 634 635 if attrs.has_key("__key__"): 636 if data in self.optimiser.all_attrnames: 637 attrs["__key__"] = data 638 else: 639 attrs["__key__"] = None 640 641 # Initialise the size, if a string. 642 643 if attrs.has_key("__size__"): 644 attrs["__size__"] = len(data) 645 646 # Define Unicode constant encoding details. 647 648 if cls == self.unicode_type: 649 650 # Reference the encoding's own constant value. 651 652 if encoding: 653 n = self.optimiser.constants[(encoding, self.string_type, None)] 654 655 # Employ a special alias that will be tested specifically in 656 # encode_member. 657 658 encoding_ref = Reference("<instance>", self.string_type, "$c%s" % n) 659 660 # Use None where no encoding was indicated. 661 662 else: 663 encoding_ref = Reference("<instance>", self.none_type) 664 665 attrs["encoding"] = encoding_ref 666 667 # Define the structure details. An object is created for the constant, 668 # but an attribute is provided, referring to the object, for access to 669 # the constant in the program. 670 671 structure = [] 672 trailing = [] 673 table_name = encode_tablename("<instance>", cls) 674 self.populate_structure(ref, attrs, ref.get_kind(), structure) 675 self.populate_trailing(ref, attrs, trailing) 676 self.write_structure(f_decls, f_defs, structure_name, table_name, 677 structure, trailing, ref) 678 679 # Define a macro for the constant. 680 681 attr_name = encode_path(const_path) 682 print >>f_decls, "#define %s __ATTRVALUE(&%s)" % (attr_name, structure_name) 683 684 def make_parameter_table(self, f_decls, f_defs, argmin, parameters): 685 686 """ 687 Write parameter table details to 'f_decls' (to declare a table) and to 688 'f_defs' (to define the contents) for the given 'argmin' and 689 'parameters'. 690 """ 691 692 # Use a signature for the table name instead of a separate name for each 693 # function. 694 695 signature = self.get_parameter_signature(argmin, parameters) 696 table_name = encode_tablename("<function>", signature) 697 min_parameters = encode_size("pmin", signature) 698 max_parameters = encode_size("pmax", signature) 699 structure_size = encode_size("psize", signature) 700 701 table = [] 702 self.populate_parameter_table(parameters, table) 703 self.write_parameter_table(f_decls, f_defs, table_name, min_parameters, max_parameters, structure_size, table) 704 705 def get_parameter_signature(self, argmin, parameters): 706 707 "Return a signature for the given 'argmin' and 'parameters'." 708 709 l = [str(argmin)] 710 for parameter in parameters: 711 if parameter is None: 712 l.append("") 713 else: 714 name, pos = parameter 715 l.append("%s_%s" % (name, pos)) 716 return l and "__".join(l) or "__void" 717 718 def get_signature_for_callable(self, path): 719 720 "Return the signature for the callable with the given 'path'." 721 722 function_path = self.callables[path] 723 argmin, argmax = self.get_argument_limits(function_path) 724 parameters = self.optimiser.parameters[function_path] 725 return self.get_parameter_signature(argmin, parameters) 726 727 def write_size_constants(self, f_consts, size_prefix, sizes, padding): 728 729 """ 730 Write size constants to 'f_consts' for the given 'size_prefix', using 731 the 'sizes' dictionary to populate the definition, adding the given 732 'padding' to the basic sizes. 733 """ 734 735 print >>f_consts, "enum %s {" % encode_size(size_prefix) 736 first = True 737 for path, size in sizes.items(): 738 if not first: 739 print >>f_consts, "," 740 else: 741 first = False 742 f_consts.write(" %s = %d" % (encode_size(size_prefix, path), size + padding)) 743 print >>f_consts, "\n };" 744 745 def write_code_constants(self, f_consts, attrnames, locations, code_prefix, 746 pos_prefix, code_encoder, pos_encoder): 747 748 """ 749 Write code constants to 'f_consts' for the given 'attrnames' and 750 attribute 'locations'. 751 """ 752 753 print >>f_consts, "enum %s {" % encode_symbol(code_prefix) 754 first = True 755 for i, attrname in enumerate(attrnames): 756 if not first: 757 print >>f_consts, "," 758 else: 759 first = False 760 f_consts.write(" %s = %d" % (code_encoder(attrname), i)) 761 print >>f_consts, "\n };" 762 763 print >>f_consts, "enum %s {" % encode_symbol(pos_prefix) 764 first = True 765 for i, attrnames in enumerate(locations): 766 for attrname in attrnames: 767 if not first: 768 print >>f_consts, "," 769 else: 770 first = False 771 f_consts.write(" %s = %d" % (pos_encoder(attrname), i)) 772 print >>f_consts, "\n };" 773 774 def write_table(self, f_decls, f_defs, table_name, structure_size, table, 775 ref): 776 777 """ 778 Write the declarations to 'f_decls' and definitions to 'f_defs' for 779 the object having the given 'table_name' and the given 'structure_size', 780 with 'table' details used to populate the definition. 781 782 To support value copying, the 'ref' is provided to be able to encode the 783 structure size. 784 """ 785 786 print >>f_decls, "extern const __table %s;\n" % table_name 787 788 # Generate an object size of 0 for non-copyable types. 789 790 path = ref.get_origin() 791 792 if ref.get_kind() == "<instance>" and self.trailing_data_types.has_key(path): 793 obj_type = encode_symbol("obj", path) or "__obj" 794 obj_size = "sizeof(%s)" % obj_type 795 else: 796 obj_size = "0" 797 798 # Write the corresponding definition. 799 800 print >>f_defs, """\ 801 const __table %s = { 802 %s, 803 %s, 804 { 805 %s 806 } 807 }; 808 """ % (table_name, structure_size, obj_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): 842 843 """ 844 Write a declaration to 'f_decls' for the object having the given 'path'. 845 """ 846 847 structure_size = encode_size("<instance>", path) 848 849 # Write an instance-specific type definition for instances of classes. 850 # See: templates/types.h 851 852 trailing_area = path in self.trailing_data_types and encode_trailing_area(path) or "" 853 854 print >>f_decls, """\ 855 typedef struct { 856 const __table * table; 857 __pos pos; 858 __attr attrs[%s]; 859 %s 860 } %s; 861 """ % (structure_size, trailing_area, encode_symbol("obj", path)) 862 863 def write_structure(self, f_decls, f_defs, structure_name, table_name, 864 structure, trailing, ref): 865 866 """ 867 Write the declarations to 'f_decls' and definitions to 'f_defs' for 868 the object having the given 'structure_name', the given 'table_name', 869 the given 'structure' details and any 'trailing' member details, used to 870 populate the definition. 871 """ 872 873 origin = ref.get_origin() 874 pos = ref.has_kind("<class>") and encode_pos(encode_type_attribute(origin)) or str(self.instancepos) 875 876 obj_type = ref.has_kind("<instance>") and encode_symbol("obj", origin) or "__obj" 877 obj_name = encode_path(structure_name) 878 879 if f_decls: 880 print >>f_decls, "extern %s %s;\n" % (obj_type, obj_name) 881 882 print >>f_defs, """\ 883 %s %s = { 884 &%s, 885 %s, 886 { 887 %s 888 }, 889 %s 890 }; 891 """ % ( 892 obj_type, obj_name, 893 table_name, 894 pos, 895 ",\n ".join(structure), 896 trailing and ",\n ".join(trailing) or "") 897 898 def get_argument_limits(self, path): 899 900 """ 901 Return the argument minimum and maximum for the callable at 'path', 902 adding an argument position for a universal context. 903 """ 904 905 parameters = self.importer.function_parameters[path] 906 defaults = self.importer.function_defaults.get(path) 907 num_parameters = len(parameters) + 1 908 return num_parameters - (defaults and len(defaults) or 0), num_parameters 909 910 def get_static_attributes(self, kind, name, attrnames): 911 912 """ 913 Return a mapping of attribute names to paths for attributes belonging 914 to objects of the given 'kind' (being "<class>" or "<module>") with 915 the given 'name' and supporting the given 'attrnames'. 916 """ 917 918 attrs = {} 919 920 for attrname in attrnames: 921 if attrname is None: 922 continue 923 if kind == "<class>": 924 path = self.importer.all_class_attrs[name][attrname] 925 elif kind == "<module>": 926 path = "%s.%s" % (name, attrname) 927 else: 928 continue 929 930 # The module may be hidden. 931 932 attr = self.importer.get_object(path) 933 if not attr: 934 module = self.importer.hidden.get(path) 935 if module: 936 attr = Reference(module.name, "<module>") 937 attrs[attrname] = attr 938 939 return attrs 940 941 def get_instance_attributes(self, name, attrnames): 942 943 """ 944 Return a mapping of attribute names to references for attributes 945 belonging to instances of the class with the given 'name', where the 946 given 'attrnames' are supported. 947 """ 948 949 consts = self.importer.all_instance_attr_constants[name] 950 attrs = {} 951 for attrname in attrnames: 952 if attrname is None: 953 continue 954 const = consts.get(attrname) 955 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 956 957 # Instances with trailing data. 958 959 if name in self.trailing_data_types: 960 attrs["__trailing__"] = Reference("<var>", "%s.__trailing__" % name) 961 962 return attrs 963 964 def populate_table(self, path, table): 965 966 """ 967 Traverse the attributes in the determined order for the structure having 968 the given 'path', adding entries to the attribute 'table'. 969 """ 970 971 for attrname in self.optimiser.structures[path]: 972 973 # Handle gaps in the structure. 974 975 if attrname is None: 976 table.append("0") 977 else: 978 table.append(encode_code(attrname)) 979 980 def populate_parameter_table(self, parameters, table): 981 982 """ 983 Traverse the 'parameters' in the determined order, adding entries to the 984 attribute 'table'. 985 """ 986 987 for value in parameters: 988 989 # Handle gaps in the structure. 990 991 if value is None: 992 table.append(("0", "0")) 993 else: 994 name, pos = value 995 table.append((encode_symbol("pcode", name), pos)) 996 997 def populate_function(self, path, function_instance_attrs): 998 999 """ 1000 Populate a structure for the function with the given 'path'. The given 1001 'attrs' provide the instance attributes. 1002 """ 1003 1004 structure = [] 1005 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure) 1006 1007 # Append default members. 1008 1009 self.append_defaults(path, structure) 1010 return structure 1011 1012 def populate_structure(self, ref, attrs, kind, structure): 1013 1014 """ 1015 Traverse the attributes in the determined order for the structure having 1016 the given 'ref' whose members are provided by the 'attrs' mapping, in a 1017 structure of the given 'kind', adding entries to the object 'structure'. 1018 """ 1019 1020 structure_ref = self.get_target_structure(ref) 1021 origin = structure_ref.get_origin() 1022 1023 for attrname in self.optimiser.structures[structure_ref]: 1024 1025 # Handle gaps in the structure. 1026 1027 if attrname is None: 1028 structure.append("__NULL") 1029 1030 # Handle non-constant and constant members. 1031 1032 else: 1033 attr = attrs[attrname] 1034 1035 # Special function pointer member. 1036 1037 if attrname == "__fn__": 1038 1039 # Classes offer instantiators which can be called without a 1040 # context. 1041 1042 if kind == "<class>": 1043 attr = encode_instantiator_pointer(attr) 1044 else: 1045 attr = encode_function_pointer(attr) 1046 1047 structure.append("{.fn=%s}" % attr) 1048 continue 1049 1050 # Special argument specification member. 1051 1052 elif attrname == "__args__": 1053 signature = self.get_signature_for_callable(ref.get_origin()) 1054 ptable = encode_tablename("<function>", signature) 1055 1056 structure.append("{.ptable=&%s}" % ptable) 1057 continue 1058 1059 # Special internal data member. 1060 1061 elif attrname == "__data__": 1062 structure.append("{.%s=%s}" % ( 1063 encode_literal_constant_member(attr), 1064 encode_literal_constant_value(attr))) 1065 continue 1066 1067 # Special internal size member. 1068 1069 elif attrname == "__size__": 1070 structure.append("{.sizevalue=%d}" % attr) 1071 continue 1072 1073 # Special internal key member. 1074 1075 elif attrname == "__key__": 1076 structure.append("{.code=%s, .pos=%s}" % (attr and encode_code(attr) or "0", 1077 attr and encode_pos(attr) or "0")) 1078 continue 1079 1080 # Special cases. 1081 1082 elif attrname in ("__file__", "__name__"): 1083 path = ref.get_origin() 1084 value_type = self.string_type 1085 1086 # Provide constant values. These must match the values 1087 # originally recorded during inspection. 1088 1089 if attrname == "__file__": 1090 module = self.importer.get_module(path) 1091 value = module.filename 1092 1093 # Function and class names are leafnames. 1094 1095 elif attrname == "__name__" and not ref.has_kind("<module>"): 1096 value = path.rsplit(".", 1)[-1] 1097 1098 # All other names just use the object path information. 1099 1100 else: 1101 value = path 1102 1103 encoding = None 1104 1105 local_number = self.importer.all_constants[path][(value, value_type, encoding)] 1106 constant_name = "$c%d" % local_number 1107 attr_path = "%s.%s" % (path, constant_name) 1108 constant_number = self.optimiser.constant_numbers[attr_path] 1109 constant_value = "__const%s" % constant_number 1110 structure.append("%s /* %s */" % (constant_value, attrname)) 1111 continue 1112 1113 elif attrname == "__parent__": 1114 path = ref.get_origin() 1115 1116 # Parents of classes and functions are derived from their 1117 # object paths. 1118 1119 value = path.rsplit(".", 1)[0] 1120 structure.append("{.value=&%s}" % encode_path(value)) 1121 continue 1122 1123 # Special context member. 1124 # Set the context depending on the kind of attribute. 1125 # For methods: <parent> 1126 # For other attributes: __NULL 1127 1128 elif attrname == "__context__": 1129 path = ref.get_origin() 1130 1131 # Contexts of methods are derived from their object paths. 1132 1133 context = "0" 1134 1135 if ref.get_kind() == "<function>": 1136 parent = path.rsplit(".", 1)[0] 1137 if self.importer.classes.has_key(parent): 1138 context = "&%s" % encode_path(parent) 1139 1140 structure.append("{.value=%s}" % context) 1141 continue 1142 1143 # Special class relationship attributes. 1144 1145 elif is_type_attribute(attrname): 1146 structure.append("{.value=&%s}" % encode_path(decode_type_attribute(attrname))) 1147 continue 1148 1149 # All other kinds of members. 1150 1151 structure.append(self.encode_member(origin, attrname, attr, kind)) 1152 1153 def populate_trailing(self, ref, attrs, trailing): 1154 1155 """ 1156 For the structure having the given 'ref', whose members are provided by 1157 the 'attrs' mapping, adding entries to the 'trailing' member collection. 1158 """ 1159 1160 if self.get_trailing_data_type(ref): 1161 trailing.append(encode_literal_constant_value(attrs["__trailing__"])) 1162 1163 def get_trailing_data_type(self, ref): 1164 1165 """ 1166 For the structure having the given 'ref', return the type of any 1167 trailing data. 1168 """ 1169 1170 structure_ref = self.get_target_structure(ref) 1171 1172 # Instances with trailing data. 1173 1174 if structure_ref.get_kind() == "<instance>": 1175 return self.trailing_data_types.get(structure_ref.get_origin()) 1176 else: 1177 return None 1178 1179 def get_target_structure(self, ref): 1180 1181 "Return the target structure type and reference for 'ref'." 1182 1183 # Populate function instance structures for functions. 1184 1185 if ref.has_kind("<function>"): 1186 return Reference("<instance>", self.function_type) 1187 1188 # Otherwise, just populate the appropriate structures. 1189 1190 else: 1191 return ref 1192 1193 def encode_member(self, path, name, ref, structure_type): 1194 1195 """ 1196 Encode within the structure provided by 'path', the member whose 'name' 1197 provides 'ref', within the given 'structure_type'. 1198 """ 1199 1200 kind = ref.get_kind() 1201 origin = ref.get_origin() 1202 1203 # References to constant literals. 1204 1205 if kind == "<instance>" and ref.is_constant_alias(): 1206 alias = ref.get_name() 1207 1208 # Use the alias directly if appropriate. 1209 1210 if alias.startswith("$c"): 1211 constant_value = encode_literal_constant(alias[2:]) 1212 return "%s /* %s */" % (constant_value, name) 1213 1214 # Obtain a constant value directly assigned to the attribute. 1215 1216 if self.optimiser.constant_numbers.has_key(alias): 1217 constant_number = self.optimiser.constant_numbers[alias] 1218 constant_value = encode_literal_constant(constant_number) 1219 return "%s /* %s */" % (constant_value, name) 1220 1221 # Usage of predefined constants, currently only None supported. 1222 1223 if kind == "<instance>" and origin == self.none_type: 1224 attr_path = encode_predefined_reference(self.none_value) 1225 return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) 1226 1227 # Predefined constant members. 1228 1229 if (path, name) in self.predefined_constant_members: 1230 attr_path = encode_predefined_reference("%s.%s" % (path, name)) 1231 return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) 1232 1233 # General undetermined members. 1234 1235 if kind in ("<var>", "<instance>"): 1236 attr_path = encode_predefined_reference(self.none_value) 1237 return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) 1238 1239 else: 1240 return "{.value=(__ref) &%s}" % encode_path(origin) 1241 1242 def append_defaults(self, path, structure): 1243 1244 """ 1245 For the given 'path', append default parameter members to the given 1246 'structure'. 1247 """ 1248 1249 for name, default in self.importer.function_defaults.get(path): 1250 structure.append(self.encode_member(path, name, default, "<instance>")) 1251 1252 def write_instantiator(self, f_code, f_signatures, path, init_ref): 1253 1254 """ 1255 Write an instantiator to 'f_code', with a signature to 'f_signatures', 1256 for instances of the class with the given 'path', with 'init_ref' as the 1257 initialiser function reference. 1258 1259 NOTE: This also needs to initialise any __fn__ and __args__ members 1260 NOTE: where __call__ is provided by the class. 1261 """ 1262 1263 initialiser = init_ref.get_origin() 1264 parameters = self.importer.function_parameters[initialiser] 1265 argmin, argmax = self.get_argument_limits(initialiser) 1266 1267 l = [] 1268 for name in parameters: 1269 l.append("__attr %s" % name) 1270 1271 # Special-case the integer type. 1272 1273 # Here, the __builtins__.int.new_int function is called with the 1274 # initialiser's parameters. 1275 1276 if path == self.int_type: 1277 print >>f_code, """\ 1278 __attr %s(__attr __self, __attr number_or_string, __attr base) 1279 { 1280 return __fn___builtins___int_new_int(__NULL, number_or_string, base); 1281 } 1282 """ % ( 1283 encode_instantiator_pointer(path), 1284 ) 1285 1286 # Special-case the string types. 1287 1288 # Here, the __builtins__.str.new_str function is called with the 1289 # initialiser's parameter. 1290 1291 elif path == self.string_type: 1292 print >>f_code, """\ 1293 __attr %s(__attr __self, __attr obj) 1294 { 1295 return __fn___builtins___str_new_str(__NULL, obj); 1296 } 1297 """ % ( 1298 encode_instantiator_pointer(path), 1299 ) 1300 1301 # Generic instantiation support. 1302 1303 else: 1304 print >>f_code, """\ 1305 __attr %s(__attr __self%s) 1306 { 1307 return %s(__NEWINSTANCE(%s)%s); 1308 } 1309 """ % ( 1310 encode_instantiator_pointer(path), 1311 l and ", %s" % ",".join(l) or "", 1312 encode_function_pointer(initialiser), 1313 encode_path(path), 1314 parameters and ", %s" % ", ".join(parameters) or "" 1315 ) 1316 1317 # Signature: __new_typename(__attr __self, ...) 1318 1319 print >>f_signatures, "__attr %s(__attr __self%s);" % ( 1320 encode_instantiator_pointer(path), 1321 l and ", %s" % ", ".join(l) or "" 1322 ) 1323 1324 print >>f_signatures, "#define __HAVE_%s" % encode_path(path) 1325 1326 def write_main_program(self, f_code, f_signatures): 1327 1328 """ 1329 Write the main program to 'f_code', invoking the program's modules. Also 1330 write declarations for module main functions to 'f_signatures'. 1331 """ 1332 1333 print >>f_code, """\ 1334 _Thread_local __attr __stack; 1335 1336 /* Global used to prevent compiler over-optimisation. */ 1337 1338 __attr __stack_global; 1339 1340 int main(int argc, char *argv[]) 1341 { 1342 __exc __tmp_exc; 1343 __attr __stack_base; 1344 1345 GC_INIT(); 1346 1347 __signals_install_handlers(); 1348 __stack = __stack_init(); 1349 1350 /* Record the stack accessor on the function stack for libgc. */ 1351 1352 __stack_base = __stack; 1353 __stack_global = __stack_base; 1354 1355 __Try 1356 {""" 1357 1358 # Write a main function invocation for all but the native modules. 1359 1360 for name in self.importer.order_modules(): 1361 parts = name.split(".") 1362 1363 if parts[0] == "native": 1364 continue 1365 1366 function_name = "__main_%s" % encode_path(name) 1367 print >>f_signatures, "void %s();" % function_name 1368 print >>f_code, """\ 1369 %s();""" % function_name 1370 1371 # Finish the main section with an exception handler. 1372 1373 print >>f_code, """\ 1374 } 1375 __Catch(__tmp_exc) 1376 { 1377 if (__ISINSTANCE(__tmp_exc.arg, __ATTRVALUE(&__builtins___exception_system_SystemExit))) 1378 return __TOINT(__load_via_object(__VALUE(__tmp_exc.arg), value)); 1379 1380 fprintf(stderr, "Program terminated due to exception: %%s.\\n", 1381 __load_via_object( 1382 __VALUE(%s(__NULL, __tmp_exc.arg)), 1383 __data__).strvalue); 1384 return 1; 1385 } 1386 1387 return 0; 1388 } 1389 """ % encode_instantiator_pointer("__builtins__.str.str") 1390 1391 # vim: tabstop=4 expandtab shiftwidth=4