diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index aeefdcbba..013cb8724 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -447,7 +447,7 @@ typedef jerry_value_t (*jerry_external_handler_t) (const jerry_value_t function_ Native free callback of an object. It is used in `jerry_object_native_info_t` and for external Array buffers. *Note*: - - This callback method **must not** call any JerryScript API methods. + - Referred values by this method must have at least 1 reference. (Correct API usage satisfies this condition) **Prototype** @@ -456,6 +456,7 @@ typedef void (*jerry_object_native_free_callback_t) (void *native_p); ``` *New in version 2.0*: Renamed from `jerry_object_free_callback_t`. +*Changed in version 2.2*: API calls are once again allowed. (See note) **See also** @@ -6145,7 +6146,7 @@ You can get them by calling [jerry_get_object_native_pointer](#jerry_get_object_ it will be called by the garbage collector when the object is freed. - If the object is only referenced via the "global" object (or one of it's "child"), the free callback will be invoked during the execution of `jerry_cleanup`. - - The free callback **must not** invoke API functions. + - The free callback can invoke API functions. *Note*: If possible do not store API values in native pointers, rather check [jerry_set_internal_property](#jerry_set_internal_property). diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index e7dbfa153..02d5f2661 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -2956,7 +2956,7 @@ jerry_get_object_native_pointer (const jerry_value_t obj_val, /**< object to get * Note: * If a non-NULL free callback is specified in the native type info, * it will be called by the garbage collector when the object is freed. - * This callback **must not** invoke API functions. + * Referred values by this method must have at least 1 reference. (Correct API usage satisfies this condition) * The type info always overwrites the previous value, so passing * a NULL value deletes the current type info. */ diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index ff8c2cfa5..e578494bc 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -700,10 +700,6 @@ ecma_gc_free_native_pointer (ecma_property_t *property_p) /**< property */ native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value_p->value); -#ifndef JERRY_NDEBUG - JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_API_AVAILABLE; -#endif /* !JERRY_NDEBUG */ - while (native_pointer_p != NULL) { if (native_pointer_p->info_p != NULL) @@ -722,10 +718,6 @@ ecma_gc_free_native_pointer (ecma_property_t *property_p) /**< property */ native_pointer_p = next_p; } - -#ifndef JERRY_NDEBUG - JERRY_CONTEXT (status_flags) |= ECMA_STATUS_API_AVAILABLE; -#endif /* !JERRY_NDEBUG */ } /* ecma_gc_free_native_pointer */ /** @@ -1405,6 +1397,7 @@ ecma_gc_run (void) while (marked_anything_during_current_iteration); black_end_p->gc_next_cp = JMEM_CP_NULL; + JERRY_CONTEXT (ecma_gc_objects_cp) = black_list_head.gc_next_cp; /* Sweep objects that are currently unmarked. */ obj_iter_cp = white_gray_list_head.gc_next_cp; @@ -1420,8 +1413,6 @@ ecma_gc_run (void) obj_iter_cp = obj_next_cp; } - JERRY_CONTEXT (ecma_gc_objects_cp) = black_list_head.gc_next_cp; - #if ENABLED (JERRY_BUILTIN_REGEXP) /* Free RegExp bytecodes stored in cache */ re_cache_gc_run (); diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index d7c454851..3062889e6 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -29,6 +29,11 @@ * @{ */ +/** + * Maximum number of GC loops on cleanup. + */ +#define JERRY_GC_LOOP_LIMIT 100 + /** * Initialize ECMA components */ @@ -73,8 +78,17 @@ ecma_finalize (void) #endif /* ENABLED (JERRY_ES2015) */ ecma_finalize_global_lex_env (); - ecma_finalize_builtins (); - ecma_gc_run (); + uint8_t runs = 0; + do + { + ecma_finalize_builtins (); + ecma_gc_run (); + if (++runs >= JERRY_GC_LOOP_LIMIT) + { + jerry_fatal (ERR_UNTERMINATED_GC_LOOPS); + } + } + while (JERRY_CONTEXT (ecma_gc_new_objects) != 0); ecma_finalize_lit_storage (); } /* ecma_finalize */ diff --git a/jerry-core/include/jerryscript-port.h b/jerry-core/include/jerryscript-port.h index 9a9e4463e..37609fd4d 100644 --- a/jerry-core/include/jerryscript-port.h +++ b/jerry-core/include/jerryscript-port.h @@ -51,6 +51,7 @@ typedef enum ERR_OUT_OF_MEMORY = 10, ERR_REF_COUNT_LIMIT = 12, ERR_DISABLED_BYTE_CODE = 13, + ERR_UNTERMINATED_GC_LOOPS = 14, ERR_FAILED_INTERNAL_ASSERTION = 120 } jerry_fatal_code_t; diff --git a/jerry-core/jrt/jrt-fatals.c b/jerry-core/jrt/jrt-fatals.c index 8421981f1..404cc5a04 100644 --- a/jerry-core/jrt/jrt-fatals.c +++ b/jerry-core/jrt/jrt-fatals.c @@ -42,6 +42,11 @@ jerry_fatal (jerry_fatal_code_t code) /**< status code */ JERRY_ERROR_MSG ("Error: ERR_REF_COUNT_LIMIT\n"); break; } + case ERR_UNTERMINATED_GC_LOOPS: + { + JERRY_ERROR_MSG ("Error: ERR_UNTERMINATED_GC_LOOPS\n"); + break; + } case ERR_DISABLED_BYTE_CODE: { JERRY_ERROR_MSG ("Error: ERR_DISABLED_BYTE_CODE\n"); diff --git a/tests/unit-core/test-native-callback-nested.c b/tests/unit-core/test-native-callback-nested.c new file mode 100644 index 000000000..6207cef7b --- /dev/null +++ b/tests/unit-core/test-native-callback-nested.c @@ -0,0 +1,69 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * 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. + */ + + +#include "jerryscript.h" +#include "jerryscript-port.h" +#include "jerryscript-port-default.h" +#include "test-common.h" + +static void native_cb2 (void) +{ + jerry_value_t array = jerry_create_array (100); + jerry_release_value (array); +} /* native_cb2 */ + +static const jerry_object_native_info_t native_info2 = +{ + .free_cb = (jerry_object_native_free_callback_t) native_cb2 +}; + +static void native_cb (void) +{ + jerry_value_t array = jerry_create_array (100); + + jerry_set_object_native_pointer (array, NULL, &native_info2); + + jerry_release_value (array); +} /* native_cb */ + +static const jerry_object_native_info_t native_info = +{ + .free_cb = (jerry_object_native_free_callback_t) native_cb +}; + +static void * +context_alloc_fn (size_t size, void *cb_data) +{ + (void) cb_data; + return malloc (size); +} /* context_alloc_fn */ + +int +main (void) +{ + jerry_context_t *ctx_p = jerry_create_context (1024, context_alloc_fn, NULL); + jerry_port_default_set_current_context (ctx_p); + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t obj = jerry_create_object (); + + jerry_set_object_native_pointer (obj, NULL, &native_info); + jerry_release_value (obj); + + jerry_cleanup (); + free (ctx_p); + return 0; +} /* main */