diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 013cb8724..7961c4acc 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -7655,6 +7655,138 @@ main (void) - [jerry_create_external_function](#jerry_create_external_function) +## jerry_get_new_target + +**Summary** + +Returns the current "new.target" JavaScript function at the call site. + +If used outside of a native C function it will return "undefined" value. + +*Notes*: +- Returned value must be freed with [jerry_release_value](#jerry_release_value) when it +is no longer needed. +- This feature depends on build option (`JERRY_ES2015`) and can be checked + in runtime with the `JERRY_FEATURE_SYMBOL` feature enum value (as symbols are enabled in case of ES2015), + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- If the ES2015 mode is not enabled this method will always return the "undefined" value. + +**Prototype** + +```c +jerry_value_t +jerry_get_new_target (void); +``` +- return + - "undefined" - if at the call site it was not a constructor call. + - function object - if the current call site is in a constructor call. + +*New in version 2.2*. + +**Example 1** + +[doctest]: # (name="02.API-REFERENCE-jsnewtarget-01.c") + +```c +#include +#include +#include + +static jerry_value_t +demo_handler (const jerry_value_t func_obj_val, + const jerry_value_t this_val, + const jerry_value_t args_p[], + const jerry_length_t args_cnt) +{ + jerry_value_t new_target = jerry_get_new_target (); + + /* new_target is the "demo" JS function object */ + if (jerry_value_get_type (new_target) == JERRY_TYPE_FUNCTION) + { + printf ("This is a construct call\r\n"); + } + + jerry_release_value (new_target); + + return jerry_create_undefined (); +} + +int +main (int argc, char** argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t function_val = jerry_create_external_function (demo_handler); + + jerry_value_t ret_val = jerry_construct_object (function_val, NULL, 0); + + jerry_release_value (ret_val); + jerry_release_value (function_val); + + jerry_cleanup (); + return 0; +} +``` + +**Example 2** + +[doctest]: # (name="02.API-REFERENCE-jsnewtarget-02.c") + +```c +#include +#include +#include + +static jerry_value_t +demo_handler (const jerry_value_t func_obj_val, + const jerry_value_t this_val, + const jerry_value_t args_p[], + const jerry_length_t args_cnt) +{ + jerry_value_t new_target = jerry_get_new_target (); + + /* new_target is a JS function object */ + if (jerry_value_get_type (new_target) == JERRY_TYPE_FUNCTION) + { + printf ("This is a construct call\r\n"); + } + + jerry_release_value (new_target); + + return jerry_create_undefined (); +} + +int +main (int argc, char** argv) +{ + jerry_init (JERRY_INIT_EMPTY); + + /* register C method */ + jerry_value_t global_obj_val = jerry_get_global_object (); + jerry_value_t function_val = jerry_create_external_function (demo_handler); + jerry_value_t function_name_val = jerry_create_string ((const jerry_char_t *) "demo"); + jerry_value_t result_val = jerry_set_property (global_obj_val, function_name_val, function_val); + jerry_release_value (result_val); + jerry_release_value (function_name_val); + jerry_release_value (function_val); + jerry_release_value (global_obj_val); + + /* Invoke C method via JS */ + const char *src = "new demo ()"; + jerry_value_t ret_val = jerry_eval ((const jerry_char_t *) src, + strlen (src), + JERRY_PARSE_NO_OPTS); + + jerry_release_value (ret_val); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_construct_object](#jerry_construct_object) # ArrayBuffer and TypedArray functions diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 02d5f2661..fe64bb855 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -3395,6 +3395,34 @@ jerry_get_resource_name (const jerry_value_t value) /**< jerry api value */ return ecma_make_magic_string_value (LIT_MAGIC_STRING_RESOURCE_ANON); } /* jerry_get_resource_name */ +/** + * Access the "new.target" value. + * + * The "new.target" value depends on the current call site. That is + * this method will only have a function object result if, at the call site + * it was called inside a constructor method invoked with "new". + * + * @return "undefined" - if at the call site it was not a constructor call. + * function object - if the current call site is in a constructor call. + */ +jerry_value_t +jerry_get_new_target (void) +{ +#if ENABLED (JERRY_ES2015) + ecma_object_t *current_new_target = JERRY_CONTEXT (current_new_target); + + if (current_new_target == NULL || current_new_target == JERRY_CONTEXT_INVALID_NEW_TARGET) + { + return jerry_create_undefined (); + } + + ecma_ref_object (current_new_target); + return ecma_make_object_value (current_new_target); +#else /* !ENABLED (JERRY_ES2015) */ + return jerry_create_undefined (); +#endif /* ENABLED (JERRY_ES2015) */ +} /* jerry_get_new_target */ + /** * Check if the given value is an ArrayBuffer object. * diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 93a06eef6..e06dd17f0 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -606,6 +606,7 @@ jerry_context_t *jerry_create_context (uint32_t heap_size, jerry_context_alloc_t void jerry_set_vm_exec_stop_callback (jerry_vm_exec_stop_callback_t stop_cb, void *user_p, uint32_t frequency); jerry_value_t jerry_get_backtrace (uint32_t max_depth); jerry_value_t jerry_get_resource_name (const jerry_value_t value); +jerry_value_t jerry_get_new_target (void); /** * Array buffer components. diff --git a/tests/unit-core/test-newtarget.c b/tests/unit-core/test-newtarget.c index fac13d799..606991d26 100644 --- a/tests/unit-core/test-newtarget.c +++ b/tests/unit-core/test-newtarget.c @@ -46,16 +46,6 @@ enum TEST_ID_CONSTRUCT_AND_CALL_SUB = 3, }; -/** - * Helper method to access the "new.target" via an eval call. - */ -static jerry_value_t -get_new_target (void) -{ - const char *src = "new.target"; - return jerry_eval ((const jerry_char_t *) src, strlen (src), 0); -} /* get_new_target */ - static jerry_value_t construct_handler (const jerry_value_t func_obj_val, /**< function object */ const jerry_value_t this_val, /**< this arg */ @@ -78,7 +68,7 @@ construct_handler (const jerry_value_t func_obj_val, /**< function object */ case TEST_ID_SIMPLE_CONSTRUCT: { /* Method was called with "new": new.target should be equal to the function object. */ - jerry_value_t target = get_new_target (); + jerry_value_t target = jerry_get_new_target (); TEST_ASSERT (!jerry_value_is_undefined (target)); TEST_ASSERT (target == func_obj_val); jerry_release_value (target); @@ -87,7 +77,7 @@ construct_handler (const jerry_value_t func_obj_val, /**< function object */ case TEST_ID_SIMPLE_CALL: { /* Method was called directly without "new": new.target should be equal undefined. */ - jerry_value_t target = get_new_target (); + jerry_value_t target = jerry_get_new_target (); TEST_ASSERT (jerry_value_is_undefined (target)); TEST_ASSERT (target != func_obj_val); jerry_release_value (target); @@ -96,7 +86,7 @@ construct_handler (const jerry_value_t func_obj_val, /**< function object */ case TEST_ID_CONSTRUCT_AND_CALL_SUB: { /* Method was called with "new": new.target should be equal to the function object. */ - jerry_value_t target = get_new_target (); + jerry_value_t target = jerry_get_new_target (); TEST_ASSERT (!jerry_value_is_undefined (target)); TEST_ASSERT (target == func_obj_val); jerry_release_value (target);