| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 """Blink IDL Intermediate Representation (IR) classes. |  | 
| 30 |  | 
| 31 Also JSON export, using legacy Perl terms and format, to ensure that both |  | 
| 32 parsers produce the same output. |  | 
| 33 FIXME: remove BaseIdl, JSON export (json_serializable and to_json), and Perl |  | 
| 34 compatibility functions and hacks once Perl compiler gone. |  | 
| 35 """ |  | 
| 36 |  | 
| 37 # Disable attribute hiding check (else JSONEncoder default raises an error) |  | 
| 38 # pylint: disable=E0202 |  | 
| 39 # pylint doesn't understand ABCs. |  | 
| 40 # pylint: disable=W0232, E0203, W0201 |  | 
| 41 |  | 
| 42 import abc |  | 
| 43 import json |  | 
| 44 import re |  | 
| 45 |  | 
| 46 |  | 
| 47 # Base classes |  | 
| 48 |  | 
| 49 |  | 
| 50 class BaseIdl: |  | 
| 51     """Abstract base class, used for JSON serialization.""" |  | 
| 52     __metaclass__ = abc.ABCMeta |  | 
| 53 |  | 
| 54     @abc.abstractmethod |  | 
| 55     def json_serializable(self): |  | 
| 56         """Returns a JSON serializable form of the object. |  | 
| 57 |  | 
| 58         This should be a dictionary, with keys scoped names of the form |  | 
| 59         Class::key, where the scope is the class name. |  | 
| 60         This is so we produce identical output to the Perl code, which uses |  | 
| 61         the Perl module JSON.pm, which uses this format. |  | 
| 62         """ |  | 
| 63         pass |  | 
| 64 |  | 
| 65 |  | 
| 66 class TypedObject: |  | 
| 67     """Object with a type, such as an Attribute or Operation (return value). |  | 
| 68 |  | 
| 69     The type can be an actual type, or can be a typedef, which must be resolved |  | 
| 70     before passing data to the code generator. |  | 
| 71     """ |  | 
| 72     __metaclass__ = abc.ABCMeta |  | 
| 73     data_type = None |  | 
| 74     extended_attributes = None |  | 
| 75 |  | 
| 76     def resolve_typedefs(self, typedefs): |  | 
| 77         """Resolve typedefs to actual types in the object.""" |  | 
| 78         additional_extended_attributes = {} |  | 
| 79         # Convert string representation to and from an IdlType object |  | 
| 80         # to handle parsing |  | 
| 81         data_type_object = IdlType.from_string(self.data_type) |  | 
| 82         base_type = data_type_object.base_type |  | 
| 83         if base_type in typedefs: |  | 
| 84             replacement_type = typedefs[base_type] |  | 
| 85             data_type_object.base_type = replacement_type.data_type |  | 
| 86             additional_extended_attributes = replacement_type.extended_attribute
     s |  | 
| 87         self.data_type = str(data_type_object) |  | 
| 88         self.extended_attributes.update(additional_extended_attributes) |  | 
| 89 |  | 
| 90 |  | 
| 91 # IDL classes |  | 
| 92 |  | 
| 93 |  | 
| 94 class IdlDefinitions(BaseIdl): |  | 
| 95     def __init__(self, callback_functions=None, enumerations=None, exceptions=No
     ne, file_name=None, interfaces=None, typedefs=None): |  | 
| 96         self.callback_functions = callback_functions or {} |  | 
| 97         self.enumerations = enumerations or {} |  | 
| 98         self.exceptions = exceptions or {} |  | 
| 99         self.file_name = file_name or None |  | 
| 100         self.interfaces = interfaces or {} |  | 
| 101         # Typedefs are not exposed by bindings; resolve typedefs with the |  | 
| 102         # actual types and then discard the Typedefs. |  | 
| 103         # http://www.w3.org/TR/WebIDL/#idl-typedefs |  | 
| 104         if typedefs: |  | 
| 105             self.resolve_typedefs(typedefs) |  | 
| 106 |  | 
| 107     def resolve_typedefs(self, typedefs): |  | 
| 108         for callback_function in self.callback_functions.itervalues(): |  | 
| 109             callback_function.resolve_typedefs(typedefs) |  | 
| 110         for exception in self.exceptions.itervalues(): |  | 
| 111             exception.resolve_typedefs(typedefs) |  | 
| 112         for interface in self.interfaces.itervalues(): |  | 
| 113             interface.resolve_typedefs(typedefs) |  | 
| 114 |  | 
| 115     def json_serializable(self): |  | 
| 116         return { |  | 
| 117                 'idlDocument::callbackFunctions': self.callback_functions.values
     (), |  | 
| 118                 'idlDocument::enumerations': self.enumerations.values(), |  | 
| 119                 'idlDocument::fileName': self.file_name, |  | 
| 120                 # Perl treats exceptions as a kind of interface |  | 
| 121                 'idlDocument::interfaces': sorted(self.exceptions.values() + sel
     f.interfaces.values()), |  | 
| 122                 } |  | 
| 123 |  | 
| 124     def to_json(self, debug=False): |  | 
| 125         """Returns a JSON string representing the Definitions. |  | 
| 126 |  | 
| 127         The JSON output should be identical with the output of the Perl parser, |  | 
| 128         specifically the function serializeJSON in deprecated_idl_serializer.pm, |  | 
| 129         which takes a Perl object created by deprecated_idl_parser.pm. |  | 
| 130         """ |  | 
| 131         # Sort so order consistent, allowing comparison of output |  | 
| 132         if debug: |  | 
| 133             # indent turns on pretty-printing for legibility |  | 
| 134             return json.dumps(self, cls=IdlEncoder, sort_keys=True, indent=4) |  | 
| 135         # Use compact separators so output identical to Perl |  | 
| 136         return json.dumps(self, cls=IdlEncoder, sort_keys=True, separators=(',',
      ':')) |  | 
| 137 |  | 
| 138 |  | 
| 139 class IdlCallbackFunction(BaseIdl, TypedObject): |  | 
| 140     def __init__(self, name=None, data_type=None, arguments=None): |  | 
| 141         self.data_type = data_type |  | 
| 142         self.name = name |  | 
| 143         self.arguments = arguments or [] |  | 
| 144 |  | 
| 145     def resolve_typedefs(self, typedefs): |  | 
| 146         TypedObject.resolve_typedefs(self, typedefs) |  | 
| 147         for argument in self.arguments: |  | 
| 148             argument.resolve_typedefs(typedefs) |  | 
| 149         raise ValueError('Typedefs in callback functions are untested!') |  | 
| 150 |  | 
| 151     def json_serializable(self): |  | 
| 152         return { |  | 
| 153             'callbackFunction::name': self.name, |  | 
| 154             'callbackFunction::type': self.data_type, |  | 
| 155             'callbackFunction::parameters': self.arguments, |  | 
| 156             } |  | 
| 157 |  | 
| 158 |  | 
| 159 class IdlEnum(BaseIdl): |  | 
| 160     def __init__(self, name=None, values=None): |  | 
| 161         self.name = name |  | 
| 162         self.values = values or [] |  | 
| 163 |  | 
| 164     def json_serializable(self): |  | 
| 165         return { |  | 
| 166             'domEnum::name': self.name, |  | 
| 167             'domEnum::values': self.values, |  | 
| 168             } |  | 
| 169 |  | 
| 170 |  | 
| 171 class IdlInterface(BaseIdl): |  | 
| 172     def __init__(self, attributes=None, constants=None, constructors=None, custo
     m_constructors=None, extended_attributes=None, operations=None, is_callback=Fals
     e, is_partial=False, name=None, parent=None): |  | 
| 173         self.attributes = attributes or [] |  | 
| 174         self.constants = constants or [] |  | 
| 175         self.constructors = constructors or [] |  | 
| 176         self.custom_constructors = custom_constructors or [] |  | 
| 177         self.extended_attributes = extended_attributes or {} |  | 
| 178         self.operations = operations or [] |  | 
| 179         self.is_callback = is_callback |  | 
| 180         self.is_partial = is_partial |  | 
| 181         self.name = name |  | 
| 182         self.parent = parent |  | 
| 183 |  | 
| 184     def resolve_typedefs(self, typedefs): |  | 
| 185         for attribute in self.attributes: |  | 
| 186             attribute.resolve_typedefs(typedefs) |  | 
| 187         for constant in self.constants: |  | 
| 188             constant.resolve_typedefs(typedefs) |  | 
| 189         for constructor in self.constructors: |  | 
| 190             constructor.resolve_typedefs(typedefs) |  | 
| 191         for custom_constructor in self.custom_constructors: |  | 
| 192             custom_constructor.resolve_typedefs(typedefs) |  | 
| 193         for operation in self.operations: |  | 
| 194             operation.resolve_typedefs(typedefs) |  | 
| 195 |  | 
| 196     def json_serializable(self): |  | 
| 197         return { |  | 
| 198             'domInterface::attributes': self.attributes, |  | 
| 199             'domInterface::constants': self.constants, |  | 
| 200             'domInterface::constructors': self.constructors, |  | 
| 201             'domInterface::customConstructors': self.custom_constructors, |  | 
| 202             'domInterface::extendedAttributes': none_to_value_is_missing(self.ex
     tended_attributes), |  | 
| 203             'domInterface::functions': self.operations, |  | 
| 204             'domInterface::isException': None, |  | 
| 205             'domInterface::isCallback': boolean_to_perl(false_to_none(self.is_ca
     llback)), |  | 
| 206             'domInterface::isPartial': false_to_none(self.is_partial), |  | 
| 207             'domInterface::name': self.name, |  | 
| 208             'domInterface::parent': self.parent, |  | 
| 209             } |  | 
| 210 |  | 
| 211 |  | 
| 212 class IdlException(BaseIdl): |  | 
| 213     def __init__(self, name=None, constants=None, operations=None, attributes=No
     ne, extended_attributes=None): |  | 
| 214         self.attributes = attributes or [] |  | 
| 215         self.constants = constants or [] |  | 
| 216         self.extended_attributes = extended_attributes or {} |  | 
| 217         self.operations = operations or [] |  | 
| 218         self.name = name |  | 
| 219 |  | 
| 220     def resolve_typedefs(self, typedefs): |  | 
| 221         for constant in self.constants: |  | 
| 222             constant.resolve_typedefs(typedefs) |  | 
| 223         for attribute in self.attributes: |  | 
| 224             attribute.resolve_typedefs(typedefs) |  | 
| 225         for operations in self.operations: |  | 
| 226             operations.resolve_typedefs(typedefs) |  | 
| 227 |  | 
| 228     def json_serializable(self): |  | 
| 229         return { |  | 
| 230             # Perl code treats Exceptions as a kind of Interface |  | 
| 231             'domInterface::name': self.name, |  | 
| 232             'domInterface::attributes': self.attributes, |  | 
| 233             'domInterface::constants': self.constants, |  | 
| 234             'domInterface::extendedAttributes': none_to_value_is_missing(self.ex
     tended_attributes), |  | 
| 235             'domInterface::functions': self.operations, |  | 
| 236             # These values don't vary for exceptions |  | 
| 237             'domInterface::constructors': [], |  | 
| 238             'domInterface::customConstructors': [], |  | 
| 239             'domInterface::isException': 1, |  | 
| 240             'domInterface::isCallback': None, |  | 
| 241             'domInterface::isPartial': None, |  | 
| 242             'domInterface::parent': None, |  | 
| 243             } |  | 
| 244 |  | 
| 245 |  | 
| 246 class IdlAttribute(BaseIdl, TypedObject): |  | 
| 247     def __init__(self, data_type=None, extended_attributes=None, getter_exceptio
     ns=None, is_nullable=False, is_static=False, is_read_only=False, name=None, sett
     er_exceptions=None): |  | 
| 248         self.data_type = data_type |  | 
| 249         self.extended_attributes = extended_attributes or {} |  | 
| 250         self.getter_exceptions = getter_exceptions or [] |  | 
| 251         self.is_nullable = is_nullable |  | 
| 252         self.is_static = is_static |  | 
| 253         self.is_read_only = is_read_only |  | 
| 254         self.name = name |  | 
| 255         self.setter_exceptions = setter_exceptions or [] |  | 
| 256 |  | 
| 257     def json_serializable(self): |  | 
| 258         return { |  | 
| 259             'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex
     tended_attributes), |  | 
| 260             'domAttribute::getterExceptions': self.getter_exceptions, |  | 
| 261             'domAttribute::isNullable': boolean_to_perl_quoted(false_to_none(sel
     f.is_nullable)), |  | 
| 262             'domAttribute::isReadOnly': boolean_to_perl(false_to_none(self.is_re
     ad_only)), |  | 
| 263             'domAttribute::isStatic': boolean_to_perl(false_to_none(self.is_stat
     ic)), |  | 
| 264             'domAttribute::name': self.name, |  | 
| 265             'domAttribute::setterExceptions': self.setter_exceptions, |  | 
| 266             'domAttribute::type': self.data_type, |  | 
| 267             } |  | 
| 268 |  | 
| 269 |  | 
| 270 class IdlConstant(BaseIdl, TypedObject): |  | 
| 271     def __init__(self, name=None, data_type=None, value=None, extended_attribute
     s=None): |  | 
| 272         self.data_type = data_type |  | 
| 273         self.extended_attributes = extended_attributes or {} |  | 
| 274         self.name = name |  | 
| 275         self.value = value |  | 
| 276 |  | 
| 277     def json_serializable(self): |  | 
| 278         return { |  | 
| 279             'domConstant::extendedAttributes': none_to_value_is_missing(self.ext
     ended_attributes), |  | 
| 280             'domConstant::name': self.name, |  | 
| 281             'domConstant::type': self.data_type, |  | 
| 282             'domConstant::value': self.value, |  | 
| 283             } |  | 
| 284 |  | 
| 285 |  | 
| 286 class IdlOperation(BaseIdl, TypedObject): |  | 
| 287     def __init__(self, is_static=False, name=None, data_type=None, extended_attr
     ibutes=None, specials=None, arguments=None, overloaded_index=None): |  | 
| 288         self.is_static = is_static |  | 
| 289         self.name = name or '' |  | 
| 290         self.data_type = data_type |  | 
| 291         self.extended_attributes = extended_attributes or {} |  | 
| 292         self.specials = specials or [] |  | 
| 293         self.arguments = arguments or [] |  | 
| 294         self.overloaded_index = overloaded_index |  | 
| 295 |  | 
| 296     def resolve_typedefs(self, typedefs): |  | 
| 297         TypedObject.resolve_typedefs(self, typedefs) |  | 
| 298         for argument in self.arguments: |  | 
| 299             argument.resolve_typedefs(typedefs) |  | 
| 300 |  | 
| 301     def json_serializable(self): |  | 
| 302         return { |  | 
| 303             'domFunction::extendedAttributes': none_to_value_is_missing(self.ext
     ended_attributes), |  | 
| 304             'domFunction::isStatic': boolean_to_perl(false_to_none(self.is_stati
     c)), |  | 
| 305             'domFunction::name': self.name, |  | 
| 306             'domFunction::overloadedIndex': self.overloaded_index, |  | 
| 307             'domFunction::parameters': self.arguments, |  | 
| 308             'domFunction::specials': self.specials, |  | 
| 309             'domFunction::type': self.data_type, |  | 
| 310             } |  | 
| 311 |  | 
| 312 |  | 
| 313 class IdlArgument(BaseIdl, TypedObject): |  | 
| 314     def __init__(self, name=None, data_type=None, extended_attributes=None, is_o
     ptional=False, is_nullable=None, is_variadic=False): |  | 
| 315         self.data_type = data_type |  | 
| 316         self.extended_attributes = extended_attributes or {} |  | 
| 317         # FIXME: boolean values are inconsistent. |  | 
| 318         # The below hack is so that generated JSON is identical to |  | 
| 319         # Perl-generated JSON, where the exact values depend on the code path. |  | 
| 320         # False and None (Perl: 0 and undef) are semantically interchangeable, |  | 
| 321         # but yield different JSON. |  | 
| 322         # Once Perl removed, have all default to False. |  | 
| 323         if is_optional is None: |  | 
| 324             is_optional = False |  | 
| 325             if is_variadic is None: |  | 
| 326                 is_variadic = False |  | 
| 327         self.is_nullable = is_nullable  # (T?) |  | 
| 328         self.is_optional = is_optional  # (optional T) |  | 
| 329         self.is_variadic = is_variadic  # (T...) |  | 
| 330         self.name = name |  | 
| 331 |  | 
| 332     def json_serializable(self): |  | 
| 333         return { |  | 
| 334             'domParameter::extendedAttributes': none_to_value_is_missing(self.ex
     tended_attributes), |  | 
| 335             'domParameter::isNullable': boolean_to_perl_quoted(self.is_nullable)
     , |  | 
| 336             'domParameter::isOptional': boolean_to_perl(self.is_optional), |  | 
| 337             'domParameter::isVariadic': boolean_to_perl(self.is_variadic), |  | 
| 338             'domParameter::name': self.name, |  | 
| 339             'domParameter::type': self.data_type, |  | 
| 340             } |  | 
| 341 |  | 
| 342 # Type classes |  | 
| 343 |  | 
| 344 |  | 
| 345 class IdlType: |  | 
| 346     # FIXME: replace type strings with these objects, |  | 
| 347     # so don't need to parse everywhere types are used. |  | 
| 348     # Types are stored internally as strings, not objects, |  | 
| 349     # e.g., as 'sequence<Foo>' or 'Foo[]', |  | 
| 350     # hence need to parse the string whenever a type is used. |  | 
| 351     # FIXME: incorporate Nullable, Variadic, etc. |  | 
| 352     # FIXME: properly should nest types |  | 
| 353     # Formally types are nested, e.g., short?[] vs. short[]?, |  | 
| 354     # but in practice these complex types aren't used and can treat |  | 
| 355     # as orthogonal properties. |  | 
| 356     def __init__(self, base_type, is_array=False, is_sequence=False): |  | 
| 357         if is_array and is_sequence: |  | 
| 358             raise ValueError('Array of Sequences are not allowed.') |  | 
| 359         self.base_type = base_type |  | 
| 360         self.is_array = is_array |  | 
| 361         self.is_sequence = is_sequence |  | 
| 362 |  | 
| 363     def __str__(self): |  | 
| 364         type_string = self.base_type |  | 
| 365         if self.is_array: |  | 
| 366             return type_string + '[]' |  | 
| 367         if self.is_sequence: |  | 
| 368             return 'sequence<%s>' % type_string |  | 
| 369         return type_string |  | 
| 370 |  | 
| 371     @classmethod |  | 
| 372     def from_string(cls, type_string): |  | 
| 373         sequence_re = r'^sequence<([^>]*)>$' |  | 
| 374         if type_string.endswith('[]'): |  | 
| 375             type_string = type_string[:-2] |  | 
| 376             sequence_match = re.match(sequence_re, type_string) |  | 
| 377             if sequence_match: |  | 
| 378                 raise ValueError('Array of Sequences are not allowed.') |  | 
| 379             return cls(type_string, is_array=True) |  | 
| 380         sequence_match = re.match(sequence_re, type_string) |  | 
| 381         if sequence_match: |  | 
| 382             base_type = sequence_match.group(1) |  | 
| 383             return cls(base_type, is_sequence=True) |  | 
| 384         return cls(type_string) |  | 
| 385 |  | 
| 386 |  | 
| 387 class IdlTypedef: |  | 
| 388     # Internal to IDL parsing: typedefs are all translated during IdlObject |  | 
| 389     # construction, and the typedefs themselves not stored in the object. |  | 
| 390     def __init__(self, extended_attributes=None, data_type=None): |  | 
| 391         self.extended_attributes = extended_attributes or {} |  | 
| 392         self.data_type = data_type |  | 
| 393 |  | 
| 394 |  | 
| 395 class IdlUnionType(BaseIdl): |  | 
| 396     def __init__(self, union_member_types=None): |  | 
| 397         self.union_member_types = union_member_types or [] |  | 
| 398 |  | 
| 399     def json_serializable(self): |  | 
| 400         return { |  | 
| 401             'UnionType::unionMemberTypes': self.union_member_types, |  | 
| 402             } |  | 
| 403 |  | 
| 404 |  | 
| 405 # Perl JSON compatibility functions |  | 
| 406 |  | 
| 407 def none_to_value_is_missing(extended_attributes): |  | 
| 408     # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in |  | 
| 409     # extended attributes, so add this as a filter when exporting to JSON. |  | 
| 410     new_extended_attributes = {} |  | 
| 411     for key, value in extended_attributes.iteritems(): |  | 
| 412         if value is None: |  | 
| 413             new_extended_attributes[key] = 'VALUE_IS_MISSING' |  | 
| 414         else: |  | 
| 415             new_extended_attributes[key] = value |  | 
| 416     return new_extended_attributes |  | 
| 417 |  | 
| 418 |  | 
| 419 def boolean_to_perl(value): |  | 
| 420     # Perl stores booleans as 1, 0, or undefined (JSON null); |  | 
| 421     # convert to this format. |  | 
| 422     if value is None: |  | 
| 423         return None |  | 
| 424     return int(value) |  | 
| 425 |  | 
| 426 |  | 
| 427 def boolean_to_perl_quoted(value): |  | 
| 428     # Bug-for-bug compatibility with Perl. |  | 
| 429     # The value of isNullable is quoted ('1', '0', or undefined), rather than |  | 
| 430     # an integer, so add quotes. |  | 
| 431     if value is None: |  | 
| 432         return None |  | 
| 433     return str(int(value)) |  | 
| 434 |  | 
| 435 |  | 
| 436 def false_to_none(value): |  | 
| 437     # The Perl parser generally uses undefined (Python None) rather than False |  | 
| 438     # for boolean flags, because the value is simply left undefined, rather than |  | 
| 439     # explicitly set to False. |  | 
| 440     if value is False: |  | 
| 441         return None |  | 
| 442     return value |  | 
| 443 |  | 
| 444 |  | 
| 445 # JSON export |  | 
| 446 |  | 
| 447 |  | 
| 448 class IdlEncoder(json.JSONEncoder): |  | 
| 449     def default(self, obj): |  | 
| 450         if isinstance(obj, BaseIdl): |  | 
| 451             return obj.json_serializable() |  | 
| 452         return json.JSONEncoder.default(self, obj) |  | 
| OLD | NEW | 
|---|