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