From e042998f02c2df895623712ec78bea645cce1f85 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Tue, 11 May 2021 12:22:37 +0200 Subject: [PATCH] Support function object retrieval for async functions (#4668) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/api/jerry.c | 6 +- jerry-core/ecma/base/ecma-gc.c | 1 + .../ecma/operations/ecma-arguments-object.c | 2 +- .../ecma/operations/ecma-function-object.c | 2 +- jerry-core/vm/opcodes.c | 3 +- jerry-core/vm/vm-defines.h | 5 +- jerry-core/vm/vm.c | 7 +- tests/unit-core/test-backtrace.c | 177 +++++++++++++----- 8 files changed, 141 insertions(+), 62 deletions(-) diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 784b0f5f3..a08f798d7 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -5266,11 +5266,9 @@ jerry_backtrace_get_function (jerry_backtrace_frame_t *frame_p) /**< frame point { vm_frame_ctx_t *context_p = frame_p->context_p; - if (context_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_HAS_ARG_LIST) + if (context_p->shared_p->function_object_p != NULL) { - vm_frame_ctx_shared_args_t *shared_args_p = (vm_frame_ctx_shared_args_t *) context_p->shared_p; - - frame_p->function = ecma_make_object_value (shared_args_p->function_object_p); + frame_p->function = ecma_make_object_value (context_p->shared_p->function_object_p); return &frame_p->function; } } diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 4550fac44..5a0704a24 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -640,6 +640,7 @@ ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ } ecma_gc_set_object_visited (executable_object_p->frame_ctx.lex_env_p); + ecma_gc_set_object_visited (executable_object_p->shared.function_object_p); if (!ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p)) { diff --git a/jerry-core/ecma/operations/ecma-arguments-object.c b/jerry-core/ecma/operations/ecma-arguments-object.c index eece3c46b..9ae1cf2dd 100644 --- a/jerry-core/ecma/operations/ecma-arguments-object.c +++ b/jerry-core/ecma/operations/ecma-arguments-object.c @@ -46,7 +46,7 @@ ecma_op_create_arguments_object (vm_frame_ctx_shared_args_t *shared_p, /**< shar ecma_object_t *lex_env_p) /**< lexical environment the Arguments * object is created for */ { - ecma_object_t *func_obj_p = shared_p->function_object_p; + ecma_object_t *func_obj_p = shared_p->header.function_object_p; const ecma_compiled_code_t *bytecode_data_p = shared_p->header.bytecode_header_p; uint16_t formal_params_number; diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index ee93b2172..92a4ee9ba 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -1040,7 +1040,7 @@ ecma_op_function_call_simple (ecma_object_t *func_obj_p, /**< Function object */ vm_frame_ctx_shared_args_t shared_args; shared_args.header.status_flags = VM_FRAME_CTX_SHARED_HAS_ARG_LIST; - shared_args.function_object_p = func_obj_p; + shared_args.header.function_object_p = func_obj_p; shared_args.arg_list_p = arguments_list_p; shared_args.arg_list_len = arguments_list_len; diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index d51686622..6bb5ee7dc 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -621,7 +621,7 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context } JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_NON_ARROW_FUNC); - proto_p = ecma_op_get_prototype_from_constructor (VM_FRAME_CTX_GET_FUNCTION_OBJECT (frame_ctx_p), + proto_p = ecma_op_get_prototype_from_constructor (frame_ctx_p->shared_p->function_object_p, default_proto_id); } @@ -645,6 +645,7 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context /* Copy shared data and frame context. */ vm_frame_ctx_shared_t *new_shared_p = &(executable_object_p->shared); *new_shared_p = *(frame_ctx_p->shared_p); + new_shared_p->status_flags &= (uint32_t) ~VM_FRAME_CTX_SHARED_HAS_ARG_LIST; vm_frame_ctx_t *new_frame_ctx_p = &(executable_object_p->frame_ctx); *new_frame_ctx_p = *frame_ctx_p; diff --git a/jerry-core/vm/vm-defines.h b/jerry-core/vm/vm-defines.h index 35262e2be..d10f7662d 100644 --- a/jerry-core/vm/vm-defines.h +++ b/jerry-core/vm/vm-defines.h @@ -57,6 +57,7 @@ typedef enum typedef struct { const ecma_compiled_code_t *bytecode_header_p; /**< currently executed byte-code data */ + ecma_object_t *function_object_p; /**< function obj */ uint32_t status_flags; /**< combination of vm_frame_ctx_shared_flags_t bits */ } vm_frame_ctx_shared_t; @@ -66,16 +67,12 @@ typedef struct typedef struct { vm_frame_ctx_shared_t header; /**< shared data header */ - ecma_object_t *function_object_p; /**< function obj */ const ecma_value_t *arg_list_p; /**< arguments list */ uint32_t arg_list_len; /**< arguments list length */ } vm_frame_ctx_shared_args_t; #if JERRY_ESNEXT -#define VM_FRAME_CTX_GET_FUNCTION_OBJECT(frame_ctx_p) \ - (((vm_frame_ctx_shared_args_t *) (frame_ctx_p)->shared_p)->function_object_p) - /** * Shared data extended with computed class fields */ diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index d733496fd..76be1d26b 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -290,6 +290,7 @@ vm_run_global (const ecma_compiled_code_t *bytecode_p) /**< pointer to bytecode vm_frame_ctx_shared_t shared; shared.bytecode_header_p = bytecode_p; + shared.function_object_p = NULL; shared.status_flags = 0; #if JERRY_BUILTIN_REALMS @@ -386,6 +387,7 @@ vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ vm_frame_ctx_shared_t shared; shared.bytecode_header_p = bytecode_data_p; + shared.function_object_p = NULL; shared.status_flags = (parse_opts & ECMA_PARSE_DIRECT_EVAL) ? VM_FRAME_CTX_SHARED_DIRECT_EVAL : 0; ecma_value_t completion_value = vm_run (&shared, this_binding, lex_env_p); @@ -427,6 +429,7 @@ vm_run_module (ecma_module_t *module_p) /**< module to be executed */ vm_frame_ctx_shared_t shared; shared.bytecode_header_p = module_p->u.compiled_code_p; + shared.function_object_p = &module_p->header.object; shared.status_flags = 0; return vm_run (&shared, ECMA_VALUE_UNDEFINED, module_p->scope_p); @@ -540,7 +543,7 @@ vm_get_class_function (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_NON_ARROW_FUNC) { - return VM_FRAME_CTX_GET_FUNCTION_OBJECT (frame_ctx_p); + return frame_ctx_p->shared_p->function_object_p; } ecma_environment_record_t *environment_record_p = ecma_op_get_environment_record (frame_ctx_p->lex_env_p); @@ -2117,7 +2120,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_RUN_FIELD_INIT: { JERRY_ASSERT (frame_ctx_p->shared_p->status_flags & VM_FRAME_CTX_SHARED_NON_ARROW_FUNC); - result = opfunc_init_class_fields (ecma_make_object_value (VM_FRAME_CTX_GET_FUNCTION_OBJECT (frame_ctx_p)), + result = opfunc_init_class_fields (ecma_make_object_value (frame_ctx_p->shared_p->function_object_p), frame_ctx_p->this_binding); if (ECMA_IS_VALUE_ERROR (result)) diff --git a/tests/unit-core/test-backtrace.c b/tests/unit-core/test-backtrace.c index 3797ec563..79fca6f16 100644 --- a/tests/unit-core/test-backtrace.c +++ b/tests/unit-core/test-backtrace.c @@ -95,6 +95,40 @@ backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information */ return false; } /* backtrace_callback */ +static bool +async_backtrace_callback (jerry_backtrace_frame_t *frame_p, /* frame information */ + void *user_p) /* user data */ +{ + TEST_ASSERT ((void *) handler_args_p == user_p); + TEST_ASSERT (jerry_backtrace_get_frame_type (frame_p) == JERRY_BACKTRACE_FRAME_JS); + + const jerry_backtrace_location_t *location_p = jerry_backtrace_get_location (frame_p); + const jerry_value_t *function_p = jerry_backtrace_get_function (frame_p); + + TEST_ASSERT (location_p != NULL); + TEST_ASSERT (function_p != NULL); + + compare_string (location_p->resource_name, "async_capture_test.js"); + + ++frame_index; + + if (frame_index == 1) + { + TEST_ASSERT (jerry_backtrace_is_strict (frame_p)); + TEST_ASSERT (location_p->line == 3); + TEST_ASSERT (location_p->column == 1); + TEST_ASSERT (handler_args_p[0] == *function_p); + return true; + } + + TEST_ASSERT (frame_index == 2); + TEST_ASSERT (!jerry_backtrace_is_strict (frame_p)); + TEST_ASSERT (location_p->line == 8); + TEST_ASSERT (location_p->column == 1); + TEST_ASSERT (handler_args_p[1] == *function_p); + return true; +} /* async_backtrace_callback */ + static jerry_value_t capture_handler (const jerry_call_info_t *call_info_p, /**< call information */ const jerry_value_t args_p[], /**< argument list */ @@ -104,12 +138,14 @@ capture_handler (const jerry_call_info_t *call_info_p, /**< call information */ JERRY_UNUSED (args_p); JERRY_UNUSED (args_count); - TEST_ASSERT (args_count == 3); + TEST_ASSERT (args_count == 2 || args_count == 3); + TEST_ASSERT (frame_index == 0); - frame_index = 0; handler_args_p = args_p; - jerry_backtrace_capture (backtrace_callback, (void *) args_p); - TEST_ASSERT (frame_index == 3); + + jerry_backtrace_capture (args_count == 3 ? backtrace_callback : async_backtrace_callback, (void *) args_p); + + TEST_ASSERT (frame_index == (int) args_count); return jerry_create_undefined (); } /* capture_handler */ @@ -186,21 +222,21 @@ test_get_backtrace_api_call (void) register_callback (backtrace_handler, "backtrace"); register_callback (capture_handler, "capture"); - const char *source = ("function f() {\n" - " return backtrace(0);\n" - "}\n" - "\n" - "function g() {\n" - " return f();\n" - "}\n" - "\n" - "function h() {\n" - " return g();\n" - "}\n" - "\n" - "h();\n"); + const char *source_p = ("function f() {\n" + " return backtrace(0);\n" + "}\n" + "\n" + "function g() {\n" + " return f();\n" + "}\n" + "\n" + "function h() {\n" + " return g();\n" + "}\n" + "\n" + "h();\n"); - jerry_value_t backtrace = run ("something.js", source); + jerry_value_t backtrace = run ("something.js", source_p); TEST_ASSERT (!jerry_value_is_error (backtrace) && jerry_value_is_array (backtrace)); @@ -216,21 +252,21 @@ test_get_backtrace_api_call (void) /* Depth set to 2 this time. */ - source = ("function f() {\n" - " return backtrace(2);\n" - "}\n" - "\n" - "function g() {\n" - " return f();\n" - "}\n" - "\n" - "function h() {\n" - " return g();\n" - "}\n" - "\n" - "h();\n"); + source_p = ("function f() {\n" + " return backtrace(2);\n" + "}\n" + "\n" + "function g() {\n" + " return f();\n" + "}\n" + "\n" + "function h() {\n" + " return g();\n" + "}\n" + "\n" + "h();\n"); - backtrace = run ("something_else.js", source); + backtrace = run ("something_else.js", source_p); TEST_ASSERT (!jerry_value_is_error (backtrace) && jerry_value_is_array (backtrace)); @@ -244,25 +280,68 @@ test_get_backtrace_api_call (void) /* Test frame capturing. */ - source = ("function f() {\n" - " return capture(f, g, h);\n" - "}\n" - "\n" - "function g() {\n" - " 'use strict';\n" - " return f();\n" - "}\n" - "\n" - "function h() {\n" - " return g();\n" - "}\n" - "\n" - "h();\n"); + frame_index = 0; + source_p = ("function f() {\n" + " return capture(f, g, h);\n" + "}\n" + "\n" + "function g() {\n" + " 'use strict';\n" + " return f();\n" + "}\n" + "\n" + "function h() {\n" + " return g();\n" + "}\n" + "\n" + "h();\n"); - backtrace = run ("capture_test.js", source); + jerry_value_t result = run ("capture_test.js", source_p); - TEST_ASSERT (jerry_value_is_undefined (backtrace)); - jerry_release_value (backtrace); + TEST_ASSERT (jerry_value_is_undefined (result)); + jerry_release_value (result); + + TEST_ASSERT (frame_index == 3); + + /* Test async frame capturing. */ + source_p = "async function f() {}"; + result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), JERRY_PARSE_NO_OPTS); + + if (!jerry_value_is_error (result)) + { + jerry_release_value (result); + + frame_index = 0; + source_p = ("function f() {\n" + " 'use strict';\n" + " return capture(f, g);\n" + "}\n" + "\n" + "async function g() {\n" + " await 0;\n" + " return f();\n" + "}\n" + "\n" + "g();\n"); + + result = run ("async_capture_test.js", source_p); + + TEST_ASSERT (jerry_value_is_promise (result)); + jerry_release_value (result); + + TEST_ASSERT (frame_index == 0); + + result = jerry_run_all_enqueued_jobs (); + TEST_ASSERT (!jerry_value_is_error (result)); + + TEST_ASSERT (frame_index == 2); + } + else + { + TEST_ASSERT (jerry_get_error_type (result) == JERRY_ERROR_SYNTAX); + } + + jerry_release_value (result); jerry_cleanup (); } /* test_get_backtrace_api_call */