| Index: Source/bindings/scripts/ast_to_ir.py
|
| diff --git a/Source/bindings/scripts/ast_to_ir.py b/Source/bindings/scripts/ast_to_ir.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9dc61b80d4f7f8a739dd64043e1e23b49fd456cd
|
| --- /dev/null
|
| +++ b/Source/bindings/scripts/ast_to_ir.py
|
| @@ -0,0 +1,531 @@
|
| +# Copyright (C) 2013 Google Inc. All rights reserved.
|
| +#
|
| +# Redistribution and use in source and binary forms, with or without
|
| +# modification, are permitted provided that the following conditions are
|
| +# met:
|
| +#
|
| +# * Redistributions of source code must retain the above copyright
|
| +# notice, this list of conditions and the following disclaimer.
|
| +# * Redistributions in binary form must reproduce the above
|
| +# copyright notice, this list of conditions and the following disclaimer
|
| +# in the documentation and/or other materials provided with the
|
| +# distribution.
|
| +# * Neither the name of Google Inc. nor the names of its
|
| +# contributors may be used to endorse or promote products derived from
|
| +# this software without specific prior written permission.
|
| +#
|
| +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| +
|
| +"""Convert generic Web IDL AST to Blink IR
|
| +
|
| +Last phase of frontend, after lexer and parser.
|
| +IR then consumed by code_generator_v8.py to produce .cpp/.h files.
|
| +Currently contains legacy code for compatibility with Perl format.
|
| +Ideally parser would generate IR directly, rather then requiring another phase.
|
| +"""
|
| +
|
| +from ir import IdlDocument, DomInterface, DomFunction, CallbackFunction, DomParameter, DomAttribute, DomConstant, DomEnum, Typedef, UnionType
|
| +
|
| +
|
| +SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'CREATOR', 'DELETER', 'LEGACYCALLER']
|
| +
|
| +# Generic Web IDL AST to Blink IR
|
| +
|
| +
|
| +def web_idl_ast_to_blink_ir(node):
|
| + if node is None:
|
| + return None
|
| + node_class = node.GetClass()
|
| + if node_class == 'File':
|
| + return file_node_to_idl_document(node)
|
| + raise ValueError('Unrecognized node class: %s' % node_class)
|
| +
|
| +
|
| +def file_node_to_idl_document(node):
|
| + callback_functions = []
|
| + enumerations = []
|
| + file_name = node.GetName()
|
| + interfaces = []
|
| + typedefs = {}
|
| +
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Interface':
|
| + interfaces.append(interface_node_to_dom_interface(child))
|
| + elif child_class == 'Exception':
|
| + interfaces.append(exception_node_to_dom_interface(child))
|
| + elif child_class == 'Typedef':
|
| + type_name = child.GetName()
|
| + typedefs[type_name] = typedef_node_to_typedef(child)
|
| + elif child_class == 'Enum':
|
| + enumerations.append(enum_node_to_dom_enum(child))
|
| + elif child_class == 'Callback':
|
| + callback_functions.append(callback_node_to_callback_function(child))
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| +
|
| + return IdlDocument(callback_functions=callback_functions, enumerations=enumerations, file_name=file_name, interfaces=interfaces, typedefs=typedefs)
|
| +
|
| +# Interface
|
| +
|
| +
|
| +def interface_node_to_dom_interface(node, is_exception=None):
|
| + def multiple_inherit_node_to_parents(node):
|
| + parents = []
|
| + for child in node.GetChildren():
|
| + parents.append(child.GetName())
|
| + return parents
|
| +
|
| + attributes = []
|
| + constants = []
|
| + extended_attributes = {}
|
| + functions = []
|
| + is_callback = node.GetProperty('Callback')
|
| + name = node.GetName()
|
| + parents = []
|
| +
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Attribute':
|
| + attributes.append(attribute_node_to_dom_attribute(child))
|
| + elif child_class == 'MultipleInherit':
|
| + parents = multiple_inherit_node_to_parents(child)
|
| + elif child_class == 'Const':
|
| + constants.append(constant_node_to_dom_constant(child))
|
| + elif child_class == 'Operation':
|
| + functions.append(operation_node_to_dom_function(child))
|
| + elif child_class == 'OperationOrIterator':
|
| + functions.append(operation_or_iterator_node_to_dom_function(child))
|
| + elif child_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(child)
|
| + elif child_class == 'ExceptionFieldToString':
|
| + functions.append(exception_field_to_string_node_to_dom_function(child))
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| + constructors, custom_constructors = extended_attributes_to_constructors(extended_attributes)
|
| +
|
| + return DomInterface(name=name, attributes=attributes, constants=constants, constructors=constructors, custom_constructors=custom_constructors, extended_attributes=extended_attributes, functions=functions, is_callback=is_callback, is_exception=is_exception, parents=parents)
|
| +
|
| +
|
| +def attribute_node_to_dom_attribute(node):
|
| + data_type = None
|
| + extended_attributes = {}
|
| + is_nullable = None
|
| + property_dictionary = node.GetProperties()
|
| + is_read_only = get_property('READONLY', property_dictionary)
|
| + name = node.GetName()
|
| +
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Type':
|
| + data_type = type_node_to_type(child)
|
| + type_property_dictionary = child.GetProperties()
|
| + is_nullable = get_quoted_property('NULLABLE', type_property_dictionary)
|
| + elif child_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(child)
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| +
|
| + return DomAttribute(data_type=data_type, extended_attributes=extended_attributes, is_read_only=is_read_only, is_nullable=is_nullable, name=name)
|
| +
|
| +
|
| +def constant_node_to_dom_constant(node):
|
| + name = node.GetName()
|
| +
|
| + children = node.GetChildren()
|
| + num_children = len(children)
|
| + if num_children > 3:
|
| + raise ValueError('Expected at most 3 children, got %s' % num_children)
|
| +
|
| + type_node = children[0]
|
| + # FIXME: use inner get type function
|
| + data_type = type_node.GetName()
|
| +
|
| + value_node = children[1]
|
| + value_node_class = value_node.GetClass()
|
| + if value_node_class != 'Value':
|
| + raise ValueError('Expected Value node, got %s' % value_node_class)
|
| + value = value_node.GetName()
|
| +
|
| + extended_attributes = None
|
| + if num_children == 3:
|
| + ext_attributes_node = children[2]
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
|
| +
|
| + return DomConstant(data_type=data_type, extended_attributes=extended_attributes, name=name, value=value)
|
| +
|
| +
|
| +def operation_node_to_dom_function(node):
|
| + # FIXME: make Operation, and OperationOrIterator have
|
| + # same tree structure so can merge these
|
| +
|
| + name = node.GetName()
|
| + # FIXME: AST should use None internally
|
| + if name == '_unnamed_':
|
| + name = None
|
| +
|
| + property_dictionary = node.GetProperties()
|
| + is_static = None
|
| + if 'STATIC' in property_dictionary:
|
| + is_static = property_dictionary['STATIC']
|
| + specials = []
|
| + for special_keyword in SPECIAL_KEYWORD_LIST:
|
| + if special_keyword in property_dictionary:
|
| + specials.append(special_keyword.lower())
|
| +
|
| + extended_attributes = None
|
| + parameters = []
|
| + return_type = None
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Arguments':
|
| + parameters = argument_list_node_to_parameters(child)
|
| + elif child_class == 'Type':
|
| + return_type = type_node_to_type(child)
|
| + elif child_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(child)
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| +
|
| + return DomFunction(name=name, data_type=return_type, extended_attributes=extended_attributes, is_static=is_static, parameters=parameters, specials=specials)
|
| +
|
| +
|
| +def operation_or_iterator_node_to_dom_function(node):
|
| + extended_attributes = {}
|
| + name = None
|
| + parameters = []
|
| + return_type = None
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Type':
|
| + return_type = type_node_to_type(child)
|
| + elif child_class == 'Operation':
|
| + name = child.GetName()
|
| + # FIXME: this nesting is really ugly
|
| + grandchildren = child.GetChildren()
|
| + for grandchild in grandchildren:
|
| + grandchild_class = grandchild.GetClass()
|
| + if grandchild_class == 'Arguments':
|
| + parameters = argument_list_node_to_parameters(grandchild)
|
| + elif grandchild_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(grandchild)
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % grandchild_class)
|
| + elif child_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(child)
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| +
|
| + return DomFunction(name=name, data_type=return_type, extended_attributes=extended_attributes, parameters=parameters)
|
| +
|
| +
|
| +def argument_list_node_to_parameters(argument_list_node):
|
| + # FIXME: is this check necessary?
|
| + if argument_list_node is None:
|
| + return []
|
| + parameters = []
|
| + argument_list = argument_list_node.GetChildren()
|
| + for argument_node in argument_list:
|
| + parameters.append(argument_node_to_dom_parameter(argument_node))
|
| + return parameters
|
| +
|
| +
|
| +def argument_node_to_dom_parameter(node):
|
| + name = node.GetName()
|
| + is_optional = node.GetProperty('OPTIONAL')
|
| +
|
| + data_type = None
|
| + extended_attributes = {}
|
| + is_nullable = False
|
| + is_variadic = None
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Type':
|
| + data_type = type_node_to_type(child)
|
| + type_property_dictionary = child.GetProperties()
|
| + is_nullable = get_quoted_property('NULLABLE', type_property_dictionary)
|
| + elif child_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(child)
|
| + elif child_class == 'Argument':
|
| + child_name = child.GetName()
|
| + if child_name != '...':
|
| + raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name)
|
| + is_variadic = child.GetProperty('ELLIPSIS')
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| +
|
| + return DomParameter(name=name, data_type=data_type, extended_attributes=extended_attributes, is_nullable=is_nullable, is_optional=is_optional, is_variadic=is_variadic)
|
| +
|
| +# Minor definitions
|
| +
|
| +
|
| +def callback_node_to_callback_function(node):
|
| + name = node.GetName()
|
| + children = node.GetChildren()
|
| + num_children = len(children)
|
| + if num_children != 2:
|
| + raise ValueError('Expected 2 children, got %s' % num_children)
|
| +
|
| + type_node = children[0]
|
| + data_type = type_node_to_type(type_node)
|
| +
|
| + arguments_node = children[1]
|
| + arguments_node_class = arguments_node.GetClass()
|
| + if arguments_node_class != 'Arguments':
|
| + raise ValueError('Expected Value node, got %s' % arguments_node_class)
|
| + parameters = argument_list_node_to_parameters(arguments_node)
|
| +
|
| + return CallbackFunction(name=name, data_type=data_type, parameters=parameters)
|
| +
|
| +
|
| +def enum_node_to_dom_enum(node):
|
| + name = node.GetName()
|
| + values = []
|
| + for child in node.GetChildren():
|
| + values.append(child.GetName())
|
| + return DomEnum(name=name, values=values)
|
| +
|
| +
|
| +def exception_field_to_string_node_to_dom_function(node):
|
| + extended_attributes = {}
|
| + name = node.GetName()
|
| + children = node.GetChildren()
|
| + if len(children) > 2:
|
| + raise ValueError('ExceptionFieldToString node with %s children, expected at most 2' % len(children))
|
| +
|
| + type_node = children[0]
|
| + return_type = type_node_to_type(type_node)
|
| +
|
| + if len(children) > 1:
|
| + ext_attributes_node = children[1]
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
|
| +
|
| + return DomFunction(name=name, data_type=return_type, extended_attributes=extended_attributes)
|
| +
|
| +
|
| +def exception_node_to_dom_interface(node):
|
| + # Exceptions treated as interfaces with a flag set,
|
| + # rather than a different class
|
| + return interface_node_to_dom_interface(node, is_exception=True)
|
| +
|
| +
|
| +def typedef_node_to_typedef(node):
|
| + data_type = None
|
| + extended_attributes = None
|
| +
|
| + children = node.GetChildren()
|
| + for child in children:
|
| + child_class = child.GetClass()
|
| + if child_class == 'Type':
|
| + data_type = type_node_to_type(child)
|
| + elif child_class == 'ExtAttributes':
|
| + extended_attributes = ext_attributes_node_to_extended_attributes(child)
|
| + raise ValueError('Extended attributes in a typedef are untested!')
|
| + else:
|
| + raise ValueError('Unrecognized node class: %s' % child_class)
|
| +
|
| + return Typedef(data_type=data_type, extended_attributes=extended_attributes)
|
| +
|
| +# Extended attributes
|
| +
|
| +
|
| +def ext_attributes_node_to_extended_attributes(node):
|
| + # Constructors and Custom Constructors can have duplicate entries due to
|
| + # overloading, but Named Constructors cannot
|
| + constructors = []
|
| + custom_constructors = []
|
| + extended_attributes = {}
|
| +
|
| + attribute_list = node.GetChildren()
|
| + for attribute in attribute_list:
|
| + name = attribute.GetName()
|
| + children = attribute.GetChildren()
|
| + if name in ['Constructor', 'CustomConstructor', 'NamedConstructor']:
|
| + child = None
|
| + child_class = None
|
| + # FIXME: is child required?? append(None)??
|
| + if children:
|
| + if len(children) > 1:
|
| + raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children))
|
| + child = children[0]
|
| + child_class = child.GetClass()
|
| + if name == 'Constructor':
|
| + if child_class and child_class != 'Arguments':
|
| + raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class)
|
| + constructors.append(child)
|
| + elif name == 'CustomConstructor':
|
| + if child_class and child_class != 'Arguments':
|
| + raise ValueError('Custom Constructor only supports Arguments as child, but has child of class: %s' % child_class)
|
| + custom_constructors.append(child)
|
| + else: # name == 'NamedConstructor'
|
| + if child_class and child_class != 'Call':
|
| + raise ValueError('Named Constructor only supports Call as child, but has child of class: %s' % child_class)
|
| + extended_attributes[name] = child
|
| + elif children:
|
| + raise ValueError('Non-constructor ExtAttributes node with children: %s' % name)
|
| + else:
|
| + value = attribute.GetProperty('VALUE')
|
| + extended_attributes[name] = value
|
| +
|
| + # Store constructors and custom constructors in special list attributes,
|
| + # which are deleted later. Note plural in key.
|
| + if constructors:
|
| + extended_attributes['Constructors'] = constructors
|
| + if custom_constructors:
|
| + extended_attributes['CustomConstructors'] = custom_constructors
|
| +
|
| + return extended_attributes
|
| +
|
| +
|
| +def extended_attributes_to_constructors(extended_attributes):
|
| + """Returns constructors and custom_constructors (lists of DomFunctions),
|
| + deletes the special list attributes, and puts dummy empty value in
|
| + Constructor and CustomConstructor extended attributes.
|
| + Auxiliary function for interface_node_to_dom_interface."""
|
| + constructors = []
|
| + custom_constructors = []
|
| + if 'Constructors' in extended_attributes:
|
| + constructor_list = extended_attributes['Constructors']
|
| + # If not overloaded, have index 0, otherwise index from 1
|
| + overloaded_index = 0 if len(constructor_list) == 1 else 1
|
| + for arguments in constructor_list:
|
| + name = 'Constructor'
|
| + parameters = argument_list_node_to_parameters(arguments)
|
| + # FIXME: other default for is_variadic
|
| + constructors.append(DomFunction(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, parameters=parameters))
|
| + overloaded_index += 1
|
| + del extended_attributes['Constructors']
|
| + extended_attributes['Constructor'] = None
|
| +
|
| + # Prefix 'CallWith' and 'RaisesException' with 'Constructor'
|
| + # FIXME: I have no idea why this is necessary
|
| + if 'CallWith' in extended_attributes:
|
| + extended_attributes['ConstructorCallWith'] = extended_attributes['CallWith']
|
| + del extended_attributes['CallWith']
|
| + if 'RaisesException' in extended_attributes:
|
| + extended_attributes['ConstructorRaisesException'] = extended_attributes['RaisesException']
|
| + del extended_attributes['RaisesException']
|
| +
|
| + if 'CustomConstructors' in extended_attributes:
|
| + custom_constructor_list = extended_attributes['CustomConstructors']
|
| + # If not overloaded, have index 0, otherwise index from 1
|
| + overloaded_index = 0 if len(custom_constructor_list) == 1 else 1
|
| + for arguments in custom_constructor_list:
|
| + name = 'CustomConstructor'
|
| + parameters = argument_list_node_to_parameters(arguments)
|
| + custom_constructors.append(DomFunction(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, parameters=parameters))
|
| + overloaded_index += 1
|
| + del extended_attributes['CustomConstructors']
|
| + extended_attributes['CustomConstructor'] = None
|
| +
|
| + if 'NamedConstructor' in extended_attributes:
|
| + name = 'NamedConstructor'
|
| + call_node = extended_attributes['NamedConstructor']
|
| + extended_attributes['NamedConstructor'] = call_node.GetName()
|
| + arguments = call_node.GetChildren()[0]
|
| + parameters = argument_list_node_to_parameters(arguments)
|
| + overloaded_index = None # FIXME: handle overloaded named constructors
|
| + constructors.append(DomFunction(name=name, extended_attributes=extended_attributes, overloaded_index=overloaded_index, parameters=parameters))
|
| +
|
| + return constructors, custom_constructors
|
| +
|
| +# Types
|
| +
|
| +
|
| +def type_node_to_type(node):
|
| + children = node.GetChildren()
|
| + if not children:
|
| + raise ValueError('Type node expects children, got none.')
|
| + if len(children) > 2:
|
| + raise ValueError('Type node expects at most 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children))
|
| +
|
| + type_node_child = children[0]
|
| + data_type = type_node_inner_to_type(type_node_child)
|
| +
|
| + if len(children) == 2:
|
| + array_node = children[1]
|
| + array_node_class = array_node.GetClass()
|
| + if array_node_class != 'Array':
|
| + raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class)
|
| + data_type += '[]'
|
| +
|
| + return data_type
|
| +
|
| +
|
| +def type_node_inner_to_type(node):
|
| + """Auxiliary."""
|
| + # FIXME: better comment
|
| + node_class = node.GetClass()
|
| + if node_class in ['PrimitiveType', 'Typeref']:
|
| + return node.GetName()
|
| + elif node_class == 'Any':
|
| + return 'any'
|
| + elif node_class == 'Sequence':
|
| + return sequence_node_to_type(node)
|
| + elif node_class == 'UnionType':
|
| + return union_type_node_to_type(node)
|
| + raise ValueError('Unrecognized node class: %s' % node_class)
|
| +
|
| +
|
| +def sequence_node_to_type(node):
|
| + children = node.GetChildren()
|
| + if len(children) != 1:
|
| + raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children))
|
| + sequence_child = children[0]
|
| + sequence_child_class = sequence_child.GetClass()
|
| + if sequence_child_class != 'Type':
|
| + raise ValueError('Unrecognized node class: %s' % sequence_child_class)
|
| + sequence_type = type_node_to_type(sequence_child)
|
| + return 'sequence<%s>' % sequence_type
|
| +
|
| +
|
| +def union_type_node_to_type(node):
|
| + union_member_types = []
|
| + for member_type_node in node.GetChildren():
|
| + member_type = type_node_inner_to_type(member_type_node)
|
| + union_member_types.append(member_type)
|
| + return UnionType(union_member_types=union_member_types)
|
| +
|
| +
|
| +# Perl JSON compatibility functions
|
| +
|
| +
|
| +def get_property(key, dictionary):
|
| + # Need to use 1/0/None rather than True/False for compatibility
|
| + # with Perl-generated JSON.
|
| + # FIXME: can this be replaced by boolean_to_perl in to_json instead?
|
| + # FIXME: removed once verify generated code same
|
| + if key in dictionary:
|
| + if dictionary[key]:
|
| + return 1
|
| + else:
|
| + return 0
|
| + return None
|
| +
|
| +
|
| +def get_quoted_property(key, dictionary):
|
| + # More bug-for-bug compatibility with Perl,
|
| + # here giving certain values as strings rather than integers.
|
| + # FIXME: remove once verify generated code same
|
| + if key in dictionary:
|
| + if dictionary[key]:
|
| + return '1'
|
| + else:
|
| + return '0'
|
| + return None
|
|
|