Lichen

generator.py

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