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