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 |