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