| #!/usr/bin/env python3 |
| # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| # for details. All rights reserved. Use of this source code is governed by a |
| # BSD-style license that can be found in the LICENSE file. |
| """This module provides shared functionality for the system to generate |
| dart:html APIs from the IDL database.""" |
| |
| import emitter |
| from generator import AnalyzeOperation, ConstantOutputOrder, \ |
| DartDomNameOfAttribute, FindMatchingAttribute, IsPureInterface, \ |
| TypeOrNothing, ConvertToFuture, GetCallbackInfo |
| from copy import deepcopy |
| from htmlrenamer import convert_to_future_members, custom_html_constructors, \ |
| GetDDC_Extension, keep_overloaded_members, overloaded_and_renamed,\ |
| private_html_members, renamed_html_members, renamed_overloads, \ |
| removed_html_members |
| from generator import TypeOrVar |
| import logging |
| from mdnreader import MDNReader |
| import monitored |
| import sys |
| |
| _logger = logging.getLogger('htmldartgenerator') |
| |
| # Types that are accessible cross-frame in a limited fashion. |
| # In these cases, the base type (e.g., WindowBase) provides restricted access |
| # while the subtype (e.g., Window) provides full access to the |
| # corresponding objects if there are from the same frame. |
| _secure_base_types = { |
| 'Window': 'WindowBase', |
| 'Location': 'LocationBase', |
| 'History': 'HistoryBase', |
| } |
| |
| _custom_factories = [ |
| 'Notification', |
| 'EventSource', |
| ] |
| |
| |
| class HtmlDartGenerator(object): |
| |
| def __init__(self, interface, options, dart_use_blink, logger): |
| self._dart_use_blink = dart_use_blink |
| self._database = options.database |
| self._interface = interface |
| self._type_registry = options.type_registry |
| self._interface_type_info = self._type_registry.TypeInfo( |
| self._interface.id) |
| self._renamer = options.renamer |
| self._metadata = options.metadata |
| self._library_name = self._renamer.GetLibraryName(self._interface) |
| self._mdn_reader = MDNReader() |
| _logger.setLevel(logger.level) |
| |
| def EmitSupportCheck(self): |
| if self.HasSupportCheck(): |
| check = self.GetSupportCheck() |
| if type(check) != tuple: |
| signature = 'get supported' |
| else: |
| signature = check[0] |
| check = check[1] |
| self._members_emitter.Emit( |
| '\n' |
| ' /// Checks if this type is supported on the current platform.\n' |
| ' static bool $SIGNATURE => $SUPPORT_CHECK;\n', |
| SIGNATURE=signature, |
| SUPPORT_CHECK=check) |
| |
| def EmitEventGetter(self, events_class_name): |
| self._members_emitter.Emit( |
| "EventTarget.removeEventListener, EventTarget.dispatchEvent')" |
| "\n @deprecated" |
| "\n $TYPE get on =>\n new $TYPE(this);\n", |
| TYPE=events_class_name) |
| |
| def AddMembers(self, interface, declare_only=False, dart_js_interop=False): |
| if self._interface.id == 'WebGLRenderingContextBase' or self._interface.id == 'WebGL2RenderingContextBase' or \ |
| self._interface.id == 'WebGLDrawBuffers': |
| # Constants in classes WebGLRenderingContextBase, WebGL2RenderingContext, WebGLDrawBuffers are consolidated into |
| # one synthesized class (WebGL). |
| self._gl_constants.extend(interface.constants) |
| else: |
| for const in sorted(interface.constants, ConstantOutputOrder): |
| self.AddConstant(const) |
| |
| for attr in sorted(interface.attributes, ConstantOutputOrder): |
| if attr.type.id != 'EventHandler' and attr.type.id != 'EventListener': |
| self.AddAttribute(attr, declare_only) |
| |
| # The implementation should define an indexer if the interface directly |
| # extends List. |
| element_type = None |
| requires_indexer = False |
| if self._interface_type_info.list_item_type(): |
| self.AddIndexer(self._interface_type_info.list_item_type(), |
| self._interface_type_info.list_item_type_nullable()) |
| else: |
| for parent in self._database.Hierarchy(self._interface): |
| if parent == self._interface: |
| continue |
| parent_type_info = self._type_registry.TypeInfo(parent.id) |
| if parent_type_info.list_item_type(): |
| self.AmendIndexer(parent_type_info.list_item_type()) |
| break |
| |
| # Group overloaded operations by name. |
| self._AddRenamedOverloads(interface) |
| operationsByName = self._OperationsByName(interface) |
| if self.OmitOperationOverrides(): |
| self._RemoveShadowingOperationsWithSameSignature( |
| operationsByName, interface) |
| |
| # Generate operations. |
| for id in sorted(operationsByName.keys()): |
| operations = operationsByName[id] |
| info = AnalyzeOperation(interface, operations) |
| self.AddOperation(info, declare_only, dart_js_interop) |
| if ('%s.%s' % (interface.id, |
| info.declared_name) in convert_to_future_members): |
| self.AddOperation(ConvertToFuture(info), declare_only) |
| |
| def AddSecondaryMembers(self, interface): |
| secondary_parents = self._database.TransitiveSecondaryParents( |
| interface, not self._dart_use_blink) |
| remove_duplicate_parents = list(set(secondary_parents)) |
| if len(secondary_parents) != len(remove_duplicate_parents): |
| secondary_parents = remove_duplicate_parents |
| parent_list = ", ".join( |
| [" %s" % (parent.id) for parent in secondary_parents]) |
| _logger.warn('Interface %s has duplicate parent interfaces %s - ' \ |
| 'ignoring duplicates. Please file a bug with the dart:html team.' % (interface.id, parent_list)) |
| |
| for parent_interface in sorted(secondary_parents): |
| if isinstance(parent_interface, str): |
| continue |
| |
| for attr in sorted(parent_interface.attributes, |
| ConstantOutputOrder): |
| if not FindMatchingAttribute(interface, attr): |
| if attr.type.id != 'EventHandler': |
| self.SecondaryContext(parent_interface) |
| self.AddAttribute(attr) |
| |
| # Group overloaded operations by name. |
| operationsByName = self._OperationsByName(parent_interface) |
| |
| if self.OmitOperationOverrides(): |
| self._RemoveShadowingOperationsWithSameSignature( |
| operationsByName, interface) |
| |
| # Generate operations. |
| for id in sorted(operationsByName.keys()): |
| if not any(op.id == id for op in interface.operations): |
| operations = operationsByName[id] |
| info = AnalyzeOperation(interface, operations) |
| self.SecondaryContext(parent_interface) |
| self.AddOperation(info) |
| |
| def _RemoveShadowingOperationsWithSameSignature(self, operationsByName, |
| interface): |
| if not interface.parents: |
| return |
| |
| parent_name = interface.parents[0].type.id |
| parent = self._database.GetInterface(parent_name) |
| if parent == self._interface or parent == interface: |
| return |
| |
| # Never remove operations that are added as a result of an implements they |
| # are pure interfaces (mixins to this interface). |
| if (IsPureInterface(parent_name, self._database)): |
| return |
| |
| for operation in parent.operations: |
| if operation.id in operationsByName: |
| operations = operationsByName[operation.id] |
| for existing_operation in operations: |
| if existing_operation.SameSignatureAs(operation): |
| del operationsByName[operation.id] |
| |
| def _AddRenamedOverloads(self, interface): |
| """The IDL has a number of functions with the same name but that accept |
| different types. This is fine for JavaScript, but results in vague type |
| signatures for Dart. We rename some of these (by adding a new identical |
| operation with a different DartName), but leave the original version as |
| well in some cases.""" |
| potential_added_operations = set() |
| operations_by_name = self._OperationsByName(interface) |
| already_renamed = [ |
| operation.ext_attrs['DartName'] |
| if 'DartName' in operation.ext_attrs else '' |
| for operation in interface.operations |
| ] |
| |
| added_operations = [] |
| for operation in interface.operations: |
| full_operation_str = self._GetStringRepresentation( |
| interface, operation) |
| if (full_operation_str in renamed_overloads and |
| renamed_overloads[full_operation_str] not in already_renamed |
| ): |
| if '%s.%s' % (interface.id, |
| operation.id) in overloaded_and_renamed: |
| cloned_operation = deepcopy(operation) |
| cloned_operation.ext_attrs['DartName'] = renamed_overloads[ |
| full_operation_str] |
| added_operations.append(cloned_operation) |
| else: |
| dart_name = renamed_overloads[full_operation_str] |
| if not dart_name: |
| continue |
| |
| operation.ext_attrs['DartName'] = dart_name |
| potential_added_operations.add(operation.id) |
| self._EnsureNoMultipleTypeSignatures(interface, operation, |
| operations_by_name) |
| interface.operations += added_operations |
| self._AddDesiredOverloadedOperations(potential_added_operations, |
| interface, operations_by_name) |
| |
| def _AddDesiredOverloadedOperations(self, potential_added_operations, |
| interface, original_operations_by_name): |
| """For some cases we desire to keep the overloaded version in dart, for |
| simplicity of API, and explain the parameters accepted in documentation.""" |
| updated_operations_by_name = self._OperationsByName(interface) |
| for operation_id in potential_added_operations: |
| if (operation_id not in updated_operations_by_name and '%s.%s' % |
| (interface.id, operation_id) in keep_overloaded_members): |
| for operation in original_operations_by_name[operation_id]: |
| cloned_operation = deepcopy(operation) |
| cloned_operation.ext_attrs['DartName'] = operation_id |
| interface.operations.append(cloned_operation) |
| |
| def _EnsureNoMultipleTypeSignatures(self, interface, operation, |
| operations_by_name): |
| """Make sure that there is now at most one operation with a particular |
| operation.id. If not, stop library generation, and throw an error, requiring |
| programmer input about the best name change before proceeding.""" |
| operation_str = '%s.%s' % (interface.id, operation.id) |
| |
| if (operation.id in operations_by_name and |
| len(operations_by_name[operation.id]) > 1 and len( |
| filter(lambda overload: overload.startswith(operation_str), |
| renamed_overloads.keys())) == 0 and |
| operation_str not in keep_overloaded_members and |
| operation_str not in overloaded_and_renamed and |
| operation_str not in renamed_html_members and |
| operation_str not in private_html_members and |
| operation_str not in removed_html_members and |
| operation.id != '__getter__' and |
| operation.id != '__setter__' and operation.id != '__delete__'): |
| _logger.warn( |
| 'Multiple type signatures for %s.%s. Please file a bug with' |
| ' the dart:html team to determine if one of these functions should be' |
| ' renamed.' % (interface.id, operation.id)) |
| |
| def _GetStringRepresentation(self, interface, operation): |
| """Given an IDLOperation, return a object-independent representation of the |
| operations's signature.""" |
| return '%s.%s(%s)' % (interface.id, operation.id, ', '.join( |
| ['%s %s' % (arg.type.id, arg.id) for arg in operation.arguments])) |
| |
| def _OperationsByName(self, interface): |
| operationsByName = {} |
| for operation in interface.operations: |
| name = operation.ext_attrs.get('DartName', operation.id) |
| operationsByName.setdefault(name, []).append(operation) |
| return operationsByName |
| |
| def OmitOperationOverrides(self): |
| return False |
| |
| def AddConstant(self, constant): |
| const_name = self._renamer.RenameMember( |
| self._interface.id, |
| constant, |
| constant.id, |
| 'get:', |
| dartify_name=False) |
| if not const_name: |
| return |
| |
| annotations = self._metadata.GetFormattedMetadata( |
| self._library_name, self._interface, constant.id, ' ') |
| |
| type = TypeOrNothing(self._DartType(constant.type.id), constant.type.id) |
| self._members_emitter.Emit( |
| '\n $(ANNOTATIONS)static const $TYPE$NAME = $VALUE;\n', |
| ANNOTATIONS=annotations, |
| NAME=const_name, |
| TYPE=type, |
| VALUE=constant.value) |
| |
| def AddAttribute(self, attribute, declare_only=False): |
| """ Adds an attribute to the generated class. |
| Arguments: |
| attribute - The attribute which is to be added. |
| declare_only- True if the attribute should be declared as an abstract |
| member and not include invocation code. |
| """ |
| dom_name = DartDomNameOfAttribute(attribute) |
| attr_name = self._renamer.RenameMember(self._interface.id, attribute, |
| dom_name, 'get:') |
| if not attr_name: |
| return |
| |
| html_setter_name = self._renamer.RenameMember( |
| self._interface.id, attribute, dom_name, 'set:') |
| read_only = (attribute.is_read_only or |
| 'Replaceable' in attribute.ext_attrs or |
| not html_setter_name) |
| |
| # We don't yet handle inconsistent renames of the getter and setter yet. |
| assert (not html_setter_name or attr_name == html_setter_name) |
| |
| # any is assumed to be nullable |
| if attribute.type.id == 'any': |
| attribute.type.nullable = True |
| |
| if declare_only: |
| self.DeclareAttribute(attribute, attr_name, read_only) |
| else: |
| self.EmitAttribute(attribute, attr_name, read_only) |
| |
| def AddOperation(self, info, declare_only=False, dart_js_interop=False): |
| # TODO(terry): Hack window has 2 overloaded getter one returns Window and |
| # and other object (we'll always return Window)? |
| if self._interface.id == "Window" and info.name == '__getter__': |
| info.operations[1].type = info.operations[0].type |
| """ Adds an operation to the generated class. |
| Arguments: |
| info - The operation info of the operation to be added. |
| declare_only- True if the operation should be declared as an abstract |
| member and not include invocation code. |
| """ |
| # FIXME: When we pass in operations[0] below, we're assuming all |
| # overloaded operations have the same security attributes. This |
| # is currently true, but we should consider filtering earlier or |
| # merging the relevant data into info itself. |
| method_name = self._renamer.RenameMember( |
| self._interface.id, info.operations[0], info.name, 'call:') |
| if not method_name: |
| if info.name == 'item': |
| # FIXME: item should be renamed to operator[], not removed. |
| self.EmitOperation(info, '_item') |
| return |
| |
| if declare_only: |
| self.DeclareOperation(info, |
| self.SecureOutputType(info.type_name, |
| nullable=info.type_nullable), |
| method_name) |
| else: |
| self.EmitOperation(info, method_name, dart_js_interop) |
| |
| def _GenerateOverloadDispatcher( |
| self, |
| info, |
| signatures, |
| is_void, |
| declaration, |
| generate_call, |
| is_optional, |
| emitter, |
| can_omit_type_check=lambda type, pos: False): |
| |
| parameter_names = [p.name for p in info.param_infos] |
| number_of_required_in_dart = info.NumberOfRequiredInDart() |
| |
| body_emitter = emitter.Emit('\n' |
| ' $DECLARATION {\n' |
| '$!BODY' |
| ' }\n', |
| DECLARATION=declaration) |
| |
| version = [0] |
| |
| def GenerateCall(signature_index, argument_count, checks): |
| if checks: |
| (stmts_emitter, call_emitter) = body_emitter.Emit( |
| ' if ($CHECKS) {\n$!STMTS$!CALL }\n', |
| INDENT=' ', |
| CHECKS=' && '.join(checks)) |
| else: |
| (stmts_emitter, call_emitter) = body_emitter.Emit( |
| '$!STMTS$!CALL', INDENT=' ') |
| |
| if is_void: |
| call_emitter = call_emitter.Emit( |
| '$(INDENT)$!CALL;\n$(INDENT)return;\n') |
| else: |
| call_emitter = call_emitter.Emit('$(INDENT)return $!CALL;\n') |
| |
| version[0] += 1 |
| generate_call(stmts_emitter, call_emitter, version[0], |
| signature_index, argument_count) |
| |
| def IsTypeChecking(interface_argument): |
| return 'LegacyInterfaceTypeChecking' in interface_argument.ext_attrs or \ |
| self._database.HasInterface(interface_argument.id) |
| |
| def GenerateChecksAndCall(signature_index, argument_count): |
| checks = [] |
| typechecked_interface = IsTypeChecking(self._interface) |
| |
| for i in reversed(range(0, argument_count)): |
| argument = signatures[signature_index][i] |
| parameter_name = parameter_names[i] |
| |
| test_type = self._NarrowToImplementationType(argument.type.id) |
| |
| if test_type in ['dynamic', 'Object']: |
| checks.append('%s != null' % parameter_name) |
| elif not can_omit_type_check(test_type, i): |
| typechecked = typechecked_interface or IsTypeChecking( |
| argument) |
| converts_null = \ |
| ('TreatNullAs' in argument.ext_attrs) or \ |
| (argument.default_value is not None) or \ |
| (argument.default_value_is_null) |
| if argument.type.nullable or converts_null or not typechecked: |
| checks.append( |
| '(%s is %s || %s == null)' % |
| (parameter_name, test_type, parameter_name)) |
| else: |
| checks.append( |
| '(%s is %s)' % (parameter_name, test_type)) |
| elif i >= number_of_required_in_dart and not argument.type.nullable: |
| checks.append('%s != null' % parameter_name) |
| |
| # There can be multiple presence checks. We need them all since a later |
| # optional argument could have been passed by name, leaving 'holes'. |
| checks.extend([ |
| '%s == null' % name for name in parameter_names[argument_count:] |
| ]) |
| |
| GenerateCall(signature_index, argument_count, checks) |
| |
| # TODO: Optimize the dispatch to avoid repeated checks. |
| if len(signatures) > 1: |
| index_swaps = {} |
| for signature_index, signature in enumerate(signatures): |
| for argument_position, argument in enumerate(signature): |
| if argument.type.id != 'ArrayBuffer': |
| continue |
| candidates = enumerate(signatures[signature_index + 1:], |
| signature_index + 1) |
| for candidate_index, candidate in candidates: |
| if len(candidate) <= argument_position: |
| continue |
| if candidate[ |
| argument_position].type.id != 'ArrayBufferView': |
| continue |
| if len(index_swaps): |
| raise Exception( |
| 'Cannot deal with more than a single swap') |
| index_swaps[candidate_index] = signature_index |
| index_swaps[signature_index] = candidate_index |
| |
| for signature_index in range(len(signatures)): |
| signature_index = index_swaps.get(signature_index, |
| signature_index) |
| signature = signatures[signature_index] |
| for argument_position, argument in enumerate(signature): |
| if is_optional(signature_index, argument): |
| GenerateChecksAndCall(signature_index, |
| argument_position) |
| GenerateChecksAndCall(signature_index, len(signature)) |
| body_emitter.Emit( |
| ' throw new ArgumentError("Incorrect number or type of arguments");' |
| '\n') |
| else: |
| signature = signatures[0] |
| argument_count = len(signature) |
| for argument_position, argument in list(enumerate(signature))[::-1]: |
| if is_optional(0, argument): |
| check = '%s != null' % parameter_names[argument_position] |
| # argument_count instead of argument_position + 1 is used here to cover one |
| # complicated case with the effectively optional argument in the middle. |
| # Consider foo(x, optional y, [Default=NullString] optional z) |
| # (as of now it's modelled after HTMLMediaElement.webkitAddKey). |
| # y is optional in WebCore, while z is not. |
| # In this case, if y was actually passed, we'd like to emit foo(x, y, z) invocation, |
| # not foo(x, y). |
| GenerateCall(0, argument_count, [check]) |
| argument_count = argument_position |
| GenerateCall(0, argument_count, []) |
| |
| def _GenerateDispatcherBody(self, |
| info, |
| operations, |
| declaration, |
| generate_call, |
| is_optional, |
| can_omit_type_check=lambda type, pos: False): |
| |
| def GenerateCall(stmts_emitter, call_emitter, version, signature_index, |
| argument_count): |
| generate_call(stmts_emitter, call_emitter, version, |
| operations[signature_index], argument_count) |
| |
| def IsOptional(signature_index, argument): |
| return is_optional(argument) |
| |
| emitter = self._members_emitter |
| |
| self._GenerateOverloadDispatcher( |
| info, [operation.arguments for operation in operations], |
| operations[0].type.id == 'void', declaration, GenerateCall, |
| IsOptional, emitter, can_omit_type_check) |
| |
| def AdditionalImplementedInterfaces(self): |
| # TODO: Include all implemented interfaces, including other Lists. |
| implements = [] |
| if self._interface_type_info.list_item_type(): |
| item_type = self._type_registry.TypeInfo( |
| self._interface_type_info.list_item_type()).dart_type() |
| if self._interface_type_info.list_item_type_nullable(): |
| item_type += '?' |
| implements.append('List<%s>' % item_type) |
| return implements |
| |
| def Mixins(self): |
| mixins = [] |
| if self._interface_type_info.list_item_type(): |
| item_type = self._type_registry.TypeInfo( |
| self._interface_type_info.list_item_type()).dart_type() |
| if self._interface_type_info.list_item_type_nullable(): |
| item_type += '?' |
| mixins.append('ListMixin<%s>' % item_type) |
| mixins.append('ImmutableListMixin<%s>' % item_type) |
| |
| return mixins |
| |
| def AddConstructors(self, constructors, factory_name, |
| factory_constructor_name): |
| """ Adds all of the constructors. |
| Arguments: |
| constructors - List of the constructors to be added. |
| factory_name - Name of the factory for this class. |
| factory_constructor_name - The name of the constructor on the |
| factory_name to call (calls an autogenerated FactoryProvider |
| if unspecified) |
| """ |
| for constructor_info in constructors: |
| self._AddConstructor(constructor_info, factory_name, |
| factory_constructor_name) |
| |
| def _AddConstructor(self, constructor_info, factory_name, |
| factory_constructor_name): |
| # Hack to ignore the constructor used by JavaScript. |
| if ((self._interface.id == 'HTMLImageElement' or |
| self._interface.id == 'Blob' or |
| self._interface.id == 'DOMException') and |
| not constructor_info.pure_dart_constructor): |
| return |
| |
| if self.GenerateCustomFactory(constructor_info): |
| return |
| |
| metadata = self._metadata.GetFormattedMetadata( |
| self._library_name, self._interface, self._interface.id, ' ') |
| |
| if not factory_constructor_name: |
| factory_constructor_name = '_create' |
| factory_parameters = constructor_info.ParametersAsArgumentList() |
| else: |
| factory_parameters = ', '.join(constructor_info.factory_parameters) |
| |
| def InputType(type_name): |
| conversion = self._InputConversion(type_name, |
| constructor_info.declared_name) |
| if conversion: |
| return conversion.input_type |
| else: |
| return self._NarrowInputType( |
| type_name) if type_name else 'dynamic' |
| |
| if constructor_info.pure_dart_constructor: |
| # TODO(antonm): use common dispatcher generation for this case as well. |
| has_optional = any(param_info.is_optional |
| for param_info in constructor_info.param_infos) |
| factory_call = self.MakeFactoryCall( |
| factory_name, factory_constructor_name, factory_parameters, |
| constructor_info) |
| if not has_optional: |
| self._members_emitter.Emit( |
| '\n $(METADATA)' |
| 'factory $CTOR($PARAMS) => ' |
| '$FACTORY_CALL;\n', |
| CTOR=constructor_info._ConstructorFullName(self._DartType), |
| PARAMS=constructor_info.ParametersAsDeclaration(InputType), |
| FACTORY_CALL=factory_call, |
| METADATA=metadata) |
| else: |
| inits = self._members_emitter.Emit( |
| '\n $(METADATA)' |
| 'factory $CONSTRUCTOR($PARAMS) {\n' |
| ' $CONSTRUCTOR e = $FACTORY_CALL;\n' |
| '$!INITS' |
| ' return e;\n' |
| ' }\n', |
| CONSTRUCTOR=constructor_info._ConstructorFullName( |
| self._DartType), |
| METADATA=metadata, |
| FACTORY_CALL=factory_call, |
| PARAMS=constructor_info.ParametersAsDeclaration(InputType)) |
| |
| for index, param_info in enumerate( |
| constructor_info.param_infos): |
| if param_info.is_optional: |
| inits.Emit( |
| ' if ($E != null) e.$E = $E;\n', |
| E=param_info.name) |
| else: |
| custom_factory_ctr = self._interface.id in _custom_factories |
| if self._interface_type_info.has_generated_interface(): |
| constructor_full_name = constructor_info._ConstructorFullName( |
| self._DartType) |
| else: |
| # The interface is suppress_interface so use the implementation_name not |
| # the dart_type. |
| constructor_full_name = self._interface_type_info.implementation_name( |
| ) |
| factory_name = constructor_full_name |
| |
| def GenerateCall(stmts_emitter, call_emitter, version, |
| signature_index, argument_count): |
| name = emitter.Format('_create_$VERSION', VERSION=version) |
| arguments = constructor_info.idl_args[ |
| signature_index][:argument_count] |
| args = None |
| call_template = '' |
| if self._dart_use_blink: |
| type_ids = [p.type.id for p in arguments] |
| base_name, rs = \ |
| self.DeriveNativeEntry("constructorCallback", 'Constructor', argument_count) |
| qualified_name = \ |
| self.DeriveQualifiedBlinkName(self._interface.id, |
| base_name) |
| args = constructor_info.ParametersAsArgumentList( |
| argument_count) |
| |
| # Handle converting Maps to Dictionaries, etc. |
| (factory_params, converted_arguments, |
| calling_params) = self._ConvertArgumentTypes( |
| stmts_emitter, arguments, argument_count, |
| constructor_info) |
| args = ', '.join(converted_arguments) |
| call_template = '$FACTORY_NAME($FACTORY_PARAMS)' |
| else: |
| qualified_name = emitter.Format( |
| '$FACTORY.$NAME', FACTORY=factory_name, NAME=name) |
| (factory_params, converted_arguments, |
| calling_params) = self._ConvertArgumentTypes( |
| stmts_emitter, arguments, argument_count, |
| constructor_info) |
| args = ', '.join(converted_arguments) |
| call_template = '$FACTORY_NAME($FACTORY_PARAMS)' |
| call_emitter.Emit( |
| call_template, |
| FACTORY_NAME=qualified_name, |
| FACTORY_PARAMS=args) |
| self.EmitStaticFactoryOverload(constructor_info, name, |
| arguments) |
| |
| def IsOptional(signature_index, argument): |
| return self.IsConstructorArgumentOptional(argument) |
| |
| entry_declaration = emitter.Format( |
| '$(METADATA)$FACTORY_KEYWORD $CTOR($PARAMS)', |
| FACTORY_KEYWORD=('factory' if not custom_factory_ctr else |
| 'static %s' % constructor_full_name), |
| CTOR=(('' if not custom_factory_ctr else '_factory') + |
| constructor_full_name), |
| METADATA=metadata, |
| PARAMS=constructor_info.ParametersAsDeclaration(InputType)) |
| |
| overload_emitter = self._members_emitter |
| overload_declaration = entry_declaration |
| |
| self._GenerateOverloadDispatcher(constructor_info, |
| constructor_info.idl_args, False, |
| overload_declaration, GenerateCall, |
| IsOptional, overload_emitter) |
| |
| def _AddFutureifiedOperation(self, info, html_name): |
| """Given a API function that uses callbacks, convert it to using Futures. |
| |
| This conversion assumes the success callback is always provided before the |
| error callback (and so far in the DOM API, this is the case).""" |
| callback_info = GetCallbackInfo( |
| self._database.GetInterface(info.callback_args[0].type_id)) |
| |
| # Generated private members never have named arguments. |
| ignore_named_parameters = True if html_name.startswith('_') else False |
| |
| # If more than one callback then the second argument is the error callback. |
| # Some error callbacks have 2 args (e.g., executeSql) where the second arg |
| # is the error - this is the argument we want. |
| error_callback = "" |
| if len(info.callback_args) > 1: |
| error_callback_info = GetCallbackInfo( |
| self._database.GetInterface(info.callback_args[1].type_id)) |
| error_callbackNames = [] |
| for paramInfo in error_callback_info.param_infos: |
| error_callbackNames.append(paramInfo.name) |
| errorCallbackVariables = ", ".join(error_callbackNames) |
| errorName = error_callback_info.param_infos[-1].name |
| error_callback = ( |
| ',\n %s(%s) { completer.completeError(%s); }' % ( |
| ('%s : ' % info.callback_args[1].name |
| if info.requires_named_arguments and |
| info.callback_args[1].is_optional and |
| not (ignore_named_parameters) else ''), |
| errorCallbackVariables, errorName)) |
| |
| extensions = GetDDC_Extension(self._interface, info.declared_name) |
| if extensions: |
| ddc_extensions = "\n".join(extensions) |
| else: |
| ddc_extensions = '' |
| |
| # Some callbacks have more than one parameters. If so use all of |
| # those parameters. However, if more than one argument use the |
| # type of the last argument to be returned e.g., executeSql the callback |
| # is (transaction, resultSet) and only the resultSet is returned SqlResultSet. |
| callbackArgsLen = len(callback_info.param_infos) |
| future_generic = '' |
| callbackVariables = '' |
| completerVariable = '' |
| if callbackArgsLen == 1: |
| callbackVariables = 'value' |
| completerVariable = callbackVariables |
| if callback_info.param_infos[0].type_id: |
| future_generic = '<%s>' % self._DartType( |
| callback_info.param_infos[0].type_id) |
| elif callbackArgsLen > 1: |
| callbackNames = [] |
| for paramInfo in callback_info.param_infos: |
| callbackNames.append(paramInfo.name) |
| callbackVariables = ",".join(callbackNames) |
| completerVariable = callbackNames[-1] |
| future_generic = '<%s>' % self._DartType( |
| callback_info.param_infos[-1].type_id) |
| |
| param_list = info.ParametersAsArgumentList(None, |
| ignore_named_parameters) |
| dictionary_argument = info.dictionaryArgumentName() |
| |
| convert_map = '' |
| if dictionary_argument is not None: |
| mapArg = dictionary_argument[0] |
| tempVariable = '%s_dict' % mapArg |
| mapArgOptional = dictionary_argument[1] |
| |
| if not (extensions): |
| if not (param_list.endswith(', mapArg') or |
| param_list.endswith(', options') or |
| param_list == mapArg): |
| print( |
| "ERROR: %s.%s - Last parameter or only parameter %s is not of type Map" |
| % (self._interface.id, html_name, mapArg)) |
| param_list = '%s_dict' % param_list |
| |
| if mapArgOptional: |
| convert_map = ' var %s = null;\n'\ |
| ' if (%s != null) {\n'\ |
| ' %s = convertDartToNative_Dictionary(%s);\n'\ |
| ' }\n' % (tempVariable, mapArg, tempVariable, mapArg) |
| else: |
| convert_map = ' var %s = convertDartToNative_Dictionary(%s);\n' % ( |
| tempVariable, mapArg) |
| |
| metadata = '' |
| if '_RenamingAnnotation' in dir(self): |
| metadata = ( |
| self._RenamingAnnotation(info.declared_name, html_name) + |
| self._Metadata(info.type_name, info.declared_name, None, |
| info.type_nullable)) |
| self._members_emitter.Emit( |
| '\n' |
| ' $METADATA$MODIFIERS$TYPE$FUTURE_GENERIC $NAME($PARAMS) {\n' |
| ' $CONVERT_DICTIONARY' |
| ' var completer = new Completer$(FUTURE_GENERIC)();\n' |
| ' $ORIGINAL_FUNCTION($PARAMS_LIST\n' |
| ' $NAMED_PARAM($VARIABLE_NAME) { ' |
| '$DDC_EXTENSION\n' |
| 'completer.complete($COMPLETER_NAME); }' |
| '$ERROR_CALLBACK);\n' |
| ' return completer.future;\n' |
| ' }\n', |
| METADATA=metadata, |
| MODIFIERS='static ' if info.IsStatic() else '', |
| TYPE=self.SecureOutputType(info.type_name, |
| nullable=info.type_nullable), |
| NAME=html_name[1:], |
| PARAMS=info. |
| ParametersAsDeclaration(self._NarrowInputType if '_NarrowInputType' |
| in dir(self) else self._DartType), |
| CONVERT_DICTIONARY=convert_map, |
| PARAMS_LIST='' if param_list == '' else param_list + ',', |
| NAMED_PARAM=('%s : ' % info.callback_args[0].name |
| if info.requires_named_arguments and |
| info.callback_args[0].is_optional and |
| not (ignore_named_parameters) else ''), |
| VARIABLE_NAME=callbackVariables, |
| COMPLETER_NAME=completerVariable, |
| DDC_EXTENSION=ddc_extensions, |
| ERROR_CALLBACK=error_callback, |
| FUTURE_GENERIC=future_generic, |
| ORIGINAL_FUNCTION=html_name) |
| |
| def EmitHelpers(self, base_class): |
| if not self._members_emitter: |
| return |
| |
| if self._interface.id not in custom_html_constructors: |
| self._members_emitter.Emit( |
| ' // To suppress missing implicit constructor warnings.\n' |
| ' factory $CLASSNAME._() { ' |
| 'throw new UnsupportedError("Not supported"); }\n', |
| CLASSNAME=self._interface_type_info.implementation_name()) |
| |
| def DeclareAttribute(self, attribute, attr_name, read_only): |
| """ Declares an attribute but does not include the code to invoke it. |
| """ |
| if read_only: |
| # HACK(terry): Element is not abstract for Dartium so isContentEditable |
| # must have a body see impl_Element.darttemplate |
| if (self._interface.id == 'Element' and |
| attr_name == 'isContentEditable' and self._dart_js_interop): |
| return |
| else: |
| template = '\n $TYPE get $NAME;\n' |
| else: |
| template = '\n $TYPE get $NAME native;\n' \ |
| '\n set $NAME($TYPE value) native;\n' |
| |
| # Nullability is determined by attribute compatibility. |
| is_compat = self._mdn_reader.is_compatible(attribute) |
| nullable = attribute.type.nullable or not is_compat |
| |
| self._members_emitter.Emit(template, |
| NAME=attr_name, |
| TYPE=self.SecureOutputType( |
| attribute.type.id, nullable=nullable)) |
| |
| def DeclareOperation(self, operation, return_type_name, method_name): |
| """ Declares an operation but does not include the code to invoke it. |
| Arguments: |
| operation - The operation to be declared. |
| return_type_name - The name of the return type. |
| method_name - The name of the method. |
| """ |
| # HACK(terry): Element is not abstract for Dartium so click |
| # must have a body see impl_Element.darttemplate |
| if (self._interface.id == 'Element' and method_name == 'click' and |
| self._dart_js_interop): |
| return |
| else: |
| template = '\n $TYPE $NAME($PARAMS);\n' |
| |
| self._members_emitter.Emit( |
| template, |
| TYPE=return_type_name, |
| NAME=method_name, |
| PARAMS=operation.ParametersAsDeclaration(self._DartType)) |
| |
| def EmitListMixin(self, element_name, nullable): |
| # TODO(sra): Use separate mixins for mutable implementations of List<T>. |
| # TODO(sra): Use separate mixins for typed array implementations of List<T>. |
| template_file = 'immutable_list_mixin.darttemplate' |
| has_length = False |
| has_length_setter = False |
| |
| def _HasExplicitIndexedGetter(self): |
| return any(op.id == 'getItem' for op in self._interface.operations) |
| |
| def _HasCustomIndexedGetter(self): |
| return 'CustomIndexedGetter' in self._interface.ext_attrs |
| |
| def _HasNativeIndexedGetter(self): |
| return not (_HasCustomIndexedGetter(self) or |
| _HasExplicitIndexedGetter(self)) |
| |
| if _HasExplicitIndexedGetter(self): |
| getter_name = 'getItem' |
| else: |
| getter_name = '_nativeIndexedGetter' |
| |
| for attr in self._interface.attributes: |
| if attr.id == 'length': |
| has_length = True |
| has_length_setter = not attr.is_read_only |
| |
| has_num_items = any( |
| attr.id == 'numberOfItems' for attr in self._interface.attributes) |
| |
| template = self._template_loader.Load( |
| template_file, { |
| 'DEFINE_LENGTH_AS_NUM_ITEMS': |
| not has_length and has_num_items, |
| 'DEFINE_LENGTH_SETTER': |
| not has_length_setter, |
| 'USE_NATIVE_INDEXED_GETTER': |
| _HasNativeIndexedGetter(self) or |
| _HasExplicitIndexedGetter(self), |
| }) |
| if nullable: |
| element_js = element_name + "|Null" |
| element_name += '?' |
| else: |
| element_js = element_name |
| self._members_emitter.Emit( |
| template, E=element_name, EJS=element_js, GETTER=getter_name) |
| |
| def SecureOutputType(self, |
| type_name, |
| is_dart_type=False, |
| can_narrow_type=False, |
| nullable=False): |
| """ Converts the type name to the secure type name for return types. |
| Arguments: |
| can_narrow_type - True if the output type can be narrowed further than |
| what would be accepted for input, used to narrow num APIs down to double |
| or int. |
| """ |
| if is_dart_type: |
| dart_name = type_name |
| else: |
| type_info = self._TypeInfo(type_name) |
| dart_name = type_info.dart_type() |
| if can_narrow_type and dart_name == 'num': |
| dart_name = type_info.native_type() |
| |
| # We only need to secure Window. Only local History and Location are |
| # returned in generated code. |
| assert (dart_name != 'HistoryBase' and dart_name != 'LocationBase') |
| if dart_name == 'Window': |
| dart_name = _secure_base_types[dart_name] |
| if type_name == 'any': |
| dart_name = 'Object' |
| if nullable and dart_name != 'dynamic': |
| dart_name = dart_name + '?' |
| return dart_name |
| |
| def SecureBaseName(self, type_name): |
| if type_name in _secure_base_types: |
| return _secure_base_types[type_name] |
| |
| def is_DOM_type(self, type_name): |
| try: |
| self._type_registry.TypeInfo(type_name) |
| return True |
| except RuntimeError: |
| return False |
| |
| def _NarrowToImplementationType(self, type_name): |
| return self._type_registry.TypeInfo(type_name).narrow_dart_type() |
| |
| def _NarrowInputType(self, type_name): |
| return self._NarrowToImplementationType(type_name) |
| |
| def _DartType(self, type_name): |
| return self._type_registry.DartType(type_name) |
| |
| def _TypeInfo(self, type_name): |
| return self._type_registry.TypeInfo(type_name) |
| |
| def _CallbackConvert(self, argType, info): |
| if self._database.HasInterface(argType): |
| interface = self._database.GetInterface(argType) |
| if "Callback" in interface.ext_attrs: |
| return interface.ext_attrs['Callback'] |
| return None |
| |
| def _ConvertArgumentTypes(self, stmts_emitter, arguments, argument_count, |
| info): |
| temp_version = [0] |
| converted_arguments = [] |
| target_parameters = [] |
| calling_parameters = [] |
| for position, arg in enumerate(arguments[:argument_count]): |
| callBackInfo = self._CallbackConvert( |
| arg.type.id, info) # Returns callback arity (# of parameters) |
| if callBackInfo is None: |
| conversion = self._InputConversion(arg.type.id, |
| info.declared_name) |
| else: |
| conversion = self._InputConversion('Callback', |
| info.declared_name) |
| |
| param_name = arguments[position].id |
| if conversion: |
| temp_version[0] += 1 |
| temp_name = '%s_%s' % (param_name, temp_version[0]) |
| temp_type = conversion.output_type |
| null_assert_needed = info.param_infos[position].is_nullable \ |
| and not conversion.nullable_input |
| stmts_emitter.Emit( |
| '$(INDENT)$TYPE $NAME = $CONVERT($ARG$NULLASSERT);\n' |
| if callBackInfo is None else |
| '$(INDENT)$TYPE $NAME = $CONVERT($ARG$NULLASSERT, $ARITY);\n', |
| TYPE=TypeOrVar(temp_type), |
| NAME=temp_name, |
| CONVERT=conversion.function_name, |
| ARG=info.param_infos[position].name, |
| NULLASSERT='!' if null_assert_needed else '', |
| ARITY=callBackInfo) |
| converted_arguments.append(temp_name) |
| param_type = temp_type |
| verified_type = temp_type # verified by assignment in checked mode. |
| else: |
| converted_arguments.append(info.param_infos[position].name) |
| if self._database.HasTypeDef(arg.type.id): |
| param_type = 'dynamic' |
| else: |
| param_type = self._NarrowInputType(arg.type.id) |
| # Verified by argument checking on entry to the dispatcher. |
| |
| verified_type = self._InputType( |
| info.param_infos[position].type_id, info) |
| # The native method does not need an argument type if we know the type. |
| # But we do need the native methods to have correct function types, so |
| # be conservative. |
| if param_type == verified_type: |
| if param_type in [ |
| 'String', 'num', 'int', 'double', 'bool', |
| 'Object' |
| ]: |
| param_type = 'dynamic' |
| arg_is_nullable = arg.type.nullable |
| # If the parameter is either nullable or optional with no non-null |
| # default value, it is nullable. |
| if (info.param_infos[position].is_optional and |
| (info.param_infos[position].default_value_is_null == True or |
| info.param_infos[position].default_value == None) |
| ) or info.param_infos[position].is_nullable: |
| arg_is_nullable = True |
| target_parameters.append( |
| '%s%s' % (TypeOrNothing(param_type, nullable=arg_is_nullable), |
| param_name)) |
| calling_parameters.append(',%s ' % param_name) |
| |
| return target_parameters, converted_arguments, calling_parameters |
| |
| def _InputType(self, type_name, info): |
| conversion = self._InputConversion(type_name, info.declared_name) |
| if conversion: |
| return conversion.input_type |
| else: |
| # If typedef it's a union return dynamic. |
| if self._database.HasTypeDef(type_name): |
| return 'dynamic' |
| else: |
| return self._NarrowInputType( |
| type_name) if type_name else 'dynamic' |