| 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 |