Lichen

generator.py

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