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