diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 87f59bb0f..1d18b611a 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -1457,13 +1457,54 @@ jerry_value_has_error_flag (const jerry_value_t value); **See also** - [jerry_value_t](#jerry_value_t) +- [jerry_value_has_abort_flag](#jerry_value_has_abort_flag) + + +## jerry_value_has_abort_flag + +**Summary** + +Returns whether the given `jerry_value_t` has the error and abort flags set. + +**Prototype** + +```c +bool +jerry_value_has_abort_flag (const jerry_value_t value); +``` + +- `value` - api value +- return value + - true, if the given `jerry_value_t` has the error and abort flags set + - false, otherwise + +**Example** + +```c +{ + jerry_value_t value; + ... // create or acquire value + + if (jerry_value_has_abort_flag (value)) + { + ... + } + + jerry_release_value (value); +} +``` + +**See also** + +- [jerry_value_t](#jerry_value_t) +- [jerry_value_has_error_flag](#jerry_value_has_error_flag) ## jerry_value_clear_error_flag **Summary** -Clear the error flag. +Clear both the error and abort flags. **Prototype** @@ -1490,6 +1531,8 @@ jerry_value_clear_error_flag (jerry_value_t *value_p); **See also** - [jerry_value_t](#jerry_value_t) +- [jerry_value_set_error_flag](#jerry_value_set_error_flag) +- [jerry_value_set_abort_flag](#jerry_value_set_abort_flag) ## jerry_value_set_error_flag @@ -1523,6 +1566,43 @@ jerry_value_set_error_flag (jerry_value_t *value_p); **See also** - [jerry_value_t](#jerry_value_t) +- [jerry_value_clear_error_flag](#jerry_value_clear_error_flag) +- [jerry_value_set_abort_flag](#jerry_value_set_abort_flag) + + +## jerry_value_set_abort_flag + +**Summary** + +Set both the error and abort flags. + +**Prototype** + +```c +void +jerry_value_set_abort_flag (jerry_value_t *value_p); +``` + +- `value_p` - pointer to an api value + +**Example** + +```c +{ + jerry_value_t value; + ... // create or acquire value + + jerry_value_set_abort_flag (&value); + + jerry_release_value (value); +} +``` + +**See also** + +- [jerry_value_t](#jerry_value_t) +- [jerry_value_clear_error_flag](#jerry_value_clear_error_flag) +- [jerry_value_set_error_flag](#jerry_value_set_error_flag) ## jerry_get_value_without_error_flag diff --git a/jerry-core/api/jerry-snapshot.c b/jerry-core/api/jerry-snapshot.c index e67dc7347..03243e539 100644 --- a/jerry-core/api/jerry-snapshot.c +++ b/jerry-core/api/jerry-snapshot.c @@ -730,7 +730,7 @@ jerry_snapshot_result_at (const uint32_t *snapshot_p, /**< snapshot */ if (ECMA_IS_VALUE_ERROR (ret_val)) { - return ecma_create_error_reference (JERRY_CONTEXT (error_value)); + return ecma_create_error_reference_from_context (); } return ret_val; diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 16a2f47ec..a2ba098c2 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -82,10 +82,10 @@ static const char * const wrong_args_msg_p = "wrong type of argument"; * Assert that it is correct to call API in current state. * * Note: - * By convention, there can be some states when API could not be invoked. + * By convention, there are some states when API could not be invoked. * - * While, API can be invoked jerry_api_available flag is set, - * and while it is incorrect to invoke API - it is not set. + * The API can be and only be invoked when the ECMA_STATUS_API_AVAILABLE + * flag is set. * * This procedure checks whether the API is available, and terminates * the engine if it is unavailable. Otherwise it is a no-op. @@ -98,7 +98,7 @@ static const char * const wrong_args_msg_p = "wrong type of argument"; static inline void __attr_always_inline___ jerry_assert_api_available (void) { - if (unlikely (!JERRY_CONTEXT (jerry_api_available))) + if (unlikely (!(JERRY_CONTEXT (status_flags) & ECMA_STATUS_API_AVAILABLE))) { /* Terminates the execution. */ JERRY_UNREACHABLE (); @@ -111,7 +111,7 @@ jerry_assert_api_available (void) static inline void __attr_always_inline___ jerry_make_api_available (void) { - JERRY_CONTEXT (jerry_api_available) = true; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_API_AVAILABLE; } /* jerry_make_api_available */ /** @@ -120,7 +120,7 @@ jerry_make_api_available (void) static inline void __attr_always_inline___ jerry_make_api_unavailable (void) { - JERRY_CONTEXT (jerry_api_available) = false; + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_API_AVAILABLE; } /* jerry_make_api_unavailable */ /** @@ -152,7 +152,7 @@ jerry_return (jerry_value_t value) /**< return value */ { if (ECMA_IS_VALUE_ERROR (value)) { - value = ecma_create_error_reference (JERRY_CONTEXT (error_value)); + value = ecma_create_error_reference_from_context (); } return value; @@ -167,7 +167,7 @@ static inline jerry_value_t __attr_always_inline___ jerry_throw (jerry_value_t value) /**< return value */ { JERRY_ASSERT (ECMA_IS_VALUE_ERROR (value)); - return ecma_create_error_reference (JERRY_CONTEXT (error_value)); + return ecma_create_error_reference_from_context (); } /* jerry_throw */ /** @@ -176,7 +176,7 @@ jerry_throw (jerry_value_t value) /**< return value */ void jerry_init (jerry_init_flag_t flags) /**< combination of Jerry flags */ { - if (unlikely (JERRY_CONTEXT (jerry_api_available))) + if (unlikely (JERRY_CONTEXT (status_flags) & ECMA_STATUS_API_AVAILABLE)) { /* This function cannot be called twice unless jerry_cleanup is called. */ JERRY_UNREACHABLE (); @@ -391,7 +391,7 @@ jerry_parse (const jerry_char_t *source_p, /**< script source */ if (ECMA_IS_VALUE_ERROR (parse_status)) { - return ecma_create_error_reference (JERRY_CONTEXT (error_value)); + return ecma_create_error_reference_from_context (); } ecma_free_value (parse_status); @@ -493,7 +493,7 @@ jerry_parse_function (const jerry_char_t *resource_name_p, /**< resource name (u if (ECMA_IS_VALUE_ERROR (parse_status)) { - return ecma_create_error_reference (JERRY_CONTEXT (error_value)); + return ecma_create_error_reference_from_context (); } ecma_free_value (parse_status); @@ -809,7 +809,7 @@ bool jerry_is_feature_enabled (const jerry_feature_t feature) } /* jerry_is_feature_enabled */ /** - * Check if the specified value is an error value. + * Check if the specified value is an error or abort value. * * @return true - if the error flag of the specified value is true, * false - otherwise @@ -822,6 +822,27 @@ jerry_value_has_error_flag (const jerry_value_t value) /**< api value */ return ecma_is_value_error_reference (value); } /* jerry_value_has_error_flag */ +/** + * Check if the specified value is an abort value. + * + * @return true - if both the error and abort flags of the specified value are true, + * false - otherwise + */ +bool +jerry_value_has_abort_flag (const jerry_value_t value) /**< api value */ +{ + jerry_assert_api_available (); + + if (!ecma_is_value_error_reference (value)) + { + return false; + } + + ecma_error_reference_t *error_ref_p = ecma_get_error_reference_from_value (value); + + return (error_ref_p->refs_and_flags & ECMA_ERROR_REF_ABORT) != 0; +} /* jerry_value_has_abort_flag */ + /** * Clear the error flag */ @@ -832,24 +853,56 @@ jerry_value_clear_error_flag (jerry_value_t *value_p) if (ecma_is_value_error_reference (*value_p)) { - *value_p = ecma_clear_error_reference (*value_p); + *value_p = ecma_clear_error_reference (*value_p, false); } } /* jerry_value_clear_error_flag */ /** - * Set the error flag. + * Set the error flag if the value is not an error reference. */ void jerry_value_set_error_flag (jerry_value_t *value_p) { jerry_assert_api_available (); - if (!ecma_is_value_error_reference (*value_p)) + if (unlikely (ecma_is_value_error_reference (*value_p))) { - *value_p = ecma_create_error_reference (*value_p); + /* This is a rare case so it is optimized for + * binary size rather than performance. */ + if (!jerry_value_has_abort_flag (*value_p)) + { + return; + } + + jerry_value_clear_error_flag (value_p); } + + *value_p = ecma_create_error_reference (*value_p, true); } /* jerry_value_set_error_flag */ +/** + * Set both the abort and error flags if the value is not an error reference. + */ +void +jerry_value_set_abort_flag (jerry_value_t *value_p) +{ + jerry_assert_api_available (); + + if (unlikely (ecma_is_value_error_reference (*value_p))) + { + /* This is a rare case so it is optimized for + * binary size rather than performance. */ + if (jerry_value_has_abort_flag (*value_p)) + { + return; + } + + jerry_value_clear_error_flag (value_p); + } + + *value_p = ecma_create_error_reference (*value_p, false); +} /* jerry_value_set_abort_flag */ + /** * If the input value is an error value, then return a new reference to its referenced value. * Otherwise, return a new reference to the value itself. diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 3353bea06..137a64291 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -902,7 +902,7 @@ ecma_free_unused_memory (jmem_free_unused_memory_severity_t severity) /**< sever { --JERRY_CONTEXT (ecma_prop_hashmap_alloc_state); } - JERRY_CONTEXT (ecma_prop_hashmap_alloc_last_is_hs_gc) = false; + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_SEV_GC; #endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ /* @@ -921,14 +921,14 @@ ecma_free_unused_memory (jmem_free_unused_memory_severity_t severity) /**< sever JERRY_ASSERT (severity == JMEM_FREE_UNUSED_MEMORY_SEVERITY_HIGH); #ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE - if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_last_is_hs_gc)) + if (JERRY_CONTEXT (status_flags) & ECMA_STATUS_HIGH_SEV_GC) { JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) = ECMA_PROP_HASHMAP_ALLOC_MAX; } else if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) < ECMA_PROP_HASHMAP_ALLOC_MAX) { ++JERRY_CONTEXT (ecma_prop_hashmap_alloc_state); - JERRY_CONTEXT (ecma_prop_hashmap_alloc_last_is_hs_gc) = true; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_HIGH_SEV_GC; } #endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 686c429c2..43ef3fcb8 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -64,6 +64,19 @@ typedef enum ECMA_INIT_MEM_STATS = (1u << 2), /**< dump memory statistics */ } ecma_init_flag_t; +/** + * JerryScript status flags. + */ +typedef enum +{ + ECMA_STATUS_API_AVAILABLE = (1u << 0), /**< api available */ + ECMA_STATUS_DIRECT_EVAL = (1u << 1), /**< eval is called directly */ +#ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE + ECMA_STATUS_HIGH_SEV_GC = (1u << 2), /**< last gc run was a high severity run */ +#endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ + ECMA_STATUS_EXCEPTION = (1u << 3), /**< last exception is a normal exception */ +} ecma_status_flag_t; + /** * Type of ecma value */ @@ -1161,12 +1174,27 @@ typedef struct lit_utf8_size_t long_utf8_string_length; /**< length of this long utf-8 string in bytes */ } ecma_long_string_t; +/** + * Abort flag for error reference. + */ +#define ECMA_ERROR_REF_ABORT 0x1 + +/** + * Value for increasing or decreasing the reference counter. + */ +#define ECMA_ERROR_REF_ONE (1u << 1) + +/** + * Maximum value of the reference counter. + */ +#define ECMA_ERROR_MAX_REF (UINT32_MAX - 1u) + /** * Representation of a thrown value on API level. */ typedef struct { - uint32_t refs; /**< reference counter */ + uint32_t refs_and_flags; /**< reference counter */ ecma_value_t value; /**< referenced value */ } ecma_error_reference_t; diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index ee711059a..0e1148c7a 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -19,6 +19,7 @@ #include "ecma-helpers.h" #include "ecma-lcache.h" #include "ecma-property-hashmap.h" +#include "jcontext.h" #include "jrt-bit-fields.h" #include "byte-code.h" #include "re-compiler.h" @@ -26,7 +27,6 @@ #ifdef JERRY_DEBUGGER #include "debugger.h" -#include "jcontext.h" #endif /* JERRY_DEBUGGER */ /** \addtogroup ecma ECMA @@ -1335,15 +1335,28 @@ JERRY_STATIC_ASSERT (sizeof (ecma_error_reference_t) == 8, * @return error reference value */ ecma_value_t -ecma_create_error_reference (ecma_value_t value) /**< referenced value */ +ecma_create_error_reference (ecma_value_t value, /**< referenced value */ + bool is_exception) /**< error reference is an exception */ { ecma_error_reference_t *error_ref_p = (ecma_error_reference_t *) jmem_pools_alloc (sizeof (ecma_error_reference_t)); - error_ref_p->refs = 1; + error_ref_p->refs_and_flags = ECMA_ERROR_REF_ONE | (is_exception ? 0 : ECMA_ERROR_REF_ABORT); error_ref_p->value = value; return ecma_make_error_reference_value (error_ref_p); } /* ecma_create_error_reference */ +/** + * Create an error reference from the currently thrown error value. + * + * @return error reference value + */ +ecma_value_t +ecma_create_error_reference_from_context (void) +{ + return ecma_create_error_reference (JERRY_CONTEXT (error_value), + (JERRY_CONTEXT (status_flags) & ECMA_STATUS_EXCEPTION) != 0); +} /* ecma_create_error_reference_from_context */ + /** * Create an error reference from a given object. * @@ -1355,7 +1368,7 @@ ecma_create_error_reference (ecma_value_t value) /**< referenced value */ inline ecma_value_t __attr_always_inline___ ecma_create_error_object_reference (ecma_object_t *object_p) /**< referenced object */ { - return ecma_create_error_reference (ecma_make_object_value (object_p)); + return ecma_create_error_reference (ecma_make_object_value (object_p), true); } /* ecma_create_error_object_reference */ /** @@ -1364,9 +1377,9 @@ ecma_create_error_object_reference (ecma_object_t *object_p) /**< referenced obj void ecma_ref_error_reference (ecma_error_reference_t *error_ref_p) /**< error reference */ { - if (likely (error_ref_p->refs < UINT32_MAX)) + if (likely (error_ref_p->refs_and_flags < ECMA_ERROR_MAX_REF)) { - error_ref_p->refs++; + error_ref_p->refs_and_flags += ECMA_ERROR_REF_ONE; } else { @@ -1380,11 +1393,11 @@ ecma_ref_error_reference (ecma_error_reference_t *error_ref_p) /**< error refere void ecma_deref_error_reference (ecma_error_reference_t *error_ref_p) /**< error reference */ { - JERRY_ASSERT (error_ref_p->refs > 0); + JERRY_ASSERT (error_ref_p->refs_and_flags >= ECMA_ERROR_REF_ONE); - error_ref_p->refs--; + error_ref_p->refs_and_flags -= ECMA_ERROR_REF_ONE; - if (error_ref_p->refs == 0) + if (error_ref_p->refs_and_flags < ECMA_ERROR_REF_ONE) { ecma_free_value (error_ref_p->value); jmem_pools_free (error_ref_p, sizeof (ecma_error_reference_t)); @@ -1397,15 +1410,28 @@ ecma_deref_error_reference (ecma_error_reference_t *error_ref_p) /**< error refe * @return value referenced by the error */ ecma_value_t -ecma_clear_error_reference (ecma_value_t value) +ecma_clear_error_reference (ecma_value_t value, /**< error reference */ + bool set_abort_flag) /**< set abort flag */ { ecma_error_reference_t *error_ref_p = ecma_get_error_reference_from_value (value); - JERRY_ASSERT (error_ref_p->refs > 0); - - if (error_ref_p->refs > 1) + if (set_abort_flag) { - error_ref_p->refs--; + if (error_ref_p->refs_and_flags & ECMA_ERROR_REF_ABORT) + { + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; + } + else + { + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + } + } + + JERRY_ASSERT (error_ref_p->refs_and_flags >= ECMA_ERROR_REF_ONE); + + if (error_ref_p->refs_and_flags >= 2 * ECMA_ERROR_REF_ONE) + { + error_ref_p->refs_and_flags -= ECMA_ERROR_REF_ONE; return ecma_copy_value (error_ref_p->value); } diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 5ce635073..7c888f226 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -352,11 +352,12 @@ void ecma_set_property_lcached (ecma_property_t *property_p, bool is_lcached); ecma_property_descriptor_t ecma_make_empty_property_descriptor (void); void ecma_free_property_descriptor (ecma_property_descriptor_t *prop_desc_p); -ecma_value_t ecma_create_error_reference (ecma_value_t value); +ecma_value_t ecma_create_error_reference (ecma_value_t value, bool is_exception); +ecma_value_t ecma_create_error_reference_from_context (void); ecma_value_t ecma_create_error_object_reference (ecma_object_t *object_p); void ecma_ref_error_reference (ecma_error_reference_t *error_ref_p); void ecma_deref_error_reference (ecma_error_reference_t *error_ref_p); -ecma_value_t ecma_clear_error_reference (ecma_value_t value); +ecma_value_t ecma_clear_error_reference (ecma_value_t value, bool set_abort_flag); void ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p); void ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p); diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index 95b08d49b..d9bf0bed2 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -43,7 +43,7 @@ ecma_init (void) #ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) = ECMA_PROP_HASHMAP_ALLOC_ON; - JERRY_CONTEXT (ecma_prop_hashmap_alloc_last_is_hs_gc) = false; + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_SEV_GC; #endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ #ifndef CONFIG_DISABLE_ES2015_PROMISE_BUILTIN diff --git a/jerry-core/ecma/operations/ecma-exceptions.c b/jerry-core/ecma/operations/ecma-exceptions.c index aecd9afa0..49cf0fa38 100644 --- a/jerry-core/ecma/operations/ecma-exceptions.c +++ b/jerry-core/ecma/operations/ecma-exceptions.c @@ -157,6 +157,7 @@ ecma_raise_standard_error (ecma_standard_error_t error_type, /**< error type */ } JERRY_CONTEXT (error_value) = ecma_make_object_value (error_obj_p); + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; return ECMA_VALUE_ERROR; } /* ecma_raise_standard_error */ @@ -243,6 +244,7 @@ ecma_raise_standard_error_with_format (ecma_standard_error_t error_type, /**< er ecma_deref_ecma_string (error_msg_p); JERRY_CONTEXT (error_value) = ecma_make_object_value (error_obj_p); + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; return ECMA_VALUE_ERROR; } /* ecma_raise_standard_error_with_format */ diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index 407ba4877..d073c0911 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -470,14 +470,14 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ if (unlikely (ecma_is_value_error_reference (ret_value))) { - JERRY_CONTEXT (error_value) = ecma_clear_error_reference (ret_value); + JERRY_CONTEXT (error_value) = ecma_clear_error_reference (ret_value, true); ret_value = ECMA_VALUE_ERROR; } } else { JERRY_ASSERT (ecma_get_object_type (func_obj_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); - JERRY_CONTEXT (is_direct_eval_form_call) = false; + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL; /* 2-3. */ ecma_extended_object_t *ext_function_p = (ecma_extended_object_t *) func_obj_p; diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 055db6a3f..4ee692cbb 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -293,8 +293,10 @@ bool jerry_is_feature_enabled (const jerry_feature_t feature); * Error flag manipulation functions. */ bool jerry_value_has_error_flag (const jerry_value_t value); +bool jerry_value_has_abort_flag (const jerry_value_t value); void jerry_value_clear_error_flag (jerry_value_t *value_p); void jerry_value_set_error_flag (jerry_value_t *value_p); +void jerry_value_set_abort_flag (jerry_value_t *value_p); jerry_value_t jerry_get_value_without_error_flag (jerry_value_t value); /** diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 01d611b23..148af8f0c 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -84,13 +84,11 @@ typedef struct ecma_value_t error_value; /**< currently thrown error value */ uint32_t lit_magic_string_ex_count; /**< external magic strings count */ uint32_t jerry_init_flags; /**< run-time configuration flags */ - uint8_t is_direct_eval_form_call; /**< direct call from eval */ - uint8_t jerry_api_available; /**< API availability flag */ + uint32_t status_flags; /**< run-time flags */ #ifndef CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE uint8_t ecma_prop_hashmap_alloc_state; /**< property hashmap allocation state: 0-4, * if !0 property hashmap allocation is disabled */ - bool ecma_prop_hashmap_alloc_last_is_hs_gc; /**< true, if and only if the last gc action was a high severity gc */ #endif /* !CONFIG_ECMA_PROPERTY_HASHMAP_DISABLE */ #ifndef CONFIG_DISABLE_REGEXP_BUILTIN diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index fcd5908b1..d8e48897b 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -2744,6 +2744,7 @@ parser_parse_script (const uint8_t *arg_list_p, /**< function argument list */ /* It is unlikely that memory can be allocated in an out-of-memory * situation. However, a simple value can still be thrown. */ JERRY_CONTEXT (error_value) = ECMA_VALUE_NULL; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; return ECMA_VALUE_ERROR; } #ifdef JERRY_ENABLE_ERROR_MESSAGES diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index c99805d15..b9dfc8012 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -428,7 +428,7 @@ opfunc_call (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ arguments_list_len); } - JERRY_CONTEXT (is_direct_eval_form_call) = false; + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL; /* Free registers. */ for (uint32_t i = 0; i < arguments_list_len; i++) @@ -888,8 +888,10 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } else { - JERRY_CONTEXT (error_value) = ecma_clear_error_reference (result); + JERRY_CONTEXT (error_value) = ecma_clear_error_reference (result, false); } + + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; result = ECMA_VALUE_ERROR; goto error; } @@ -1405,6 +1407,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_OC_THROW: { JERRY_CONTEXT (error_value) = left_value; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + result = ECMA_VALUE_ERROR; left_value = ECMA_VALUE_UNDEFINED; goto error; @@ -1416,7 +1420,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_EVAL: { - JERRY_CONTEXT (is_direct_eval_form_call) = true; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_DIRECT_EVAL; JERRY_ASSERT (*byte_code_p >= CBC_CALL && *byte_code_p <= CBC_CALL2_PROP_BLOCK); continue; } @@ -2449,6 +2453,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ case VM_CONTEXT_FINALLY_THROW: { JERRY_CONTEXT (error_value) = stack_top_p[-2]; + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); @@ -2714,10 +2719,6 @@ error: if (!ECMA_IS_VALUE_ERROR (result)) { - JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); - - stack_top_p = frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth; - if (vm_stack_find_finally (frame_ctx_p, &stack_top_p, VM_CONTEXT_FINALLY_RETURN, @@ -2731,7 +2732,7 @@ error: continue; } } - else + else if (JERRY_CONTEXT (status_flags) & ECMA_STATUS_EXCEPTION) { if (vm_stack_find_finally (frame_ctx_p, &stack_top_p, @@ -2774,40 +2775,19 @@ error: stack_top_p[-2] = JERRY_CONTEXT (error_value); } -#ifdef JERRY_VM_EXEC_STOP - if (JERRY_CONTEXT (vm_exec_stop_cb) != NULL - && --JERRY_CONTEXT (vm_exec_stop_counter) == 0) - { - result = JERRY_CONTEXT (vm_exec_stop_cb) (JERRY_CONTEXT (vm_exec_stop_user_p)); - - if (ecma_is_value_undefined (result)) - { - JERRY_CONTEXT (vm_exec_stop_counter) = JERRY_CONTEXT (vm_exec_stop_frequency); - } - else - { - JERRY_CONTEXT (vm_exec_stop_counter) = 1; - - left_value = ECMA_VALUE_UNDEFINED; - right_value = ECMA_VALUE_UNDEFINED; - - if (!ecma_is_value_error_reference (result)) - { - JERRY_CONTEXT (error_value) = result; - } - else - { - JERRY_CONTEXT (error_value) = ecma_clear_error_reference (result); - } - result = ECMA_VALUE_ERROR; - goto error; - } - } -#endif /* JERRY_VM_EXEC_STOP */ - continue; } } + else + { + do + { + JERRY_ASSERT (frame_ctx_p->registers_p + register_end + frame_ctx_p->context_depth == stack_top_p); + + stack_top_p = vm_stack_context_abort (frame_ctx_p, stack_top_p); + } + while (frame_ctx_p->context_depth > 0); + } ecma_free_value (block_result); return result; @@ -2871,7 +2851,7 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ } } - JERRY_CONTEXT (is_direct_eval_form_call) = false; + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_DIRECT_EVAL; JERRY_CONTEXT (vm_top_context_p) = frame_ctx_p; @@ -3000,7 +2980,7 @@ vm_is_strict_mode (void) inline bool __attr_always_inline___ vm_is_direct_eval_form_call (void) { - return JERRY_CONTEXT (is_direct_eval_form_call); + return (JERRY_CONTEXT (status_flags) & ECMA_STATUS_DIRECT_EVAL) != 0; } /* vm_is_direct_eval_form_call */ /** diff --git a/tests/unit-core/test-abort.c b/tests/unit-core/test-abort.c new file mode 100644 index 000000000..9a716ab95 --- /dev/null +++ b/tests/unit-core/test-abort.c @@ -0,0 +1,129 @@ +/* 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 "config.h" +#include "jerryscript.h" + +#include "test-common.h" + +static jerry_value_t +callback_func (const jerry_value_t function_obj, + const jerry_value_t this_val, + const jerry_value_t args_p[], + const jerry_length_t args_count) +{ + JERRY_UNUSED (function_obj); + JERRY_UNUSED (this_val); + JERRY_UNUSED (args_p); + JERRY_UNUSED (args_count); + + jerry_value_t value = jerry_create_string ((jerry_char_t *) "Abort run!"); + jerry_value_set_abort_flag (&value); + return value; +} /* callback_func */ + +int +main (void) +{ + TEST_INIT (); + + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t global = jerry_get_global_object (); + jerry_value_t callback_name = jerry_create_string ((jerry_char_t *) "callback"); + jerry_value_t func = jerry_create_external_function (callback_func); + jerry_value_t res = jerry_set_property (global, callback_name, func); + TEST_ASSERT (!jerry_value_has_error_flag (res)); + + jerry_release_value (res); + jerry_release_value (func); + jerry_release_value (callback_name); + jerry_release_value (global); + + const char *inf_loop_code_src_p = ("while(true) {\n" + " with ({}) {\n" + " try {\n" + " callback();\n" + " } catch (e) {\n" + " } finally {\n" + " }\n" + " }\n" + "}"); + + jerry_value_t parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, + strlen (inf_loop_code_src_p), + false); + + TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val)); + res = jerry_run (parsed_code_val); + + TEST_ASSERT (jerry_value_has_abort_flag (res)); + + jerry_release_value (res); + jerry_release_value (parsed_code_val); + + inf_loop_code_src_p = ("function f() {" + " while(true) {\n" + " with ({}) {\n" + " try {\n" + " callback();\n" + " } catch (e) {\n" + " } finally {\n" + " }\n" + " }\n" + " }" + "}\n" + "function g() {\n" + " for (a in { x:5 })\n" + " f();\n" + "}\n" + "\n" + "with({})\n" + " f();\n"); + + parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, + strlen (inf_loop_code_src_p), + false); + + TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val)); + res = jerry_run (parsed_code_val); + + TEST_ASSERT (jerry_value_has_abort_flag (res)); + + jerry_release_value (res); + jerry_release_value (parsed_code_val); + + /* Test flag overwrites. */ + jerry_value_t value = jerry_create_string ((jerry_char_t *) "Error description"); + TEST_ASSERT (!jerry_value_has_abort_flag (value)); + TEST_ASSERT (!jerry_value_has_error_flag (value)); + + jerry_value_set_abort_flag (&value); + TEST_ASSERT (jerry_value_has_abort_flag (value)); + TEST_ASSERT (jerry_value_has_error_flag (value)); + + jerry_value_set_error_flag (&value); + TEST_ASSERT (!jerry_value_has_abort_flag (value)); + TEST_ASSERT (jerry_value_has_error_flag (value)); + + jerry_value_set_abort_flag (&value); + TEST_ASSERT (jerry_value_has_abort_flag (value)); + TEST_ASSERT (jerry_value_has_error_flag (value)); + + jerry_release_value (value); + + jerry_cleanup (); + return 0; +} /* main */ diff --git a/tests/unit-core/test-api.c b/tests/unit-core/test-api.c index c9af2977a..948d19876 100644 --- a/tests/unit-core/test-api.c +++ b/tests/unit-core/test-api.c @@ -166,21 +166,6 @@ handler_construct (const jerry_value_t func_obj_val, /**< function object */ return jerry_create_boolean (true); } /* handler_construct */ -static jerry_value_t -vm_exec_stop_callback (void *user_p) -{ - int *int_p = (int *) user_p; - - if (*int_p > 0) - { - (*int_p)--; - - return jerry_create_undefined (); - } - - return jerry_create_string ((const jerry_char_t *) "Abort script"); -} /* vm_exec_stop_callback */ - /** * Extended Magic Strings */ @@ -1100,50 +1085,6 @@ main (void) jerry_cleanup (); } - /* Test stopping an infinite loop. */ - if (jerry_is_feature_enabled (JERRY_FEATURE_VM_EXEC_STOP)) - { - jerry_init (JERRY_INIT_EMPTY); - - int countdown = 6; - jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16); - - const char *inf_loop_code_src_p = "while(true) {}"; - parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, strlen (inf_loop_code_src_p), false); - TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val)); - res = jerry_run (parsed_code_val); - TEST_ASSERT (countdown == 0); - - TEST_ASSERT (jerry_value_has_error_flag (res)); - - jerry_release_value (res); - jerry_release_value (parsed_code_val); - - /* A more complex example. Although the callback error is captured - * by the catch block, it is automatically thrown again. */ - - /* We keep the callback function, only the countdown is reset. */ - countdown = 6; - - inf_loop_code_src_p = ("function f() { while (true) ; }\n" - "try { f(); } catch(e) {}"); - - parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, strlen (inf_loop_code_src_p), false); - - TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val)); - res = jerry_run (parsed_code_val); - TEST_ASSERT (countdown == 0); - - /* The result must have an error flag which proves that - * the error is thrown again inside the catch block. */ - TEST_ASSERT (jerry_value_has_error_flag (res)); - - jerry_release_value (res); - jerry_release_value (parsed_code_val); - - jerry_cleanup (); - } - /* External Magic String */ jerry_init (JERRY_INIT_SHOW_OPCODES); diff --git a/tests/unit-core/test-exec-stop.c b/tests/unit-core/test-exec-stop.c new file mode 100644 index 000000000..c9ef7f211 --- /dev/null +++ b/tests/unit-core/test-exec-stop.c @@ -0,0 +1,92 @@ +/* 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 "config.h" +#include "jerryscript.h" + +#include "test-common.h" + +static jerry_value_t +vm_exec_stop_callback (void *user_p) +{ + int *int_p = (int *) user_p; + + if (*int_p > 0) + { + (*int_p)--; + + return jerry_create_undefined (); + } + + return jerry_create_string ((const jerry_char_t *) "Abort script"); +} /* vm_exec_stop_callback */ + +int +main (void) +{ + TEST_INIT (); + + /* Test stopping an infinite loop. */ + if (!jerry_is_feature_enabled (JERRY_FEATURE_VM_EXEC_STOP)) + { + return 0; + } + + jerry_init (JERRY_INIT_EMPTY); + + int countdown = 6; + jerry_set_vm_exec_stop_callback (vm_exec_stop_callback, &countdown, 16); + + const char *inf_loop_code_src_p = "while(true) {}"; + jerry_value_t parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, + strlen (inf_loop_code_src_p), + false); + + TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val)); + jerry_value_t res = jerry_run (parsed_code_val); + TEST_ASSERT (countdown == 0); + + TEST_ASSERT (jerry_value_has_error_flag (res)); + + jerry_release_value (res); + jerry_release_value (parsed_code_val); + + /* A more complex example. Although the callback error is captured + * by the catch block, it is automatically thrown again. */ + + /* We keep the callback function, only the countdown is reset. */ + countdown = 6; + + inf_loop_code_src_p = ("function f() { while (true) ; }\n" + "try { f(); } catch(e) {}"); + + parsed_code_val = jerry_parse ((jerry_char_t *) inf_loop_code_src_p, + strlen (inf_loop_code_src_p), + false); + + TEST_ASSERT (!jerry_value_has_error_flag (parsed_code_val)); + res = jerry_run (parsed_code_val); + TEST_ASSERT (countdown == 0); + + /* The result must have an error flag which proves that + * the error is thrown again inside the catch block. */ + TEST_ASSERT (jerry_value_has_error_flag (res)); + + jerry_release_value (res); + jerry_release_value (parsed_code_val); + + jerry_cleanup (); + return 0; +} /* main */