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