blob: 6adbab704e74096c0c3cde666e9ae9e56c33e46a [file] [log] [blame]
{#
# Copyright 2016 Google Inc. 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() %}
const JSClass* proto_class =
{{binding_class}}::PrototypeClass(context);
if (proto_class == JS_GetClass(object)) {
// Simply returns true if the object is this class's prototype object.
// There is no need to return any value due to the object is not a platform
// object. The execution reaches here when Object.getOwnPropertyDescriptor
// gets called on native object prototypes.
return true;
}
MozjsGlobalEnvironment* global_environment =
static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
if (!wrapper_factory->DoesObjectImplementInterface(
object, base::GetTypeId<{{impl_class}}>())) {
MozjsExceptionState exception(context);
exception.SetSimpleException(script::kDoesNotImplementInterface);
return false;
}
{%- endmacro %}
{#
# Function body for operation bindings.
# Parameters:
# operation: The operation context object
#}
{% macro function_implementation(operation) %}
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
{% if operation.is_static %}
{{ static_function_prologue() }}
{% else %}
// Compute the 'this' value.
JS::RootedValue this_value(context, JS_ComputeThis(context, vp));
// 'this' should be an object.
JS::RootedObject object(context);
if (JS_TypeOfValue(context, this_value) != JSTYPE_OBJECT) {
NOTREACHED();
return false;
}
if (!JS_ValueToObject(context, this_value, &object)) {
NOTREACHED();
return false;
}
{{ 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()) {
args.rval().set(result_value);
}
{% endif %}
return !exception_state.is_exception_set();
{%- 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 false;
}
if (!exception_state.is_exception_set()) {
{{ set_attribute_implementation(attribute.put_forwards, attribute.type,
"forwarded_" + cobalt_impl_prefix) -}}
}
return !exception_state.is_exception_set();
} // End scope of {{attribute.type}} forwarded_{{cobalt_impl_prefix}}impl.
{% else %}
TypeTraits<{{attribute.type}} >::ConversionType value;
if (args.length() != 1) {
NOTREACHED();
return false;
}
FromJSValue(context, args[0], {{attribute.conversion_flags}}, &exception_state,
&value);
if (exception_state.is_exception_set()) {
return false;
}
{{ call_cobalt_function(impl_class, "void",
attribute.setter_function_name,
["value"], attribute.raises_exception,
attribute.call_with, attribute.is_static,
cobalt_impl_prefix) }}
return !exception_state.is_exception_set();
{% 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 (args.length() < kMinArguments) {
exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
return false;
}
{% 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}}, args.length());
JS::RootedValue non_optional_value{{loop.index0}}(
context, args[{{loop.index0}}]);
FromJSValue(context,
non_optional_value{{loop.index0}},
{{argument.conversion_flags}},
&exception_state, &{{argument.name}});
if (exception_state.is_exception_set()) {
return false;
}
{% endfor -%}
{% for argument in optional_arguments %}
{% if loop.first %}
size_t num_set_arguments = {{
non_optional_arguments|length + num_default_arguments}};
{% endif %}
if (args.length() > {{loop.index0 + non_optional_arguments|length}}) {
JS::RootedValue optional_value{{loop.index0}}(
context, args[{{loop.index0 + non_optional_arguments|length}}]);
FromJSValue(context,
optional_value{{loop.index0}},
{{argument.conversion_flags}},
&exception_state,
&{{argument.name}});
if (exception_state.is_exception_set()) {
return false;
}
{% 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 (args.length() > kFirstVariadicArgIndex) {
{{variadic_argument.name}}.resize(args.length() - kFirstVariadicArgIndex);
for (int i = 0; i + kFirstVariadicArgIndex < args.length(); ++i) {
JS::RootedValue variadic_argument_value{{i}}(
context, args[i + kFirstVariadicArgIndex]);
FromJSValue(context,
variadic_argument_value{{i}},
{{variadic_argument.conversion_flags}},
&exception_state,
&{{variadic_argument.name}}[i]);
if (exception_state.is_exception_set()) {
return false;
}
}
}
{% 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 false;
}
{% 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 %}
MozjsGlobalEnvironment* callwith_global_environment =
static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
{% 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(return_type, function_name, arguments,
impl_class, is_static) %}
if (!exception_state.is_exception_set()) {
{% if not is_static %}
ToJSValue(context,
impl->{{function_name}}({{arguments|join(", ")}}),
&result_value);
{% else %}
ToJSValue(context,
{{impl_class}}::{{function_name}}({{arguments|join(', ')}}),
&result_value);
{% endif %}
}
{%- endmacro %}
{% macro call_void_function(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 %}
result_value.set(JS::UndefinedHandleValue);
{%- endmacro %}
{% macro get_impl_class_instance(impl_class) %}
WrapperPrivate* wrapper_private =
WrapperPrivate::GetFromObject(context, object);
{{impl_class}}* impl =
wrapper_private->wrappable<{{impl_class}}>().get();
{%- endmacro %}
{% macro static_function_prologue() %}
MozjsExceptionState exception_state(context);
JS::RootedValue result_value(context);
{% 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) %}
MozjsExceptionState exception_state(context);
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
{% call(arguments_list) extract_arguments(constructor) %}
{{ 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 false;
}
{% endif %}
JS::RootedValue result_value(context);
ToJSValue(context, new_object, &result_value);
DCHECK(result_value.isObject());
args.rval().setObject(result_value.toObject());
return true;
{%- 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) %}
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
switch(argc) {
{% 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 don't need the arg. #}
{% if resolution_tests|length > 1 %}
JS::RootedValue arg(context, args[{{distinguishing_argument_index}}]);
MozjsGlobalEnvironment* global_environment =
static_cast<MozjsGlobalEnvironment*>(JS_GetContextPrivate(context));
WrapperFactory* wrapper_factory = global_environment->wrapper_factory();
JS::RootedObject object(context);
if (arg.isObject()) {
object = JS::RootedObject(context, &arg.toObject());
}
{% endif %}
{% for test, overload in resolution_tests %}
if ({{test("arg")}}) {
return {{bound_function_prefix}}{{overload.overload_index}}(
context, argc, vp);
}
{% 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.
MozjsExceptionState exception_state(context);
exception_state.SetSimpleException(script::kInvalidNumberOfArguments);
return false;
{%- endmacro %}