Home | History | Annotate | Download | only in scripts
      1 # Copyright (C) 2013 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #     * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #     * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #     * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 """Builds an IdlDefinitions object from an AST (produced by blink_idl_parser)."""
     30 
     31 import os
     32 
     33 from idl_definitions import IdlDefinitions, IdlInterface, IdlException, IdlOperation, IdlCallbackFunction, IdlArgument, IdlAttribute, IdlConstant, IdlEnum, IdlTypedef, IdlUnionType
     34 
     35 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER']
     36 
     37 
     38 def build_idl_definitions_from_ast(node):
     39     if node is None:
     40         return None
     41     node_class = node.GetClass()
     42     if node_class != 'File':
     43         raise ValueError('Unrecognized node class: %s' % node_class)
     44     return file_node_to_idl_definitions(node)
     45 
     46 
     47 def file_node_to_idl_definitions(node):
     48     callback_functions = {}
     49     enumerations = {}
     50     exceptions = {}
     51     interfaces = {}
     52     typedefs = {}
     53 
     54     # FIXME: only needed for Perl, remove later
     55     file_name = os.path.abspath(node.GetName())
     56 
     57     children = node.GetChildren()
     58     for child in children:
     59         child_class = child.GetClass()
     60         if child_class == 'Interface':
     61             interface = interface_node_to_idl_interface(child)
     62             interfaces[interface.name] = interface
     63         elif child_class == 'Exception':
     64             exception = exception_node_to_idl_exception(child)
     65             exceptions[exception.name] = exception
     66         elif child_class == 'Typedef':
     67             type_name = child.GetName()
     68             typedefs[type_name] = typedef_node_to_idl_typedef(child)
     69         elif child_class == 'Enum':
     70             enumeration = enum_node_to_idl_enum(child)
     71             enumerations[enumeration.name] = enumeration
     72         elif child_class == 'Callback':
     73             callback_function = callback_node_to_idl_callback_function(child)
     74             callback_functions[callback_function.name] = callback_function
     75         elif child_class == 'Implements':
     76             # Implements is handled at the interface merging step
     77             pass
     78         else:
     79             raise ValueError('Unrecognized node class: %s' % child_class)
     80 
     81     return IdlDefinitions(callback_functions=callback_functions, enumerations=enumerations, exceptions=exceptions, file_name=file_name, interfaces=interfaces, typedefs=typedefs)
     82 
     83 # Constructors for Interface definitions and interface members
     84 
     85 
     86 def interface_node_to_idl_interface(node):
     87     attributes = []
     88     constants = []
     89     constructors = None
     90     custom_constructors = None
     91     extended_attributes = None
     92     operations = []
     93     is_callback = node.GetProperty('CALLBACK') or False
     94     # FIXME: uppercase 'Partial' in base IDL parser
     95     is_partial = node.GetProperty('Partial') or False
     96     name = node.GetName()
     97     parent = None
     98 
     99     children = node.GetChildren()
    100     for child in children:
    101         child_class = child.GetClass()
    102         if child_class == 'Attribute':
    103             attribute = attribute_node_to_idl_attribute(child)
    104             # FIXME: This is a hack to support [CustomConstructor] for
    105             # window.HTMLImageElement. Remove the hack.
    106             clear_constructor_attributes(attribute.extended_attributes)
    107             attributes.append(attribute)
    108         elif child_class == 'Const':
    109             constants.append(constant_node_to_idl_constant(child))
    110         elif child_class == 'ExtAttributes':
    111             extended_attributes = ext_attributes_node_to_extended_attributes(child)
    112             constructors, custom_constructors = extended_attributes_to_constructors(extended_attributes)
    113             clear_constructor_attributes(extended_attributes)
    114         elif child_class == 'Operation':
    115             operations.append(operation_node_to_idl_operation(child))
    116         elif child_class == 'Inherit':
    117             parent = child.GetName()
    118         else:
    119             raise ValueError('Unrecognized node class: %s' % child_class)
    120 
    121     return IdlInterface(name=name, attributes=attributes, constants=constants, constructors=constructors, custom_constructors=custom_constructors, extended_attributes=extended_attributes, operations=operations, is_callback=is_callback, is_partial=is_partial, parent=parent)
    122 
    123 
    124 def attribute_node_to_idl_attribute(node):
    125     data_type = None
    126     extended_attributes = {}
    127     is_nullable = False
    128     is_read_only = node.GetProperty('READONLY') or False
    129     is_static = node.GetProperty('STATIC') or False
    130     name = node.GetName()
    131 
    132     children = node.GetChildren()
    133     for child in children:
    134         child_class = child.GetClass()
    135         if child_class == 'Type':
    136             data_type = type_node_to_type(child)
    137             is_nullable = child.GetProperty('NULLABLE') or False
    138         elif child_class == 'ExtAttributes':
    139             extended_attributes = ext_attributes_node_to_extended_attributes(child)
    140         else:
    141             raise ValueError('Unrecognized node class: %s' % child_class)
    142 
    143     return IdlAttribute(data_type=data_type, extended_attributes=extended_attributes, is_nullable=is_nullable, is_read_only=is_read_only, is_static=is_static, name=name)
    144 
    145 
    146 def constant_node_to_idl_constant(node):
    147     name = node.GetName()
    148 
    149     children = node.GetChildren()
    150     num_children = len(children)
    151     if num_children < 2 or num_children > 3:
    152         raise ValueError('Expected 2 or 3 children, got %s' % num_children)
    153 
    154     type_node = children[0]
    155     # ConstType is more limited than Type, so subtree is smaller and we don't
    156     # use the full type_node_to_type function.
    157     data_type = type_node_inner_to_type(type_node)
    158 
    159     value_node = children[1]
    160     value_node_class = value_node.GetClass()
    161     if value_node_class != 'Value':
    162         raise ValueError('Expected Value node, got %s' % value_node_class)
    163     value = value_node.GetName()
    164 
    165     extended_attributes = None
    166     if num_children == 3:
    167         ext_attributes_node = children[2]
    168         extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
    169 
    170     return IdlConstant(data_type=data_type, extended_attributes=extended_attributes, name=name, value=value)
    171 
    172 
    173 def operation_node_to_idl_operation(node):
    174     name = node.GetName()
    175     # FIXME: AST should use None internally
    176     if name == '_unnamed_':
    177         name = None
    178 
    179     is_static = node.GetProperty('STATIC') or False
    180     specials = []
    181     property_dictionary = node.GetProperties()
    182     for special_keyword in SPECIAL_KEYWORD_LIST:
    183         if special_keyword in property_dictionary:
    184             specials.append(special_keyword.lower())
    185 
    186     extended_attributes = None
    187     arguments = []
    188     return_type = None
    189     children = node.GetChildren()
    190     for child in children:
    191         child_class = child.GetClass()
    192         if child_class == 'Arguments':
    193             arguments = arguments_node_to_arguments(child)
    194         elif child_class == 'Type':
    195             return_type = type_node_to_type(child)
    196         elif child_class == 'ExtAttributes':
    197             extended_attributes = ext_attributes_node_to_extended_attributes(child)
    198         else:
    199             raise ValueError('Unrecognized node class: %s' % child_class)
    200 
    201     return IdlOperation(name=name, data_type=return_type, extended_attributes=extended_attributes, is_static=is_static, arguments=arguments, specials=specials)
    202 
    203 
    204 def arguments_node_to_arguments(node):
    205     # [Constructor] and [CustomConstructor] without arguments (the bare form)
    206     # have None instead of an arguments node, but have the same meaning as using
    207     # an empty argument list, [Constructor()], so special-case this.
    208     # http://www.w3.org/TR/WebIDL/#Constructor
    209     if node is None:
    210         return []
    211     arguments = []
    212     argument_node_list = node.GetChildren()
    213     for argument_node in argument_node_list:
    214         arguments.append(argument_node_to_idl_argument(argument_node))
    215     return arguments
    216 
    217 
    218 def argument_node_to_idl_argument(node):
    219     name = node.GetName()
    220 
    221     data_type = None
    222     extended_attributes = {}
    223     # FIXME: Boolean values are inconsistent due to Perl compatibility.
    224     # Make all default to False once Perl removed.
    225     is_nullable = False
    226     is_optional = node.GetProperty('OPTIONAL')
    227     is_variadic = None
    228     children = node.GetChildren()
    229     for child in children:
    230         child_class = child.GetClass()
    231         if child_class == 'Type':
    232             data_type = type_node_to_type(child)
    233             is_nullable = child.GetProperty('NULLABLE')
    234         elif child_class == 'ExtAttributes':
    235             extended_attributes = ext_attributes_node_to_extended_attributes(child)
    236         elif child_class == 'Argument':
    237             child_name = child.GetName()
    238             if child_name != '...':
    239                 raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name)
    240             is_variadic = child.GetProperty('ELLIPSIS') or False
    241         else:
    242             raise ValueError('Unrecognized node class: %s' % child_class)
    243 
    244     return IdlArgument(name=name, data_type=data_type, extended_attributes=extended_attributes, is_nullable=is_nullable, is_optional=is_optional, is_variadic=is_variadic)
    245 
    246 # Constructors for for non-interface definitions
    247 
    248 
    249 def callback_node_to_idl_callback_function(node):
    250     name = node.GetName()
    251     children = node.GetChildren()
    252     num_children = len(children)
    253     if num_children != 2:
    254         raise ValueError('Expected 2 children, got %s' % num_children)
    255 
    256     type_node = children[0]
    257     data_type = type_node_to_type(type_node)
    258 
    259     arguments_node = children[1]
    260     arguments_node_class = arguments_node.GetClass()
    261     if arguments_node_class != 'Arguments':
    262         raise ValueError('Expected Value node, got %s' % arguments_node_class)
    263     arguments = arguments_node_to_arguments(arguments_node)
    264 
    265     return IdlCallbackFunction(name=name, data_type=data_type, arguments=arguments)
    266 
    267 
    268 def enum_node_to_idl_enum(node):
    269     name = node.GetName()
    270     values = []
    271     for child in node.GetChildren():
    272         values.append(child.GetName())
    273     return IdlEnum(name=name, values=values)
    274 
    275 
    276 def exception_operation_node_to_idl_operation(node):
    277     # Needed to handle one case in DOMException.idl:
    278     # // Override in a Mozilla compatible format
    279     # [NotEnumerable] DOMString toString();
    280     # FIXME: can we remove this? replace with a stringifier?
    281     extended_attributes = {}
    282     name = node.GetName()
    283     children = node.GetChildren()
    284     if len(children) < 1 or len(children) > 2:
    285         raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children))
    286 
    287     type_node = children[0]
    288     return_type = type_node_to_type(type_node)
    289 
    290     if len(children) > 1:
    291         ext_attributes_node = children[1]
    292         extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
    293 
    294     return IdlOperation(name=name, data_type=return_type, extended_attributes=extended_attributes)
    295 
    296 
    297 def exception_node_to_idl_exception(node):
    298     # Exceptions are similar to Interfaces, but simpler
    299     attributes = []
    300     constants = []
    301     extended_attributes = None
    302     operations = []
    303     name = node.GetName()
    304 
    305     children = node.GetChildren()
    306     for child in children:
    307         child_class = child.GetClass()
    308         if child_class == 'Attribute':
    309             attribute = attribute_node_to_idl_attribute(child)
    310             attributes.append(attribute)
    311         elif child_class == 'Const':
    312             constants.append(constant_node_to_idl_constant(child))
    313         elif child_class == 'ExtAttributes':
    314             extended_attributes = ext_attributes_node_to_extended_attributes(child)
    315         elif child_class == 'ExceptionOperation':
    316             operations.append(exception_operation_node_to_idl_operation(child))
    317         else:
    318             raise ValueError('Unrecognized node class: %s' % child_class)
    319 
    320     return IdlException(name=name, attributes=attributes, constants=constants, extended_attributes=extended_attributes, operations=operations)
    321 
    322 
    323 def typedef_node_to_idl_typedef(node):
    324     data_type = None
    325     extended_attributes = None
    326 
    327     children = node.GetChildren()
    328     for child in children:
    329         child_class = child.GetClass()
    330         if child_class == 'Type':
    331             data_type = type_node_to_type(child)
    332         elif child_class == 'ExtAttributes':
    333             extended_attributes = ext_attributes_node_to_extended_attributes(child)
    334             raise ValueError('Extended attributes in a typedef are untested!')
    335         else:
    336             raise ValueError('Unrecognized node class: %s' % child_class)
    337 
    338     return IdlTypedef(data_type=data_type, extended_attributes=extended_attributes)
    339 
    340 # Extended attributes
    341 
    342 
    343 def ext_attributes_node_to_extended_attributes(node):
    344     """
    345     Returns:
    346       Dictionary of {ExtAttributeName: ExtAttributeValue}.
    347       Value is usually a string, with three exceptions:
    348       Constructors: value is a list of Arguments nodes, corresponding to
    349       possibly signatures of the constructor.
    350       CustomConstructors: value is a list of Arguments nodes, corresponding to
    351       possibly signatures of the custom constructor.
    352       NamedConstructor: value is a Call node, corresponding to the single
    353       signature of the named constructor.
    354     """
    355     # Primarily just make a dictionary from the children.
    356     # The only complexity is handling various types of constructors:
    357     # Constructors and Custom Constructors can have duplicate entries due to
    358     # overloading, and thus are stored in temporary lists.
    359     # However, Named Constructors cannot be overloaded, and thus do not have
    360     # a list.
    361     # FIXME: Add overloading for Named Constructors and remove custom bindings
    362     # for HTMLImageElement
    363     constructors = []
    364     custom_constructors = []
    365     extended_attributes = {}
    366 
    367     attribute_list = node.GetChildren()
    368     for attribute in attribute_list:
    369         name = attribute.GetName()
    370         children = attribute.GetChildren()
    371         if name in ['Constructor', 'CustomConstructor', 'NamedConstructor']:
    372             child = None
    373             child_class = None
    374             if children:
    375                 if len(children) > 1:
    376                     raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children))
    377                 child = children[0]
    378                 child_class = child.GetClass()
    379             if name == 'Constructor':
    380                 if child_class and child_class != 'Arguments':
    381                     raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class)
    382                 constructors.append(child)
    383             elif name == 'CustomConstructor':
    384                 if child_class and child_class != 'Arguments':
    385                     raise ValueError('Custom Constructor only supports Arguments as child, but has child of class: %s' % child_class)
    386                 custom_constructors.append(child)
    387             else:  # name == 'NamedConstructor'
    388                 if child_class and child_class != 'Call':
    389                     raise ValueError('Named Constructor only supports Call as child, but has child of class: %s' % child_class)
    390                 extended_attributes[name] = child
    391         elif children:
    392             raise ValueError('Non-constructor ExtAttributes node with children: %s' % name)
    393         else:
    394             value = attribute.GetProperty('VALUE')
    395             extended_attributes[name] = value
    396 
    397     # Store constructors and custom constructors in special list attributes,
    398     # which are deleted later. Note plural in key.
    399     if constructors:
    400         extended_attributes['Constructors'] = constructors
    401     if custom_constructors:
    402         extended_attributes['CustomConstructors'] = custom_constructors
    403 
    404     return extended_attributes
    405 
    406 
    407 def extended_attributes_to_constructors(extended_attributes):
    408     """Returns constructors and custom_constructors (lists of IdlOperations).
    409 
    410     Auxiliary function for interface_node_to_idl_interface.
    411     """
    412     constructors = []
    413     custom_constructors = []
    414     if 'Constructors' in extended_attributes:
    415         constructor_list = extended_attributes['Constructors']
    416         # If not overloaded, have index 0, otherwise index from 1
    417         overloaded_index = 0 if len(constructor_list) == 1 else 1
    418         for arguments_node in constructor_list:
    419             name = 'Constructor'
    420             arguments = arguments_node_to_arguments(arguments_node)
    421             constructor = IdlOperation(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, arguments=arguments)
    422             constructors.append(constructor)
    423             overloaded_index += 1
    424 
    425         # Prefix 'CallWith' and 'RaisesException' with 'Constructor'
    426         # FIXME: Change extended attributes to include prefix explicitly.
    427         if 'CallWith' in extended_attributes:
    428             extended_attributes['ConstructorCallWith'] = extended_attributes['CallWith']
    429             del extended_attributes['CallWith']
    430         if 'RaisesException' in extended_attributes:
    431             extended_attributes['ConstructorRaisesException'] = extended_attributes['RaisesException']
    432             del extended_attributes['RaisesException']
    433 
    434     if 'CustomConstructors' in extended_attributes:
    435         custom_constructor_list = extended_attributes['CustomConstructors']
    436         # If not overloaded, have index 0, otherwise index from 1
    437         overloaded_index = 0 if len(custom_constructor_list) == 1 else 1
    438         for arguments_node in custom_constructor_list:
    439             name = 'CustomConstructor'
    440             arguments = arguments_node_to_arguments(arguments_node)
    441             custom_constructor = IdlOperation(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, arguments=arguments)
    442             custom_constructors.append(custom_constructor)
    443             overloaded_index += 1
    444 
    445     if 'NamedConstructor' in extended_attributes:
    446         name = 'NamedConstructor'
    447         call_node = extended_attributes['NamedConstructor']
    448         extended_attributes['NamedConstructor'] = call_node.GetName()
    449         overloaded_index = None  # named constructors are not overloaded
    450         children = call_node.GetChildren()
    451         if len(children) != 1:
    452             raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children))
    453         arguments_node = children[0]
    454         arguments = arguments_node_to_arguments(arguments_node)
    455         named_constructor = IdlOperation(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, arguments=arguments)
    456         constructors.append(named_constructor)
    457 
    458     return constructors, custom_constructors
    459 
    460 
    461 def clear_constructor_attributes(extended_attributes):
    462     # Deletes Constructor*s* (plural), sets Constructor (singular)
    463     if 'Constructors' in extended_attributes:
    464         del extended_attributes['Constructors']
    465         extended_attributes['Constructor'] = None
    466     if 'CustomConstructors' in extended_attributes:
    467         del extended_attributes['CustomConstructors']
    468         extended_attributes['CustomConstructor'] = None
    469 
    470 
    471 # Types
    472 
    473 
    474 def type_node_to_type(node):
    475     children = node.GetChildren()
    476     if len(children) < 1 or len(children) > 2:
    477         raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children))
    478 
    479     type_node_child = children[0]
    480     data_type = type_node_inner_to_type(type_node_child)
    481 
    482     if len(children) == 2:
    483         array_node = children[1]
    484         array_node_class = array_node.GetClass()
    485         if array_node_class != 'Array':
    486             raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class)
    487         data_type += '[]'
    488 
    489     return data_type
    490 
    491 
    492 def type_node_inner_to_type(node):
    493     node_class = node.GetClass()
    494     # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
    495     # either a typedef shorthand (but not a Typedef declaration itself) or an
    496     # interface type. We do not distinguish these, and just use the type name.
    497     if node_class in ['PrimitiveType', 'Typeref']:
    498         return node.GetName()
    499     elif node_class == 'Any':
    500         return 'any'
    501     elif node_class == 'Sequence':
    502         return sequence_node_to_type(node)
    503     elif node_class == 'UnionType':
    504         return union_type_node_to_idl_union_type(node)
    505     raise ValueError('Unrecognized node class: %s' % node_class)
    506 
    507 
    508 def sequence_node_to_type(node):
    509     children = node.GetChildren()
    510     if len(children) != 1:
    511         raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children))
    512     sequence_child = children[0]
    513     sequence_child_class = sequence_child.GetClass()
    514     if sequence_child_class != 'Type':
    515         raise ValueError('Unrecognized node class: %s' % sequence_child_class)
    516     sequence_type = type_node_to_type(sequence_child)
    517     return 'sequence<%s>' % sequence_type
    518 
    519 
    520 def union_type_node_to_idl_union_type(node):
    521     union_member_types = []
    522     for member_type_node in node.GetChildren():
    523         member_type = type_node_to_type(member_type_node)
    524         union_member_types.append(member_type)
    525     return IdlUnionType(union_member_types=union_member_types)
    526