Home | History | Annotate | Download | only in generators
      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 """Generates JavaScript source files from a mojom.Module."""
      6 
      7 import mojom.generate.generator as generator
      8 import mojom.generate.module as mojom
      9 import mojom.generate.pack as pack
     10 from mojom.generate.template_expander import UseJinja
     11 
     12 _kind_to_javascript_default_value = {
     13   mojom.BOOL:                  "false",
     14   mojom.INT8:                  "0",
     15   mojom.UINT8:                 "0",
     16   mojom.INT16:                 "0",
     17   mojom.UINT16:                "0",
     18   mojom.INT32:                 "0",
     19   mojom.UINT32:                "0",
     20   mojom.FLOAT:                 "0",
     21   mojom.HANDLE:                "null",
     22   mojom.DCPIPE:                "null",
     23   mojom.DPPIPE:                "null",
     24   mojom.MSGPIPE:               "null",
     25   mojom.SHAREDBUFFER:          "null",
     26   mojom.NULLABLE_HANDLE:       "null",
     27   mojom.NULLABLE_DCPIPE:       "null",
     28   mojom.NULLABLE_DPPIPE:       "null",
     29   mojom.NULLABLE_MSGPIPE:      "null",
     30   mojom.NULLABLE_SHAREDBUFFER: "null",
     31   mojom.INT64:                 "0",
     32   mojom.UINT64:                "0",
     33   mojom.DOUBLE:                "0",
     34   mojom.STRING:                "null",
     35   mojom.NULLABLE_STRING:       "null"
     36 }
     37 
     38 
     39 def JavaScriptType(kind):
     40   name = []
     41   if kind.imported_from:
     42     name.append(kind.imported_from["unique_name"])
     43   if kind.parent_kind:
     44     name.append(kind.parent_kind.name)
     45   name.append(kind.name)
     46   return ".".join(name)
     47 
     48 
     49 def JavaScriptDefaultValue(field):
     50   if field.default:
     51     if mojom.IsStructKind(field.kind):
     52       assert field.default == "default"
     53       return "new %s()" % JavaScriptType(field.kind)
     54     return ExpressionToText(field.default)
     55   if field.kind in mojom.PRIMITIVES:
     56     return _kind_to_javascript_default_value[field.kind]
     57   if mojom.IsStructKind(field.kind):
     58     return "null"
     59   if mojom.IsUnionKind(field.kind):
     60     return "null"
     61   if mojom.IsArrayKind(field.kind):
     62     return "null"
     63   if mojom.IsMapKind(field.kind):
     64     return "null"
     65   if mojom.IsInterfaceKind(field.kind):
     66     return "new %sPtr()" % JavaScriptType(field.kind)
     67   if mojom.IsInterfaceRequestKind(field.kind):
     68     return "new bindings.InterfaceRequest()"
     69   if mojom.IsAssociatedKind(field.kind):
     70     return "null"
     71   if mojom.IsEnumKind(field.kind):
     72     return "0"
     73   raise Exception("No valid default: %s" % field)
     74 
     75 
     76 def JavaScriptPayloadSize(packed):
     77   packed_fields = packed.packed_fields
     78   if not packed_fields:
     79     return 0
     80   last_field = packed_fields[-1]
     81   offset = last_field.offset + last_field.size
     82   pad = pack.GetPad(offset, 8)
     83   return offset + pad
     84 
     85 
     86 _kind_to_codec_type = {
     87   mojom.BOOL:                  "codec.Uint8",
     88   mojom.INT8:                  "codec.Int8",
     89   mojom.UINT8:                 "codec.Uint8",
     90   mojom.INT16:                 "codec.Int16",
     91   mojom.UINT16:                "codec.Uint16",
     92   mojom.INT32:                 "codec.Int32",
     93   mojom.UINT32:                "codec.Uint32",
     94   mojom.FLOAT:                 "codec.Float",
     95   mojom.HANDLE:                "codec.Handle",
     96   mojom.DCPIPE:                "codec.Handle",
     97   mojom.DPPIPE:                "codec.Handle",
     98   mojom.MSGPIPE:               "codec.Handle",
     99   mojom.SHAREDBUFFER:          "codec.Handle",
    100   mojom.NULLABLE_HANDLE:       "codec.NullableHandle",
    101   mojom.NULLABLE_DCPIPE:       "codec.NullableHandle",
    102   mojom.NULLABLE_DPPIPE:       "codec.NullableHandle",
    103   mojom.NULLABLE_MSGPIPE:      "codec.NullableHandle",
    104   mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle",
    105   mojom.INT64:                 "codec.Int64",
    106   mojom.UINT64:                "codec.Uint64",
    107   mojom.DOUBLE:                "codec.Double",
    108   mojom.STRING:                "codec.String",
    109   mojom.NULLABLE_STRING:       "codec.NullableString",
    110 }
    111 
    112 
    113 def CodecType(kind):
    114   if kind in mojom.PRIMITIVES:
    115     return _kind_to_codec_type[kind]
    116   if mojom.IsStructKind(kind):
    117     pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \
    118         else "PointerTo"
    119     return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind))
    120   if mojom.IsUnionKind(kind):
    121     return JavaScriptType(kind)
    122   if mojom.IsArrayKind(kind):
    123     array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
    124     array_length = "" if kind.length is None else ", %d" % kind.length
    125     element_type = ElementCodecType(kind.kind)
    126     return "new codec.%s(%s%s)" % (array_type, element_type, array_length)
    127   if mojom.IsInterfaceKind(kind):
    128     return "new codec.%s(%sPtr)" % (
    129         "NullableInterface" if mojom.IsNullableKind(kind) else "Interface",
    130         JavaScriptType(kind))
    131   if mojom.IsInterfaceRequestKind(kind):
    132     return "codec.%s" % (
    133         "NullableInterfaceRequest" if mojom.IsNullableKind(kind)
    134                                    else "InterfaceRequest")
    135   if mojom.IsAssociatedInterfaceKind(kind):
    136     return "codec.AssociatedInterfaceNotSupported"
    137   if mojom.IsAssociatedInterfaceRequestKind(kind):
    138     return "codec.AssociatedInterfaceRequestNotSupported"
    139   if mojom.IsEnumKind(kind):
    140     return "new codec.Enum(%s)" % JavaScriptType(kind)
    141   if mojom.IsMapKind(kind):
    142     map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf"
    143     key_type = ElementCodecType(kind.key_kind)
    144     value_type = ElementCodecType(kind.value_kind)
    145     return "new codec.%s(%s, %s)" % (map_type, key_type, value_type)
    146   raise Exception("No codec type for %s" % kind)
    147 
    148 
    149 def ElementCodecType(kind):
    150   return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind)
    151 
    152 
    153 def JavaScriptDecodeSnippet(kind):
    154   if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
    155       mojom.IsAnyInterfaceKind(kind)):
    156     return "decodeStruct(%s)" % CodecType(kind)
    157   if mojom.IsStructKind(kind):
    158     return "decodeStructPointer(%s)" % JavaScriptType(kind)
    159   if mojom.IsMapKind(kind):
    160     return "decodeMapPointer(%s, %s)" % \
    161         (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
    162   if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
    163     return "decodeArrayPointer(codec.PackedBool)"
    164   if mojom.IsArrayKind(kind):
    165     return "decodeArrayPointer(%s)" % CodecType(kind.kind)
    166   if mojom.IsUnionKind(kind):
    167     return "decodeUnion(%s)" % CodecType(kind)
    168   if mojom.IsEnumKind(kind):
    169     return JavaScriptDecodeSnippet(mojom.INT32)
    170   raise Exception("No decode snippet for %s" % kind)
    171 
    172 
    173 def JavaScriptEncodeSnippet(kind):
    174   if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
    175       mojom.IsAnyInterfaceKind(kind)):
    176     return "encodeStruct(%s, " % CodecType(kind)
    177   if mojom.IsUnionKind(kind):
    178     return "encodeStruct(%s, " % JavaScriptType(kind)
    179   if mojom.IsStructKind(kind):
    180     return "encodeStructPointer(%s, " % JavaScriptType(kind)
    181   if mojom.IsMapKind(kind):
    182     return "encodeMapPointer(%s, %s, " % \
    183         (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
    184   if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
    185     return "encodeArrayPointer(codec.PackedBool, ";
    186   if mojom.IsArrayKind(kind):
    187     return "encodeArrayPointer(%s, " % CodecType(kind.kind)
    188   if mojom.IsEnumKind(kind):
    189     return JavaScriptEncodeSnippet(mojom.INT32)
    190   raise Exception("No encode snippet for %s" % kind)
    191 
    192 
    193 def JavaScriptUnionDecodeSnippet(kind):
    194   if mojom.IsUnionKind(kind):
    195     return "decodeStructPointer(%s)" % JavaScriptType(kind)
    196   return JavaScriptDecodeSnippet(kind)
    197 
    198 
    199 def JavaScriptUnionEncodeSnippet(kind):
    200   if mojom.IsUnionKind(kind):
    201     return "encodeStructPointer(%s, " % JavaScriptType(kind)
    202   return JavaScriptEncodeSnippet(kind)
    203 
    204 
    205 def JavaScriptFieldOffset(packed_field):
    206   return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
    207 
    208 
    209 def JavaScriptNullableParam(field):
    210   return "true" if mojom.IsNullableKind(field.kind) else "false"
    211 
    212 
    213 def GetArrayExpectedDimensionSizes(kind):
    214   expected_dimension_sizes = []
    215   while mojom.IsArrayKind(kind):
    216     expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0)
    217     kind = kind.kind
    218   # Strings are serialized as variable-length arrays.
    219   if (mojom.IsStringKind(kind)):
    220     expected_dimension_sizes.append(0)
    221   return expected_dimension_sizes
    222 
    223 
    224 def JavaScriptValidateArrayParams(field):
    225   nullable = JavaScriptNullableParam(field)
    226   element_kind = field.kind.kind
    227   element_size = pack.PackedField.GetSizeForKind(element_kind)
    228   expected_dimension_sizes = GetArrayExpectedDimensionSizes(
    229       field.kind)
    230   element_type = ElementCodecType(element_kind)
    231   return "%s, %s, %s, %s, 0" % \
    232       (element_size, element_type, nullable,
    233        expected_dimension_sizes)
    234 
    235 
    236 def JavaScriptValidateEnumParams(field):
    237   return JavaScriptType(field.kind)
    238 
    239 def JavaScriptValidateStructParams(field):
    240   nullable = JavaScriptNullableParam(field)
    241   struct_type = JavaScriptType(field.kind)
    242   return "%s, %s" % (struct_type, nullable)
    243 
    244 def JavaScriptValidateUnionParams(field):
    245   nullable = JavaScriptNullableParam(field)
    246   union_type = JavaScriptType(field.kind)
    247   return "%s, %s" % (union_type, nullable)
    248 
    249 def JavaScriptValidateMapParams(field):
    250   nullable = JavaScriptNullableParam(field)
    251   keys_type = ElementCodecType(field.kind.key_kind)
    252   values_kind = field.kind.value_kind;
    253   values_type = ElementCodecType(values_kind)
    254   values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false"
    255   return "%s, %s, %s, %s" % \
    256       (nullable, keys_type, values_type, values_nullable)
    257 
    258 
    259 def TranslateConstants(token):
    260   if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
    261     # Both variable and enum constants are constructed like:
    262     # NamespaceUid.Struct[.Enum].CONSTANT_NAME
    263     name = []
    264     if token.imported_from:
    265       name.append(token.imported_from["unique_name"])
    266     if token.parent_kind:
    267       name.append(token.parent_kind.name)
    268     if isinstance(token, mojom.EnumValue):
    269       name.append(token.enum.name)
    270     name.append(token.name)
    271     return ".".join(name)
    272 
    273   if isinstance(token, mojom.BuiltinValue):
    274     if token.value == "double.INFINITY" or token.value == "float.INFINITY":
    275       return "Infinity";
    276     if token.value == "double.NEGATIVE_INFINITY" or \
    277        token.value == "float.NEGATIVE_INFINITY":
    278       return "-Infinity";
    279     if token.value == "double.NAN" or token.value == "float.NAN":
    280       return "NaN";
    281 
    282   return token
    283 
    284 
    285 def ExpressionToText(value):
    286   return TranslateConstants(value)
    287 
    288 def IsArrayPointerField(field):
    289   return mojom.IsArrayKind(field.kind)
    290 
    291 def IsEnumField(field):
    292   return mojom.IsEnumKind(field.kind)
    293 
    294 def IsStringPointerField(field):
    295   return mojom.IsStringKind(field.kind)
    296 
    297 def IsStructPointerField(field):
    298   return mojom.IsStructKind(field.kind)
    299 
    300 def IsMapPointerField(field):
    301   return mojom.IsMapKind(field.kind)
    302 
    303 def IsHandleField(field):
    304   return mojom.IsAnyHandleKind(field.kind)
    305 
    306 def IsInterfaceField(field):
    307   return mojom.IsInterfaceKind(field.kind)
    308 
    309 def IsInterfaceRequestField(field):
    310   return mojom.IsInterfaceRequestKind(field.kind)
    311 
    312 def IsUnionField(field):
    313   return mojom.IsUnionKind(field.kind)
    314 
    315 def IsBoolField(field):
    316   return mojom.IsBoolKind(field.kind)
    317 
    318 def IsObjectField(field):
    319   return mojom.IsObjectKind(field.kind)
    320 
    321 def IsAnyHandleOrInterfaceField(field):
    322   return mojom.IsAnyHandleOrInterfaceKind(field.kind)
    323 
    324 def IsEnumField(field):
    325   return mojom.IsEnumKind(field.kind)
    326 
    327 
    328 class Generator(generator.Generator):
    329 
    330   js_filters = {
    331     "decode_snippet": JavaScriptDecodeSnippet,
    332     "default_value": JavaScriptDefaultValue,
    333     "encode_snippet": JavaScriptEncodeSnippet,
    334     "expression_to_text": ExpressionToText,
    335     "field_offset": JavaScriptFieldOffset,
    336     "has_callbacks": mojom.HasCallbacks,
    337     "is_any_handle_or_interface_field": IsAnyHandleOrInterfaceField,
    338     "is_array_pointer_field": IsArrayPointerField,
    339     "is_bool_field": IsBoolField,
    340     "is_enum_field": IsEnumField,
    341     "is_handle_field": IsHandleField,
    342     "is_interface_field": IsInterfaceField,
    343     "is_interface_request_field": IsInterfaceRequestField,
    344     "is_map_pointer_field": IsMapPointerField,
    345     "is_object_field": IsObjectField,
    346     "is_string_pointer_field": IsStringPointerField,
    347     "is_struct_pointer_field": IsStructPointerField,
    348     "is_union_field": IsUnionField,
    349     "js_type": JavaScriptType,
    350     "payload_size": JavaScriptPayloadSize,
    351     "stylize_method": generator.StudlyCapsToCamel,
    352     "union_decode_snippet": JavaScriptUnionDecodeSnippet,
    353     "union_encode_snippet": JavaScriptUnionEncodeSnippet,
    354     "validate_array_params": JavaScriptValidateArrayParams,
    355     "validate_enum_params": JavaScriptValidateEnumParams,
    356     "validate_map_params": JavaScriptValidateMapParams,
    357     "validate_nullable_params": JavaScriptNullableParam,
    358     "validate_struct_params": JavaScriptValidateStructParams,
    359     "validate_union_params": JavaScriptValidateUnionParams,
    360   }
    361 
    362   def GetParameters(self):
    363     return {
    364       "namespace": self.module.namespace,
    365       "imports": self.GetImports(),
    366       "kinds": self.module.kinds,
    367       "enums": self.module.enums,
    368       "module": self.module,
    369       "structs": self.GetStructs() + self.GetStructsFromMethods(),
    370       "unions": self.GetUnions(),
    371       "interfaces": self.GetInterfaces(),
    372       "imported_interfaces": self.GetImportedInterfaces(),
    373     }
    374 
    375   @staticmethod
    376   def GetTemplatePrefix():
    377     return "js_templates"
    378 
    379   @classmethod
    380   def GetFilters(cls):
    381     return cls.js_filters
    382 
    383   @UseJinja("module.amd.tmpl")
    384   def GenerateAMDModule(self):
    385     return self.GetParameters()
    386 
    387   def GenerateFiles(self, args):
    388     if self.variant:
    389       raise Exception("Variants not supported in JavaScript bindings.")
    390 
    391     self.Write(self.GenerateAMDModule(),
    392         self.MatchMojomFilePath("%s.js" % self.module.name))
    393 
    394   def GetImports(self):
    395     used_names = set()
    396     for each_import in self.module.imports:
    397       simple_name = each_import["module_name"].split(".")[0]
    398 
    399       # Since each import is assigned a variable in JS, they need to have unique
    400       # names.
    401       unique_name = simple_name
    402       counter = 0
    403       while unique_name in used_names:
    404         counter += 1
    405         unique_name = simple_name + str(counter)
    406 
    407       used_names.add(unique_name)
    408       each_import["unique_name"] = unique_name + "$"
    409       counter += 1
    410     return self.module.imports
    411 
    412   def GetImportedInterfaces(self):
    413     interface_to_import = {};
    414     for each_import in self.module.imports:
    415       for each_interface in each_import["module"].interfaces:
    416         name = each_interface.name
    417         interface_to_import[name] = each_import["unique_name"] + "." + name
    418     return interface_to_import;
    419 
    420