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