Chromium Code Reviews| Index: Source/bindings/scripts/ir.py |
| diff --git a/Source/bindings/scripts/ir.py b/Source/bindings/scripts/ir.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0cf0fcebf36601cdd7cc42f4634acc878fa36737 |
| --- /dev/null |
| +++ b/Source/bindings/scripts/ir.py |
| @@ -0,0 +1,407 @@ |
| +# Copyright (C) 2013 Google Inc. All rights reserved. |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Your comments need work.
Fragments do not work we
Nils Barth (inactive)
2013/06/26 04:43:37
Will do.
|
| +# |
| +# 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. |
| + |
| +"""Blink IDL Intermediate Representation (IR) classes |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
End the first line of a docstring comment with a p
|
| + |
| +Also JSON export. |
| +""" |
| + |
| +# Disable attribute hiding check (else JSONEncoder default raises an error) |
| +# pylint: disable=E0202 |
| + |
| +import abc |
| +import json |
| +import os.path |
| +import re |
| + |
| + |
| +# Base classes |
| + |
| + |
| +class BaseIdl(): |
| + """Abstract base class, used for JSON serialization.""" |
| + __metaclass__ = abc.ABCMeta |
| + |
| + @abc.abstractmethod |
| + def to_json(self): |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Is there a better name for this? It doesn't return
Nils Barth (inactive)
2013/06/26 04:43:37
"json_serializable" is more precise; will change.
|
| + """Return a serializable form of the object. |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Return -> Returns
|
| + |
| + Compatible with Perl IR and JSON import. |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Wrap text; separate paragraphs with blank lines. Y
dominicc (has gone to gerrit)
2013/06/26 04:20:53
This documentation needs to be improved.
"Compati
|
| + In practice a dictionary, whose keys specify the class.""" |
| + pass |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
It would be better to raise an exception here. If
Nils Barth (inactive)
2013/06/26 04:43:37
The ABC (Abstract Base Class) library handles this
|
| + |
| + |
| +class TypedIdlObject(BaseIdl): |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
If this class handles typedefs, maybe it should ha
|
| + """Auxiliary class for handling typedefs.""" |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Auxiliary is a meaningless word without more conte
|
| + data_type = None |
| + extended_attributes = {} |
|
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Won't this hash object be shared by all instances
Nils Barth (inactive)
2013/06/26 04:43:37
(Just added this to silence lint errors; probably
|
| + |
| + def apply_typedefs(self, typedefs): |
| + """Applies typedefs to object itself (e.g., return type of function). |
| + |
| + Override if calling on parameters as well.""" |
| + new_extended_attributes = {} |
| + # Convert string representation to and from a Type object |
| + # to handle parsing |
| + data_type_object = Type.from_string(self.data_type) |
| + base_type = data_type_object.base_type |
| + if base_type in typedefs: |
| + replacement_type = typedefs[base_type] |
| + data_type_object.base_type = replacement_type.data_type |
| + new_extended_attributes = replacement_type.extended_attributes |
| + self.data_type = str(data_type_object) |
| + self.extended_attributes.update(new_extended_attributes) |
| + if new_extended_attributes: |
| + raise ValueError('Extended attributes in a typedef are untested!') |
| + |
| + |
| +# IDL classes |
| + |
| + |
| +class IdlDocument(BaseIdl): |
| + def __init__(self, callback_functions=None, enumerations=None, file_name=None, interfaces=None, typedefs=None): |
| + self.callback_functions = callback_functions or [] |
| + self.enumerations = enumerations or [] |
| + if file_name: |
| + self.file_name = os.path.abspath(file_name) |
| + self.interfaces = interfaces or [] |
| + if typedefs: |
| + self.apply_typedefs(typedefs) |
| + |
| + def apply_typedefs(self, typedefs): |
| + for callback_function in self.callback_functions: |
| + callback_function.apply_typedefs(typedefs) |
| + for interface in self.interfaces: |
| + interface.apply_typedefs(typedefs) |
| + |
| + def to_json(self): |
| + return { |
| + 'idlDocument::callbackFunctions': self.callback_functions, |
| + 'idlDocument::enumerations': self.enumerations, |
| + 'idlDocument::fileName': self.file_name, |
| + 'idlDocument::interfaces': self.interfaces, |
| + } |
| + |
| + |
| +class CallbackFunction(TypedIdlObject): |
| + def __init__(self, name=None, data_type=None, parameters=None): |
| + """parameters: List of DomParameters""" |
| + self.data_type = data_type |
| + self.name = name or "" # FIXME: is "" needed? |
| + self.parameters = parameters or [] |
| + |
| + def apply_typedefs(self, typedefs): |
| + TypedIdlObject.apply_typedefs(self, typedefs) |
| + for parameter in self.parameters: |
| + parameter.apply_typedefs(typedefs) |
| + raise ValueError('Typedefs in CallbackFunctions are untested!') |
| + |
| + def to_json(self): |
| + return { |
| + 'callbackFunction::name': self.name, |
| + 'callbackFunction::type': self.data_type, |
| + 'callbackFunction::parameters': self.parameters, |
| + } |
| + |
| + |
| +class DomEnum(BaseIdl): |
| + def __init__(self, name=None, values=None): |
| + """name: enumeration identifier |
| + values: enumeration values, list of unique strings |
| + """ |
| + self.name = name |
| + self.values = values or [] |
| + |
| + def to_json(self): |
| + return { |
| + 'domEnum::name': self.name, |
| + 'domEnum::values': self.values, |
| + } |
| + |
| + |
| +class DomInterface(BaseIdl): |
| + def __init__(self, name=None, parents=None, constants=None, functions=None, attributes=None, extended_attributes=None, constructors=None, custom_constructors=None, is_exception=None, is_callback=None, is_partial=None): |
| + """ |
| + attributes: list of DomAttributes |
| + constants: list of DomConstants |
| + constructors: list of DomFunctions |
| + custom_constructors: list of DomFunctions |
| + functions: list of DomFunctions |
| + is_exception: used for exceptions |
| + parents: list of strings |
| + """ |
| + self.attributes = attributes or [] |
| + self.constants = constants or [] |
| + self.constructors = constructors or [] |
| + self.custom_constructors = custom_constructors or [] |
| + self.extended_attributes = extended_attributes or {} |
| + self.functions = functions or [] |
| + self.is_exception = is_exception |
| + self.is_callback = is_callback |
| + self.is_partial = is_partial |
| + self.name = name |
| + self.parents = parents or [] |
| + |
| + def apply_typedefs(self, typedefs): |
| + for constant in self.constants: |
| + constant.apply_typedefs(typedefs) |
| + for attribute in self.attributes: |
| + attribute.apply_typedefs(typedefs) |
| + for function in self.functions: |
| + function.apply_typedefs(typedefs) |
| + for constructor in self.constructors: |
| + constructor.apply_typedefs(typedefs) |
| + for custom_constructor in self.custom_constructors: |
| + custom_constructor.apply_typedefs(typedefs) |
| + |
| + def to_json(self): |
| + return { |
| + 'domInterface::name': self.name, |
| + 'domInterface::parents': self.parents, |
| + 'domInterface::constants': self.constants, |
| + 'domInterface::functions': self.functions, |
| + 'domInterface::attributes': self.attributes, |
| + 'domInterface::extendedAttributes': none_to_value_is_missing(self.extended_attributes), |
| + 'domInterface::constructors': self.constructors, |
| + 'domInterface::customConstructors': self.custom_constructors, |
| + 'domInterface::isException': boolean_to_perl(self.is_exception), |
| + 'domInterface::isCallback': boolean_to_perl(self.is_callback), |
| + 'domInterface::isPartial': self.is_partial, |
| + } |
| + |
| + |
| +class DomAttribute(TypedIdlObject): |
| + def __init__(self, data_type=None, name=None, is_nullable=None, is_static=None, is_read_only=None, getter_exceptions=None, setter_exceptions=None, extended_attributes=None): |
| + """data_type: Attribute type (including namespace), string or UnionType |
| + is_nullable: (T?) |
| + getter_exceptions: Possibly raised exceptions |
| + setter_exceptions: Possibly raised exceptions |
| + """ |
| + self.data_type = data_type |
| + self.extended_attributes = extended_attributes or {} |
| + self.getter_exceptions = getter_exceptions or [] |
| + self.is_nullable = is_nullable |
| + self.is_static = is_static |
| + self.is_read_only = is_read_only |
| + self.name = name |
| + self.setter_exceptions = setter_exceptions or [] |
| + |
| + def to_json(self): |
| + return { |
| + 'domAttribute::extendedAttributes': none_to_value_is_missing(self.extended_attributes), |
| + 'domAttribute::getterExceptions': self.getter_exceptions, |
| + 'domAttribute::isNullable': self.is_nullable, |
| + 'domAttribute::isReadOnly': self.is_read_only, |
| + 'domAttribute::isStatic': self.is_static, |
| + 'domAttribute::name': self.name, |
| + 'domAttribute::setterExceptions': self.setter_exceptions, |
| + 'domAttribute::type': self.data_type, |
| + } |
| + |
| + |
| +class DomConstant(TypedIdlObject): |
| + def __init__(self, name=None, data_type=None, value=None, extended_attributes=None): |
| + self.data_type = data_type |
| + self.extended_attributes = extended_attributes or {} |
| + self.name = name |
| + self.value = value |
| + |
| + def to_json(self): |
| + return { |
| + 'domConstant::extendedAttributes': none_to_value_is_missing(self.extended_attributes), |
| + 'domConstant::name': self.name, |
| + 'domConstant::type': self.data_type, |
| + 'domConstant::value': self.value, |
| + } |
| + |
| + |
| +class DomFunction(TypedIdlObject): |
| + def __init__(self, is_static=None, name=None, data_type=None, extended_attributes=None, specials=None, parameters=None, overloaded_index=None): |
| + """parameters: List of DomParameters""" |
| + self.is_static = is_static |
| + self.name = name or "" |
| + self.data_type = data_type |
| + self.extended_attributes = extended_attributes or {} |
| + self.specials = specials or [] |
| + self.parameters = parameters or [] |
| + self.overloaded_index = overloaded_index |
| + |
| + def apply_typedefs(self, typedefs): |
| + TypedIdlObject.apply_typedefs(self, typedefs) |
| + for parameter in self.parameters: |
| + parameter.apply_typedefs(typedefs) |
| + |
| + def to_json(self): |
| + return { |
| + 'domFunction::extendedAttributes': none_to_value_is_missing(self.extended_attributes), |
| + 'domFunction::isStatic': boolean_to_perl(self.is_static), |
| + 'domFunction::name': self.name, |
| + 'domFunction::overloadedIndex': self.overloaded_index, |
| + 'domFunction::parameters': self.parameters, |
| + 'domFunction::specials': self.specials, |
| + 'domFunction::type': self.data_type, |
| + } |
| + |
| + |
| +class DomParameter(TypedIdlObject): |
| + def __init__(self, name=None, data_type=None, extended_attributes=None, is_optional=False, is_nullable=None, is_variadic=False): |
| + """Used to represent a map of 'variable name' <-> 'variable type' |
| + |
| + data_type: string or UnionType |
| + is_optional: (optional T) |
| + is_nullable: (T?) |
| + is_variadic: (long... numbers) |
| + """ |
| + self.data_type = data_type |
| + self.extended_attributes = extended_attributes or {} |
| + # FIXME: boolean values are inconsistent (due to Perl), |
| + # being sometimes True, False, or None (Perl: 1, 0, undef) |
| + # Should all default to False, and be either True or False |
| + if is_optional is None: |
| + is_optional = False |
| + if is_variadic is None: |
| + is_variadic = False |
| + self.is_nullable = is_nullable |
| + # FIXME: can these be in to_json instead? |
| + self.is_optional = boolean_to_perl(is_optional) |
| + self.is_variadic = boolean_to_perl(is_variadic) |
| + self.name = name |
| + |
| + def to_json(self): |
| + return { |
| + 'domParameter::extendedAttributes': none_to_value_is_missing(self.extended_attributes), |
| + 'domParameter::isNullable': self.is_nullable, |
| + 'domParameter::isOptional': self.is_optional, |
| + 'domParameter::isVariadic': self.is_variadic, |
| + 'domParameter::name': self.name, |
| + 'domParameter::type': self.data_type, |
| + } |
| + |
| +# Type classes |
| + |
| + |
| +class Type(): |
| + # FIXME: replace Type strings with these objects, |
| + # so don't need to parse everywhere types are used. |
| + # Types are stored internally as strings, not objects, |
| + # e.g., as 'sequence<Foo>' or 'Foo[]', |
| + # hence need to parse the string whenever a type is used. |
| + # FIXME: incorporate Nullable, Variadic, etc. |
| + # FIXME: properly should nest types |
| + # Formally types are nested, e.g., short?[] vs. short[]?, |
| + # but in practice these complex types aren't used and can treat |
| + # as orthogonal properties. |
| + def __init__(self, base_type=None, is_array=False, is_sequence=False): |
| + if is_array and is_sequence: |
| + raise ValueError('Array of Sequences not allowed.') |
| + self.base_type = base_type |
| + self.is_array = is_array |
| + self.is_sequence = is_sequence |
| + |
| + def __str__(self): |
| + type_string = self.base_type |
| + if self.is_array: |
| + type_string += '[]' |
| + if self.is_sequence: |
| + type_string = 'sequence<%s>' % type_string |
| + return type_string |
| + |
| + @classmethod |
| + def from_string(cls, type_string): |
| + is_array = False |
| + if type_string.endswith('[]'): |
| + is_array = True |
| + type_string = type_string[:-2] |
| + |
| + is_sequence = False |
| + sequence_re = r'^sequence<([^>]*)>$' |
| + sequence_match = re.match(sequence_re, type_string) |
| + if sequence_match: |
| + is_sequence = True |
| + type_string = sequence_match.group(1) |
| + base_type = type_string |
| + return cls(base_type=base_type, is_array=is_array, is_sequence=is_sequence) |
| + |
| + |
| +class Typedef(): |
| + # Not exposed in bindings, internal to IDL parsing |
| + def __init__(self, extended_attributes=None, data_type=None): |
| + self.extended_attributes = extended_attributes or {} |
| + self.data_type = data_type |
| + |
| + |
| +class UnionType(BaseIdl): |
| + def __init__(self, union_member_types=None): |
| + """union_member_types: list of string or UnionType""" |
| + self.union_member_types = union_member_types or [] |
| + |
| + def to_json(self): |
| + return { |
| + 'UnionType::unionMemberTypes': self.union_member_types, |
| + } |
| + |
| + |
| +# Perl JSON compatiblity functions |
| +# FIXME: remove when Perl removed |
| + |
| +def none_to_value_is_missing(extended_attributes): |
| + # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in |
| + # extended attributes, so add this as a filter when exporting to JSON. |
| + new_extended_attributes = {} |
| + for key, value in extended_attributes.iteritems(): |
| + if value is None: |
| + new_extended_attributes[key] = 'VALUE_IS_MISSING' |
| + else: |
| + new_extended_attributes[key] = value |
| + return new_extended_attributes |
| + |
| + |
| +def boolean_to_perl(value): |
| + # Perl stores booleans as 1, 0, or undefined (JSON null); |
| + # convert to this format |
| + if value is None: |
| + return None |
| + return int(value) |
| + |
| +# JSON export |
| +# FIXME: remove when Perl removed |
| +# (so no longer using JSON as intermediate format) |
| + |
| + |
| +class IdlEncoder(json.JSONEncoder): |
| + def default(self, obj): |
| + if isinstance(obj, BaseIdl): |
| + return obj.to_json() |
| + return json.JSONEncoder.default(self, obj) |
| + |
| + |
| +def ir_to_json(ir, debug=False): |
| + if debug: |
| + # More legible |
| + return json.dumps(ir, cls=IdlEncoder, sort_keys=True, indent=4) |
| + return json.dumps(ir, cls=IdlEncoder, sort_keys=True, separators=(',', ':')) |