mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 10:09:07 +00:00
f34eef5b91
Migrates syntax and semantics from python 2.7. Major changes include: - filters - sorting - print statements - higher-order functions - hashing and comparison and other misc changes. go.sh consistently gives the libraries in this and the previous commits with these changes. Change-Id: I66365739887158d8f321015d36e556447da1bcd3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211542 Commit-Queue: Srujan Gaddam <srujzs@google.com> Reviewed-by: Riley Porter <rileyporter@google.com>
1066 lines
48 KiB
Python
1066 lines
48 KiB
Python
#!/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, key=ConstantOutputOrder):
|
|
self.AddConstant(const)
|
|
|
|
for attr in sorted(interface.attributes, key=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,
|
|
key=ConstantOutputOrder):
|
|
if isinstance(parent_interface, str):
|
|
continue
|
|
|
|
for attr in sorted(parent_interface.attributes,
|
|
key=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(
|
|
list(
|
|
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, emmiter):
|
|
""" 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)
|
|
emmiter - Emmiter used to emit constructors when generating classes
|
|
using the static extension pattern.
|
|
"""
|
|
for constructor_info in constructors:
|
|
self._AddConstructor(constructor_info, factory_name,
|
|
factory_constructor_name, emmiter)
|
|
|
|
def _AddConstructor(self, constructor_info, factory_name,
|
|
factory_constructor_name, emmiter):
|
|
# 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, ' ')
|
|
|
|
target_emitter = emmiter if emmiter else self._members_emitter
|
|
|
|
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:
|
|
target_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 = target_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, emmiter)
|
|
|
|
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_declaration = entry_declaration
|
|
|
|
self._GenerateOverloadDispatcher(constructor_info,
|
|
constructor_info.idl_args, False,
|
|
overload_declaration, GenerateCall,
|
|
IsOptional, target_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, emmiter):
|
|
if (not self._members_emitter) and (not emmiter):
|
|
return
|
|
|
|
if self._interface.id not in custom_html_constructors:
|
|
target_emmiter = emmiter if emmiter else self._members_emitter
|
|
target_emmiter.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'
|