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