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 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 "types.h" 196 #include "exceptions.h" 197 #include "ops.h" 198 #include "progconsts.h" 199 #include "progtypes.h" 200 #include "main.h" 201 #include "progops.h" 202 #include "calls.h" 203 """ 204 205 print >>f_call_macros, """\ 206 #ifndef __CALLS_H__ 207 #define __CALLS_H__ 208 209 #include "types.h" 210 """ 211 212 # Generate table and structure data. 213 214 function_instance_attrs = None 215 objects = self.optimiser.all_attrs.items() 216 objects.sort() 217 218 self.callables = {} 219 220 for ref, attrnames in objects: 221 kind = ref.get_kind() 222 path = ref.get_origin() 223 table_name = encode_tablename(kind, path) 224 structure_size = encode_size(kind, path) 225 226 # Generate structures for classes and modules. 227 228 if kind != "<instance>": 229 structure = [] 230 trailing = [] 231 attrs = self.get_static_attributes(kind, path, attrnames) 232 233 # Set a special instantiator on the class. 234 235 if kind == "<class>": 236 237 # Write instantiator declarations based on the 238 # applicable initialiser. 239 240 init_ref = attrs["__init__"] 241 242 # Write instantiator definitions. 243 244 self.write_instantiator(f_code, f_signatures, path, init_ref) 245 246 # Record the callable for parameter table generation. 247 248 self.callables[path] = init_ref.get_origin() 249 250 # Define special attributes. 251 252 attrs["__fn__"] = path 253 attrs["__args__"] = path 254 255 self.populate_structure(Reference(kind, path), attrs, kind, structure) 256 self.populate_trailing(Reference(kind, path), attrs, trailing) 257 258 if kind == "<class>": 259 self.write_instance_structure(f_decls, path, structure_size) 260 261 self.write_structure(f_decls, f_defs, path, table_name, 262 structure, trailing, ref) 263 264 # Record function instance details for function generation below. 265 266 else: 267 attrs = self.get_instance_attributes(path, attrnames) 268 if path == self.function_type: 269 function_instance_attrs = attrs 270 271 # Record the callable for parameter table generation. 272 273 self.callables[path] = path 274 275 # Write a table for all objects. 276 277 table = [] 278 self.populate_table(Reference(kind, path), table) 279 self.write_table(f_decls, f_defs, table_name, structure_size, table) 280 281 # Generate function instances. 282 283 functions = self.importer.function_parameters.keys() 284 functions.sort() 285 extra_function_instances = [] 286 287 for path in functions: 288 289 # Instantiators are generated above. 290 291 if self.importer.classes.has_key(path) or not self.importer.get_object(path): 292 continue 293 294 # Record the callable for parameter table generation. 295 296 self.callables[path] = path 297 298 # Define the structure details. 299 300 cls = self.function_type 301 table_name = encode_tablename("<instance>", cls) 302 structure_size = encode_size("<instance>", path) 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, structure_size) 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, structure_size): 841 842 """ 843 Write a declaration to 'f_decls' for the object having the given 'path' 844 and the given 'structure_size'. 845 """ 846 847 # Write an instance-specific type definition for instances of classes. 848 # See: templates/types.h 849 850 trailing_area = path in self.trailing_data_types and encode_trailing_area(path) or "" 851 852 print >>f_decls, """\ 853 typedef struct { 854 const __table * table; 855 __pos pos; 856 __attr attrs[%s]; 857 %s 858 } %s; 859 """ % (structure_size, trailing_area, encode_symbol("obj", path)) 860 861 def write_structure(self, f_decls, f_defs, structure_name, table_name, 862 structure, trailing, ref): 863 864 """ 865 Write the declarations to 'f_decls' and definitions to 'f_defs' for 866 the object having the given 'structure_name', the given 'table_name', 867 the given 'structure' details and any 'trailing' member details, used to 868 populate the definition. 869 """ 870 871 origin = ref.get_origin() 872 pos = ref.has_kind("<class>") and encode_pos(encode_type_attribute(origin)) or str(self.instancepos) 873 874 obj_type = ref.has_kind("<instance>") and encode_symbol("obj", origin) or "__obj" 875 obj_name = encode_path(structure_name) 876 877 if f_decls: 878 print >>f_decls, "extern %s %s;\n" % (obj_type, obj_name) 879 880 print >>f_defs, """\ 881 %s %s = { 882 &%s, 883 %s, 884 { 885 %s 886 }, 887 %s 888 }; 889 """ % ( 890 obj_type, obj_name, 891 table_name, 892 pos, 893 ",\n ".join(structure), 894 trailing and ",\n ".join(trailing) or "") 895 896 def get_argument_limits(self, path): 897 898 """ 899 Return the argument minimum and maximum for the callable at 'path', 900 adding an argument position for a universal context. 901 """ 902 903 parameters = self.importer.function_parameters[path] 904 defaults = self.importer.function_defaults.get(path) 905 num_parameters = len(parameters) + 1 906 return num_parameters - (defaults and len(defaults) or 0), num_parameters 907 908 def get_static_attributes(self, kind, name, attrnames): 909 910 """ 911 Return a mapping of attribute names to paths for attributes belonging 912 to objects of the given 'kind' (being "<class>" or "<module>") with 913 the given 'name' and supporting the given 'attrnames'. 914 """ 915 916 attrs = {} 917 918 for attrname in attrnames: 919 if attrname is None: 920 continue 921 if kind == "<class>": 922 path = self.importer.all_class_attrs[name][attrname] 923 elif kind == "<module>": 924 path = "%s.%s" % (name, attrname) 925 else: 926 continue 927 928 # The module may be hidden. 929 930 attr = self.importer.get_object(path) 931 if not attr: 932 module = self.importer.hidden.get(path) 933 if module: 934 attr = Reference(module.name, "<module>") 935 attrs[attrname] = attr 936 937 return attrs 938 939 def get_instance_attributes(self, name, attrnames): 940 941 """ 942 Return a mapping of attribute names to references for attributes 943 belonging to instances of the class with the given 'name', where the 944 given 'attrnames' are supported. 945 """ 946 947 consts = self.importer.all_instance_attr_constants[name] 948 attrs = {} 949 for attrname in attrnames: 950 if attrname is None: 951 continue 952 const = consts.get(attrname) 953 attrs[attrname] = const or Reference("<var>", "%s.%s" % (name, attrname)) 954 955 # Instances with trailing data. 956 957 if name in self.trailing_data_types: 958 attrs["__trailing__"] = Reference("<var>", "%s.__trailing__" % name) 959 960 return attrs 961 962 def populate_table(self, path, table): 963 964 """ 965 Traverse the attributes in the determined order for the structure having 966 the given 'path', adding entries to the attribute 'table'. 967 """ 968 969 for attrname in self.optimiser.structures[path]: 970 971 # Handle gaps in the structure. 972 973 if attrname is None: 974 table.append("0") 975 else: 976 table.append(encode_code(attrname)) 977 978 def populate_parameter_table(self, parameters, table): 979 980 """ 981 Traverse the 'parameters' in the determined order, adding entries to the 982 attribute 'table'. 983 """ 984 985 for value in parameters: 986 987 # Handle gaps in the structure. 988 989 if value is None: 990 table.append(("0", "0")) 991 else: 992 name, pos = value 993 table.append((encode_symbol("pcode", name), pos)) 994 995 def populate_function(self, path, function_instance_attrs): 996 997 """ 998 Populate a structure for the function with the given 'path'. The given 999 'attrs' provide the instance attributes. 1000 """ 1001 1002 structure = [] 1003 self.populate_structure(Reference("<function>", path), function_instance_attrs, "<instance>", structure) 1004 1005 # Append default members. 1006 1007 self.append_defaults(path, structure) 1008 return structure 1009 1010 def populate_structure(self, ref, attrs, kind, structure): 1011 1012 """ 1013 Traverse the attributes in the determined order for the structure having 1014 the given 'ref' whose members are provided by the 'attrs' mapping, in a 1015 structure of the given 'kind', adding entries to the object 'structure'. 1016 """ 1017 1018 structure_ref = self.get_target_structure(ref) 1019 origin = structure_ref.get_origin() 1020 1021 for attrname in self.optimiser.structures[structure_ref]: 1022 1023 # Handle gaps in the structure. 1024 1025 if attrname is None: 1026 structure.append("__NULL") 1027 1028 # Handle non-constant and constant members. 1029 1030 else: 1031 attr = attrs[attrname] 1032 1033 # Special function pointer member. 1034 1035 if attrname == "__fn__": 1036 1037 # Classes offer instantiators which can be called without a 1038 # context. 1039 1040 if kind == "<class>": 1041 attr = encode_instantiator_pointer(attr) 1042 else: 1043 attr = encode_function_pointer(attr) 1044 1045 structure.append("{.fn=%s}" % attr) 1046 continue 1047 1048 # Special argument specification member. 1049 1050 elif attrname == "__args__": 1051 signature = self.get_signature_for_callable(ref.get_origin()) 1052 ptable = encode_tablename("<function>", signature) 1053 1054 structure.append("{.ptable=&%s}" % ptable) 1055 continue 1056 1057 # Special internal data member. 1058 1059 elif attrname == "__data__": 1060 structure.append("{.%s=%s}" % ( 1061 encode_literal_constant_member(attr), 1062 encode_literal_constant_value(attr))) 1063 continue 1064 1065 # Special internal size member. 1066 1067 elif attrname == "__size__": 1068 structure.append("__INTVALUE(%d)" % attr) 1069 continue 1070 1071 # Special internal key member. 1072 1073 elif attrname == "__key__": 1074 structure.append("{.code=%s, .pos=%s}" % (attr and encode_code(attr) or "0", 1075 attr and encode_pos(attr) or "0")) 1076 continue 1077 1078 # Special cases. 1079 1080 elif attrname in ("__file__", "__name__"): 1081 path = ref.get_origin() 1082 value_type = self.string_type 1083 1084 # Provide constant values. These must match the values 1085 # originally recorded during inspection. 1086 1087 if attrname == "__file__": 1088 module = self.importer.get_module(path) 1089 value = module.filename 1090 1091 # Function and class names are leafnames. 1092 1093 elif attrname == "__name__" and not ref.has_kind("<module>"): 1094 value = path.rsplit(".", 1)[-1] 1095 1096 # All other names just use the object path information. 1097 1098 else: 1099 value = path 1100 1101 encoding = None 1102 1103 local_number = self.importer.all_constants[path][(value, value_type, encoding)] 1104 constant_name = "$c%d" % local_number 1105 attr_path = "%s.%s" % (path, constant_name) 1106 constant_number = self.optimiser.constant_numbers[attr_path] 1107 constant_value = "__const%s" % constant_number 1108 structure.append("%s /* %s */" % (constant_value, attrname)) 1109 continue 1110 1111 elif attrname == "__parent__": 1112 path = ref.get_origin() 1113 1114 # Parents of classes and functions are derived from their 1115 # object paths. 1116 1117 value = path.rsplit(".", 1)[0] 1118 structure.append("{.value=&%s}" % encode_path(value)) 1119 continue 1120 1121 # Special context member. 1122 # Set the context depending on the kind of attribute. 1123 # For methods: <parent> 1124 # For other attributes: __NULL 1125 1126 elif attrname == "__context__": 1127 path = ref.get_origin() 1128 1129 # Contexts of methods are derived from their object paths. 1130 1131 context = "0" 1132 1133 if ref.get_kind() == "<function>": 1134 parent = path.rsplit(".", 1)[0] 1135 if self.importer.classes.has_key(parent): 1136 context = "&%s" % encode_path(parent) 1137 1138 structure.append("{.value=%s}" % context) 1139 continue 1140 1141 # Special class relationship attributes. 1142 1143 elif is_type_attribute(attrname): 1144 structure.append("{.value=&%s}" % encode_path(decode_type_attribute(attrname))) 1145 continue 1146 1147 # All other kinds of members. 1148 1149 structure.append(self.encode_member(origin, attrname, attr, kind)) 1150 1151 def populate_trailing(self, ref, attrs, trailing): 1152 1153 """ 1154 For the structure having the given 'ref', whose members are provided by 1155 the 'attrs' mapping, adding entries to the 'trailing' member collection. 1156 """ 1157 1158 structure_ref = self.get_target_structure(ref) 1159 1160 # Instances with trailing data. 1161 1162 if structure_ref.get_kind() == "<instance>" and \ 1163 structure_ref.get_origin() in self.trailing_data_types: 1164 trailing.append(encode_literal_constant_value(attrs["__trailing__"])) 1165 1166 def get_target_structure(self, ref): 1167 1168 "Return the target structure type and reference for 'ref'." 1169 1170 # Populate function instance structures for functions. 1171 1172 if ref.has_kind("<function>"): 1173 return Reference("<instance>", self.function_type) 1174 1175 # Otherwise, just populate the appropriate structures. 1176 1177 else: 1178 return ref 1179 1180 def encode_member(self, path, name, ref, structure_type): 1181 1182 """ 1183 Encode within the structure provided by 'path', the member whose 'name' 1184 provides 'ref', within the given 'structure_type'. 1185 """ 1186 1187 kind = ref.get_kind() 1188 origin = ref.get_origin() 1189 1190 # References to constant literals. 1191 1192 if kind == "<instance>" and ref.is_constant_alias(): 1193 alias = ref.get_name() 1194 1195 # Use the alias directly if appropriate. 1196 1197 if alias.startswith("$c"): 1198 constant_value = encode_literal_constant(alias[2:]) 1199 return "%s /* %s */" % (constant_value, name) 1200 1201 # Obtain a constant value directly assigned to the attribute. 1202 1203 if self.optimiser.constant_numbers.has_key(alias): 1204 1205 # Encode integer constants differently. 1206 1207 value, value_type, encoding = self.importer.all_constant_values[alias] 1208 if value_type == self.int_type: 1209 return "__INTVALUE(%s) /* %s */" % (value, name) 1210 1211 constant_number = self.optimiser.constant_numbers[alias] 1212 constant_value = encode_literal_constant(constant_number) 1213 return "%s /* %s */" % (constant_value, name) 1214 1215 # Usage of predefined constants, currently only None supported. 1216 1217 if kind == "<instance>" and origin == self.none_type: 1218 attr_path = encode_predefined_reference(self.none_value) 1219 return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) 1220 1221 # Predefined constant members. 1222 1223 if (path, name) in self.predefined_constant_members: 1224 attr_path = encode_predefined_reference("%s.%s" % (path, name)) 1225 return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) 1226 1227 # General undetermined members. 1228 1229 if kind in ("<var>", "<instance>"): 1230 attr_path = encode_predefined_reference(self.none_value) 1231 return "{.value=(__ref) &%s} /* %s */" % (attr_path, name) 1232 1233 else: 1234 return "{.value=(__ref) &%s}" % encode_path(origin) 1235 1236 def append_defaults(self, path, structure): 1237 1238 """ 1239 For the given 'path', append default parameter members to the given 1240 'structure'. 1241 """ 1242 1243 for name, default in self.importer.function_defaults.get(path): 1244 structure.append(self.encode_member(path, name, default, "<instance>")) 1245 1246 def write_instantiator(self, f_code, f_signatures, path, init_ref): 1247 1248 """ 1249 Write an instantiator to 'f_code', with a signature to 'f_signatures', 1250 for instances of the class with the given 'path', with 'init_ref' as the 1251 initialiser function reference. 1252 1253 NOTE: This also needs to initialise any __fn__ and __args__ members 1254 NOTE: where __call__ is provided by the class. 1255 """ 1256 1257 initialiser = init_ref.get_origin() 1258 parameters = self.importer.function_parameters[initialiser] 1259 argmin, argmax = self.get_argument_limits(initialiser) 1260 1261 l = [] 1262 for name in parameters: 1263 l.append("__attr %s" % name) 1264 1265 print >>f_code, """\ 1266 __attr %s(__attr __self%s) 1267 { 1268 return %s(__NEWINSTANCE(%s)%s); 1269 } 1270 """ % ( 1271 encode_instantiator_pointer(path), 1272 l and ", %s" % ",".join(l) or "", 1273 encode_function_pointer(initialiser), 1274 encode_path(path), 1275 parameters and ", %s" % ", ".join(parameters) or "" 1276 ) 1277 1278 # Signature: __new_typename(__attr __self, ...) 1279 1280 print >>f_signatures, "__attr %s(__attr __self%s);" % ( 1281 encode_instantiator_pointer(path), 1282 l and ", %s" % ", ".join(l) or "" 1283 ) 1284 1285 print >>f_signatures, "#define __HAVE_%s" % encode_path(path) 1286 1287 def write_main_program(self, f_code, f_signatures): 1288 1289 """ 1290 Write the main program to 'f_code', invoking the program's modules. Also 1291 write declarations for module main functions to 'f_signatures'. 1292 """ 1293 1294 print >>f_code, """\ 1295 int main(int argc, char *argv[]) 1296 { 1297 __exc __tmp_exc; 1298 1299 GC_INIT(); 1300 1301 __Try 1302 {""" 1303 1304 for name in self.importer.order_modules(): 1305 function_name = "__main_%s" % encode_path(name) 1306 print >>f_signatures, "void %s();" % function_name 1307 1308 # Omit the native modules. 1309 1310 parts = name.split(".") 1311 1312 if parts[0] != "native": 1313 print >>f_code, """\ 1314 %s();""" % function_name 1315 1316 print >>f_code, """\ 1317 } 1318 __Catch(__tmp_exc) 1319 { 1320 if (__ISINSTANCE(__tmp_exc.arg, __ATTRVALUE(&__builtins___exception_system_SystemExit))) 1321 return __TOINT(__load_via_object(__VALUE(__tmp_exc.arg), value)); 1322 1323 fprintf(stderr, "Program terminated due to exception: %%s.\\n", 1324 __load_via_object( 1325 __VALUE(%s(__NULL, __tmp_exc.arg)), 1326 __data__).strvalue); 1327 return 1; 1328 } 1329 1330 return 0; 1331 } 1332 """ % encode_function_pointer("__builtins__.str.str") 1333 1334 # vim: tabstop=4 expandtab shiftwidth=4