Home | History | Annotate | Download | only in generate
      1 # Copyright 2013 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Code shared by the various language-specific code generators."""
      6 
      7 from functools import partial
      8 import os.path
      9 import re
     10 
     11 import module as mojom
     12 import mojom.fileutil as fileutil
     13 import pack
     14 
     15 def ExpectedArraySize(kind):
     16   if mojom.IsArrayKind(kind):
     17     return kind.length
     18   return None
     19 
     20 def StudlyCapsToCamel(studly):
     21   return studly[0].lower() + studly[1:]
     22 
     23 def UnderToCamel(under):
     24   """Converts underscore_separated strings to CamelCase strings."""
     25   return ''.join(word.capitalize() for word in under.split('_'))
     26 
     27 def WriteFile(contents, full_path):
     28   # Make sure the containing directory exists.
     29   full_dir = os.path.dirname(full_path)
     30   fileutil.EnsureDirectoryExists(full_dir)
     31 
     32   # Dump the data to disk.
     33   with open(full_path, "w+") as f:
     34     f.write(contents)
     35 
     36 class Generator(object):
     37   # Pass |output_dir| to emit files to disk. Omit |output_dir| to echo all
     38   # files to stdout.
     39   def __init__(self, module, output_dir=None, typemap=None, variant=None,
     40                bytecode_path=None, for_blink=False, use_once_callback=False,
     41                export_attribute=None, export_header=None,
     42                generate_non_variant_code=False):
     43     self.module = module
     44     self.output_dir = output_dir
     45     self.typemap = typemap or {}
     46     self.variant = variant
     47     self.bytecode_path = bytecode_path
     48     self.for_blink = for_blink
     49     self.use_once_callback = use_once_callback
     50     self.export_attribute = export_attribute
     51     self.export_header = export_header
     52     self.generate_non_variant_code = generate_non_variant_code
     53 
     54   def GetStructsFromMethods(self):
     55     result = []
     56     for interface in self.module.interfaces:
     57       for method in interface.methods:
     58         result.append(self._GetStructFromMethod(method))
     59         if method.response_parameters != None:
     60           result.append(self._GetResponseStructFromMethod(method))
     61     return result
     62 
     63   def GetStructs(self):
     64     return map(partial(self._AddStructComputedData, True), self.module.structs)
     65 
     66   def GetUnions(self):
     67     return map(self._AddUnionComputedData, self.module.unions)
     68 
     69   def GetInterfaces(self):
     70     return map(self._AddInterfaceComputedData, self.module.interfaces)
     71 
     72   # Prepend the filename with a directory that matches the directory of the
     73   # original .mojom file, relative to the import root.
     74   def MatchMojomFilePath(self, filename):
     75     return os.path.join(os.path.dirname(self.module.path), filename)
     76 
     77   def Write(self, contents, filename):
     78     if self.output_dir is None:
     79       print contents
     80       return
     81     full_path = os.path.join(self.output_dir, filename)
     82     WriteFile(contents, full_path)
     83 
     84   def GenerateFiles(self, args):
     85     raise NotImplementedError("Subclasses must override/implement this method")
     86 
     87   def GetJinjaParameters(self):
     88     """Returns default constructor parameters for the jinja environment."""
     89     return {}
     90 
     91   def GetGlobals(self):
     92     """Returns global mappings for the template generation."""
     93     return {}
     94 
     95   def _AddStructComputedData(self, exported, struct):
     96     """Adds computed data to the given struct. The data is computed once and
     97     used repeatedly in the generation process."""
     98     struct.packed = pack.PackedStruct(struct)
     99     struct.bytes = pack.GetByteLayout(struct.packed)
    100     struct.versions = pack.GetVersionInfo(struct.packed)
    101     struct.exported = exported
    102     return struct
    103 
    104   def _AddUnionComputedData(self, union):
    105     """Adds computed data to the given union. The data is computed once and
    106     used repeatedly in the generation process."""
    107     ordinal = 0
    108     for field in union.fields:
    109       if field.ordinal is not None:
    110         ordinal = field.ordinal
    111       field.ordinal = ordinal
    112       ordinal += 1
    113     return union
    114 
    115   def _AddInterfaceComputedData(self, interface):
    116     """Adds computed data to the given interface. The data is computed once and
    117     used repeatedly in the generation process."""
    118     interface.version = 0
    119     for method in interface.methods:
    120       if method.min_version is not None:
    121         interface.version = max(interface.version, method.min_version)
    122 
    123       method.param_struct = self._GetStructFromMethod(method)
    124       interface.version = max(interface.version,
    125                               method.param_struct.versions[-1].version)
    126 
    127       if method.response_parameters is not None:
    128         method.response_param_struct = self._GetResponseStructFromMethod(method)
    129         interface.version = max(
    130             interface.version,
    131             method.response_param_struct.versions[-1].version)
    132       else:
    133         method.response_param_struct = None
    134     return interface
    135 
    136   def _GetStructFromMethod(self, method):
    137     """Converts a method's parameters into the fields of a struct."""
    138     params_class = "%s_%s_Params" % (method.interface.name, method.name)
    139     struct = mojom.Struct(params_class, module=method.interface.module)
    140     for param in method.parameters:
    141       struct.AddField(param.name, param.kind, param.ordinal,
    142                       attributes=param.attributes)
    143     return self._AddStructComputedData(False, struct)
    144 
    145   def _GetResponseStructFromMethod(self, method):
    146     """Converts a method's response_parameters into the fields of a struct."""
    147     params_class = "%s_%s_ResponseParams" % (method.interface.name, method.name)
    148     struct = mojom.Struct(params_class, module=method.interface.module)
    149     for param in method.response_parameters:
    150       struct.AddField(param.name, param.kind, param.ordinal,
    151                       attributes=param.attributes)
    152     return self._AddStructComputedData(False, struct)
    153