| {# |
| # 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.arguments|rejectattr('is_optional')|rejectattr('is_variadic')|list %} |
| {% set optional_arguments = operation.arguments|selectattr('is_optional')|list %} |
| {% set num_default_arguments = optional_arguments|rejectattr('default_value', 'none')|list|length %} |
| {% set variadic_argument = operation.arguments|last if (operation.arguments|length > 0) and (operation.arguments|last).is_variadic %} |
| {% set has_non_default_optional_arguments = optional_arguments|length > num_default_arguments %} |
| |
| {%- if non_optional_arguments|length > 0 %} |
| const size_t kMinArguments = {{non_optional_arguments|length}}; |
| if (exec_state->argumentCount() < kMinArguments) { |
| return JSC::throwVMNotEnoughArgumentsError(exec_state); |
| } |
| {% 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}}, exec_state->argumentCount()); |
| FromJSValue(exec_state, |
| exec_state->argument({{loop.index0}}), |
| {{argument.conversion_flags}}, |
| &exception_state, &{{argument.name}}); |
| if (exception_state.is_exception_set()) { |
| return JSC::throwVMError(exec_state, exception_state.exception_object()); |
| } |
| {% endfor -%} |
| {% for argument in optional_arguments %} |
| {% if loop.first %} |
| |
| size_t num_set_arguments = {{non_optional_arguments|length + num_default_arguments}}; |
| {% endif %} |
| if (exec_state->argumentCount() > {{loop.index0 + non_optional_arguments|length}}) { |
| FromJSValue(exec_state, |
| exec_state->argument({{loop.index0 + non_optional_arguments|length}}), |
| {{argument.conversion_flags}}, |
| &exception_state, |
| &{{argument.name}}); |
| if (exception_state.is_exception_set()) { |
| return JSC::throwVMError(exec_state, exception_state.exception_object()); |
| } |
| {% 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 (exec_state->argumentCount() > kFirstVariadicArgIndex) { |
| {{variadic_argument.name}}.resize(exec_state->argumentCount() - kFirstVariadicArgIndex); |
| for (int i = 0; i + kFirstVariadicArgIndex < exec_state->argumentCount(); ++i) { |
| FromJSValue(exec_state, |
| exec_state->argument(i + kFirstVariadicArgIndex), |
| {{variadic_argument.conversion_flags}}, |
| &exception_state, |
| &{{variadic_argument.name}}[i]); |
| if (exception_state.is_exception_set()) { |
| return JSC::throwVMError(exec_state, exception_state.exception_object()); |
| } |
| } |
| } |
| {% 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}}: |
| { |
| {# whitespace control block #} |
| {{-caller(function_arguments)|indent(8, True)}} |
| break; |
| } |
| {% endif %} |
| {% endfor %} |
| default: |
| NOTREACHED(); |
| return JSC::JSValue::encode(JSC::jsUndefined()); |
| } |
| {% else %} {#- has_non_default_optional_arguments #} |
| {% set function_arguments = operation.arguments|map(attribute='name')|list %} |
| {# whitespace control block #} |
| {{-caller(function_arguments)|indent(2, True)}} |
| {% 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: |
| # global_object: A C++ expression to retrieve a JSCGlobalObject pointer. |
| # arguments_list: A list of C++ expressions that represent a sequence of |
| # arguments that will be passed to a function. |
| # idl_object: An IDL object that may have the extended attribute that |
| # we are interested in. |
| # is_constructor: True if we are extracting the extended attribute for |
| # a constructor. |
| # Passed to caller: |
| # arguments_list, possibly with extra arguments prepended. |
| #} |
| {% macro add_extra_arguments(global_object, arguments_list, context) %} |
| {% set prepend = [] %} |
| {% set append = [] %} |
| {% if context.call_with %} |
| {% set prepend = ['%s->Get%s()'|format(global_object, context.call_with)] %} |
| {% endif %} |
| {% if context.raises_exception %} |
| {% set append = ['&exception_state'] %} |
| {% endif %} |
| {{caller(prepend + arguments_list + append)}} |
| {%- endmacro %} |
| |
| {# |
| # Create an expression to call a member function, which might be static, |
| # with the given arguments. |
| # Parameters: |
| # this_object: A string that is the name of a C++ variable that is a |
| # pointer to an instance of the implementation class. |
| # class_name: A string that is the name of the C++ implementation class. |
| # function_name: A string that is the name of the function to be called. |
| # arguments_list: A list of C++ expressions that represent a sequence of |
| # arguments that will be passed to a function. |
| # is_static: True if the method is static. |
| #} |
| {% macro call_function(this_object, class_name, function_name, arguments_list, is_static) %} |
| {% if is_static %} |
| {{class_name}}::{{function_name}}({{arguments_list|join(', ')}}) |
| {%- else %} |
| {{this_object}}->{{function_name}}({{arguments_list|join(', ')}}) |
| {%- endif %} |
| {%- endmacro %} |
| |
| {# |
| # Function body for operation bindings. |
| # Parameters: |
| # operation: The operation context object |
| #} |
| {% macro function_implementation(operation) %} |
| JSCGlobalObject* global_object = |
| JSC::jsCast<JSCGlobalObject*>(exec_state->lexicalGlobalObject()); |
| JSCExceptionState exception_state(global_object); |
| {% if not operation.is_static %} |
| JSC::JSObject* this_object = |
| exec_state->hostThisValue().toThisObject(exec_state); |
| {{impl_class}}* impl = |
| GetWrappableOrSetException<{{impl_class}}>(exec_state, this_object); |
| if (!impl) { |
| return JSC::JSValue::encode(exec_state->exception()); |
| } |
| {% endif %} |
| |
| {% call(arguments_list) extract_arguments(operation) %} |
| {% call(arguments_list) add_extra_arguments('global_object', arguments_list, operation) %} |
| {{ 'TypeTraits<%s >::ReturnType return_value = '|format(operation.type) if operation.type != 'void'}} |
| {{- call_function("impl", impl_class, operation.name, arguments_list, operation.is_static) -}}; |
| {% if operation.raises_exception %} |
| if (exception_state.is_exception_set()) { |
| return JSC::throwVMError(exec_state, exception_state.exception_object()); |
| } |
| {% endif %} |
| return JSC::JSValue::encode({{'ToJSValue(global_object, return_value)' if operation.type != 'void' else 'JSC::jsUndefined()'}}); |
| {% endcall %} |
| {% endcall %} |
| {%- endmacro %} |
| |
| {# |
| # Function body for constructor bindings. |
| # Parameters: |
| # operation: The constructor context object |
| #} |
| {% macro constructor_implementation(constructor) %} |
| JSCGlobalObject* global_object = |
| JSC::jsCast<JSCGlobalObject*>(exec_state->lexicalGlobalObject()); |
| JSCExceptionState exception_state(global_object); |
| {% call(arguments_list) extract_arguments(constructor) %} |
| {% call(arguments_list) add_extra_arguments('global_object', arguments_list, constructor) %} |
| scoped_refptr<{{impl_class}}> new_object = |
| new {{impl_class}}({{arguments_list|join(', ')}}); |
| {% if constructor.raises_exception %} |
| if (exception_state.is_exception_set()) { |
| return JSC::throwVMError(exec_state, exception_state.exception_object()); |
| } |
| {% endif %} |
| return JSC::JSValue::encode(ToJSValue(global_object, new_object)); |
| {% endcall %} |
| {% 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) %} |
| const size_t num_arguments = exec_state->argumentCount(); |
| switch(num_arguments) { |
| {% 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 %} |
| JSC::JSValue arg = exec_state->argument({{distinguishing_argument_index}}); |
| {% endif %} |
| {% for test, overload in resolution_tests %} |
| if ({{test("arg")}}) { |
| return {{bound_function_prefix}}{{overload.overload_index}}(exec_state); |
| } |
| {% 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. |
| return JSC::throwVMTypeError(exec_state); |
| {%- 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: Variable name 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) %} |
| {% if attribute.is_constructor_attribute %} |
| NOTIMPLEMENTED() << "Setting constructors not yet supported."; |
| {% else %} |
| {% if attribute.put_forwards %} |
| { |
| {{attribute.type}} forwarded_{{cobalt_impl}} = |
| {{call_function(cobalt_impl, impl_class, attribute.getter_function_name, |
| arguments_list, attribute.is_static)-}}; |
| if (!forwarded_{{cobalt_impl}}) { |
| return; |
| } |
| {{ set_attribute_implementation(attribute.put_forwards, attribute.type, |
| "forwarded_" + cobalt_impl) }} |
| } |
| {% else %} {#- if attribute.put_forwards #} |
| TypeTraits<{{attribute.type}} >::ConversionType cobalt_value; |
| {% if attribute.is_event_listener %} |
| ToEventListenerAttribute(exec_state, value, {{attribute.conversion_flags}}, |
| &exception_state, this_object, &cobalt_value); |
| {% else %} |
| FromJSValue(exec_state, value, |
| {{attribute.conversion_flags}}, &exception_state, |
| &cobalt_value); |
| {% endif %} |
| if (exception_state.is_exception_set()) { |
| JSC::throwError(exec_state, exception_state.exception_object()); |
| return; |
| } |
| {% call(arguments_list) add_extra_arguments('global_object', ['cobalt_value'], |
| attribute) %} |
| // Check if argument conversion raised an exception. |
| if (!exec_state->hadException()) { |
| {{ call_function(cobalt_impl, impl_class, attribute.setter_function_name, |
| arguments_list, attribute.is_static) -}}; |
| {% if attribute.raises_exception %} |
| if (exception_state.is_exception_set()) { |
| JSC::throwError(exec_state, exception_state.exception_object()); |
| } |
| {% endif %} |
| } |
| {%- endcall %} |
| {% endif %} {#- attribute.put_forwards #} |
| {% endif %} {#- attribute.is_constructor_attribute #} |
| {%- endmacro %} |
| |