blob: 7155f29183a78748b268d39ed9811e4e4bbd0a75 [file] [log] [blame]
#!/usr/bin/python
# 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 systems to generate
native binding from the IDL database."""
import emitter
import os
from generator import *
from systemhtml import SecureOutputType
from htmldartgenerator import *
class DartiumBackend(HtmlDartGenerator):
"""Generates Dart implementation for one DOM IDL interface."""
def __init__(self, interface, cpp_library_emitter, options):
super(DartiumBackend, self).__init__(interface, options)
self._interface = interface
self._cpp_library_emitter = cpp_library_emitter
self._database = options.database
self._template_loader = options.templates
self._type_registry = options.type_registry
self._interface_type_info = self._type_registry.TypeInfo(self._interface.id)
def ImplementsMergedMembers(self):
# We could not add merged functions to implementation class because
# underlying c++ object doesn't implement them. Merged functions are
# generated on merged interface implementation instead.
return False
def CustomJSMembers(self):
return {}
def GenerateCallback(self, info):
if IsPureInterface(self._interface.id):
return
cpp_impl_includes = set()
cpp_header_handlers_emitter = emitter.Emitter()
cpp_impl_handlers_emitter = emitter.Emitter()
class_name = 'Dart%s' % self._interface.id
for operation in self._interface.operations:
parameters = []
arguments = []
conversion_includes = []
for argument in operation.arguments:
argument_type_info = self._TypeInfo(argument.type.id)
parameters.append('%s %s' % (argument_type_info.parameter_type(),
argument.id))
arguments.append(argument_type_info.to_dart_conversion(argument.id))
conversion_includes.extend(argument_type_info.conversion_includes())
cpp_header_handlers_emitter.Emit(
'\n'
' virtual bool handleEvent($PARAMETERS);\n',
PARAMETERS=', '.join(parameters))
if 'Custom' in operation.ext_attrs:
continue
cpp_impl_includes |= set(conversion_includes)
arguments_declaration = 'Dart_Handle arguments[] = { %s }' % ', '.join(arguments)
if not len(arguments):
arguments_declaration = 'Dart_Handle* arguments = 0'
cpp_impl_handlers_emitter.Emit(
'\n'
'bool $CLASS_NAME::handleEvent($PARAMETERS)\n'
'{\n'
' if (!m_callback.isIsolateAlive())\n'
' return false;\n'
' DartIsolateScope scope(m_callback.isolate());\n'
' DartApiScope apiScope;\n'
' $ARGUMENTS_DECLARATION;\n'
' return m_callback.handleEvent($ARGUMENT_COUNT, arguments);\n'
'}\n',
CLASS_NAME=class_name,
PARAMETERS=', '.join(parameters),
ARGUMENTS_DECLARATION=arguments_declaration,
ARGUMENT_COUNT=len(arguments))
cpp_header_emitter = self._cpp_library_emitter.CreateHeaderEmitter(self._interface.id, True)
cpp_header_emitter.Emit(
self._template_loader.Load('cpp_callback_header.template'),
INTERFACE=self._interface.id,
HANDLERS=cpp_header_handlers_emitter.Fragments())
cpp_impl_emitter = self._cpp_library_emitter.CreateSourceEmitter(self._interface.id)
cpp_impl_emitter.Emit(
self._template_loader.Load('cpp_callback_implementation.template'),
INCLUDES=self._GenerateCPPIncludes(cpp_impl_includes),
INTERFACE=self._interface.id,
HANDLERS=cpp_impl_handlers_emitter.Fragments())
def ImplementationTemplate(self):
template = None
interface_name = self._interface_type_info.interface_name()
if interface_name == self._interface.id or not self._database.HasInterface(interface_name):
template_file = 'impl_%s.darttemplate' % interface_name
template = self._template_loader.TryLoad(template_file)
if not template:
template = self._template_loader.Load('dart_implementation.darttemplate')
return template
def RootClassName(self):
return 'NativeFieldWrapperClass1'
def NativeSpec(self):
return ''
def StartInterface(self, memebers_emitter):
# Create emitters for c++ implementation.
if not IsPureInterface(self._interface.id):
self._cpp_header_emitter = self._cpp_library_emitter.CreateHeaderEmitter(self._interface.id)
self._cpp_impl_emitter = self._cpp_library_emitter.CreateSourceEmitter(self._interface.id)
else:
self._cpp_header_emitter = emitter.Emitter()
self._cpp_impl_emitter = emitter.Emitter()
self._interface_type_info = self._TypeInfo(self._interface.id)
self._members_emitter = memebers_emitter
self._cpp_declarations_emitter = emitter.Emitter()
self._cpp_impl_includes = set()
self._cpp_definitions_emitter = emitter.Emitter()
self._cpp_resolver_emitter = emitter.Emitter()
self._GenerateConstructors()
def _GenerateConstructors(self):
if not self._IsConstructable():
return
# TODO(antonm): currently we don't have information about number of arguments expected by
# the constructor, so name only dispatch.
self._cpp_resolver_emitter.Emit(
' if (name == "$(INTERFACE_NAME)_constructor_Callback")\n'
' return Dart$(INTERFACE_NAME)Internal::constructorCallback;\n',
INTERFACE_NAME=self._interface.id)
constructor_info = AnalyzeConstructor(self._interface)
ext_attrs = self._interface.ext_attrs
if 'CustomConstructor' in ext_attrs:
# We have a custom implementation for it.
self._cpp_declarations_emitter.Emit(
'\n'
'void constructorCallback(Dart_NativeArguments);\n')
return
if ext_attrs.get('ConstructorTemplate') == 'TypedArray':
self._cpp_impl_includes.add('"DartArrayBufferViewCustom.h"');
self._cpp_definitions_emitter.Emit(
'\n'
'static void constructorCallback(Dart_NativeArguments args)\n'
'{\n'
' WebCore::DartArrayBufferViewInternal::constructWebGLArray<Dart$(INTERFACE_NAME)>(args);\n'
'}\n',
INTERFACE_NAME=self._interface.id);
return
create_function = 'create'
if 'NamedConstructor' in ext_attrs:
create_function = 'createForJSConstructor'
function_expression = '%s::%s' % (self._interface_type_info.native_type(), create_function)
self._GenerateNativeCallback(
'constructorCallback',
False,
function_expression,
self._interface,
constructor_info.idl_args,
self._interface.id,
'ConstructorRaisesException' in ext_attrs)
ATTRIBUTES_OF_CONSTRUCTABLE = set([
'CustomConstructor',
'V8CustomConstructor',
'Constructor',
'NamedConstructor'])
def _IsConstructable(self):
ext_attrs = self._interface.ext_attrs
if self.ATTRIBUTES_OF_CONSTRUCTABLE & set(ext_attrs):
return True
# FIXME: support other types of ConstructorTemplate.
if ext_attrs.get('ConstructorTemplate') == 'TypedArray':
return True
return False
def EmitFactoryProvider(self, constructor_info, factory_provider, emitter):
interface_name = self._interface_type_info.interface_name()
template_file = 'factoryprovider_%s.darttemplate' % interface_name
template = self._template_loader.TryLoad(template_file)
if not template:
template = self._template_loader.Load('factoryprovider.darttemplate')
native_binding = '%s_constructor_Callback' % self._interface.id
emitter.Emit(
template,
FACTORYPROVIDER=factory_provider,
INTERFACE=interface_name,
PARAMETERS=constructor_info.ParametersDeclaration(self._DartType),
ARGUMENTS=constructor_info.ParametersAsArgumentList(),
NATIVE_NAME=native_binding)
def AddConstructors(self, constructors, factory_provider, class_name,
base_class):
super(DartiumBackend, self).AddConstructors(constructors, factory_provider,
class_name, base_class)
super_constructor = ''
if base_class and base_class != 'NativeFieldWrapperClass1':
super_constructor = ': super.internal()'
self._members_emitter.Emit(
' $CLASSNAME.internal()$SUPERCONSTRUCTOR;\n',
CLASSNAME=class_name,
SUPERCONSTRUCTOR=super_constructor)
def FinishInterface(self):
self._GenerateCPPHeader()
self._cpp_impl_emitter.Emit(
self._template_loader.Load('cpp_implementation.template'),
INTERFACE=self._interface.id,
INCLUDES=self._GenerateCPPIncludes(self._cpp_impl_includes),
CALLBACKS=self._cpp_definitions_emitter.Fragments(),
RESOLVER=self._cpp_resolver_emitter.Fragments(),
DART_IMPLEMENTATION_CLASS=self._interface_type_info.implementation_name())
def _GenerateCPPHeader(self):
to_native_emitter = emitter.Emitter()
if self._interface_type_info.custom_to_native():
to_native_emitter.Emit(
' static PassRefPtr<NativeType> toNative(Dart_Handle handle, Dart_Handle& exception);\n')
else:
to_native_emitter.Emit(
' static NativeType* toNative(Dart_Handle handle, Dart_Handle& exception)\n'
' {\n'
' return DartDOMWrapper::unwrapDartWrapper<Dart$INTERFACE>(handle, exception);\n'
' }\n',
INTERFACE=self._interface.id)
to_dart_emitter = emitter.Emitter()
ext_attrs = self._interface.ext_attrs
if ('CustomToJS' in ext_attrs or
('CustomToJSObject' in ext_attrs and 'TypedArray' not in ext_attrs) or
'PureInterface' in ext_attrs or
'CPPPureInterface' in ext_attrs or
self._interface_type_info.custom_to_dart()):
to_dart_emitter.Emit(
' static Dart_Handle toDart(NativeType* value);\n')
else:
to_dart_emitter.Emit(
' static Dart_Handle toDart(NativeType* value)\n'
' {\n'
' return DartDOMWrapper::toDart<Dart$(INTERFACE)>(value);\n'
' }\n',
INTERFACE=self._interface.id)
webcore_includes = self._GenerateCPPIncludes(
self._interface_type_info.webcore_includes())
is_node_test = lambda interface: interface.id == 'Node'
is_active_test = lambda interface: 'ActiveDOMObject' in interface.ext_attrs
is_event_target_test = lambda interface: 'EventTarget' in interface.ext_attrs
def TypeCheckHelper(test):
return 'true' if any(map(test, self._database.Hierarchy(self._interface))) else 'false'
self._cpp_header_emitter.Emit(
self._template_loader.Load('cpp_header.template'),
INTERFACE=self._interface.id,
WEBCORE_INCLUDES=webcore_includes,
WEBCORE_CLASS_NAME=self._interface_type_info.native_type(),
DECLARATIONS=self._cpp_declarations_emitter.Fragments(),
IS_NODE=TypeCheckHelper(is_node_test),
IS_ACTIVE=TypeCheckHelper(is_active_test),
IS_EVENT_TARGET=TypeCheckHelper(is_event_target_test),
TO_NATIVE=to_native_emitter.Fragments(),
TO_DART=to_dart_emitter.Fragments())
def AddAttribute(self, attribute, html_name, read_only):
self._AddGetter(attribute, html_name)
if not read_only:
self._AddSetter(attribute, html_name)
def _AddGetter(self, attr, html_name):
type_info = self._TypeInfo(attr.type.id)
dart_declaration = '%s get %s' % (SecureOutputType(self, attr.type.id), html_name)
is_custom = 'Custom' in attr.ext_attrs or 'CustomGetter' in attr.ext_attrs
cpp_callback_name = self._GenerateNativeBinding(attr.id, 1,
dart_declaration, 'Getter', is_custom)
if is_custom:
return
if 'Reflect' in attr.ext_attrs:
webcore_function_name = self._TypeInfo(attr.type.id).webcore_getter_name()
if 'URL' in attr.ext_attrs:
if 'NonEmpty' in attr.ext_attrs:
webcore_function_name = 'getNonEmptyURLAttribute'
else:
webcore_function_name = 'getURLAttribute'
elif 'ImplementedAs' in attr.ext_attrs:
webcore_function_name = attr.ext_attrs['ImplementedAs']
else:
if attr.id == 'operator':
webcore_function_name = '_operator'
elif attr.id == 'target' and attr.type.id == 'SVGAnimatedString':
webcore_function_name = 'svgTarget'
else:
webcore_function_name = self._ToWebKitName(attr.id)
if attr.type.id.startswith('SVGAnimated'):
webcore_function_name += 'Animated'
function_expression = self._GenerateWebCoreFunctionExpression(webcore_function_name, attr)
self._GenerateNativeCallback(
cpp_callback_name,
True,
function_expression,
attr,
[],
attr.type.id,
attr.get_raises)
def _AddSetter(self, attr, html_name):
type_info = self._TypeInfo(attr.type.id)
dart_declaration = 'void set %s(%s value)' % (html_name, self._DartType(attr.type.id))
is_custom = set(['Custom', 'CustomSetter', 'V8CustomSetter']) & set(attr.ext_attrs)
cpp_callback_name = self._GenerateNativeBinding(attr.id, 2,
dart_declaration, 'Setter', is_custom)
if is_custom:
return
if 'Reflect' in attr.ext_attrs:
webcore_function_name = self._TypeInfo(attr.type.id).webcore_setter_name()
else:
webcore_function_name = re.sub(r'^(xml(?=[A-Z])|\w)',
lambda s: s.group(1).upper(),
attr.id)
webcore_function_name = 'set%s' % webcore_function_name
if attr.type.id.startswith('SVGAnimated'):
webcore_function_name += 'Animated'
function_expression = self._GenerateWebCoreFunctionExpression(webcore_function_name, attr)
self._GenerateNativeCallback(
cpp_callback_name,
True,
function_expression,
attr,
[attr],
'void',
attr.set_raises)
def AddIndexer(self, element_type):
"""Adds all the methods required to complete implementation of List."""
# We would like to simply inherit the implementation of everything except
# length, [], and maybe []=. It is possible to extend from a base
# array implementation class only when there is no other implementation
# inheritance. There might be no implementation inheritance other than
# DOMBaseWrapper for many classes, but there might be some where the
# array-ness is introduced by a non-root interface:
#
# interface Y extends X, List<T> ...
#
# In the non-root case we have to choose between:
#
# class YImpl extends XImpl { add List<T> methods; }
#
# and
#
# class YImpl extends ListBase<T> { copies of transitive XImpl methods; }
#
dart_element_type = self._DartType(element_type)
if self._HasNativeIndexGetter():
self._EmitNativeIndexGetter(dart_element_type)
else:
self._members_emitter.Emit(
'\n'
' $TYPE operator[](int index) native "$(INTERFACE)_item_Callback";\n',
TYPE=SecureOutputType(self, element_type), INTERFACE=self._interface.id)
if self._HasNativeIndexSetter():
self._EmitNativeIndexSetter(dart_element_type)
else:
# The HTML library implementation of NodeList has a custom indexed setter
# implementation that uses the parent node the NodeList is associated
# with if one is available.
if self._interface.id != 'NodeList':
self._members_emitter.Emit(
'\n'
' void operator[]=(int index, $TYPE value) {\n'
' throw new UnsupportedError("Cannot assign element of immutable List.");\n'
' }\n',
TYPE=dart_element_type)
# The list interface for this class is manually generated.
if self._interface.id == 'NodeList':
return
# 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_contains = any(op.id == 'contains' for op in self._interface.operations)
template = self._template_loader.Load(
template_file,
{'DEFINE_CONTAINS': not has_contains})
self._members_emitter.Emit(template, E=dart_element_type)
def AmendIndexer(self, element_type):
# If interface is marked as having native indexed
# getter or setter, we must emit overrides as it's not
# guaranteed that the corresponding methods in C++ would be
# virtual. For example, as of time of writing, even though
# Uint8ClampedArray inherits from Uint8Array, ::set method
# is not virtual and accessing it through Uint8Array pointer
# would lead to wrong semantics (modulo vs. clamping.)
dart_element_type = self._DartType(element_type)
if self._HasNativeIndexGetter():
self._EmitNativeIndexGetter(dart_element_type)
if self._HasNativeIndexSetter():
self._EmitNativeIndexSetter(dart_element_type)
def _HasNativeIndexGetter(self):
ext_attrs = self._interface.ext_attrs
return ('CustomIndexedGetter' in ext_attrs or
'NumericIndexedGetter' in ext_attrs)
def _EmitNativeIndexGetter(self, element_type):
dart_declaration = '%s operator[](int index)' % SecureOutputType(self, element_type, True)
self._GenerateNativeBinding('numericIndexGetter', 2, dart_declaration,
'Callback', True)
def _HasNativeIndexSetter(self):
return 'CustomIndexedSetter' in self._interface.ext_attrs
def _EmitNativeIndexSetter(self, element_type):
dart_declaration = 'void operator[]=(int index, %s value)' % element_type
self._GenerateNativeBinding('numericIndexSetter', 3, dart_declaration,
'Callback', True)
def AddOperation(self, info, html_name):
"""
Arguments:
info: An OperationInfo object.
"""
operation = info.operations[0]
is_custom = 'Custom' in operation.ext_attrs
has_optional_arguments = any(self._IsArgumentOptionalInWebCore(operation, argument) for argument in operation.arguments)
needs_dispatcher = not is_custom and (len(info.operations) > 1 or has_optional_arguments)
dart_declaration = '%s%s %s(%s)' % (
'static ' if info.IsStatic() else '',
SecureOutputType(self, info.type_name),
html_name,
info.ParametersDeclaration(
(lambda x: 'dynamic') if needs_dispatcher else self._DartType))
if not needs_dispatcher:
# Bind directly to native implementation
argument_count = (0 if info.IsStatic() else 1) + len(info.param_infos)
cpp_callback_name = self._GenerateNativeBinding(
info.name, argument_count, dart_declaration, 'Callback', is_custom)
if not is_custom:
self._GenerateOperationNativeCallback(operation, operation.arguments, cpp_callback_name)
else:
self._GenerateDispatcher(info.operations, dart_declaration, [info.name for info in info.param_infos])
def AddConstant(self, constant):
type = TypeOrNothing(self._DartType(constant.type.id), constant.type.id)
self._members_emitter.Emit('\n static const $TYPE$NAME = $VALUE;\n',
NAME=constant.id,
TYPE=type,
VALUE=constant.value)
def _GenerateDispatcher(self, operations, dart_declaration, argument_names):
body = self._members_emitter.Emit(
'\n'
' $DECLARATION {\n'
'$!BODY'
' }\n',
DECLARATION=dart_declaration)
version = [1]
def GenerateCall(operation, argument_count, checks):
if checks:
if operation.type.id != 'void':
template = ' if ($CHECKS) {\n return $CALL;\n }\n'
else:
template = ' if ($CHECKS) {\n $CALL;\n return;\n }\n'
else:
if operation.type.id != 'void':
template = ' return $CALL;\n'
else:
template = ' $CALL;\n'
overload_name = '%s_%s' % (operation.id, version[0])
version[0] += 1
argument_list = ', '.join(argument_names[:argument_count])
call = '_%s(%s)' % (overload_name, argument_list)
body.Emit(template, CHECKS=' && '.join(checks), CALL=call)
dart_declaration = '%s%s _%s(%s)' % (
'static ' if operation.is_static else '',
SecureOutputType(self, operation.type.id), overload_name, argument_list)
cpp_callback_name = self._GenerateNativeBinding(
overload_name, (0 if operation.is_static else 1) + argument_count,
dart_declaration, 'Callback', False)
self._GenerateOperationNativeCallback(operation, operation.arguments[:argument_count], cpp_callback_name)
def GenerateChecksAndCall(operation, argument_count):
checks = []
for i in range(0, argument_count):
argument = operation.arguments[i]
argument_name = argument_names[i]
type = self._DartType(argument.type.id)
if type not in ['dynamic', 'Object']:
checks.append('(%s is %s || %s == null)' % (argument_name, type, argument_name))
checks.extend(['!?%s' % name for name in argument_names[argument_count:]])
GenerateCall(operation, argument_count, checks)
# TODO: Optimize the dispatch to avoid repeated checks.
if len(operations) > 1:
for operation in operations:
for position, argument in enumerate(operation.arguments):
if self._IsArgumentOptionalInWebCore(operation, argument):
GenerateChecksAndCall(operation, position)
GenerateChecksAndCall(operation, len(operation.arguments))
body.Emit(' throw "Incorrect number or type of arguments";\n');
else:
operation = operations[0]
argument_count = len(operation.arguments)
for position, argument in list(enumerate(operation.arguments))[::-1]:
if self._IsArgumentOptionalInWebCore(operation, argument):
check = '?%s' % argument_names[position]
# argument_count instead of position + 1 is used here to cover one
# complicated case with the effectively optional argument in the middle.
# Consider foo(x, [Optional] y, [Optional=DefaultIsNullString] 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(operation, argument_count, [check])
argument_count = position
GenerateCall(operation, argument_count, [])
def SecondaryContext(self, interface):
pass
def _GenerateOperationNativeCallback(self, operation, arguments, cpp_callback_name):
webcore_function_name = operation.ext_attrs.get('ImplementedAs', operation.id)
function_expression = self._GenerateWebCoreFunctionExpression(webcore_function_name, operation)
self._GenerateNativeCallback(
cpp_callback_name,
not operation.is_static,
function_expression,
operation,
arguments,
operation.type.id,
operation.raises)
def _GenerateNativeCallback(self,
callback_name,
needs_receiver,
function_expression,
node,
arguments,
return_type,
raises_dom_exception):
ext_attrs = node.ext_attrs
cpp_arguments = []
requires_v8_scope = \
any((self._TypeInfo(argument.type.id).requires_v8_scope() for argument in arguments))
runtime_check = None
raises_exceptions = raises_dom_exception or arguments
requires_stack_info = ext_attrs.get('CallWith') == 'ScriptArguments|CallStack'
if requires_stack_info:
raises_exceptions = True
requires_v8_scope = True
cpp_arguments = ['scriptArguments', 'scriptCallStack']
# WebKit uses scriptArguments to reconstruct last argument, so
# it's not needed and should be just removed.
arguments = arguments[:-1]
requires_script_execution_context = ext_attrs.get('CallWith') == 'ScriptExecutionContext'
if requires_script_execution_context:
raises_exceptions = True
cpp_arguments = ['context']
requires_dom_window = 'NamedConstructor' in ext_attrs
if requires_dom_window:
raises_exceptions = True
cpp_arguments = ['document']
if 'ImplementedBy' in ext_attrs:
assert needs_receiver
self._cpp_impl_includes.add('"%s.h"' % ext_attrs['ImplementedBy'])
cpp_arguments.append('receiver')
if 'Reflect' in ext_attrs:
cpp_arguments = [self._GenerateWebCoreReflectionAttributeName(node)]
v8EnabledPerContext = ext_attrs.get('synthesizedV8EnabledPerContext', ext_attrs.get('V8EnabledPerContext'))
v8EnabledAtRuntime = ext_attrs.get('synthesizedV8EnabledAtRuntime', ext_attrs.get('V8EnabledAtRuntime'))
assert(not (v8EnabledPerContext and v8EnabledAtRuntime))
if v8EnabledPerContext:
raises_exceptions = True
self._cpp_impl_includes.add('"ContextFeatures.h"')
self._cpp_impl_includes.add('"DOMWindow.h"')
runtime_check = emitter.Format(
' if (!ContextFeatures::$(FEATURE)Enabled(DartUtilities::domWindowForCurrentIsolate()->document())) {\n'
' exception = Dart_NewStringFromCString("Feature $FEATURE is not enabled");\n'
' goto fail;\n'
' }',
FEATURE=v8EnabledPerContext)
if v8EnabledAtRuntime:
raises_exceptions = True
self._cpp_impl_includes.add('"RuntimeEnabledFeatures.h"')
runtime_check = emitter.Format(
' if (!RuntimeEnabledFeatures::$(FEATURE)Enabled()) {\n'
' exception = Dart_NewStringFromCString("Feature $FEATURE is not enabled");\n'
' goto fail;\n'
' }',
FEATURE=self._ToWebKitName(v8EnabledAtRuntime))
body_emitter = self._cpp_definitions_emitter.Emit(
'\n'
'static void $CALLBACK_NAME(Dart_NativeArguments args)\n'
'{\n'
' DartApiScope dartApiScope;\n'
'$!BODY'
'}\n',
CALLBACK_NAME=callback_name)
if raises_exceptions:
body_emitter = body_emitter.Emit(
' Dart_Handle exception = 0;\n'
'$!BODY'
'\n'
'fail:\n'
' Dart_ThrowException(exception);\n'
' ASSERT_NOT_REACHED();\n')
body_emitter = body_emitter.Emit(
' {\n'
'$!BODY'
' return;\n'
' }\n')
if requires_v8_scope:
body_emitter.Emit(
' V8Scope v8scope;\n\n')
if runtime_check:
body_emitter.Emit(
'$RUNTIME_CHECK\n',
RUNTIME_CHECK=runtime_check)
if requires_script_execution_context:
body_emitter.Emit(
' ScriptExecutionContext* context = DartUtilities::scriptExecutionContext();\n'
' if (!context) {\n'
' exception = Dart_NewStringFromCString("Failed to retrieve a context");\n'
' goto fail;\n'
' }\n\n')
if requires_dom_window:
self._cpp_impl_includes.add('"DOMWindow.h"')
body_emitter.Emit(
' DOMWindow* domWindow = DartUtilities::domWindowForCurrentIsolate();\n'
' if (!domWindow) {\n'
' exception = Dart_NewStringFromCString("Failed to fetch domWindow");\n'
' goto fail;\n'
' }\n'
' Document* document = domWindow->document();\n')
if needs_receiver:
body_emitter.Emit(
' $WEBCORE_CLASS_NAME* receiver = DartDOMWrapper::receiver< $WEBCORE_CLASS_NAME >(args);\n',
WEBCORE_CLASS_NAME=self._interface_type_info.native_type())
if requires_stack_info:
self._cpp_impl_includes.add('"ScriptArguments.h"')
self._cpp_impl_includes.add('"ScriptCallStack.h"')
body_emitter.Emit(
'\n'
' Dart_Handle customArgument = Dart_GetNativeArgument(args, $INDEX);\n'
' RefPtr<ScriptArguments> scriptArguments(DartUtilities::createScriptArguments(customArgument, exception));\n'
' if (!scriptArguments)\n'
' goto fail;\n'
' RefPtr<ScriptCallStack> scriptCallStack(DartUtilities::createScriptCallStack());\n'
' if (!scriptCallStack->size())\n'
' return;\n',
INDEX=len(arguments) + 1)
# Emit arguments.
start_index = 1 if needs_receiver else 0
for i, argument in enumerate(arguments):
type_info = self._TypeInfo(argument.type.id)
argument_expression_template, type, cls, function = \
type_info.to_native_info(argument, self._interface.id)
if ((IsOptional(argument) and not self._IsArgumentOptionalInWebCore(node, argument)) or
(argument.ext_attrs.get('Optional') == 'DefaultIsNullString')):
function += 'WithNullCheck'
argument_name = DartDomNameOfAttribute(argument)
if type_info.pass_native_by_ref():
invocation_template =\
' $TYPE $ARGUMENT_NAME;\n'\
' $CLS::$FUNCTION(Dart_GetNativeArgument(args, $INDEX), $ARGUMENT_NAME, exception);\n'
else:
invocation_template =\
' $TYPE $ARGUMENT_NAME = $CLS::$FUNCTION(Dart_GetNativeArgument(args, $INDEX), exception);\n'
body_emitter.Emit(
'\n' +
invocation_template +
' if (exception)\n'
' goto fail;\n',
TYPE=type,
ARGUMENT_NAME=argument_name,
CLS=cls,
FUNCTION=function,
INDEX=start_index + i)
self._cpp_impl_includes.add('"%s.h"' % cls)
cpp_arguments.append(argument_expression_template % argument_name)
body_emitter.Emit('\n')
if 'NeedsUserGestureCheck' in ext_attrs:
cpp_arguments.append('DartUtilities::processingUserGesture')
invocation_emitter = body_emitter
if raises_dom_exception:
cpp_arguments.append('ec')
invocation_emitter = body_emitter.Emit(
' ExceptionCode ec = 0;\n'
'$!INVOCATION'
' if (UNLIKELY(ec)) {\n'
' exception = DartDOMWrapper::exceptionCodeToDartException(ec);\n'
' goto fail;\n'
' }\n')
function_call = '%s(%s)' % (function_expression, ', '.join(cpp_arguments))
if return_type == 'void':
invocation_emitter.Emit(
' $FUNCTION_CALL;\n',
FUNCTION_CALL=function_call)
else:
return_type_info = self._TypeInfo(return_type)
self._cpp_impl_includes |= set(return_type_info.conversion_includes())
# Generate to Dart conversion of C++ value.
to_dart_conversion = return_type_info.to_dart_conversion(function_call, self._interface.id, ext_attrs)
invocation_emitter.Emit(
' Dart_Handle returnValue = $TO_DART_CONVERSION;\n'
' if (returnValue)\n'
' Dart_SetReturnValue(args, returnValue);\n',
TO_DART_CONVERSION=to_dart_conversion)
def _GenerateNativeBinding(self, idl_name, argument_count, dart_declaration,
native_suffix, is_custom):
native_binding = '%s_%s_%s' % (self._interface.id, idl_name, native_suffix)
self._members_emitter.Emit(
'\n'
'\n /** @domName $DOMINTERFACE.$DOMNAME */'
'\n $DART_DECLARATION native "$NATIVE_BINDING";\n',
DOMINTERFACE=self._interface.id,
DOMNAME=idl_name,
DART_DECLARATION=dart_declaration,
NATIVE_BINDING=native_binding)
cpp_callback_name = '%s%s' % (idl_name, native_suffix)
self._cpp_resolver_emitter.Emit(
' if (argumentCount == $ARGC && name == "$NATIVE_BINDING")\n'
' return Dart$(INTERFACE_NAME)Internal::$CPP_CALLBACK_NAME;\n',
ARGC=argument_count,
NATIVE_BINDING=native_binding,
INTERFACE_NAME=self._interface.id,
CPP_CALLBACK_NAME=cpp_callback_name)
if is_custom:
self._cpp_declarations_emitter.Emit(
'\n'
'void $CPP_CALLBACK_NAME(Dart_NativeArguments);\n',
CPP_CALLBACK_NAME=cpp_callback_name)
return cpp_callback_name
def _GenerateWebCoreReflectionAttributeName(self, attr):
namespace = 'HTMLNames'
svg_exceptions = ['class', 'id', 'onabort', 'onclick', 'onerror', 'onload',
'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
'onmouseup', 'onresize', 'onscroll', 'onunload']
if self._interface.id.startswith('SVG') and not attr.id in svg_exceptions:
namespace = 'SVGNames'
self._cpp_impl_includes.add('"%s.h"' % namespace)
attribute_name = attr.ext_attrs['Reflect'] or attr.id.lower()
return 'WebCore::%s::%sAttr' % (namespace, attribute_name)
def _GenerateWebCoreFunctionExpression(self, function_name, idl_node):
if 'ImplementedBy' in idl_node.ext_attrs:
return '%s::%s' % (idl_node.ext_attrs['ImplementedBy'], function_name)
if idl_node.is_static:
return '%s::%s' % (self._interface_type_info.idl_type(), function_name)
return '%s%s' % (self._interface_type_info.receiver(), function_name)
def _IsArgumentOptionalInWebCore(self, operation, argument):
if not IsOptional(argument):
return False
if 'Callback' in argument.ext_attrs:
return False
if operation.id in ['addEventListener', 'removeEventListener'] and argument.id == 'useCapture':
return False
# Another option would be to adjust in IDLs, but let's keep it here for now
# as it's a single instance.
if self._interface.id == 'CSSStyleDeclaration' and operation.id == 'setProperty' and argument.id == 'priority':
return False
if argument.type.id == 'Dictionary':
return False
return True
def _GenerateCPPIncludes(self, includes):
return ''.join(['#include %s\n' % include for include in sorted(includes)])
def _ToWebKitName(self, name):
name = name[0].lower() + name[1:]
name = re.sub(r'^(hTML|uRL|jS|xML|xSLT)', lambda s: s.group(1).lower(),
name)
return re.sub(r'^(create|exclusive)',
lambda s: 'is' + s.group(1).capitalize(),
name)
def _TypeInfo(self, type_name):
return self._type_registry.TypeInfo(type_name)
def _DartType(self, type_name):
return self._type_registry.DartType(type_name)
class CPPLibraryEmitter():
def __init__(self, emitters, cpp_sources_dir):
self._emitters = emitters
self._cpp_sources_dir = cpp_sources_dir
self._headers_list = []
self._sources_list = []
def CreateHeaderEmitter(self, interface_name, is_callback=False):
path = os.path.join(self._cpp_sources_dir, 'Dart%s.h' % interface_name)
if not is_callback:
self._headers_list.append(path)
return self._emitters.FileEmitter(path)
def CreateSourceEmitter(self, interface_name):
path = os.path.join(self._cpp_sources_dir, 'Dart%s.cpp' % interface_name)
self._sources_list.append(path)
return self._emitters.FileEmitter(path)
def EmitDerivedSources(self, template, output_dir):
partitions = 20 # FIXME: this should be configurable.
sources_count = len(self._sources_list)
for i in range(0, partitions):
file_path = os.path.join(output_dir, 'DartDerivedSources%02i.cpp' % (i + 1))
includes_emitter = self._emitters.FileEmitter(file_path).Emit(template)
for source_file in self._sources_list[i::partitions]:
path = os.path.relpath(source_file, output_dir)
includes_emitter.Emit('#include "$PATH"\n', PATH=path)
def EmitResolver(self, template, output_dir):
file_path = os.path.join(output_dir, 'DartResolver.cpp')
includes_emitter, body_emitter = self._emitters.FileEmitter(file_path).Emit(template)
for header_file in self._headers_list:
path = os.path.relpath(header_file, output_dir)
includes_emitter.Emit('#include "$PATH"\n', PATH=path)
body_emitter.Emit(
' if (Dart_NativeFunction func = $CLASS_NAME::resolver(name, argumentCount))\n'
' return func;\n',
CLASS_NAME=os.path.splitext(os.path.basename(path))[0])