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