blob: 2c0b868808e4b91175f460c3d32130df31a36f27 [file] [log] [blame]
# Copyright (C) 2013 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# pylint: disable=relative-import
"""Functions for type handling and type conversion (Blink/C++ <-> V8/JS).
Extends IdlType and IdlUnionType with V8-specific properties, methods, and
class methods.
Spec:
http://www.w3.org/TR/WebIDL/#es-type-mapping
Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
"""
import posixpath
from .idl_types import IdlArrayOrSequenceType
from .idl_types import IdlNullableType
from .idl_types import IdlRecordType
from .idl_types import IdlType
from .idl_types import IdlTypeBase
from .idl_types import IdlUnionType
from .utilities import to_snake_case
from . import v8_attributes # for IdlType.constructor_type_name
from .v8_globals import includes
from .v8_utilities import extended_attribute_value_contains
################################################################################
# V8-specific handling of IDL types
################################################################################
NON_WRAPPER_TYPES = frozenset([
'Dictionary',
'EventHandler',
'EventListener',
'NodeFilter',
'SerializedScriptValue',
])
TYPED_ARRAY_TYPES = frozenset([
'Float32Array',
'Float64Array',
'Int8Array',
'Int16Array',
'Int32Array',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array',
])
ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES = TYPED_ARRAY_TYPES.union(frozenset([
'ArrayBufferView'
]))
ARRAY_BUFFER_AND_VIEW_TYPES = TYPED_ARRAY_TYPES.union(frozenset([
'ArrayBuffer',
'ArrayBufferView',
'DataView',
'SharedArrayBuffer',
]))
IdlType.is_array_buffer_or_view = property(
lambda self: self.base_type in ARRAY_BUFFER_AND_VIEW_TYPES)
IdlType.is_array_buffer_view_or_typed_array = property(
lambda self: self.base_type in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES)
IdlType.is_typed_array = property(
lambda self: self.base_type in TYPED_ARRAY_TYPES)
IdlType.is_wrapper_type = property(
lambda self: (self.is_interface_type and
not self.is_callback_interface and
self.base_type not in NON_WRAPPER_TYPES))
################################################################################
# C++ types
################################################################################
CPP_TYPE_SAME_AS_IDL_TYPE = set([
'double',
'float',
])
CPP_INTEGER_CONVERSION_RULES = {
'byte': 'int8_t',
'octet': 'uint8_t',
'short': 'int16_t',
'unsigned short': 'uint16_t',
'long': 'int32_t',
'unsigned long': 'uint32_t',
'long long': 'int64_t',
'unsigned long long': 'uint64_t',
}
CPP_SPECIAL_CONVERSION_RULES = {
'Date': 'double',
'Dictionary': 'Dictionary',
'EventHandler': 'EventListener*',
'EventListener': 'EventListener*',
'NodeFilter': 'V8NodeFilterCondition*',
'Promise': 'ScriptPromise',
'ScriptValue': 'ScriptValue',
# FIXME: Eliminate custom bindings for XPathNSResolver http://crbug.com/345529
'XPathNSResolver': 'XPathNSResolver*',
'boolean': 'bool',
'unrestricted double': 'double',
'unrestricted float': 'float',
}
def cpp_type(idl_type, extended_attributes=None, raw_type=False, used_as_rvalue_type=False, used_as_variadic_argument=False, used_in_cpp_sequence=False):
"""Returns C++ type corresponding to IDL type.
|idl_type| argument is of type IdlType, while return value is a string
Args:
idl_type:
IdlType
raw_type:
bool, True if idl_type's raw/primitive C++ type should be returned.
used_as_rvalue_type:
bool, True if the C++ type is used as an argument or the return
type of a method.
used_as_variadic_argument:
bool, True if the C++ type is used as a variadic argument of a method.
used_in_cpp_sequence:
bool, True if the C++ type is used as an element of a container.
Containers can be an array, a sequence, a dictionary or a record.
"""
def string_mode():
if idl_type.is_nullable:
return 'kTreatNullAndUndefinedAsNullString'
# TODO(lisabelle): Remove these 4 lines when we have fully supported
# annoteted types. (crbug.com/714866)
# It is because at that time 'TreatNullAs' will only appear in
# type_extended_attributes, not in extended_attributes.
if extended_attributes.get('TreatNullAs') == 'EmptyString':
return 'kTreatNullAsEmptyString'
if extended_attributes.get('TreatNullAs') == 'NullString':
return 'kTreatNullAsNullString'
type_extended_attributes = idl_type.extended_attributes or {}
if type_extended_attributes.get('TreatNullAs') == 'EmptyString':
return 'kTreatNullAsEmptyString'
if type_extended_attributes.get('TreatNullAs') == 'NullString':
return 'kTreatNullAsNullString'
return ''
extended_attributes = extended_attributes or {}
idl_type = idl_type.preprocessed_type
# Array or sequence types
if used_as_variadic_argument:
native_array_element_type = idl_type
else:
native_array_element_type = idl_type.native_array_element_type
if native_array_element_type:
vector_type = cpp_ptr_type('Vector', 'HeapVector', native_array_element_type.is_gc_type)
vector_template_type = cpp_template_type(vector_type, native_array_element_type.cpp_type_args(used_in_cpp_sequence=True))
if used_as_rvalue_type:
return 'const %s&' % vector_template_type
return vector_template_type
# Record types.
if idl_type.is_record_type:
vector_type = cpp_ptr_type('Vector', 'HeapVector', idl_type.value_type.is_gc_type)
value_type = idl_type.value_type.cpp_type_args(used_in_cpp_sequence=True)
vector_template_type = cpp_template_type(vector_type,
'std::pair<String, %s>' % value_type)
if used_as_rvalue_type:
return 'const %s&' % vector_template_type
return vector_template_type
# Simple types
base_idl_type = idl_type.base_type
if base_idl_type in CPP_TYPE_SAME_AS_IDL_TYPE:
return base_idl_type
if base_idl_type in CPP_INTEGER_CONVERSION_RULES:
return CPP_INTEGER_CONVERSION_RULES[base_idl_type]
if base_idl_type in CPP_SPECIAL_CONVERSION_RULES:
return CPP_SPECIAL_CONVERSION_RULES[base_idl_type]
if base_idl_type == 'SerializedScriptValue':
return 'RefPtr<%s>' % base_idl_type
if idl_type.is_string_type:
if not raw_type:
return 'const String&' if used_as_rvalue_type else 'String'
return 'V8StringResource<%s>' % string_mode()
if base_idl_type == 'ArrayBufferView' and 'FlexibleArrayBufferView' in extended_attributes:
return 'FlexibleArrayBufferView'
if base_idl_type in TYPED_ARRAY_TYPES and 'FlexibleArrayBufferView' in extended_attributes:
return 'Flexible' + base_idl_type + 'View'
if base_idl_type in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
if not used_in_cpp_sequence:
if 'AllowShared' in extended_attributes:
return cpp_template_type('MaybeShared', idl_type.implemented_as)
else:
return cpp_template_type('NotShared', idl_type.implemented_as)
if idl_type.is_interface_type:
implemented_as_class = idl_type.implemented_as
if raw_type or (used_as_rvalue_type and idl_type.is_garbage_collected) or not used_in_cpp_sequence:
return implemented_as_class + '*'
if not used_in_cpp_sequence:
return implemented_as_class + '*'
return cpp_template_type('Member', implemented_as_class)
if idl_type.is_dictionary:
if used_as_rvalue_type:
return 'const %s&' % base_idl_type
return base_idl_type
if idl_type.is_union_type:
# Avoid "AOrNullOrB" for cpp type of (A? or B) because we generate
# V8AOrBOrNull to handle nulle for (A? or B), (A or B?) and (A or B)?
def member_cpp_name(idl_type):
if idl_type.is_nullable:
return idl_type.inner_type.name
return idl_type.name
idl_type_name = 'Or'.join(member_cpp_name(member)
for member in idl_type.member_types)
return 'const %s&' % idl_type_name if used_as_rvalue_type else idl_type_name
if idl_type.is_callback_function:
if idl_type.is_custom_callback_function:
return 'V8%s' % base_idl_type
return 'V8%s*' % base_idl_type
if base_idl_type == 'void':
return base_idl_type
# Default, assume native type is a pointer with same type name as idl type
return base_idl_type + '*'
def cpp_type_initializer(idl_type):
"""Returns a string containing a C++ initialization statement for the
corresponding type.
|idl_type| argument is of type IdlType.
"""
base_idl_type = idl_type.base_type
if idl_type.native_array_element_type:
return ''
if idl_type.is_numeric_type:
return ' = 0'
if base_idl_type == 'boolean':
return ' = false'
if (base_idl_type in NON_WRAPPER_TYPES or
base_idl_type in CPP_SPECIAL_CONVERSION_RULES or
base_idl_type == 'any' or
idl_type.is_string_type or
idl_type.is_enum):
return ''
return ' = nullptr'
# Allow access as idl_type.cpp_type if no arguments
IdlTypeBase.cpp_type = property(cpp_type)
IdlTypeBase.cpp_type_initializer = property(cpp_type_initializer)
IdlTypeBase.cpp_type_args = cpp_type
IdlUnionType.cpp_type_initializer = ''
IdlArrayOrSequenceType.native_array_element_type = property(
lambda self: self.element_type)
def cpp_template_type(template, inner_type):
"""Returns C++ template specialized to type."""
format_string = '{template}<{inner_type}>'
return format_string.format(template=template, inner_type=inner_type)
def cpp_ptr_type(old_type, new_type, is_gc_type):
if is_gc_type:
return new_type
return old_type
def v8_type(interface_name):
return 'V8' + interface_name
# [ImplementedAs]
# This handles [ImplementedAs] on interface types, not [ImplementedAs] in the
# interface being generated. e.g., given:
# Foo.idl: interface Foo {attribute Bar bar};
# Bar.idl: [ImplementedAs=Zork] interface Bar {};
# when generating bindings for Foo, the [ImplementedAs] on Bar is needed.
# This data is external to Foo.idl, and hence computed as global information in
# compute_interfaces_info.py to avoid having to parse IDLs of all used interfaces.
IdlType.implemented_as_interfaces = {}
def implemented_as(idl_type):
base_idl_type = idl_type.base_type
if base_idl_type in IdlType.implemented_as_interfaces:
return IdlType.implemented_as_interfaces[base_idl_type]
return base_idl_type
IdlType.implemented_as = property(implemented_as)
IdlType.set_implemented_as_interfaces = classmethod(
lambda cls, new_implemented_as_interfaces:
cls.implemented_as_interfaces.update(new_implemented_as_interfaces))
# [GarbageCollected]
IdlType.garbage_collected_types = set()
IdlType.is_garbage_collected = property(
lambda self: self.base_type in IdlType.garbage_collected_types)
IdlType.set_garbage_collected_types = classmethod(
lambda cls, new_garbage_collected_types:
cls.garbage_collected_types.update(new_garbage_collected_types))
def is_gc_type(idl_type):
return idl_type.is_garbage_collected or idl_type.is_dictionary or idl_type.is_union_type
IdlTypeBase.is_gc_type = property(is_gc_type)
def is_traceable(idl_type):
return (idl_type.is_garbage_collected or idl_type.is_dictionary)
IdlTypeBase.is_traceable = property(is_traceable)
IdlUnionType.is_traceable = property(lambda self: True)
IdlArrayOrSequenceType.is_traceable = property(
lambda self: self.element_type.is_traceable)
IdlRecordType.is_traceable = property(
lambda self: self.value_type.is_traceable)
################################################################################
# Includes
################################################################################
INCLUDES_FOR_TYPE = {
'object': set(),
'ArrayBufferView': set(['bindings/core/v8/V8ArrayBufferView.h',
'core/typed_arrays/ArrayBufferViewHelpers.h',
'core/typed_arrays/FlexibleArrayBufferView.h']),
'Dictionary': set(['bindings/core/v8/Dictionary.h']),
'EventHandler': set(['bindings/core/v8/V8AbstractEventListener.h',
'bindings/core/v8/V8EventListenerHelper.h']),
'EventListener': set(['bindings/core/v8/BindingSecurity.h',
'bindings/core/v8/V8EventListenerHelper.h',
'core/frame/LocalDOMWindow.h']),
'HTMLCollection': set(['bindings/core/v8/V8HTMLCollection.h',
'core/dom/ClassCollection.h',
'core/dom/TagCollection.h',
'core/html/HTMLCollection.h',
'core/html/HTMLTableRowsCollection.h',
'core/html/forms/HTMLDataListOptionsCollection.h',
'core/html/forms/HTMLFormControlsCollection.h']),
'NodeFilter': set(['bindings/core/v8/V8NodeFilterCondition.h']),
'NodeList': set(['bindings/core/v8/V8NodeList.h',
'core/dom/NameNodeList.h',
'core/dom/NodeList.h',
'core/dom/StaticNodeList.h',
'core/html/forms/LabelsNodeList.h']),
'Promise': set(['bindings/core/v8/ScriptPromise.h']),
'SerializedScriptValue': set(['bindings/core/v8/serialization/SerializedScriptValue.h',
'bindings/core/v8/serialization/SerializedScriptValueFactory.h']),
'ScriptValue': set(['bindings/core/v8/ScriptValue.h']),
}
def includes_for_type(idl_type, extended_attributes=None):
idl_type = idl_type.preprocessed_type
extended_attributes = extended_attributes or {}
# Simple types
base_idl_type = idl_type.base_type
if base_idl_type in INCLUDES_FOR_TYPE:
return INCLUDES_FOR_TYPE[base_idl_type]
if base_idl_type in TYPED_ARRAY_TYPES:
return INCLUDES_FOR_TYPE['ArrayBufferView'].union(
set(['bindings/%s/v8/V8%s.h' % (component_dir[base_idl_type], base_idl_type)])
)
if idl_type.is_basic_type:
return set(['bindings/core/v8/IDLTypes.h',
'bindings/core/v8/NativeValueTraitsImpl.h'])
if base_idl_type.endswith('ConstructorConstructor'):
# FIXME: rename to NamedConstructor
# FIXME: replace with a [NamedConstructorAttribute] extended attribute
# Ending with 'ConstructorConstructor' indicates a named constructor,
# and these do not have header files, as they are part of the generated
# bindings for the interface
return set()
if base_idl_type.endswith('Constructor'):
# FIXME: replace with a [ConstructorAttribute] extended attribute
base_idl_type = idl_type.constructor_type_name
if idl_type.is_custom_callback_function:
return set()
if idl_type.is_callback_function:
component = IdlType.callback_functions[base_idl_type]['component_dir']
return set(['bindings/%s/v8/%s.h' % (component, to_snake_case('V8%s' % base_idl_type))])
if base_idl_type not in component_dir:
return set()
return set(['bindings/%s/v8/V8%s.h' % (component_dir[base_idl_type],
base_idl_type)])
IdlType.includes_for_type = includes_for_type
def includes_for_union_type(idl_type, extended_attributes=None):
return set.union(*[member_type.includes_for_type(extended_attributes)
for member_type in idl_type.member_types])
IdlUnionType.includes_for_type = includes_for_union_type
def includes_for_array_or_sequence_type(idl_type, extended_attributes=None):
return set.union(set(['bindings/core/v8/IDLTypes.h',
'bindings/core/v8/NativeValueTraitsImpl.h']),
idl_type.element_type.includes_for_type(extended_attributes))
IdlArrayOrSequenceType.includes_for_type = includes_for_array_or_sequence_type
def includes_for_record_type(idl_type, extended_attributes=None):
return set.union(idl_type.key_type.includes_for_type(extended_attributes),
idl_type.value_type.includes_for_type(extended_attributes))
IdlRecordType.includes_for_type = includes_for_record_type
def add_includes_for_type(idl_type, extended_attributes=None):
includes.update(idl_type.includes_for_type(extended_attributes))
IdlTypeBase.add_includes_for_type = add_includes_for_type
def includes_for_interface(interface_name):
return IdlType(interface_name).includes_for_type()
def add_includes_for_interface(interface_name):
includes.update(includes_for_interface(interface_name))
def impl_includes_for_type(idl_type, interfaces_info):
includes_for_type = set()
idl_type = idl_type.preprocessed_type
native_array_element_type = idl_type.native_array_element_type
if native_array_element_type:
includes_for_type.update(impl_includes_for_type(
native_array_element_type, interfaces_info))
includes_for_type.add('platform/wtf/Vector.h')
base_idl_type = idl_type.base_type
if idl_type.is_string_type:
includes_for_type.add('platform/wtf/text/WTFString.h')
if base_idl_type in interfaces_info:
interface_info = interfaces_info[base_idl_type]
includes_for_type.add(interface_info['include_path'])
if base_idl_type in INCLUDES_FOR_TYPE:
includes_for_type.update(INCLUDES_FOR_TYPE[base_idl_type])
if idl_type.is_array_buffer_view_or_typed_array:
return set(['core/typed_arrays/DOMTypedArray.h', 'core/typed_arrays/ArrayBufferViewHelpers.h'])
return includes_for_type
def impl_includes_for_type_union(idl_type, interfaces_info):
includes_for_type = set()
for member_type in idl_type.member_types:
includes_for_type.update(member_type.impl_includes_for_type(interfaces_info))
return includes_for_type
IdlTypeBase.impl_includes_for_type = impl_includes_for_type
IdlUnionType.impl_includes_for_type = impl_includes_for_type_union
def impl_forward_declaration_name(idl_type):
element_type = idl_type.native_array_element_type
if element_type:
return element_type.impl_forward_declaration_name
if idl_type.is_wrapper_type and not idl_type.is_typed_array:
return idl_type.implemented_as
return None
IdlTypeBase.impl_forward_declaration_name = property(
impl_forward_declaration_name)
component_dir = {}
def set_component_dirs(new_component_dirs):
component_dir.update(new_component_dirs)
################################################################################
# V8 -> C++
################################################################################
# TODO(rakuco): Get rid of this definition altogether and move to NativeValueTraits<T>::nativeValue().
# That requires not requiring ExceptionState where it is not used, and we must be careful not
# to introduce any performance regressions.
V8_VALUE_TO_CPP_VALUE = {
# Basic
'DOMString': '{v8_value}',
# Interface types
'FlexibleArrayBufferView': 'ToFlexibleArrayBufferView({isolate}, {v8_value}, {variable_name}, allocateFlexibleArrayBufferViewStorage({v8_value}))',
'Promise': 'ScriptPromise::Cast(ScriptState::Current({isolate}), {v8_value})',
'ScriptValue': 'ScriptValue(ScriptState::Current({isolate}), {v8_value})',
'Window': 'ToDOMWindow({isolate}, {v8_value})',
'XPathNSResolver': 'ToXPathNSResolver(ScriptState::Current({isolate}), {v8_value})',
}
def v8_conversion_needs_exception_state(idl_type):
return (idl_type.is_numeric_type or
idl_type.is_enum or
idl_type.is_dictionary or
idl_type.is_array_buffer_view_or_typed_array or
idl_type.name in ('Boolean', 'ByteString', 'Date', 'Dictionary', 'USVString', 'SerializedScriptValue'))
IdlType.v8_conversion_needs_exception_state = property(v8_conversion_needs_exception_state)
IdlArrayOrSequenceType.v8_conversion_needs_exception_state = True
IdlRecordType.v8_conversion_needs_exception_state = True
IdlUnionType.v8_conversion_needs_exception_state = True
TRIVIAL_CONVERSIONS = frozenset([
'any',
'boolean',
'Date',
'Dictionary',
'NodeFilter',
'XPathNSResolver',
'Promise'
])
def v8_conversion_is_trivial(idl_type):
# The conversion is a simple expression that returns the converted value and
# cannot raise an exception.
return (idl_type.base_type in TRIVIAL_CONVERSIONS or
idl_type.is_wrapper_type)
IdlType.v8_conversion_is_trivial = property(v8_conversion_is_trivial)
def native_value_traits_type_name(idl_type):
idl_type = idl_type.preprocessed_type
if idl_type.is_nullable:
idl_type = idl_type.inner_type
if idl_type.native_array_element_type:
name = 'IDLSequence<%s>' % native_value_traits_type_name(idl_type.native_array_element_type)
elif idl_type.is_record_type:
name = 'IDLRecord<%s, %s>' % (native_value_traits_type_name(idl_type.key_type),
native_value_traits_type_name(idl_type.value_type))
elif idl_type.is_basic_type or idl_type.name == 'Promise':
name = 'IDL%s' % idl_type.name
elif idl_type.implemented_as is not None:
name = idl_type.implemented_as
else:
name = idl_type.name
return name
def v8_value_to_cpp_value(idl_type, extended_attributes, v8_value, variable_name, isolate):
if idl_type.name == 'void':
return ''
# Simple types
idl_type = idl_type.preprocessed_type
base_idl_type = idl_type.as_union_type.name if idl_type.is_union_type else idl_type.base_type
type_extended_attributes = idl_type.extended_attributes or {}
if 'FlexibleArrayBufferView' in extended_attributes:
if base_idl_type not in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
raise ValueError('Unrecognized base type for extended attribute "FlexibleArrayBufferView": %s' % (idl_type.base_type))
base_idl_type = 'FlexibleArrayBufferView'
if 'AllowShared' in extended_attributes and not idl_type.is_array_buffer_view_or_typed_array:
raise ValueError('Unrecognized base type for extended attribute "AllowShared": %s' % (idl_type.base_type))
if idl_type.is_integer_type:
configuration = 'kNormalConversion'
# TODO(lisabelle): Remove these 4 lines when we have fully supported
# annoteted types.
# It is because at that time 'Clamp' and 'EnforceRange' will only
# appear in type_extended_attributes, not in extended_attributes.
if 'EnforceRange' in extended_attributes:
configuration = 'kEnforceRange'
elif 'Clamp' in extended_attributes:
configuration = 'kClamp'
if 'EnforceRange' in type_extended_attributes:
configuration = 'kEnforceRange'
elif 'Clamp' in type_extended_attributes:
configuration = 'kClamp'
arguments = ', '.join([v8_value, 'exceptionState', configuration])
elif base_idl_type == 'SerializedScriptValue':
arguments = ', '.join([
v8_value,
'SerializedScriptValue::SerializeOptions(SerializedScriptValue::kNotForStorage)',
'exceptionState'])
elif idl_type.v8_conversion_needs_exception_state:
arguments = ', '.join([v8_value, 'exceptionState'])
else:
arguments = v8_value
if base_idl_type in V8_VALUE_TO_CPP_VALUE:
cpp_expression_format = V8_VALUE_TO_CPP_VALUE[base_idl_type]
elif idl_type.name == 'ArrayBuffer':
cpp_expression_format = (
'{v8_value}->Is{idl_type}() ? '
'V8{idl_type}::ToImpl(v8::Local<v8::{idl_type}>::Cast({v8_value})) : 0')
elif idl_type.is_array_buffer_view_or_typed_array:
this_cpp_type = idl_type.cpp_type_args(extended_attributes=extended_attributes)
if 'AllowShared' in extended_attributes:
cpp_expression_format = ('ToMaybeShared<%s>({isolate}, {v8_value}, exceptionState)' % this_cpp_type)
else:
cpp_expression_format = ('ToNotShared<%s>({isolate}, {v8_value}, exceptionState)' % this_cpp_type)
elif idl_type.is_union_type:
nullable = 'UnionTypeConversionMode::kNullable' if idl_type.includes_nullable_type \
else 'UnionTypeConversionMode::kNotNullable'
cpp_expression_format = 'V8{idl_type}::ToImpl({isolate}, {v8_value}, {variable_name}, %s, exceptionState)' % nullable
elif idl_type.use_output_parameter_for_result:
cpp_expression_format = 'V8{idl_type}::ToImpl({isolate}, {v8_value}, {variable_name}, exceptionState)'
elif idl_type.is_callback_function:
cpp_expression_format = (
'V8{idl_type}::Create(ScriptState::Current({isolate}), {v8_value})')
elif idl_type.v8_conversion_needs_exception_state:
# Effectively, this if branch means everything with v8_conversion_needs_exception_state == True
# except for unions and dictionary interfaces.
base_idl_type = native_value_traits_type_name(idl_type)
cpp_expression_format = (
'NativeValueTraits<{idl_type}>::NativeValue({isolate}, {arguments})')
else:
cpp_expression_format = (
'V8{idl_type}::ToImplWithTypeCheck({isolate}, {v8_value})')
return cpp_expression_format.format(arguments=arguments, idl_type=base_idl_type, v8_value=v8_value, variable_name=variable_name, isolate=isolate)
# FIXME: this function should be refactored, as this takes too many flags.
def v8_value_to_local_cpp_value(idl_type, extended_attributes, v8_value, variable_name, declare_variable=True,
isolate='info.GetIsolate()', bailout_return_value=None, use_exception_state=False):
"""Returns an expression that converts a V8 value to a C++ value and stores it as a local value."""
this_cpp_type = idl_type.cpp_type_args(extended_attributes=extended_attributes, raw_type=True)
idl_type = idl_type.preprocessed_type
cpp_value = v8_value_to_cpp_value(idl_type, extended_attributes, v8_value, variable_name, isolate)
# Optional expression that returns a value to be assigned to the local variable.
assign_expression = None
# Optional void expression executed unconditionally.
set_expression = None
# Optional expression that returns true if the conversion fails.
check_expression = None
# Optional expression used as the return value when returning. Only
# meaningful if 'check_expression' is not None.
return_expression = bailout_return_value
if 'FlexibleArrayBufferView' in extended_attributes:
if idl_type.base_type not in ARRAY_BUFFER_VIEW_AND_TYPED_ARRAY_TYPES:
raise ValueError('Unrecognized base type for extended attribute "FlexibleArrayBufferView": %s' % (idl_type.base_type))
set_expression = cpp_value
elif idl_type.is_string_type or idl_type.v8_conversion_needs_exception_state:
# Types for which conversion can fail and that need error handling.
check_expression = 'exceptionState.HadException()'
if idl_type.is_dictionary or idl_type.is_union_type:
set_expression = cpp_value
else:
assign_expression = cpp_value
# Note: 'not idl_type.v8_conversion_needs_exception_state' implies
# 'idl_type.is_string_type', but there are types for which both are
# true (ByteString and USVString), so using idl_type.is_string_type
# as the condition here would be wrong.
if not idl_type.v8_conversion_needs_exception_state:
if use_exception_state:
check_expression = '!%s.Prepare(exceptionState)' % variable_name
else:
check_expression = '!%s.Prepare()' % variable_name
elif not idl_type.v8_conversion_is_trivial and not idl_type.is_callback_function:
return {
'error_message': 'no V8 -> C++ conversion for IDL type: %s' % idl_type.name
}
else:
assign_expression = cpp_value
# Types that don't need error handling, and simply assign a value to the
# local variable.
return {
'assign_expression': assign_expression,
'check_expression': check_expression,
'cpp_type': this_cpp_type,
'cpp_name': variable_name,
'declare_variable': declare_variable,
'return_expression': bailout_return_value,
'set_expression': set_expression,
}
IdlTypeBase.v8_value_to_local_cpp_value = v8_value_to_local_cpp_value
def use_output_parameter_for_result(idl_type):
"""True when methods/getters which return the given idl_type should
take the output argument.
"""
return idl_type.is_dictionary or idl_type.is_union_type
IdlTypeBase.use_output_parameter_for_result = property(use_output_parameter_for_result)
################################################################################
# C++ -> V8
################################################################################
def preprocess_idl_type(idl_type):
extended_attributes = idl_type.extended_attributes
if idl_type.is_nullable:
return IdlNullableType(idl_type.inner_type.preprocessed_type)
if idl_type.is_enum:
# Enumerations are internally DOMStrings
return IdlType('DOMString', extended_attributes)
if idl_type.base_type in ['any', 'object'] or idl_type.is_custom_callback_function:
return IdlType('ScriptValue', extended_attributes)
if idl_type.is_callback_function:
return idl_type
return idl_type
IdlTypeBase.preprocessed_type = property(preprocess_idl_type)
def preprocess_idl_type_and_value(idl_type, cpp_value, extended_attributes):
"""Returns IDL type and value, with preliminary type conversions applied."""
idl_type = idl_type.preprocessed_type
if idl_type.name == 'Promise':
idl_type = IdlType('ScriptValue')
if idl_type.base_type in ['long long', 'unsigned long long']:
# long long and unsigned long long are not representable in ECMAScript;
# we represent them as doubles.
is_nullable = idl_type.is_nullable
idl_type = IdlType('double')
if is_nullable:
idl_type = IdlNullableType(idl_type)
cpp_value = 'static_cast<double>(%s)' % cpp_value
# HTML5 says that unsigned reflected attributes should be in the range
# [0, 2^31). When a value isn't in this range, a default value (or 0)
# should be returned instead.
extended_attributes = extended_attributes or {}
if ('Reflect' in extended_attributes and
idl_type.base_type in ['unsigned long', 'unsigned short']):
cpp_value = cpp_value.replace('GetUnsignedIntegralAttribute',
'GetIntegralAttribute')
cpp_value = 'std::max(0, static_cast<int>(%s))' % cpp_value
return idl_type, cpp_value
def v8_conversion_type(idl_type, extended_attributes):
"""Returns V8 conversion type, adding any additional includes.
The V8 conversion type is used to select the C++ -> V8 conversion function
or V8SetReturnValue* function; it can be an idl_type, a cpp_type, or a
separate name for the type of conversion (e.g., 'DOMWrapper').
"""
extended_attributes = extended_attributes or {}
# Nullable dictionaries need to be handled differently than either
# non-nullable dictionaries or unions.
if idl_type.is_dictionary and idl_type.is_nullable:
return 'NullableDictionary'
if idl_type.is_dictionary or idl_type.is_union_type:
return 'DictionaryOrUnion'
# Array or sequence types
native_array_element_type = idl_type.native_array_element_type
if native_array_element_type:
return 'FrozenArray' if idl_type.is_frozen_array else 'sequence'
# Record types.
if idl_type.is_record_type:
return 'Record'
# Simple types
base_idl_type = idl_type.base_type
# Basic types, without additional includes
if base_idl_type in CPP_INTEGER_CONVERSION_RULES:
return CPP_INTEGER_CONVERSION_RULES[base_idl_type]
if idl_type.is_string_type:
if idl_type.is_nullable:
return 'StringOrNull'
return base_idl_type
if idl_type.is_basic_type or base_idl_type == 'ScriptValue':
return base_idl_type
# Generic dictionary type
if base_idl_type == 'Dictionary':
return 'Dictionary'
# Data type with potential additional includes
if base_idl_type in V8_SET_RETURN_VALUE: # Special V8SetReturnValue treatment
return base_idl_type
# Pointer type
return 'DOMWrapper'
IdlTypeBase.v8_conversion_type = v8_conversion_type
V8_SET_RETURN_VALUE = {
'boolean': 'V8SetReturnValueBool(info, {cpp_value})',
'DOMString': 'V8SetReturnValueString(info, {cpp_value}, info.GetIsolate())',
'ByteString': 'V8SetReturnValueString(info, {cpp_value}, info.GetIsolate())',
'USVString': 'V8SetReturnValueString(info, {cpp_value}, info.GetIsolate())',
'StringOrNull': 'V8SetReturnValueStringOrNull(info, {cpp_value}, info.GetIsolate())',
'void': '',
# All the int types below are converted to (u)int32_t in the V8SetReturnValue{Int,Unsigned}() calls.
# The 64-bit int types have already been converted to double when V8_SET_RETURN_VALUE is used, so they are not
# listed here.
'int8_t': 'V8SetReturnValueInt(info, {cpp_value})',
'int16_t': 'V8SetReturnValueInt(info, {cpp_value})',
'int32_t': 'V8SetReturnValueInt(info, {cpp_value})',
'uint8_t': 'V8SetReturnValueUnsigned(info, {cpp_value})',
'uint16_t': 'V8SetReturnValueUnsigned(info, {cpp_value})',
'uint32_t': 'V8SetReturnValueUnsigned(info, {cpp_value})',
# No special V8SetReturnValue* function (set value directly)
'float': 'V8SetReturnValue(info, {cpp_value})',
'unrestricted float': 'V8SetReturnValue(info, {cpp_value})',
'double': 'V8SetReturnValue(info, {cpp_value})',
'unrestricted double': 'V8SetReturnValue(info, {cpp_value})',
# No special V8SetReturnValue* function, but instead convert value to V8
# and then use general V8SetReturnValue.
'sequence': 'V8SetReturnValue(info, {cpp_value})',
'FrozenArray': 'V8SetReturnValue(info, {cpp_value})',
'Date': 'V8SetReturnValue(info, {cpp_value})',
'EventHandler': 'V8SetReturnValue(info, {cpp_value})',
'NodeFilter': 'V8SetReturnValue(info, {cpp_value})',
'ScriptValue': 'V8SetReturnValue(info, {cpp_value})',
'SerializedScriptValue': 'V8SetReturnValue(info, {cpp_value})',
# Records.
'Record': 'V8SetReturnValue(info, ToV8({cpp_value}, info.Holder(), info.GetIsolate()))',
# DOMWrapper
'DOMWrapperForMainWorld': 'V8SetReturnValueForMainWorld(info, {cpp_value})',
'DOMWrapperFast': 'V8SetReturnValueFast(info, {cpp_value}, {script_wrappable})',
'DOMWrapperDefault': 'V8SetReturnValue(info, {cpp_value})',
# If [CheckSecurity=ReturnValue] is specified, the returned object must be
# wrapped in its own realm, which can be different from the realm of the
# receiver object.
#
# [CheckSecurity=ReturnValue] is used only for contentDocument and
# getSVGDocument attributes of HTML{IFrame,Frame,Object,Embed}Element,
# and Window.frameElement. Except for Window.frameElement, all interfaces
# support contentWindow(), so we create a new wrapper in the realm of
# contentWindow(). Note that DOMWindow* has its own realm and there is no
# need to pass |creationContext| in for ToV8(DOMWindow*).
# Window.frameElement is implemented with [Custom].
'DOMWrapperAcrossContext': (
'V8SetReturnValue(info, ToV8({cpp_value}, ' +
'ToV8(impl->contentWindow(), v8::Local<v8::Object>(), ' +
'info.GetIsolate()).As<v8::Object>(), info.GetIsolate()))'),
# Note that static attributes and operations do not check whether |this| is
# an instance of the interface nor |this|'s creation context is the same as
# the current context. So we must always use the current context as the
# creation context of the DOM wrapper for the return value.
'DOMWrapperStatic': 'V8SetReturnValue(info, {cpp_value}, info.GetIsolate()->GetCurrentContext()->Global())',
# Generic dictionary type
'Dictionary': 'V8SetReturnValue(info, {cpp_value})',
'DictionaryStatic': '#error not implemented yet',
# Nullable dictionaries
'NullableDictionary': 'V8SetReturnValue(info, result.Get())',
'NullableDictionaryStatic': 'V8SetReturnValue(info, result.Get(), info.GetIsolate()->GetCurrentContext()->Global())',
# Union types or dictionaries
'DictionaryOrUnion': 'V8SetReturnValue(info, result)',
'DictionaryOrUnionStatic': 'V8SetReturnValue(info, result, info.GetIsolate()->GetCurrentContext()->Global())',
}
def v8_set_return_value(idl_type, cpp_value, extended_attributes=None, script_wrappable='', for_main_world=False, is_static=False):
"""Returns a statement that converts a C++ value to a V8 value and sets it as a return value.
"""
def dom_wrapper_conversion_type():
if ('CheckSecurity' in extended_attributes and
extended_attribute_value_contains(
extended_attributes['CheckSecurity'], 'ReturnValue')):
return 'DOMWrapperAcrossContext'
if is_static:
return 'DOMWrapperStatic'
if not script_wrappable:
return 'DOMWrapperDefault'
if for_main_world:
return 'DOMWrapperForMainWorld'
return 'DOMWrapperFast'
idl_type, cpp_value = preprocess_idl_type_and_value(idl_type, cpp_value, extended_attributes)
this_v8_conversion_type = idl_type.v8_conversion_type(extended_attributes)
# SetReturn-specific overrides
if this_v8_conversion_type in ('Date', 'EventHandler', 'NodeFilter', 'ScriptValue',
'SerializedScriptValue', 'sequence', 'FrozenArray'):
# Convert value to V8 and then use general V8SetReturnValue
cpp_value = idl_type.cpp_value_to_v8_value(cpp_value, extended_attributes=extended_attributes)
if this_v8_conversion_type == 'DOMWrapper':
this_v8_conversion_type = dom_wrapper_conversion_type()
if is_static and this_v8_conversion_type in ('Dictionary', 'NullableDictionary', 'DictionaryOrUnion'):
this_v8_conversion_type += 'Static'
format_string = V8_SET_RETURN_VALUE[this_v8_conversion_type]
statement = format_string.format(cpp_value=cpp_value, script_wrappable=script_wrappable)
return statement
IdlTypeBase.v8_set_return_value = v8_set_return_value
CPP_VALUE_TO_V8_VALUE = {
# Built-in types
'Date': 'V8DateOrNaN({isolate}, {cpp_value})',
'DOMString': 'V8String({isolate}, {cpp_value})',
'ByteString': 'V8String({isolate}, {cpp_value})',
'USVString': 'V8String({isolate}, {cpp_value})',
'boolean': 'v8::Boolean::New({isolate}, {cpp_value})',
# All the int types below are converted to (u)int32_t in the v8::Integer::New*() calls.
# The 64-bit int types have already been converted to double when CPP_VALUE_TO_V8_VALUE is used, so they are not
# listed here.
'int8_t': 'v8::Integer::New({isolate}, {cpp_value})',
'int16_t': 'v8::Integer::New({isolate}, {cpp_value})',
'int32_t': 'v8::Integer::New({isolate}, {cpp_value})',
'uint8_t': 'v8::Integer::NewFromUnsigned({isolate}, {cpp_value})',
'uint16_t': 'v8::Integer::NewFromUnsigned({isolate}, {cpp_value})',
'uint32_t': 'v8::Integer::NewFromUnsigned({isolate}, {cpp_value})',
'float': 'v8::Number::New({isolate}, {cpp_value})',
'unrestricted float': 'v8::Number::New({isolate}, {cpp_value})',
'double': 'v8::Number::New({isolate}, {cpp_value})',
'unrestricted double': 'v8::Number::New({isolate}, {cpp_value})',
'void': 'v8Undefined()',
'StringOrNull': '{cpp_value}.IsNull() ? v8::Local<v8::Value>(v8::Null({isolate})) : V8String({isolate}, {cpp_value})',
# Special cases
'Dictionary': '{cpp_value}.V8Value()',
'EventHandler': (
'{cpp_value} ? ' +
'V8AbstractEventListener::Cast({cpp_value})->GetListenerOrNull(' +
'{isolate}, impl->GetExecutionContext()) : ' +
'v8::Null({isolate}).As<v8::Value>()'),
'NodeFilter': 'ToV8({cpp_value}, {creation_context}, {isolate})',
'Record': 'ToV8({cpp_value}, {creation_context}, {isolate})',
'ScriptValue': '{cpp_value}.V8Value()',
'SerializedScriptValue': 'V8Deserialize({isolate}, {cpp_value})',
# General
'sequence': 'ToV8({cpp_value}, {creation_context}, {isolate})',
'FrozenArray': 'FreezeV8Object(ToV8({cpp_value}, {creation_context}, {isolate}), {isolate})',
'DOMWrapper': 'ToV8({cpp_value}, {creation_context}, {isolate})',
# Passing nullable dictionaries isn't a pattern currently used
# anywhere in the web platform, and more work would be needed in
# the code generator to distinguish between passing null, and
# passing an object which happened to not contain any of the
# dictionary's defined attributes. For now, don't define
# NullableDictionary here, which will cause an exception to be
# thrown during code generation if an argument to a method is a
# nullable dictionary type.
#
# Union types or dictionaries
'DictionaryOrUnion': 'ToV8({cpp_value}, {creation_context}, {isolate})',
}
def cpp_value_to_v8_value(idl_type, cpp_value, isolate='info.GetIsolate()', creation_context='info.Holder()', extended_attributes=None):
"""Returns an expression that converts a C++ value to a V8 value."""
# the isolate parameter is needed for callback interfaces
idl_type, cpp_value = preprocess_idl_type_and_value(idl_type, cpp_value, extended_attributes)
this_v8_conversion_type = idl_type.v8_conversion_type(extended_attributes)
format_string = CPP_VALUE_TO_V8_VALUE[this_v8_conversion_type]
statement = format_string.format(cpp_value=cpp_value, isolate=isolate, creation_context=creation_context)
return statement
IdlTypeBase.cpp_value_to_v8_value = cpp_value_to_v8_value
def literal_cpp_value(idl_type, idl_literal):
"""Converts an expression that is a valid C++ literal for this type."""
# FIXME: add validation that idl_type and idl_literal are compatible
if idl_type.base_type in ('any', 'object') and idl_literal.is_null:
return 'ScriptValue()'
literal_value = str(idl_literal)
if idl_type.base_type in ('octet', 'unsigned short', 'unsigned long'):
return literal_value + 'u'
return literal_value
def union_literal_cpp_value(idl_type, idl_literal):
if idl_literal.is_null:
return idl_type.name + '()'
elif idl_literal.idl_type == 'DOMString':
member_type = idl_type.string_member_type
elif idl_literal.idl_type in ('integer', 'float'):
member_type = idl_type.numeric_member_type
elif idl_literal.idl_type == 'boolean':
member_type = idl_type.boolean_member_type
else:
raise ValueError('Unsupported literal type: ' + idl_literal.idl_type)
return '%s::From%s(%s)' % (idl_type.name, member_type.name,
member_type.literal_cpp_value(idl_literal))
def array_or_sequence_literal_cpp_value(idl_type, idl_literal):
# Only support empty sequences.
if idl_literal.value == '[]':
return cpp_type(idl_type) + '()'
raise ValueError('Unsupported literal type: ' + idl_literal.idl_type)
IdlType.literal_cpp_value = literal_cpp_value
IdlUnionType.literal_cpp_value = union_literal_cpp_value
IdlArrayOrSequenceType.literal_cpp_value = array_or_sequence_literal_cpp_value
################################################################################
# Utility properties for nullable types
################################################################################
def cpp_type_has_null_value(idl_type):
# - String types (String/AtomicString) represent null as a null string,
# i.e. one for which String::IsNull() returns true.
# - Enum types, as they are implemented as Strings.
# - Interface types (raw pointer or RefPtr) represent null as
# a null pointer.
# - Union types, as thier container classes can represent null value.
# - 'Object' and 'any' type. We use ScriptValue for object type.
return (idl_type.is_string_type
or idl_type.is_enum
or idl_type.is_interface_type
or idl_type.is_callback_interface
or idl_type.is_callback_function
or idl_type.is_custom_callback_function
or idl_type.is_union_type
or idl_type.base_type == 'object' or idl_type.base_type == 'any')
IdlTypeBase.cpp_type_has_null_value = property(cpp_type_has_null_value)
def is_implicit_nullable(idl_type):
# Nullable type where the corresponding C++ type supports a null value.
return idl_type.is_nullable and idl_type.cpp_type_has_null_value
def is_explicit_nullable(idl_type):
# Nullable type that isn't implicit nullable (see above.) For such types,
# we use Nullable<T> or similar explicit ways to represent a null value.
return idl_type.is_nullable and not idl_type.is_implicit_nullable
IdlTypeBase.is_implicit_nullable = property(is_implicit_nullable)
IdlUnionType.is_implicit_nullable = False
IdlTypeBase.is_explicit_nullable = property(is_explicit_nullable)
def includes_nullable_type_union(idl_type):
# http://heycam.github.io/webidl/#dfn-includes-a-nullable-type
return idl_type.number_of_nullable_member_types == 1
IdlTypeBase.includes_nullable_type = False
IdlNullableType.includes_nullable_type = True
IdlUnionType.includes_nullable_type = property(includes_nullable_type_union)