| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 3 # for details. All rights reserved. Use of this source code is governed by a | |
| 4 # BSD-style license that can be found in the LICENSE file. | |
| 5 | |
| 6 import copy | |
| 7 import database | |
| 8 import idlparser | |
| 9 import logging | |
| 10 import os | |
| 11 import os.path | |
| 12 import re | |
| 13 | |
| 14 from idlnode import * | |
| 15 | |
| 16 _logger = logging.getLogger('databasebuilder') | |
| 17 | |
| 18 # Used in source annotations to specify the parent interface declaring | |
| 19 # a displaced declaration. The 'via' attribute specifies the parent interface | |
| 20 # which implements a displaced declaration. | |
| 21 _VIA_ANNOTATION_ATTR_NAME = 'via' | |
| 22 | |
| 23 # Used in source annotations to specify the module that the interface was | |
| 24 # imported from. | |
| 25 _MODULE_ANNOTATION_ATTR_NAME = 'module' | |
| 26 | |
| 27 | |
| 28 class DatabaseBuilderOptions(object): | |
| 29 """Used in specifying options when importing new interfaces""" | |
| 30 | |
| 31 def __init__(self, | |
| 32 idl_syntax=idlparser.WEBIDL_SYNTAX, | |
| 33 idl_defines=[], | |
| 34 source=None, source_attributes={}, | |
| 35 type_rename_map={}, | |
| 36 rename_operation_arguments_on_merge=False, | |
| 37 add_new_interfaces=True, | |
| 38 obsolete_old_declarations=False): | |
| 39 """Constructor. | |
| 40 Args: | |
| 41 idl_syntax -- the syntax of the IDL file that is imported. | |
| 42 idl_defines -- list of definitions for the idl gcc pre-processor | |
| 43 source -- the origin of the IDL file, used for annotating the | |
| 44 database. | |
| 45 source_attributes -- this map of attributes is used as | |
| 46 annotation attributes. | |
| 47 rename_operation_arguments_on_merge -- if True, will rename | |
| 48 operation arguments when merging using the new name rather | |
| 49 than the old. | |
| 50 add_new_interfaces -- when False, if an interface is a new | |
| 51 addition, it will be ignored. | |
| 52 obsolete_old_declarations -- when True, if a declaration | |
| 53 from a certain source is not re-declared, it will be removed. | |
| 54 """ | |
| 55 self.source = source | |
| 56 self.source_attributes = source_attributes | |
| 57 self.idl_syntax = idl_syntax | |
| 58 self.idl_defines = idl_defines | |
| 59 self.type_rename_map = type_rename_map | |
| 60 self.rename_operation_arguments_on_merge = \ | |
| 61 rename_operation_arguments_on_merge | |
| 62 self.add_new_interfaces = add_new_interfaces | |
| 63 self.obsolete_old_declarations = obsolete_old_declarations | |
| 64 | |
| 65 | |
| 66 class DatabaseBuilder(object): | |
| 67 def __init__(self, database): | |
| 68 """DatabaseBuilder is used for importing and merging interfaces into | |
| 69 the Database""" | |
| 70 self._database = database | |
| 71 self._imported_interfaces = [] | |
| 72 self._impl_stmts = [] | |
| 73 | |
| 74 def _load_idl_file(self, file_name, import_options): | |
| 75 """Loads an IDL file intor memory""" | |
| 76 idl_parser = idlparser.IDLParser(import_options.idl_syntax) | |
| 77 | |
| 78 try: | |
| 79 f = open(file_name, 'r') | |
| 80 content = f.read() | |
| 81 f.close() | |
| 82 | |
| 83 idl_ast = idl_parser.parse(content, | |
| 84 defines=import_options.idl_defines) | |
| 85 return IDLFile(idl_ast, file_name) | |
| 86 except SyntaxError, e: | |
| 87 raise RuntimeError('Failed to load file %s: %s' % (file_name, e)) | |
| 88 | |
| 89 def _resolve_type_defs(self, idl_file): | |
| 90 type_def_map = {} | |
| 91 # build map | |
| 92 for type_def in idl_file.all(IDLTypeDef): | |
| 93 if type_def.type.id != type_def.id: # sanity check | |
| 94 type_def_map[type_def.id] = type_def.type.id | |
| 95 # use the map | |
| 96 for type_node in idl_file.all(IDLType): | |
| 97 while type_node.id in type_def_map: | |
| 98 type_node.id = type_def_map[type_node.id] | |
| 99 | |
| 100 def _strip_ext_attributes(self, idl_file): | |
| 101 """Strips unuseful extended attributes.""" | |
| 102 for ext_attrs in idl_file.all(IDLExtAttrs): | |
| 103 # TODO: Decide which attributes are uninteresting. | |
| 104 pass | |
| 105 | |
| 106 def _rename_types(self, idl_file, import_options): | |
| 107 """Rename interface and type names with names provided in the | |
| 108 options. Also clears scopes from scoped names""" | |
| 109 | |
| 110 def rename(name): | |
| 111 name_parts = name.split('::') | |
| 112 name = name_parts[-1] | |
| 113 if name in import_options.type_rename_map: | |
| 114 name = import_options.type_rename_map[name] | |
| 115 return name | |
| 116 | |
| 117 def rename_node(idl_node): | |
| 118 idl_node.id = rename(idl_node.id) | |
| 119 | |
| 120 def rename_ext_attrs(ext_attrs_node): | |
| 121 for type_valued_attribute_name in ['Supplemental']: | |
| 122 if type_valued_attribute_name in ext_attrs_node: | |
| 123 value = ext_attrs_node[type_valued_attribute_name] | |
| 124 if isinstance(value, str): | |
| 125 ext_attrs_node[type_valued_attribute_name] = rename(value) | |
| 126 | |
| 127 map(rename_node, idl_file.all(IDLInterface)) | |
| 128 map(rename_node, idl_file.all(IDLType)) | |
| 129 map(rename_ext_attrs, idl_file.all(IDLExtAttrs)) | |
| 130 | |
| 131 def _annotate(self, interface, module_name, import_options): | |
| 132 """Adds @ annotations based on the source and source_attributes | |
| 133 members of import_options.""" | |
| 134 | |
| 135 source = import_options.source | |
| 136 if not source: | |
| 137 return | |
| 138 | |
| 139 def add_source_annotation(idl_node): | |
| 140 annotation = IDLAnnotation( | |
| 141 copy.deepcopy(import_options.source_attributes)) | |
| 142 idl_node.annotations[source] = annotation | |
| 143 if ((isinstance(idl_node, IDLInterface) or | |
| 144 isinstance(idl_node, IDLMember)) and | |
| 145 idl_node.is_fc_suppressed): | |
| 146 annotation['suppressed'] = None | |
| 147 | |
| 148 add_source_annotation(interface) | |
| 149 interface.annotations[source][_MODULE_ANNOTATION_ATTR_NAME] = module_name | |
| 150 | |
| 151 map(add_source_annotation, interface.parents) | |
| 152 map(add_source_annotation, interface.constants) | |
| 153 map(add_source_annotation, interface.attributes) | |
| 154 map(add_source_annotation, interface.operations) | |
| 155 | |
| 156 def _sign(self, node): | |
| 157 """Computes a unique signature for the node, for merging purposed, by | |
| 158 concatenating types and names in the declaration.""" | |
| 159 if isinstance(node, IDLType): | |
| 160 res = node.id | |
| 161 if res.startswith('unsigned '): | |
| 162 res = res[len('unsigned '):] | |
| 163 return res | |
| 164 | |
| 165 res = [] | |
| 166 if isinstance(node, IDLInterface): | |
| 167 res = ['interface', node.id] | |
| 168 elif isinstance(node, IDLParentInterface): | |
| 169 res = ['parent', self._sign(node.type)] | |
| 170 elif isinstance(node, IDLOperation): | |
| 171 res = ['op'] | |
| 172 for special in node.specials: | |
| 173 res.append(special) | |
| 174 if node.id is not None: | |
| 175 res.append(node.id) | |
| 176 for arg in node.arguments: | |
| 177 res.append(self._sign(arg.type)) | |
| 178 res.append(self._sign(node.type)) | |
| 179 elif isinstance(node, IDLAttribute): | |
| 180 res = [] | |
| 181 if node.is_read_only: | |
| 182 res.append('readonly') | |
| 183 res.append(node.id) | |
| 184 res.append(self._sign(node.type)) | |
| 185 elif isinstance(node, IDLConstant): | |
| 186 res = [] | |
| 187 res.append('const') | |
| 188 res.append(node.id) | |
| 189 res.append(node.value) | |
| 190 res.append(self._sign(node.type)) | |
| 191 else: | |
| 192 raise TypeError("Can't sign input of type %s" % type(node)) | |
| 193 return ':'.join(res) | |
| 194 | |
| 195 def _build_signatures_map(self, idl_node_list): | |
| 196 """Creates a hash table mapping signatures to idl_nodes for the | |
| 197 given list of nodes""" | |
| 198 res = {} | |
| 199 for idl_node in idl_node_list: | |
| 200 sig = self._sign(idl_node) | |
| 201 if sig is None: | |
| 202 continue | |
| 203 if sig in res: | |
| 204 raise RuntimeError('Warning: Multiple members have the same ' | |
| 205 'signature: "%s"' % sig) | |
| 206 res[sig] = idl_node | |
| 207 return res | |
| 208 | |
| 209 def _get_parent_interfaces(self, interface): | |
| 210 """Return a list of all the parent interfaces of a given interface""" | |
| 211 res = [] | |
| 212 | |
| 213 def recurse(current_interface): | |
| 214 if current_interface in res: | |
| 215 return | |
| 216 res.append(current_interface) | |
| 217 for parent in current_interface.parents: | |
| 218 parent_name = parent.type.id | |
| 219 if self._database.HasInterface(parent_name): | |
| 220 recurse(self._database.GetInterface(parent_name)) | |
| 221 | |
| 222 recurse(interface) | |
| 223 return res[1:] | |
| 224 | |
| 225 def _merge_ext_attrs(self, old_attrs, new_attrs): | |
| 226 """Merges two sets of extended attributes. | |
| 227 | |
| 228 Returns: True if old_attrs has changed. | |
| 229 """ | |
| 230 changed = False | |
| 231 for (name, value) in new_attrs.items(): | |
| 232 if name in old_attrs and old_attrs[name] == value: | |
| 233 pass # Identical | |
| 234 else: | |
| 235 old_attrs[name] = value | |
| 236 changed = True | |
| 237 return changed | |
| 238 | |
| 239 def _merge_nodes(self, old_list, new_list, import_options): | |
| 240 """Merges two lists of nodes. Annotates nodes with the source of each | |
| 241 node. | |
| 242 | |
| 243 Returns: | |
| 244 True if the old_list has changed. | |
| 245 | |
| 246 Args: | |
| 247 old_list -- the list to merge into. | |
| 248 new_list -- list containing more nodes. | |
| 249 import_options -- controls how merging is done. | |
| 250 """ | |
| 251 changed = False | |
| 252 | |
| 253 source = import_options.source | |
| 254 | |
| 255 old_signatures_map = self._build_signatures_map(old_list) | |
| 256 new_signatures_map = self._build_signatures_map(new_list) | |
| 257 | |
| 258 # Merge new items | |
| 259 for (sig, new_node) in new_signatures_map.items(): | |
| 260 if sig not in old_signatures_map: | |
| 261 # New node: | |
| 262 old_list.append(new_node) | |
| 263 changed = True | |
| 264 else: | |
| 265 # Merge old and new nodes: | |
| 266 old_node = old_signatures_map[sig] | |
| 267 if (source not in old_node.annotations | |
| 268 and source in new_node.annotations): | |
| 269 old_node.annotations[source] = new_node.annotations[source] | |
| 270 changed = True | |
| 271 # Maybe rename arguments: | |
| 272 if isinstance(old_node, IDLOperation): | |
| 273 for i in range(0, len(old_node.arguments)): | |
| 274 old_arg = old_node.arguments[i] | |
| 275 new_arg = new_node.arguments[i] | |
| 276 | |
| 277 old_arg_name = old_arg.id | |
| 278 new_arg_name = new_arg.id | |
| 279 if (old_arg_name != new_arg_name | |
| 280 and (old_arg_name == 'arg' | |
| 281 or old_arg_name.endswith('Arg') | |
| 282 or import_options.rename_operation_arguments_on_merge)): | |
| 283 old_node.arguments[i].id = new_arg_name | |
| 284 changed = True | |
| 285 | |
| 286 if self._merge_ext_attrs(old_arg.ext_attrs, new_arg.ext_attrs): | |
| 287 changed = True | |
| 288 # Maybe merge annotations: | |
| 289 if (isinstance(old_node, IDLAttribute) or | |
| 290 isinstance(old_node, IDLOperation)): | |
| 291 if self._merge_ext_attrs(old_node.ext_attrs, new_node.ext_attrs): | |
| 292 changed = True | |
| 293 | |
| 294 # Remove annotations on obsolete items from the same source | |
| 295 if import_options.obsolete_old_declarations: | |
| 296 for (sig, old_node) in old_signatures_map.items(): | |
| 297 if (source in old_node.annotations | |
| 298 and sig not in new_signatures_map): | |
| 299 _logger.warn('%s not available in %s anymore' % | |
| 300 (sig, source)) | |
| 301 del old_node.annotations[source] | |
| 302 changed = True | |
| 303 | |
| 304 return changed | |
| 305 | |
| 306 def _merge_interfaces(self, old_interface, new_interface, import_options): | |
| 307 """Merges the new_interface into the old_interface, annotating the | |
| 308 interface with the sources of each change.""" | |
| 309 | |
| 310 changed = False | |
| 311 | |
| 312 source = import_options.source | |
| 313 if (source and source not in old_interface.annotations and | |
| 314 source in new_interface.annotations and | |
| 315 not new_interface.is_supplemental): | |
| 316 old_interface.annotations[source] = new_interface.annotations[source] | |
| 317 changed = True | |
| 318 | |
| 319 def merge_list(what): | |
| 320 old_list = old_interface.__dict__[what] | |
| 321 new_list = new_interface.__dict__[what] | |
| 322 | |
| 323 if what != 'parents' and old_interface.id != new_interface.id: | |
| 324 for node in new_list: | |
| 325 node.ext_attrs['ImplementedBy'] = new_interface.id | |
| 326 | |
| 327 changed = self._merge_nodes(old_list, new_list, import_options) | |
| 328 | |
| 329 # Delete list items with zero remaining annotations. | |
| 330 if changed and import_options.obsolete_old_declarations: | |
| 331 | |
| 332 def has_annotations(idl_node): | |
| 333 return len(idl_node.annotations) | |
| 334 | |
| 335 old_interface.__dict__[what] = filter(has_annotations, old_list) | |
| 336 | |
| 337 return changed | |
| 338 | |
| 339 # Smartly merge various declarations: | |
| 340 if merge_list('parents'): | |
| 341 changed = True | |
| 342 if merge_list('constants'): | |
| 343 changed = True | |
| 344 if merge_list('attributes'): | |
| 345 changed = True | |
| 346 if merge_list('operations'): | |
| 347 changed = True | |
| 348 | |
| 349 if self._merge_ext_attrs(old_interface.ext_attrs, new_interface.ext_attrs): | |
| 350 changed = True | |
| 351 | |
| 352 _logger.info('merged interface %s (changed=%s, supplemental=%s)' % | |
| 353 (old_interface.id, changed, new_interface.is_supplemental)) | |
| 354 | |
| 355 return changed | |
| 356 | |
| 357 def _merge_impl_stmt(self, impl_stmt, import_options): | |
| 358 """Applies "X implements Y" statemetns on the proper places in the | |
| 359 database""" | |
| 360 implementor_name = impl_stmt.implementor.id | |
| 361 implemented_name = impl_stmt.implemented.id | |
| 362 _logger.info('merging impl stmt %s implements %s' % | |
| 363 (implementor_name, implemented_name)) | |
| 364 | |
| 365 source = import_options.source | |
| 366 if self._database.HasInterface(implementor_name): | |
| 367 interface = self._database.GetInterface(implementor_name) | |
| 368 if interface.parents is None: | |
| 369 interface.parents = [] | |
| 370 for parent in interface.parents: | |
| 371 if parent.type.id == implemented_name: | |
| 372 if source and source not in parent.annotations: | |
| 373 parent.annotations[source] = IDLAnnotation( | |
| 374 import_options.source_attributes) | |
| 375 return | |
| 376 # not found, so add new one | |
| 377 parent = IDLParentInterface(None) | |
| 378 parent.type = IDLType(implemented_name) | |
| 379 if source: | |
| 380 parent.annotations[source] = IDLAnnotation( | |
| 381 import_options.source_attributes) | |
| 382 interface.parents.append(parent) | |
| 383 | |
| 384 def merge_imported_interfaces(self): | |
| 385 """Merges all imported interfaces and loads them into the DB.""" | |
| 386 | |
| 387 # Step 1: Pre process imported interfaces | |
| 388 for interface, module_name, import_options in self._imported_interfaces: | |
| 389 self._annotate(interface, module_name, import_options) | |
| 390 | |
| 391 # Step 2: Add all new interfaces and merge overlapping ones | |
| 392 for interface, module_name, import_options in self._imported_interfaces: | |
| 393 if not interface.is_supplemental: | |
| 394 if self._database.HasInterface(interface.id): | |
| 395 old_interface = self._database.GetInterface(interface.id) | |
| 396 self._merge_interfaces(old_interface, interface, import_options) | |
| 397 else: | |
| 398 if import_options.add_new_interfaces: | |
| 399 self._database.AddInterface(interface) | |
| 400 | |
| 401 # Step 3: Merge in supplemental interfaces | |
| 402 for interface, module_name, import_options in self._imported_interfaces: | |
| 403 if interface.is_supplemental: | |
| 404 target_name = interface.ext_attrs['Supplemental'] | |
| 405 if target_name: | |
| 406 # [Supplemental=DOMWindow] - merge into DOMWindow. | |
| 407 target = target_name | |
| 408 else: | |
| 409 # [Supplemental] - merge into existing inteface with same name. | |
| 410 target = interface.id | |
| 411 if self._database.HasInterface(target): | |
| 412 old_interface = self._database.GetInterface(target) | |
| 413 self._merge_interfaces(old_interface, interface, import_options) | |
| 414 else: | |
| 415 raise Exception("Supplemental target '%s' not found", target) | |
| 416 | |
| 417 # Step 4: Resolve 'implements' statements | |
| 418 for impl_stmt, import_options in self._impl_stmts: | |
| 419 self._merge_impl_stmt(impl_stmt, import_options) | |
| 420 | |
| 421 self._impl_stmts = [] | |
| 422 self._imported_interfaces = [] | |
| 423 | |
| 424 def import_idl_file(self, file_path, | |
| 425 import_options=DatabaseBuilderOptions()): | |
| 426 """Parses, loads into memory and cleans up and IDL file""" | |
| 427 idl_file = self._load_idl_file(file_path, import_options) | |
| 428 | |
| 429 self._strip_ext_attributes(idl_file) | |
| 430 self._resolve_type_defs(idl_file) | |
| 431 self._rename_types(idl_file, import_options) | |
| 432 | |
| 433 def enabled(idl_node): | |
| 434 return self._is_node_enabled(idl_node, import_options.idl_defines) | |
| 435 | |
| 436 for module in idl_file.modules: | |
| 437 for interface in module.interfaces: | |
| 438 if not self._is_node_enabled(interface, import_options.idl_defines): | |
| 439 _logger.info('skipping interface %s/%s (source=%s file=%s)' | |
| 440 % (module.id, interface.id, import_options.source, | |
| 441 file_path)) | |
| 442 continue | |
| 443 | |
| 444 _logger.info('importing interface %s/%s (source=%s file=%s)' | |
| 445 % (module.id, interface.id, import_options.source, | |
| 446 file_path)) | |
| 447 interface.attributes = filter(enabled, interface.attributes) | |
| 448 interface.operations = filter(enabled, interface.operations) | |
| 449 self._imported_interfaces.append((interface, module.id, import_options)) | |
| 450 | |
| 451 for implStmt in module.implementsStatements: | |
| 452 self._impl_stmts.append((implStmt, import_options)) | |
| 453 | |
| 454 def _is_node_enabled(self, node, idl_defines): | |
| 455 if not 'Conditional' in node.ext_attrs: | |
| 456 return True | |
| 457 | |
| 458 def enabled(condition): | |
| 459 return 'ENABLE_%s' % condition in idl_defines | |
| 460 | |
| 461 conditional = node.ext_attrs['Conditional'] | |
| 462 if conditional.find('&') != -1: | |
| 463 for condition in conditional.split('&'): | |
| 464 if not enabled(condition): | |
| 465 return False | |
| 466 return True | |
| 467 | |
| 468 for condition in conditional.split('|'): | |
| 469 if enabled(condition): | |
| 470 return True | |
| 471 return False | |
| 472 | |
| 473 def fix_displacements(self, source): | |
| 474 """E.g. In W3C, something is declared on HTMLDocument but in WebKit | |
| 475 its on Document, so we need to mark that something in HTMLDocument | |
| 476 with @WebKit(via=Document). The 'via' attribute specifies the | |
| 477 parent interface that has the declaration.""" | |
| 478 | |
| 479 for interface in self._database.GetInterfaces(): | |
| 480 changed = False | |
| 481 | |
| 482 _logger.info('fixing displacements in %s' % interface.id) | |
| 483 | |
| 484 for parent_interface in self._get_parent_interfaces(interface): | |
| 485 _logger.info('scanning parent %s of %s' % | |
| 486 (parent_interface.id, interface.id)) | |
| 487 | |
| 488 def fix_nodes(local_list, parent_list): | |
| 489 changed = False | |
| 490 parent_signatures_map = self._build_signatures_map( | |
| 491 parent_list) | |
| 492 for idl_node in local_list: | |
| 493 sig = self._sign(idl_node) | |
| 494 if sig in parent_signatures_map: | |
| 495 parent_member = parent_signatures_map[sig] | |
| 496 if (source in parent_member.annotations | |
| 497 and source not in idl_node.annotations | |
| 498 and _VIA_ANNOTATION_ATTR_NAME | |
| 499 not in parent_member.annotations[source]): | |
| 500 idl_node.annotations[source] = IDLAnnotation( | |
| 501 {_VIA_ANNOTATION_ATTR_NAME: parent_interface.id}) | |
| 502 changed = True | |
| 503 return changed | |
| 504 | |
| 505 changed = fix_nodes(interface.constants, | |
| 506 parent_interface.constants) or changed | |
| 507 changed = fix_nodes(interface.attributes, | |
| 508 parent_interface.attributes) or changed | |
| 509 changed = fix_nodes(interface.operations, | |
| 510 parent_interface.operations) or changed | |
| 511 if changed: | |
| 512 _logger.info('fixed displaced declarations in %s' % | |
| 513 interface.id) | |
| 514 | |
| 515 def normalize_annotations(self, sources): | |
| 516 """Makes the IDLs less verbose by removing annotation attributes | |
| 517 that are identical to the ones defined at the interface level. | |
| 518 | |
| 519 Args: | |
| 520 sources -- list of source names to normalize.""" | |
| 521 for interface in self._database.GetInterfaces(): | |
| 522 _logger.debug('normalizing annotations for %s' % interface.id) | |
| 523 for source in sources: | |
| 524 if (source not in interface.annotations or | |
| 525 not interface.annotations[source]): | |
| 526 continue | |
| 527 top_level_annotation = interface.annotations[source] | |
| 528 | |
| 529 def normalize(idl_node): | |
| 530 if (source in idl_node.annotations | |
| 531 and idl_node.annotations[source]): | |
| 532 annotation = idl_node.annotations[source] | |
| 533 for name, value in annotation.items(): | |
| 534 if (name in top_level_annotation | |
| 535 and value == top_level_annotation[name]): | |
| 536 del annotation[name] | |
| 537 | |
| 538 map(normalize, interface.parents) | |
| 539 map(normalize, interface.constants) | |
| 540 map(normalize, interface.attributes) | |
| 541 map(normalize, interface.operations) | |
| 542 | |
| 543 def fetch_constructor_data(self, options): | |
| 544 window_interface = self._database.GetInterface('Window') | |
| 545 for attr in window_interface.attributes: | |
| 546 type = attr.type.id | |
| 547 if not type.endswith('Constructor'): | |
| 548 continue | |
| 549 type = re.sub('(Constructor)+$', '', type) | |
| 550 # TODO(antonm): Ideally we'd like to have pristine copy of WebKit IDLs and
fetch | |
| 551 # this information directly from it. Unfortunately right now database is
massaged | |
| 552 # a lot so it's difficult to maintain necessary information on DOMWindow i
tself. | |
| 553 interface = self._database.GetInterface(options.type_rename_map.get(type,
type)) | |
| 554 if 'V8EnabledPerContext' in attr.ext_attrs: | |
| 555 interface.ext_attrs['synthesizedV8EnabledPerContext'] = \ | |
| 556 attr.ext_attrs['V8EnabledPerContext'] | |
| 557 if 'V8EnabledAtRuntime' in attr.ext_attrs: | |
| 558 interface.ext_attrs['synthesizedV8EnabledAtRuntime'] = \ | |
| 559 attr.ext_attrs['V8EnabledAtRuntime'] or attr.id | |
| OLD | NEW |