blob: ce117203de8e50765f9c447b7bfeb2c57e0b8f28 [file] [log] [blame]
{#
# Copyright 2017 The Cobalt Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#}
{#
# Checks if object implements interface.
#}
{% macro check_if_object_implements_interface() %}
if (!script::v8c::shared_bindings::object_implements_interface({{binding_class}}::GetTemplate(isolate), isolate, object)) {
return;
}
{%- endmacro %}
{#
# Function body for operation bindings.
# Parameters:
# operation: The operation context object
#}
{% macro function_implementation(operation) %}
v8::Isolate* isolate = info.GetIsolate();
{% if operation.is_static %}
{{ static_function_prologue() }}
{% else %}
v8::Local<v8::Object> object = info.Holder();
{{ check_if_object_implements_interface() }}
{{ nonstatic_function_prologue(impl_class) }}
{% endif %}
{% call(arguments_list) extract_arguments(operation) %}
{{ call_cobalt_function(impl_class, operation.type, operation.name,
arguments_list, operation.raises_exception,
operation.call_with, operation.is_static) }}
{% if operation.type != 'void' %}
if (!exception_state.is_exception_set()) {
info.GetReturnValue().Set(result_value);
}
{% endif %}
{%- endcall %}
{%- endmacro %}
{#
# Function body for setting an attribute value.
# Parameters:
# attribute: The attribute context object.
# impl_class: Cobalt class name of the Cobalt implementation of the
# interface on which the attribute is a member.
# cobalt_impl_prefix: Variable name prefix of a pointer to a Cobalt
# implementation of the interface on which the attribute is a member.
#}
{% macro set_attribute_implementation(attribute, impl_class,
cobalt_impl_prefix="") %}
{% if attribute.put_forwards %}
{ // Begin scope of {{attribute.type}} forwarded_{{cobalt_impl_prefix}}impl.
{{attribute.type}} forwarded_{{cobalt_impl_prefix}}impl =
{% if not attribute.is_static %}
{{cobalt_impl_prefix}}impl->{{attribute.getter_function_name}}();
{% else %}
{{impl_class}}::{{attribute.getter_function_name}}();
{% endif %}
if (!forwarded_{{cobalt_impl_prefix}}impl) {
NOTREACHED();
return;
}
if (!exception_state.is_exception_set()) {
{{ set_attribute_implementation(attribute.put_forwards, attribute.type,
"forwarded_" + cobalt_impl_prefix) -}}
}
return;
} // End scope of {{attribute.type}} forwarded_{{cobalt_impl_prefix}}impl.
{% else %}
TypeTraits<{{attribute.type}} >::ConversionType value;
FromJSValue(isolate, v8_value, {{attribute.conversion_flags}}, &exception_state,
&value);
if (exception_state.is_exception_set()) {
return;
}
{{ call_cobalt_function(impl_class, "void",
attribute.setter_function_name,
["value"], attribute.raises_exception,
attribute.call_with, attribute.is_static,
cobalt_impl_prefix) }}
return;
{% endif %} {#- attribute.put_forwards #}
{%- endmacro %}
{#
# Extract and marshal arguments that will be passed to a function-like call.
# Parameters:
# operation: An IdlOperation object
# Passed to caller:
# A string that can be used as the parameters for a function call. It will
# be either empty, or a comma-separated list of variable names.
#}
{% macro extract_arguments(operation) %}
{% set non_optional_arguments = operation.non_optional_arguments %}
{% set optional_arguments = operation.optional_arguments %}
{% set num_default_arguments = operation.num_default_arguments %}
{% set variadic_argument = operation.variadic_argument %}
{% set has_non_default_optional_arguments =
operation.has_non_default_optional_arguments %}
{%- if non_optional_arguments|length > 0 %}
const size_t kMinArguments = {{non_optional_arguments|length}};
if (info.Length() < kMinArguments) {
exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
return;
}
{% endif -%}
{# Declare variables for all arguments #}
{% for argument in non_optional_arguments %}
{% if loop.first %}
// Non-optional arguments
{% endif %}
TypeTraits<{{argument.type}} >::ConversionType {{argument.name}};
{% endfor %}
{% for argument in optional_arguments if argument.default_value %}
{% if loop.first %}
// Optional arguments with default values
{% endif %}
TypeTraits<{{argument.type}} >::ConversionType {{argument.name}} =
{{argument.default_value}};
{% endfor %}
{% for argument in optional_arguments if not argument.default_value %}
{% if loop.first %}
// Optional arguments
{% endif %}
TypeTraits<{{argument.type}} >::ConversionType {{argument.name}};
{% endfor %}
{% if variadic_argument %}
// Variadic argument
TypeTraits<{{variadic_argument.type}} >::ConversionType {{
variadic_argument.name}};
{% endif -%}
{% for argument in non_optional_arguments %}
DCHECK_LT({{loop.index0}}, info.Length());
v8::Local<v8::Value> non_optional_value{{loop.index0}} = info[{{loop.index0}}];
FromJSValue(isolate,
non_optional_value{{loop.index0}},
{{argument.conversion_flags}},
&exception_state, &{{argument.name}});
if (exception_state.is_exception_set()) {
return;
}
{% endfor -%}
{% for argument in optional_arguments %}
{% if loop.first %}
size_t num_set_arguments = {{
non_optional_arguments|length + num_default_arguments}};
{% endif %}
if (info.Length() > {{loop.index0 + non_optional_arguments|length}}) {
v8::Local<v8::Value> optional_value{{loop.index0}} = info[{{loop.index0 + non_optional_arguments|length}}];
{% if argument.default_value %}
if (!optional_value{{loop.index0}}->IsUndefined()) {
{% else %}
{
{% endif %}
FromJSValue(isolate,
optional_value{{loop.index0}},
{{argument.conversion_flags}},
&exception_state,
&{{argument.name}});
}
if (exception_state.is_exception_set()) {
return;
}
{% if not argument.default_value %}
++num_set_arguments;
{% endif %}
}
{% endfor %}
{% if variadic_argument %}
// Get variadic arguments.
{% if optional_arguments|length %}
const size_t kLastOptionalArgIndex = {{
non_optional_arguments|length + optional_arguments|length}};
if (num_set_arguments == kLastOptionalArgIndex) {
// If the last optional argument has been set, we will call the overload
// that takes the variadic argument, possibly with an empty vector in the
// case that there are no more arguments left.
++num_set_arguments;
}
{% endif %}
const size_t kFirstVariadicArgIndex = {{operation.arguments|length - 1}};
if (info.Length() > kFirstVariadicArgIndex) {
{{variadic_argument.name}}.resize(info.Length() - kFirstVariadicArgIndex);
for (int i = 0; i + kFirstVariadicArgIndex < info.Length(); ++i) {
v8::Local<v8::Value> variadic_argument_value{{i}} = info[i + kFirstVariadicArgIndex];
FromJSValue(isolate,
variadic_argument_value{{i}},
{{variadic_argument.conversion_flags}},
&exception_state,
&{{variadic_argument.name}}[i]);
if (exception_state.is_exception_set()) {
return;
}
}
}
{% endif -%}
{# Call the implementation function, based on the number of set arguments. #}
{% if has_non_default_optional_arguments %}
switch (num_set_arguments) {
{% for num_arguments in range(
non_optional_arguments|length + num_default_arguments,
operation.arguments|length + 1) %}
{# If no variadic arguments have been set, we still call the function with
signature that has the variadic argument and pass an empty vector. There is
no such function signature that takes the optional parameter immediately
preceeding the variadic argument but does not take the variadic arguments. #}
{% if loop.last or not operation.arguments[num_arguments].is_variadic %}
{% set function_arguments =
operation.arguments[0:num_arguments]|map(attribute='name')|list %}
case {{num_arguments}}:
{
{{- caller(function_arguments)|indent(8, false) }}
}
break;
{% endif %}
{% endfor %}
default:
NOTREACHED();
return;
}
{% else %} {#- has_non_default_optional_arguments #}
{% set function_arguments = operation.arguments|map(attribute='name')|list %}
{# whitespace control block #}
{{-caller(function_arguments)}}
{% endif %}
{% endmacro %}
{#
# Append extra arguments that should be passed to a cobalt function.
# Specifically, this will prepend parameters specified on IDLs using the
# [CallWith=] extended attribute.
# Parameters:
# arguments_list: A list of C++ expressions that represent a sequence of
# arguments that will be passed to a function.
# context: An IDL object that may have the extended attribute that
# we are interested in.
# Passed to caller:
# arguments_list, possibly with extra arguments prepended and appended.
#}
{% macro add_extra_arguments(arguments_list, raises_exception, call_with) %}
{% if call_with %}
V8cGlobalEnvironment* callwith_global_environment = V8cGlobalEnvironment::GetFromIsolate(isolate);
{% do arguments_list.insert(
0, 'callwith_global_environment->Get%s()'|format(call_with)) %}
{% endif %}
{% do arguments_list.append('&exception_state') if raises_exception %}
{%- endmacro %}
{% macro call_nonvoid_function_internal(function_name, arguments,
impl_class, is_static) %}
{% if not is_static %}
ToJSValue(isolate,
impl->{{function_name}}({{arguments|join(", ")}}),
&result_value);
{% else %}
ToJSValue(isolate,
{{impl_class}}::{{function_name}}({{arguments|join(', ')}}),
&result_value);
{% endif %}
{%- endmacro %}
{% macro call_void_function_internal(function_name, arguments,
impl_class, is_static, cobalt_impl_prefix) %}
{% if not is_static %}
{{cobalt_impl_prefix}}impl->{{function_name}}({{arguments|join(", ")}});
{% else %}
{{impl_class}}::{{function_name}}({{arguments|join(', ')}});
{% endif %}
{%- endmacro %}
{% macro get_cobalt_value(impl_class, cobalt_type, function_name, arguments,
raises_exception, call_with, is_static, cobalt_impl_prefix) %}
{{ add_extra_arguments(arguments, raises_exception, call_with) }}
{% if cobalt_type == "void" %}
{{call_void_function_internal(function_name, arguments,
impl_class, is_static) -}}
{% else %}
{{call_nonvoid_function_internal(function_name, arguments,
impl_class, is_static) -}}
{% endif %}
{%- endmacro %}
{% macro call_nonvoid_function(return_type, function_name, arguments,
impl_class, is_static) %}
if (!exception_state.is_exception_set()) {
{{call_nonvoid_function_internal(function_name, arguments,
impl_class, is_static) -}}
}
{%- endmacro %}
{% macro call_void_function(function_name, arguments, impl_class, is_static,
cobalt_impl_prefix) %}
{{ call_void_function_internal(function_name, arguments, impl_class, is_static,
cobalt_impl_prefix) -}}
result_value = v8::Undefined(isolate);
{%- endmacro %}
{% macro get_impl_class_instance(impl_class) %}
{{impl_class}}* impl =
script::v8c::shared_bindings::get_impl_from_object<
{{impl_class}}>(object);
if (!impl) {
return;
}
{%- endmacro %}
{% macro static_function_prologue() %}
V8cExceptionState exception_state{isolate};
v8::Local<v8::Value> result_value;
{% endmacro %}
{% macro nonstatic_function_prologue(impl_class) %}
{{ static_function_prologue() }}
{{ get_impl_class_instance(impl_class) }}
{%- endmacro %}
{#
# Call a function on an instance of a Cobalt platform object.
#}
{% macro call_cobalt_function(impl_class, cobalt_type, function_name, arguments,
raises_exception, call_with, is_static,
cobalt_impl_prefix) %}
{{ add_extra_arguments(arguments, raises_exception, call_with) }}
{% if cobalt_type == "void" %}
{{ call_void_function(function_name, arguments, impl_class, is_static,
cobalt_impl_prefix) -}}
{% else %}
{{ call_nonvoid_function(cobalt_type, function_name, arguments, impl_class,
is_static) -}}
{% endif %}
{% endmacro %}
{#
# Function body for constructor bindings.
# Parameters:
# constructor: The constructor context object
#}
{% macro constructor_implementation(constructor) %}
V8cExceptionState exception_state(isolate);
{% call(arguments_list) extract_arguments(constructor) %}
if (!info.IsConstructCall()) {
exception_state.SetSimpleException(script::kTypeError, "Illegal constructor");
return;
}
{{ add_extra_arguments(arguments_list, constructor.raises_exception,
constructor.call_with) }}
scoped_refptr<{{impl_class}}> new_object =
new {{impl_class}}({{arguments_list|join(', ')}});
{% if constructor.raises_exception %}
// In case that an exception is thrown from constructor.
if (exception_state.is_exception_set()) {
return;
}
{% endif %}
v8::Local<v8::Value> result_value;
ToJSValue(isolate, new_object, &result_value);
DCHECK(result_value->IsObject());
info.GetReturnValue().Set(result_value);
{%- endcall %}
{%- endmacro %}
{#
# Function body for overload resolution function.
# Parameters:
# overload_context: The overload context object.
# bound_function_prefix: The prefix of the function to be called on
# resolution. The overload index will be appended to this.
#}
{% macro overload_resolution_implementation(
overload_context, bound_function_prefix) %}
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
switch(info.Length()) {
{% for length, distinguishing_argument_index, resolution_tests in
overload_context.overload_resolution_by_length %}
case({{length}}): {
// Overload resolution algorithm details found here:
// http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm
{# In the case there is only one resolution condition, we do not need the arg. #}
{% if resolution_tests|length > 1 %}
v8::Local<v8::Value> arg = info[{{distinguishing_argument_index}}];
WrapperFactory* wrapper_factory = V8cGlobalEnvironment::GetFromIsolate(isolate)->wrapper_factory();
v8::Local<v8::Object> object;
if (arg->IsObject()) {
object = arg->ToObject(context).ToLocalChecked();
}
{% endif %}
{% for test, overload in resolution_tests %}
if ({{test("arg")}}) {
{{bound_function_prefix}}{{overload.overload_index}}(
info);
return;
}
{% endfor %}
break;
}
{% endfor %}
}
// Invalid number of args
// http://heycam.github.io/webidl/#dfn-overload-resolution-algorithm
// 4. If S is empty, then throw a TypeError.
V8cExceptionState exception_state(isolate);
exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
return;
{%- endmacro %}