diff --git a/jerry-core/ecma/operations/ecma-function-object.cpp b/jerry-core/ecma/operations/ecma-function-object.cpp index f2f299af0..28f52cb85 100644 --- a/jerry-core/ecma/operations/ecma-function-object.cpp +++ b/jerry-core/ecma/operations/ecma-function-object.cpp @@ -629,9 +629,91 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ } /* ecma_op_function_call */ /** - * [[Construct]] implementation for Function objects, - * created through 13.2 (ECMA_OBJECT_TYPE_FUNCTION) - * or 15.3.4.5 (ECMA_OBJECT_TYPE_BOUND_FUNCTION). + * [[Construct]] implementation for Function objects (13.2.2), + * created through 13.2 (ECMA_OBJECT_TYPE_FUNCTION) and + * externally defined (host) functions (ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION). + * + * @return completion value + * Returned value must be freed with ecma_free_completion_value + */ +static ecma_completion_value_t +ecma_op_function_construct_simple_or_external (ecma_object_t *func_obj_p, /**< Function object */ + const ecma_value_t* arguments_list_p, /**< arguments list */ + ecma_length_t arguments_list_len) /**< length of arguments list */ +{ + JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION + || ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION); + + ecma_completion_value_t ret_value; + + ecma_string_t *prototype_magic_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_PROTOTYPE); + + // 5. + ECMA_TRY_CATCH (func_obj_prototype_prop_value, + ecma_op_object_get (func_obj_p, + prototype_magic_string_p), + ret_value); + + // 6. + ecma_object_t *prototype_p; + if (ecma_is_value_object (func_obj_prototype_prop_value)) + { + prototype_p = ecma_get_object_from_value (func_obj_prototype_prop_value); + ecma_ref_object (prototype_p); + } + else + { + // 7. + prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); + } + + // 1., 2., 4. + ecma_object_t *obj_p = ecma_create_object (prototype_p, true, ECMA_OBJECT_TYPE_GENERAL); + + // 3. + ecma_property_t *class_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CLASS); + class_prop_p->u.internal_property.value = ECMA_MAGIC_STRING_OBJECT_UL; + + ecma_deref_object (prototype_p); + + // 8. + ECMA_TRY_CATCH (call_completion, + ecma_op_function_call (func_obj_p, + ecma_make_object_value (obj_p), + arguments_list_p, + arguments_list_len), + ret_value); + + ecma_value_t obj_value; + + // 9. + if (ecma_is_value_object (call_completion)) + { + ecma_deref_object (obj_p); + + obj_value = ecma_copy_value (call_completion, true); + } + else + { + // 10. + obj_value = ecma_make_object_value (obj_p); + } + + ret_value = ecma_make_normal_completion_value (obj_value); + + ECMA_FINALIZE (call_completion); + ECMA_FINALIZE (func_obj_prototype_prop_value); + + ecma_deref_ecma_string (prototype_magic_string_p); + + return ret_value; +} /* ecma_op_function_construct_simple_or_external */ + +/** + * [[Construct]] implementation: + * 13.2.2 - for Function objects, created through 13.2 (ECMA_OBJECT_TYPE_FUNCTION), + * and externally defined host functions (ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION); + * 15.3.4.5.1 - for Function objects, created through 15.3.4.5 (ECMA_OBJECT_TYPE_BOUND_FUNCTION). * * @return completion value * Returned value must be freed with ecma_free_completion_value @@ -648,74 +730,17 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION) { - if (unlikely (ecma_get_object_is_builtin (func_obj_p))) + if (unlikely (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_FUNCTION + && ecma_get_object_is_builtin (func_obj_p))) { return ecma_builtin_dispatch_construct (func_obj_p, arguments_list_p, arguments_list_len); } - ecma_completion_value_t ret_value; - - ecma_string_t *prototype_magic_string_p = ecma_get_magic_string (ECMA_MAGIC_STRING_PROTOTYPE); - - // 5. - ECMA_TRY_CATCH (func_obj_prototype_prop_value, - ecma_op_object_get (func_obj_p, - prototype_magic_string_p), - ret_value); - - // 6. - ecma_object_t *prototype_p; - if (ecma_is_value_object (func_obj_prototype_prop_value)) - { - prototype_p = ecma_get_object_from_value (func_obj_prototype_prop_value); - ecma_ref_object (prototype_p); - } - else - { - // 7. - prototype_p = ecma_builtin_get (ECMA_BUILTIN_ID_OBJECT_PROTOTYPE); - } - - // 1., 2., 4. - ecma_object_t *obj_p = ecma_create_object (prototype_p, true, ECMA_OBJECT_TYPE_GENERAL); - - // 3. - ecma_property_t *class_prop_p = ecma_create_internal_property (obj_p, ECMA_INTERNAL_PROPERTY_CLASS); - class_prop_p->u.internal_property.value = ECMA_MAGIC_STRING_FUNCTION_UL; - - ecma_deref_object (prototype_p); - - // 8. - ECMA_TRY_CATCH (call_completion, - ecma_op_function_call (func_obj_p, - ecma_make_object_value (obj_p), - arguments_list_p, - arguments_list_len), - ret_value); - - ecma_value_t obj_value; - - // 9. - if (ecma_is_value_object (call_completion)) - { - ecma_deref_object (obj_p); - - obj_value = ecma_copy_value (call_completion, true); - } - else - { - // 10. - obj_value = ecma_make_object_value (obj_p); - } - - ret_value = ecma_make_normal_completion_value (obj_value); - - ECMA_FINALIZE (call_completion); - ECMA_FINALIZE (func_obj_prototype_prop_value); - - ecma_deref_ecma_string (prototype_magic_string_p); - - return ret_value; + return ecma_op_function_construct_simple_or_external (func_obj_p, arguments_list_p, arguments_list_len); + } + else if (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION) + { + return ecma_op_function_construct_simple_or_external (func_obj_p, arguments_list_p, arguments_list_len); } else { diff --git a/tests/unit/test_api.cpp b/tests/unit/test_api.cpp index 871f80c6f..a65637d02 100644 --- a/tests/unit/test_api.cpp +++ b/tests/unit/test_api.cpp @@ -38,6 +38,9 @@ const char *test_source = ( "function call_external () {" " return this.external ('1', true);" "}" + "function call_external_construct () {" + " return new external_construct (true);" + "}" ); /** @@ -104,6 +107,26 @@ handler (const jerry_api_object_t *function_obj_p, return true; } /* handler */ +static bool +handler_construct (const jerry_api_object_t *function_obj_p, + const jerry_api_value_t *this_p, + jerry_api_value_t *ret_val_p, + const jerry_api_value_t args_p [], + const uint16_t args_cnt) +{ + printf ("ok construct %p %p %p %d %p\n", function_obj_p, this_p, args_p, args_cnt, ret_val_p); + + assert (this_p != NULL); + assert (this_p->type == JERRY_API_DATA_TYPE_OBJECT); + + assert (args_cnt == 1); + assert (args_p [0].type == JERRY_API_DATA_TYPE_BOOLEAN); + assert (args_p [0].v_bool == true); + + jerry_api_set_object_field_value (this_p->v_object, "value_field", &args_p [0]); + + return true; +} /* handler_construct */ int main (void) @@ -112,10 +135,10 @@ main (void) bool is_ok; ssize_t sz; - jerry_api_value_t val_t, val_foo, val_bar, val_A, val_A_prototype, val_a, val_a_foo; - jerry_api_value_t val_external, val_call_external; + jerry_api_value_t val_t, val_foo, val_bar, val_A, val_A_prototype, val_a, val_a_foo, val_value_field; + jerry_api_value_t val_external, val_external_construct, val_call_external, val_call_external_construct; jerry_api_object_t* global_obj_p; - jerry_api_object_t* external_func_p; + jerry_api_object_t* external_func_p, *external_construct_p; jerry_api_value_t res, args [2]; char buffer [32]; @@ -232,7 +255,9 @@ main (void) // Create native handler bound function object and set it to 'external' variable external_func_p = jerry_api_create_external_function (handler); - assert (external_func_p != NULL); + assert (external_func_p != NULL + && jerry_api_is_function (external_func_p) + && jerry_api_is_constructor (external_func_p)); test_api_init_api_value_object (&val_external, external_func_p); is_ok = jerry_api_set_object_field_value (global_obj_p, @@ -262,6 +287,43 @@ main (void) jerry_api_release_object (global_obj_p); + // Create native handler bound function object and set it to 'external_construct' variable + external_construct_p = jerry_api_create_external_function (handler_construct); + assert (external_construct_p != NULL + && jerry_api_is_function (external_construct_p) + && jerry_api_is_constructor (external_construct_p)); + + test_api_init_api_value_object (&val_external_construct, external_construct_p); + is_ok = jerry_api_set_object_field_value (global_obj_p, + "external_construct", + &val_external_construct); + assert (is_ok); + jerry_api_release_value (&val_external_construct); + jerry_api_release_object (external_construct_p); + + // Call 'call_external_construct' function that should call external function created above, as constructor + is_ok = jerry_api_get_object_field_value (global_obj_p, "call_external_construct", &val_call_external_construct); + assert (is_ok + && val_call_external_construct.type == JERRY_API_DATA_TYPE_OBJECT); + is_ok = jerry_api_call_function (val_call_external_construct.v_object, + global_obj_p, + &res, + NULL, 0); + jerry_api_release_value (&val_call_external_construct); + assert (is_ok + && res.type == JERRY_API_DATA_TYPE_OBJECT); + is_ok = jerry_api_get_object_field_value (res.v_object, + "value_field", + &val_value_field); + + // Get 'value_field' of constructed object + assert (is_ok + && val_value_field.type == JERRY_API_DATA_TYPE_BOOLEAN + && val_value_field.v_bool == true); + jerry_api_release_value (&val_value_field); + + jerry_api_release_value (&res); + jerry_cleanup (); return 0;