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   if kind.imported_from:
     41     return kind.imported_from["unique_name"] + "." + kind.name
     42   return kind.name
     43 
     44 
     45 def JavaScriptDefaultValue(field):
     46   if field.default:
     47     if mojom.IsStructKind(field.kind):
     48       assert field.default == "default"
     49       return "new %s()" % JavaScriptType(field.kind)
     50     return ExpressionToText(field.default)
     51   if field.kind in mojom.PRIMITIVES:
     52     return _kind_to_javascript_default_value[field.kind]
     53   if mojom.IsStructKind(field.kind):
     54     return "null"
     55   if mojom.IsAnyArrayKind(field.kind):
     56     return "null"
     57   if mojom.IsInterfaceKind(field.kind) or \
     58      mojom.IsInterfaceRequestKind(field.kind):
     59     return _kind_to_javascript_default_value[mojom.MSGPIPE]
     60   if mojom.IsEnumKind(field.kind):
     61     return "0"
     62 
     63 
     64 def JavaScriptPayloadSize(packed):
     65   packed_fields = packed.packed_fields
     66   if not packed_fields:
     67     return 0
     68   last_field = packed_fields[-1]
     69   offset = last_field.offset + last_field.size
     70   pad = pack.GetPad(offset, 8)
     71   return offset + pad
     72 
     73 
     74 _kind_to_codec_type = {
     75   mojom.BOOL:                  "codec.Uint8",
     76   mojom.INT8:                  "codec.Int8",
     77   mojom.UINT8:                 "codec.Uint8",
     78   mojom.INT16:                 "codec.Int16",
     79   mojom.UINT16:                "codec.Uint16",
     80   mojom.INT32:                 "codec.Int32",
     81   mojom.UINT32:                "codec.Uint32",
     82   mojom.FLOAT:                 "codec.Float",
     83   mojom.HANDLE:                "codec.Handle",
     84   mojom.DCPIPE:                "codec.Handle",
     85   mojom.DPPIPE:                "codec.Handle",
     86   mojom.MSGPIPE:               "codec.Handle",
     87   mojom.SHAREDBUFFER:          "codec.Handle",
     88   mojom.NULLABLE_HANDLE:       "codec.NullableHandle",
     89   mojom.NULLABLE_DCPIPE:       "codec.NullableHandle",
     90   mojom.NULLABLE_DPPIPE:       "codec.NullableHandle",
     91   mojom.NULLABLE_MSGPIPE:      "codec.NullableHandle",
     92   mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle",
     93   mojom.INT64:                 "codec.Int64",
     94   mojom.UINT64:                "codec.Uint64",
     95   mojom.DOUBLE:                "codec.Double",
     96   mojom.STRING:                "codec.String",
     97   mojom.NULLABLE_STRING:       "codec.NullableString",
     98 }
     99 
    100 
    101 def CodecType(kind):
    102   if kind in mojom.PRIMITIVES:
    103     return _kind_to_codec_type[kind]
    104   if mojom.IsStructKind(kind):
    105     pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \
    106         else "PointerTo"
    107     return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind))
    108   if mojom.IsAnyArrayKind(kind):
    109     array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
    110     element_type = "codec.PackedBool" if mojom.IsBoolKind(kind.kind) \
    111         else CodecType(kind.kind)
    112     return "new codec.%s(%s)" % (array_type, element_type)
    113   if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
    114     return CodecType(mojom.MSGPIPE)
    115   if mojom.IsEnumKind(kind):
    116     return _kind_to_codec_type[mojom.INT32]
    117   return kind
    118 
    119 
    120 def JavaScriptDecodeSnippet(kind):
    121   if kind in mojom.PRIMITIVES:
    122     return "decodeStruct(%s)" % CodecType(kind)
    123   if mojom.IsStructKind(kind):
    124     return "decodeStructPointer(%s)" % JavaScriptType(kind)
    125   if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind):
    126     return "decodeArrayPointer(codec.PackedBool)"
    127   if mojom.IsAnyArrayKind(kind):
    128     return "decodeArrayPointer(%s)" % CodecType(kind.kind)
    129   if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
    130     return JavaScriptDecodeSnippet(mojom.MSGPIPE)
    131   if mojom.IsEnumKind(kind):
    132     return JavaScriptDecodeSnippet(mojom.INT32)
    133 
    134 
    135 def JavaScriptEncodeSnippet(kind):
    136   if kind in mojom.PRIMITIVES:
    137     return "encodeStruct(%s, " % CodecType(kind)
    138   if mojom.IsStructKind(kind):
    139     return "encodeStructPointer(%s, " % JavaScriptType(kind)
    140   if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind):
    141     return "encodeArrayPointer(codec.PackedBool, ";
    142   if mojom.IsAnyArrayKind(kind):
    143     return "encodeArrayPointer(%s, " % CodecType(kind.kind)
    144   if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind):
    145     return JavaScriptEncodeSnippet(mojom.MSGPIPE)
    146   if mojom.IsEnumKind(kind):
    147     return JavaScriptEncodeSnippet(mojom.INT32)
    148 
    149 
    150 def JavaScriptFieldOffset(packed_field):
    151   return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
    152 
    153 
    154 def JavaScriptNullableParam(packed_field):
    155   return "true" if mojom.IsNullableKind(packed_field.field.kind) else "false"
    156 
    157 
    158 def JavaScriptValidateArrayParams(packed_field):
    159   nullable = JavaScriptNullableParam(packed_field)
    160   field_offset = JavaScriptFieldOffset(packed_field)
    161   element_kind = packed_field.field.kind.kind
    162   element_size = pack.PackedField.GetSizeForKind(element_kind)
    163   element_count = generator.ExpectedArraySize(packed_field.field.kind)
    164   element_type = "codec.PackedBool" if mojom.IsBoolKind(element_kind) \
    165       else CodecType(element_kind)
    166   return "%s, %s, %s, %s, %s" % \
    167       (field_offset, element_size, element_count, element_type, nullable)
    168 
    169 
    170 def JavaScriptValidateStructParams(packed_field):
    171   nullable = JavaScriptNullableParam(packed_field)
    172   field_offset = JavaScriptFieldOffset(packed_field)
    173   struct_type = JavaScriptType(packed_field.field.kind)
    174   return "%s, %s, %s" % (field_offset, struct_type, nullable)
    175 
    176 
    177 def JavaScriptValidateStringParams(packed_field):
    178   nullable = JavaScriptNullableParam(packed_field)
    179   return "%s, %s" % (JavaScriptFieldOffset(packed_field), nullable)
    180 
    181 
    182 def JavaScriptValidateHandleParams(packed_field):
    183   nullable = JavaScriptNullableParam(packed_field)
    184   field_offset = JavaScriptFieldOffset(packed_field)
    185   return "%s, %s" % (field_offset, nullable)
    186 
    187 
    188 def TranslateConstants(token):
    189   if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
    190     # Both variable and enum constants are constructed like:
    191     # NamespaceUid.Struct[.Enum].CONSTANT_NAME
    192     name = []
    193     if token.imported_from:
    194       name.append(token.imported_from["unique_name"])
    195     if token.parent_kind:
    196       name.append(token.parent_kind.name)
    197     if isinstance(token, mojom.EnumValue):
    198       name.append(token.enum.name)
    199     name.append(token.name)
    200     return ".".join(name)
    201 
    202   if isinstance(token, mojom.BuiltinValue):
    203     if token.value == "double.INFINITY" or token.value == "float.INFINITY":
    204       return "Infinity";
    205     if token.value == "double.NEGATIVE_INFINITY" or \
    206        token.value == "float.NEGATIVE_INFINITY":
    207       return "-Infinity";
    208     if token.value == "double.NAN" or token.value == "float.NAN":
    209       return "NaN";
    210 
    211   return token
    212 
    213 
    214 def ExpressionToText(value):
    215   return TranslateConstants(value)
    216 
    217 
    218 def IsArrayPointerField(field):
    219   return mojom.IsAnyArrayKind(field.kind)
    220 
    221 def IsStringPointerField(field):
    222   return mojom.IsStringKind(field.kind)
    223 
    224 def IsStructPointerField(field):
    225   return mojom.IsStructKind(field.kind)
    226 
    227 def IsHandleField(field):
    228   return mojom.IsAnyHandleKind(field.kind)
    229 
    230 
    231 class Generator(generator.Generator):
    232 
    233   js_filters = {
    234     "default_value": JavaScriptDefaultValue,
    235     "payload_size": JavaScriptPayloadSize,
    236     "decode_snippet": JavaScriptDecodeSnippet,
    237     "encode_snippet": JavaScriptEncodeSnippet,
    238     "expression_to_text": ExpressionToText,
    239     "field_offset": JavaScriptFieldOffset,
    240     "has_callbacks": mojom.HasCallbacks,
    241     "is_array_pointer_field": IsArrayPointerField,
    242     "is_struct_pointer_field": IsStructPointerField,
    243     "is_string_pointer_field": IsStringPointerField,
    244     "is_handle_field": IsHandleField,
    245     "js_type": JavaScriptType,
    246     "stylize_method": generator.StudlyCapsToCamel,
    247     "validate_array_params": JavaScriptValidateArrayParams,
    248     "validate_handle_params": JavaScriptValidateHandleParams,
    249     "validate_string_params": JavaScriptValidateStringParams,
    250     "validate_struct_params": JavaScriptValidateStructParams,
    251   }
    252 
    253   @UseJinja("js_templates/module.js.tmpl", filters=js_filters)
    254   def GenerateJsModule(self):
    255     return {
    256       "namespace": self.module.namespace,
    257       "imports": self.GetImports(),
    258       "kinds": self.module.kinds,
    259       "enums": self.module.enums,
    260       "module": self.module,
    261       "structs": self.GetStructs() + self.GetStructsFromMethods(),
    262       "interfaces": self.module.interfaces,
    263     }
    264 
    265   def GenerateFiles(self, args):
    266     self.Write(self.GenerateJsModule(), "%s.js" % self.module.name)
    267 
    268   def GetImports(self):
    269     # Since each import is assigned a variable in JS, they need to have unique
    270     # names.
    271     counter = 1
    272     for each in self.module.imports:
    273       each["unique_name"] = "import" + str(counter)
    274       counter += 1
    275     return self.module.imports
    276