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