From df92c86ecfe1c779c33ca1b725093d6a933ed7f9 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Tue, 8 Dec 2020 14:36:36 +0100 Subject: [PATCH] Implement realm object and support realms for built-ins and JS functions (#4354) - Type for realm objects is introduced (ecma_global_object_t) - Realm reference is added to built-in objects and ECMAScript functions - Resolving built-ins, global environments, and scopes require realm object - Unnecessary global object accesses are removed from the code Missing: external functions and static snapshot functions have no realm reference JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 34 ++ jerry-core/api/jerry-snapshot.c | 32 +- jerry-core/api/jerry-snapshot.h | 3 +- jerry-core/api/jerry.c | 42 +- jerry-core/config.h | 8 + jerry-core/ecma/base/ecma-gc.c | 120 +++++- jerry-core/ecma/base/ecma-globals.h | 22 ++ jerry-core/ecma/base/ecma-helpers.c | 2 +- jerry-core/ecma/base/ecma-init-finalize.c | 3 +- jerry-core/ecma/base/ecma-module.c | 9 +- .../ecma/builtin-objects/ecma-builtins.c | 358 +++++++++++++----- .../ecma/builtin-objects/ecma-builtins.h | 33 +- .../ecma/operations/ecma-function-object.c | 122 +++++- .../ecma/operations/ecma-function-object.h | 5 + .../ecma/operations/ecma-get-put-value.c | 4 +- jerry-core/ecma/operations/ecma-lex-env.c | 49 +-- jerry-core/ecma/operations/ecma-lex-env.h | 6 +- jerry-core/ecma/operations/ecma-objects.c | 2 +- jerry-core/ecma/operations/ecma-reference.c | 22 +- jerry-core/include/jerryscript-core.h | 2 + jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/jcontext/jcontext.h | 14 +- jerry-core/parser/js/byte-code.h | 7 + jerry-core/parser/js/js-parser.c | 14 +- jerry-core/vm/opcodes.c | 10 + jerry-core/vm/vm.c | 30 +- jerry-main/main-utils.c | 14 + tests/debugger/do_variables.expected | 4 + tests/jerry/es.next/realms1.js | 95 +++++ tests/jerry/es.next/realms2.js | 48 +++ tests/jerry/es.next/realms3.js | 198 ++++++++++ tests/jerry/regression-test-issue-2105.js | 2 +- 32 files changed, 1128 insertions(+), 188 deletions(-) create mode 100644 tests/jerry/es.next/realms1.js create mode 100644 tests/jerry/es.next/realms2.js create mode 100644 tests/jerry/es.next/realms3.js diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 018349adc..ab43cb8e3 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -141,6 +141,8 @@ Possible compile time enabled feature types: - JERRY_FEATURE_SET - Set support - JERRY_FEATURE_WEAKMAP - WeakMap support - JERRY_FEATURE_WEAKSET - WeakSet support + - JERRY_FEATURE_BIGINT - BigInt support + - JERRY_FEATURE_REALM - realm support *New in version 2.0*. @@ -5969,6 +5971,38 @@ jerry_create_undefined (void); - [jerry_release_value](#jerry_release_value) +## jerry_create_realm + +**Summary** + +Creates a `jerry_value_t` representing a new global object. + +**Prototype** + +```c +jerry_value_t +jerry_create_realm (void); +``` + +- return value - realm object value + +**Example** + +```c +{ + jerry_value_t realm_value = jerry_create_realm (); + + ... // usage of the value + + jerry_release_value (realm_value); +} +``` + +**See also** + +- [jerry_release_value](#jerry_release_value) + + # General API functions of JS objects ## jerry_has_property diff --git a/jerry-core/api/jerry-snapshot.c b/jerry-core/api/jerry-snapshot.c index 78aa33301..9d0b4f6fb 100644 --- a/jerry-core/api/jerry-snapshot.c +++ b/jerry-core/api/jerry-snapshot.c @@ -49,6 +49,9 @@ snapshot_get_global_flags (bool has_regex, /**< regex literal is present */ #if ENABLED (JERRY_ESNEXT) flags |= (has_class ? JERRY_SNAPSHOT_HAS_CLASS_LITERAL : 0); #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_REALMS) + flags |= JERRY_SNAPSHOT_HAS_REALM_VALUE; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ return flags; } /* snapshot_get_global_flags */ @@ -67,6 +70,9 @@ snapshot_check_global_flags (uint32_t global_flags) /**< global flags */ #if ENABLED (JERRY_ESNEXT) global_flags &= (uint32_t) ~JERRY_SNAPSHOT_HAS_CLASS_LITERAL; #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_REALMS) + global_flags |= JERRY_SNAPSHOT_HAS_REALM_VALUE; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ return global_flags == snapshot_get_global_flags (false, false); } /* snapshot_check_global_flags */ @@ -380,6 +386,10 @@ static_snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< c cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) buffer_p; literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); + +#if ENABLED (JERRY_BUILTIN_REALMS) + args_p->realm_value = ECMA_VALUE_UNDEFINED; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } else { @@ -388,6 +398,10 @@ static_snapshot_add_compiled_code (ecma_compiled_code_t *compiled_code_p, /**< c cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) buffer_p; literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); + +#if ENABLED (JERRY_BUILTIN_REALMS) + args_p->realm_value = ECMA_VALUE_UNDEFINED; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } for (uint32_t i = 0; i < const_literal_end; i++) @@ -585,6 +599,10 @@ snapshot_load_compiled_code (const uint8_t *base_addr_p, /**< base address of th const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); header_size = sizeof (cbc_uint16_arguments_t); + +#if ENABLED (JERRY_BUILTIN_REALMS) + args_p->realm_value = ecma_make_object_value (ecma_builtin_get_global ()); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } else { @@ -595,6 +613,10 @@ snapshot_load_compiled_code (const uint8_t *base_addr_p, /**< base address of th const_literal_end = (uint32_t) (args_p->const_literal_end - args_p->register_end); literal_end = (uint32_t) (args_p->literal_end - args_p->register_end); header_size = sizeof (cbc_uint8_arguments_t); + +#if ENABLED (JERRY_BUILTIN_REALMS) + args_p->realm_value = ecma_make_object_value (ecma_builtin_get_global ()); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } if (copy_bytecode @@ -1003,14 +1025,20 @@ jerry_snapshot_result (const uint32_t *snapshot_p, /**< snapshot */ if (as_function) { + ecma_object_t *global_object_p = ecma_builtin_get_global (); + +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_ASSERT (global_object_p == ecma_get_object_from_value (ecma_op_function_get_realm (bytecode_p))); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + #if ENABLED (JERRY_ESNEXT) if (bytecode_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) { - ecma_create_global_lexical_block (); + ecma_create_global_lexical_block (global_object_p); } #endif /* ENABLED (JERRY_ESNEXT) */ - ecma_object_t *lex_env_p = ecma_get_global_scope (); + ecma_object_t *lex_env_p = ecma_get_global_scope (global_object_p); ecma_object_t *func_obj_p = ecma_op_create_simple_function_object (lex_env_p, bytecode_p); if (!(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) diff --git a/jerry-core/api/jerry-snapshot.h b/jerry-core/api/jerry-snapshot.h index 94d09178c..e8997eecd 100644 --- a/jerry-core/api/jerry-snapshot.h +++ b/jerry-core/api/jerry-snapshot.h @@ -45,7 +45,8 @@ typedef enum { /* 8 bits are reserved for dynamic features */ JERRY_SNAPSHOT_HAS_REGEX_LITERAL = (1u << 0), /**< byte code has regex literal */ - JERRY_SNAPSHOT_HAS_CLASS_LITERAL = (1u << 1), /**< byte code has class literal */ + JERRY_SNAPSHOT_HAS_REALM_VALUE = (1u << 1), /**< byte code has realm value */ + JERRY_SNAPSHOT_HAS_CLASS_LITERAL = (1u << 2), /**< byte code has class literal */ /* 24 bits are reserved for compile time features */ JERRY_SNAPSHOT_FOUR_BYTE_CPOINTER = (1u << 8) /**< deprecated, an unused placeholder now */ } jerry_snapshot_global_flags_t; diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index ece2dae80..a4b3a3cb9 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -468,7 +468,13 @@ jerry_parse (const jerry_char_t *resource_name_p, /**< resource name (usually a return ecma_create_error_reference_from_context (); } - ecma_object_t *lex_env_p = ecma_get_global_environment (); + ecma_object_t *global_object_p = ecma_builtin_get_global (); + +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_ASSERT (global_object_p == ecma_get_object_from_value (ecma_op_function_get_realm (bytecode_data_p))); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + ecma_object_t *lex_env_p = ecma_get_global_environment (global_object_p); ecma_object_t *func_obj_p = ecma_op_create_simple_function_object (lex_env_p, bytecode_data_p); ecma_bytecode_deref (bytecode_data_p); @@ -541,7 +547,13 @@ jerry_parse_function (const jerry_char_t *resource_name_p, /**< resource name (u return ecma_create_error_reference_from_context (); } - ecma_object_t *lex_env_p = ecma_get_global_environment (); + ecma_object_t *global_object_p = ecma_builtin_get_global (); + +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_ASSERT (global_object_p == ecma_get_object_from_value (ecma_op_function_get_realm (bytecode_p))); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + ecma_object_t *lex_env_p = ecma_get_global_environment (global_object_p); ecma_object_t *func_obj_p = ecma_op_create_simple_function_object (lex_env_p, bytecode_p); ecma_bytecode_deref (bytecode_p); @@ -586,10 +598,9 @@ jerry_run (const jerry_value_t func_val) /**< function to run */ ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) func_obj_p; - ecma_object_t *scope_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, - ext_func_p->u.function.scope_cp); + const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); - if (scope_p != ecma_get_global_environment ()) + if (CBC_FUNCTION_GET_TYPE (bytecode_data_p->status_flags) != CBC_FUNCTION_SCRIPT) { return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (wrong_args_msg_p))); } @@ -1341,6 +1352,9 @@ jerry_is_feature_enabled (const jerry_feature_t feature) /**< feature to check * #if ENABLED (JERRY_BUILTIN_BIGINT) || feature == JERRY_FEATURE_BIGINT #endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ +#if ENABLED (JERRY_BUILTIN_REALMS) + || feature == JERRY_FEATURE_REALM +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ ); } /* jerry_is_feature_enabled */ @@ -2299,6 +2313,24 @@ jerry_create_regexp_sz (const jerry_char_t *pattern_p, /**< zero-terminated UTF- #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ } /* jerry_create_regexp_sz */ +/** + * Creates a new realm (global object). + * + * @return new realm object + */ +jerry_value_t +jerry_create_realm (void) +{ + jerry_assert_api_available (); + +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_global_object_t *global_object_p = ecma_builtin_create_global_object (); + return ecma_make_object_value ((ecma_object_t *) global_object_p); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Realms are disabled."))); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ +} /* jerry_create_realm */ + /** * Get length of an array object * diff --git a/jerry-core/config.h b/jerry-core/config.h index c8c1be01f..8b3f1fff4 100644 --- a/jerry-core/config.h +++ b/jerry-core/config.h @@ -79,6 +79,10 @@ # define JERRY_ESNEXT 1 #endif /* !defined (JERRY_ESNEXT) */ +#ifndef JERRY_BUILTIN_REALMS +# define JERRY_BUILTIN_REALMS JERRY_ESNEXT +#endif /* !defined (JERRY_BUILTIN_REALMS) */ + #ifndef JERRY_BUILTIN_DATAVIEW # define JERRY_BUILTIN_DATAVIEW JERRY_ESNEXT #endif /* !defined (JERRY_BUILTIN_DATAVIEW) */ @@ -523,6 +527,10 @@ || ((JERRY_ESNEXT != 0) && (JERRY_ESNEXT != 1)) # error "Invalid value for JERRY_ESNEXT macro." #endif +#if !defined (JERRY_BUILTIN_REALMS) \ +|| ((JERRY_BUILTIN_REALMS != 0) && (JERRY_BUILTIN_REALMS != 1)) +# error "Invalid value for JERRY_BUILTIN_REALMS macro." +#endif #if !defined (JERRY_BUILTIN_DATAVIEW) \ || ((JERRY_BUILTIN_DATAVIEW != 0) && (JERRY_BUILTIN_DATAVIEW != 1)) # error "Invalid value for JERRY_BUILTIN_DATAVIEW macro." diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 5383ddd8d..50a624234 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -149,6 +149,34 @@ ecma_deref_object (ecma_object_t *object_p) /**< object */ object_p->type_flags_refs = (uint16_t) (object_p->type_flags_refs - ECMA_OBJECT_REF_ONE); } /* ecma_deref_object */ +/** + * Mark objects referenced by global object + */ +static void +ecma_gc_mark_global_object (ecma_global_object_t *global_object_p) /**< global object */ +{ + JERRY_ASSERT (global_object_p->extended_object.u.built_in.routine_id == 0); + + ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, global_object_p->global_env_cp)); + +#if ENABLED (JERRY_ESNEXT) + if (global_object_p->global_scope_cp != global_object_p->global_env_cp) + { + ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, global_object_p->global_scope_cp)); + } +#endif /* ENABLED (JERRY_ESNEXT) */ + + jmem_cpointer_t *builtin_objects_p = global_object_p->builtin_objects; + + for (int i = 0; i < ECMA_BUILTIN_OBJECTS_COUNT; i++) + { + if (builtin_objects_p[i] != JMEM_CP_NULL) + { + ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, builtin_objects_p[i])); + } + } +} /* ecma_gc_mark_global_object */ + /** * Mark objects referenced by arguments object */ @@ -620,14 +648,52 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ { ecma_object_t *binding_object_p = ecma_get_lex_env_binding_object (object_p); ecma_gc_set_object_visited (binding_object_p); - return; } } else { - switch (ecma_get_object_type (object_p)) + ecma_object_type_t object_type = ecma_get_object_type (object_p); + +#if ENABLED (JERRY_BUILTIN_REALMS) + if (JERRY_UNLIKELY (ecma_get_object_is_builtin (object_p))) { + ecma_value_t realm_value; + + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) + { + realm_value = ((ecma_extended_built_in_object_t *) object_p)->built_in.realm_value; + } + else + { + ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) object_p; + + if (object_type == ECMA_OBJECT_TYPE_GENERAL + && extended_object_p->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) + { + ecma_gc_mark_global_object ((ecma_global_object_t *) object_p); + } + + realm_value = extended_object_p->u.built_in.realm_value; + } + + ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); + } +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + switch (object_type) + { +#if !ENABLED (JERRY_BUILTIN_REALMS) + case ECMA_OBJECT_TYPE_GENERAL: + { + if (JERRY_UNLIKELY (ecma_get_object_is_builtin (object_p)) + && ((ecma_extended_object_t *) object_p)->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) + { + ecma_gc_mark_global_object ((ecma_global_object_t *) object_p); + } + break; + } +#endif /* !ENABLED (JERRY_BUILTIN_REALMS) */ case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; @@ -823,9 +889,11 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_func_p->u.function.scope_cp)); -#if ENABLED (JERRY_ESNEXT) +#if ENABLED (JERRY_ESNEXT) || ENABLED (JERRY_BUILTIN_REALMS) const ecma_compiled_code_t *byte_code_p = ecma_op_function_get_compiled_code (ext_func_p); +#endif /* ENABLED (JERRY_ESNEXT) || ENABLED (JERRY_BUILTIN_REALMS) */ +#if ENABLED (JERRY_ESNEXT) if (CBC_FUNCTION_IS_ARROW (byte_code_p->status_flags)) { ecma_arrow_function_t *arrow_func_p = (ecma_arrow_function_t *) object_p; @@ -842,6 +910,30 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ } #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_REALMS) +#if ENABLED (JERRY_SNAPSHOT_EXEC) + if (ext_func_p->u.function.bytecode_cp == JMEM_CP_NULL) + { + /* Static snapshot functions have a global realm */ + break; + } +#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ + + ecma_value_t realm_value; + + if (byte_code_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) byte_code_p; + realm_value = args_p->realm_value; + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) byte_code_p; + realm_value = args_p->realm_value; + } + + ecma_gc_set_object_visited (ecma_get_object_from_value (realm_value)); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ break; } #if ENABLED (JERRY_ESNEXT) @@ -1305,7 +1397,7 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ { uint8_t length_and_bitset_size; - if (object_type == ECMA_OBJECT_TYPE_CLASS || object_type == ECMA_OBJECT_TYPE_ARRAY) + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { ext_object_size = sizeof (ecma_extended_built_in_object_t); length_and_bitset_size = ((ecma_extended_built_in_object_t *) object_p)->built_in.u.length_and_bitset_size; @@ -1313,15 +1405,16 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ } else { - if (object_type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION - && ecma_builtin_function_is_routine (object_p)) - { -#if ENABLED (JERRY_ESNEXT) - ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) object_p; + ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) object_p; - if (ext_obj_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER) + if (extended_object_p->u.built_in.routine_id > 0) + { + JERRY_ASSERT (object_type == ECMA_OBJECT_TYPE_NATIVE_FUNCTION); + +#if ENABLED (JERRY_ESNEXT) + if (extended_object_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER) { - switch (ext_obj_p->u.built_in.routine_id) + switch (extended_object_p->u.built_in.routine_id) { case ECMA_NATIVE_HANDLER_PROMISE_RESOLVE: case ECMA_NATIVE_HANDLER_PROMISE_REJECT: @@ -1367,6 +1460,11 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ } #endif /* ENABLED (JERRY_ESNEXT) */ } + else if (extended_object_p->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) + { + JERRY_ASSERT (object_type == ECMA_OBJECT_TYPE_GENERAL); + ext_object_size = sizeof (ecma_global_object_t); + } else { length_and_bitset_size = ((ecma_extended_object_t *) object_p)->u.built_in.u.length_and_bitset_size; diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 5420893ef..74af0237c 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -864,14 +864,30 @@ typedef struct uint8_t instantiated_bitset[1]; /**< instantiated property bit set for generic built-ins */ uint8_t routine_flags; /**< flags for built-in routines */ } u2; + +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_value_t realm_value; /**< realm value */ +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ uint32_t continue_instantiated_bitset[1]; /**< bit set for instantiated properties */ +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } ecma_built_in_props_t; +#if ENABLED (JERRY_BUILTIN_REALMS) + +/** + * Number of bits available in the instantiated bitset without allocation + */ +#define ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE (8) + +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + /** * Number of bits available in the instantiated bitset without allocation */ #define ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE (8 + 32) +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + /** * Builtin routine function object status flags */ @@ -992,6 +1008,12 @@ typedef struct ecma_built_in_props_t built_in; /**< built-in object part */ } ecma_extended_built_in_object_t; +/** + * Checks whether the built-in is an ecma_extended_built_in_object_t + */ +#define ECMA_BUILTIN_IS_EXTENDED_BUILT_IN(object_type) \ + ((object_type) == ECMA_OBJECT_TYPE_CLASS || (object_type) == ECMA_OBJECT_TYPE_ARRAY) + /** * Flags for array.length_prop_and_hole_count */ diff --git a/jerry-core/ecma/base/ecma-helpers.c b/jerry-core/ecma/base/ecma-helpers.c index cccb5dc39..d89129824 100644 --- a/jerry-core/ecma/base/ecma-helpers.c +++ b/jerry-core/ecma/base/ecma-helpers.c @@ -250,7 +250,7 @@ ecma_get_object_builtin_id (ecma_object_t *object_p) /**< object */ ecma_built_in_props_t *built_in_props_p; ecma_object_type_t object_type = ecma_get_object_type (object_p); - if (object_type == ECMA_OBJECT_TYPE_CLASS || object_type == ECMA_OBJECT_TYPE_ARRAY) + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { built_in_props_p = &((ecma_extended_built_in_object_t *) object_p)->built_in; } diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index e875cbc2c..8acab129b 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -77,9 +77,9 @@ ecma_finalize (void) ecma_finalize_global_environment (); uint8_t runs = 0; + do { - ecma_finalize_builtins (); ecma_gc_run (); if (++runs >= JERRY_GC_LOOP_LIMIT) { @@ -87,6 +87,7 @@ ecma_finalize (void) } } while (JERRY_CONTEXT (ecma_gc_new_objects) != 0); + ecma_finalize_lit_storage (); } /* ecma_finalize */ diff --git a/jerry-core/ecma/base/ecma-module.c b/jerry-core/ecma/base/ecma-module.c index fb6fa6cb7..634a446cd 100644 --- a/jerry-core/ecma/base/ecma-module.c +++ b/jerry-core/ecma/base/ecma-module.c @@ -485,8 +485,15 @@ ecma_module_evaluate (ecma_module_t *module_p) /**< module */ return ECMA_VALUE_EMPTY; } +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_object_t *global_object_p; + global_object_p = ecma_get_object_from_value (ecma_op_function_get_realm (module_p->compiled_code_p)); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + ecma_object_t *global_object_p = ecma_builtin_get_global (); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + module_p->state = ECMA_MODULE_STATE_EVALUATING; - module_p->scope_p = ecma_create_decl_lex_env (ecma_get_global_environment ()); + module_p->scope_p = ecma_create_decl_lex_env (ecma_get_global_environment (global_object_p)); module_p->context_p->parent_p = JERRY_CONTEXT (module_top_context_p); JERRY_CONTEXT (module_top_context_p) = module_p->context_p; diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.c b/jerry-core/ecma/builtin-objects/ecma-builtins.c index 941db1bc3..8ecad7ec8 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.c @@ -34,7 +34,8 @@ * @{ */ -static void ecma_instantiate_builtin (ecma_builtin_id_t id); +JERRY_STATIC_ASSERT (ECMA_BUILTIN_ID_GLOBAL == ECMA_BUILTIN_OBJECTS_COUNT, + ecma_builtin_id_global_must_be_the_last_builtin_id); /** * Helper definition for ecma_builtin_property_list_references. @@ -270,39 +271,45 @@ ecma_builtin_get_property_count (ecma_builtin_id_t builtin_id) /**< built-in ID * false - otherwise */ bool -ecma_builtin_is (ecma_object_t *obj_p, /**< pointer to an object */ +ecma_builtin_is (ecma_object_t *object_p, /**< pointer to an object */ ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { - JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); + JERRY_ASSERT (object_p != NULL && !ecma_is_lexical_environment (object_p)); JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); - /* If a built-in object is not instantiated, its value is NULL, - hence it cannot be equal to a valid object. */ - jmem_cpointer_t builtin_cp = JERRY_CONTEXT (ecma_builtin_objects)[builtin_id]; + if (!ecma_get_object_is_builtin (object_p)) + { + return false; + } - return (builtin_cp != JMEM_CP_NULL && (obj_p == ECMA_GET_NON_NULL_POINTER (ecma_object_t, builtin_cp))); + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (ecma_get_object_type (object_p))) + { + ecma_extended_built_in_object_t *extended_built_in_object_p = (ecma_extended_built_in_object_t *) object_p; + + return (extended_built_in_object_p->built_in.id == builtin_id + && extended_built_in_object_p->built_in.routine_id == 0); + } + + ecma_extended_object_t *built_in_object_p = (ecma_extended_object_t *) object_p; + + return (built_in_object_p->u.built_in.id == builtin_id + && built_in_object_p->u.built_in.routine_id == 0); } /* ecma_builtin_is */ /** - * Get reference to specified built-in object + * Check if passed object is a global built-in. * - * Note: - * Does not increase the reference counter. - * - * @return pointer to the object's instance + * @return true - if the object is a global built-in + * false - otherwise */ -ecma_object_t * -ecma_builtin_get (ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ +bool +ecma_builtin_is_global (ecma_object_t *object_p) /**< pointer to an object */ { - JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); + JERRY_ASSERT (ecma_get_object_is_builtin (object_p)); - if (JERRY_UNLIKELY (JERRY_CONTEXT (ecma_builtin_objects)[builtin_id] == JMEM_CP_NULL)) - { - ecma_instantiate_builtin (builtin_id); - } - - return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_builtin_objects)[builtin_id]); -} /* ecma_builtin_get */ + return (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_GENERAL + && ((ecma_extended_object_t *) object_p)->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL); +} /* ecma_builtin_is_global */ /** * Get reference to the global object @@ -315,9 +322,9 @@ ecma_builtin_get (ecma_builtin_id_t builtin_id) /**< id of built-in to check on inline ecma_object_t * JERRY_ATTR_ALWAYS_INLINE ecma_builtin_get_global (void) { - JERRY_ASSERT (JERRY_CONTEXT (ecma_builtin_objects)[ECMA_BUILTIN_ID_GLOBAL] != JMEM_CP_NULL); + JERRY_ASSERT (JERRY_CONTEXT (global_object_p) != NULL); - return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_builtin_objects)[ECMA_BUILTIN_ID_GLOBAL]); + return (ecma_object_t *) JERRY_CONTEXT (global_object_p); } /* ecma_builtin_get_global */ /** @@ -336,14 +343,51 @@ ecma_builtin_function_is_routine (ecma_object_t *func_obj_p) /**< function objec return (ext_func_obj_p->u.built_in.routine_id != 0); } /* ecma_builtin_function_is_routine */ +#if ENABLED (JERRY_BUILTIN_REALMS) + +/** + * Get reference to the realm provided by another built-in object + * + * Note: + * Does not increase the reference counter. + * + * @return pointer to the global object + */ +static ecma_global_object_t * +ecma_builtin_get_realm (ecma_object_t *builtin_object_p) /**< built-in object */ +{ + JERRY_ASSERT (ecma_get_object_is_builtin (builtin_object_p)); + + ecma_object_type_t object_type = ecma_get_object_type (builtin_object_p); + ecma_value_t realm_value; + + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) + { + realm_value = ((ecma_extended_built_in_object_t *) builtin_object_p)->built_in.realm_value; + } + else + { + realm_value = ((ecma_extended_object_t *) builtin_object_p)->u.built_in.realm_value; + } + + return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, realm_value); +} /* ecma_builtin_get_realm */ + +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + /** * Instantiate specified ECMA built-in object + * + * @return the newly instantiated built-in */ -static void -ecma_instantiate_builtin (ecma_builtin_id_t obj_builtin_id) /**< built-in id */ +static ecma_object_t * +ecma_instantiate_builtin (ecma_global_object_t *global_object_p, /**< global object */ + ecma_builtin_id_t obj_builtin_id) /**< built-in id */ { - JERRY_ASSERT (obj_builtin_id < ECMA_BUILTIN_ID__COUNT); - JERRY_ASSERT (JERRY_CONTEXT (ecma_builtin_objects)[obj_builtin_id] == JMEM_CP_NULL); + jmem_cpointer_t *builtin_objects = global_object_p->builtin_objects; + + JERRY_ASSERT (obj_builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); + JERRY_ASSERT (builtin_objects[obj_builtin_id] == JMEM_CP_NULL); ecma_builtin_descriptor_t builtin_desc = ecma_builtin_descriptors[obj_builtin_id]; ecma_builtin_id_t object_prototype_builtin_id = (ecma_builtin_id_t) (builtin_desc >> ECMA_BUILTIN_PROTOTYPE_ID_SHIFT); @@ -357,19 +401,17 @@ ecma_instantiate_builtin (ecma_builtin_id_t obj_builtin_id) /**< built-in id */ } else { - if (JERRY_CONTEXT (ecma_builtin_objects)[object_prototype_builtin_id] == JMEM_CP_NULL) + if (builtin_objects[object_prototype_builtin_id] == JMEM_CP_NULL) { - ecma_instantiate_builtin (object_prototype_builtin_id); + ecma_instantiate_builtin (global_object_p, object_prototype_builtin_id); } - prototype_obj_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, - JERRY_CONTEXT (ecma_builtin_objects)[object_prototype_builtin_id]); + prototype_obj_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, builtin_objects[object_prototype_builtin_id]); JERRY_ASSERT (prototype_obj_p != NULL); } ecma_object_type_t obj_type = (ecma_object_type_t) (builtin_desc & ECMA_BUILTIN_OBJECT_TYPE_MASK); - bool is_extended_built_in = (obj_type == ECMA_OBJECT_TYPE_CLASS - || obj_type == ECMA_OBJECT_TYPE_ARRAY); + bool is_extended_built_in = ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (obj_type); size_t ext_object_size = (is_extended_built_in ? sizeof (ecma_extended_built_in_object_t) : sizeof (ecma_extended_object_t)); @@ -418,15 +460,19 @@ ecma_instantiate_builtin (ecma_builtin_id_t obj_builtin_id) /**< built-in id */ built_in_props_p->routine_id = 0; built_in_props_p->u.length_and_bitset_size = 0; built_in_props_p->u2.instantiated_bitset[0] = 0; +#if ENABLED (JERRY_BUILTIN_REALMS) + ECMA_SET_INTERNAL_VALUE_POINTER (built_in_props_p->realm_value, global_object_p); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ built_in_props_p->continue_instantiated_bitset[0] = 0; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ - if (property_count > 40) + if (property_count > ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE) { built_in_props_p->u.length_and_bitset_size = 1 << ECMA_BUILT_IN_BITSET_SHIFT; - uint32_t *instantiated_bitset_p = built_in_props_p->continue_instantiated_bitset; + uint32_t *instantiated_bitset_p = (uint32_t *) (built_in_props_p + 1); + instantiated_bitset_p[0] = 0; instantiated_bitset_p[1] = 0; - instantiated_bitset_p[2] = 0; } /** Initializing [[PrimitiveValue]] properties of built-in prototype objects */ @@ -522,27 +568,135 @@ ecma_instantiate_builtin (ecma_builtin_id_t obj_builtin_id) /**< built-in id */ } } - ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_builtin_objects)[obj_builtin_id], obj_p); + ECMA_SET_NON_NULL_POINTER (builtin_objects[obj_builtin_id], obj_p); + ecma_deref_object (obj_p); + return obj_p; } /* ecma_instantiate_builtin */ /** - * Finalize ECMA built-in objects + * Create a global object + * + * @return a new global object */ -void -ecma_finalize_builtins (void) +ecma_global_object_t * +ecma_builtin_create_global_object (void) { - for (ecma_builtin_id_t id = (ecma_builtin_id_t) 0; - id < ECMA_BUILTIN_ID__COUNT; - id = (ecma_builtin_id_t) (id + 1)) + ecma_builtin_descriptor_t builtin_desc = ecma_builtin_descriptors[ECMA_BUILTIN_ID_GLOBAL]; + ecma_builtin_id_t prototype_builtin_id = (ecma_builtin_id_t) (builtin_desc >> ECMA_BUILTIN_PROTOTYPE_ID_SHIFT); + ecma_object_type_t obj_type = (ecma_object_type_t) (builtin_desc & ECMA_BUILTIN_OBJECT_TYPE_MASK); + size_t property_count = ecma_builtin_get_property_count (ECMA_BUILTIN_ID_GLOBAL); + + JERRY_ASSERT (prototype_builtin_id != ECMA_BUILTIN_ID__COUNT); + JERRY_ASSERT (obj_type != ECMA_OBJECT_TYPE_CLASS && obj_type != ECMA_OBJECT_TYPE_ARRAY); + + /* Whenever this assertion fails, the size of extra_instantiated_bitset in ecma_global_object_t + * must be increased and 32 must be added to these constants. Furthermore the new uint32 item + * must be set to zero. */ +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_ASSERT (property_count <= ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE + 64); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + JERRY_ASSERT (property_count <= ECMA_BUILTIN_INSTANTIATED_BITSET_MIN_SIZE + 32); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + ecma_object_t *object_p = ecma_create_object (NULL, sizeof (ecma_global_object_t), obj_type); + + ecma_op_ordinary_object_set_extensible (object_p); + ecma_set_object_is_builtin (object_p); + + ecma_global_object_t *global_object_p = (ecma_global_object_t *) object_p; + + global_object_p->extended_object.u.built_in.id = (uint8_t) ECMA_BUILTIN_ID_GLOBAL; + global_object_p->extended_object.u.built_in.routine_id = 0; + /* Bitset size is ignored by the gc. */ + global_object_p->extended_object.u.built_in.u.length_and_bitset_size = 0; + global_object_p->extended_object.u.built_in.u2.instantiated_bitset[0] = 0; + global_object_p->extra_instantiated_bitset[0] = 0; +#if ENABLED (JERRY_BUILTIN_REALMS) + ECMA_SET_INTERNAL_VALUE_POINTER (global_object_p->extended_object.u.built_in.realm_value, global_object_p); + global_object_p->extra_realms_bitset = 0; +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + global_object_p->extended_object.u.built_in.continue_instantiated_bitset[0] = 0; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + memset (global_object_p->builtin_objects, 0, (sizeof (jmem_cpointer_t) * ECMA_BUILTIN_OBJECTS_COUNT)); + + /* Temporary self reference for GC mark. */ + ECMA_SET_NON_NULL_POINTER (global_object_p->global_env_cp, object_p); +#if ENABLED (JERRY_ESNEXT) + global_object_p->global_scope_cp = global_object_p->global_env_cp; +#endif /* ENABLED (JERRY_ESNEXT) */ + + ecma_object_t *global_lex_env_p = ecma_create_object_lex_env (NULL, + object_p, + ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); + ECMA_SET_NON_NULL_POINTER (global_object_p->global_env_cp, global_lex_env_p); +#if ENABLED (JERRY_ESNEXT) + global_object_p->global_scope_cp = global_object_p->global_env_cp; +#endif /* ENABLED (JERRY_ESNEXT) */ + ecma_deref_object (global_lex_env_p); + + ecma_object_t *prototype_object_p; + prototype_object_p = ecma_instantiate_builtin (global_object_p, prototype_builtin_id); + JERRY_ASSERT (prototype_object_p != NULL); + + ECMA_SET_NON_NULL_POINTER (object_p->u2.prototype_cp, prototype_object_p); + + return global_object_p; +} /* ecma_builtin_create_global_object */ + +/** + * Get reference to specified built-in object + * + * Note: + * Does not increase the reference counter. + * + * @return pointer to the object's instance + */ +ecma_object_t * +ecma_builtin_get (ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ +{ + JERRY_ASSERT (builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); + + ecma_global_object_t *global_object_p = (ecma_global_object_t *) ecma_builtin_get_global (); + jmem_cpointer_t *builtin_p = global_object_p->builtin_objects + builtin_id; + + if (JERRY_UNLIKELY (*builtin_p == JMEM_CP_NULL)) { - if (JERRY_CONTEXT (ecma_builtin_objects)[id] != JMEM_CP_NULL) - { - ecma_object_t *obj_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_builtin_objects)[id]); - ecma_deref_object (obj_p); - JERRY_CONTEXT (ecma_builtin_objects)[id] = JMEM_CP_NULL; - } + return ecma_instantiate_builtin (global_object_p, builtin_id); } -} /* ecma_finalize_builtins */ + + return ECMA_GET_NON_NULL_POINTER (ecma_object_t, *builtin_p); +} /* ecma_builtin_get */ + +/** + * Get reference to specified built-in object using the realm provided by another built-in object + * + * Note: + * Does not increase the reference counter. + * + * @return pointer to the object's instance + */ +static ecma_object_t * +ecma_builtin_get_from_realm (ecma_object_t *builtin_object_p, /**< built-in object */ + ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ +{ + JERRY_ASSERT (builtin_id < ECMA_BUILTIN_OBJECTS_COUNT); + +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_global_object_t *global_object_p = ecma_builtin_get_realm (builtin_object_p); + jmem_cpointer_t *builtin_p = global_object_p->builtin_objects + builtin_id; + + if (JERRY_UNLIKELY (*builtin_p == JMEM_CP_NULL)) + { + return ecma_instantiate_builtin (global_object_p, builtin_id); + } + + return ECMA_GET_NON_NULL_POINTER (ecma_object_t, *builtin_p); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + JERRY_UNUSED (builtin_object_p); + return ecma_builtin_get (builtin_id); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ +} /* ecma_builtin_get_from_realm */ /** * Construct a Function object for specified built-in routine @@ -552,13 +706,14 @@ ecma_finalize_builtins (void) * @return pointer to constructed Function object */ static ecma_object_t * -ecma_builtin_make_function_object_for_routine (ecma_builtin_id_t builtin_id, /**< identifier of built-in object */ +ecma_builtin_make_function_object_for_routine (ecma_object_t *builtin_object_p, /**< builtin object */ uint8_t routine_id, /**< builtin-wide identifier of the built-in * object's routine property */ uint32_t routine_index, /**< property descriptor index of routine */ uint8_t flags) /**< see also: ecma_builtin_routine_flags */ { - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); + ecma_object_t *prototype_obj_p = ecma_builtin_get_from_realm (builtin_object_p, + ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE); size_t ext_object_size = sizeof (ecma_extended_object_t); @@ -571,12 +726,27 @@ ecma_builtin_make_function_object_for_routine (ecma_builtin_id_t builtin_id, /** JERRY_ASSERT (routine_id > 0); JERRY_ASSERT (routine_index <= UINT8_MAX); + ecma_built_in_props_t *built_in_props_p; + + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (ecma_get_object_type (builtin_object_p))) + { + built_in_props_p = &((ecma_extended_built_in_object_t *) builtin_object_p)->built_in; + } + else + { + built_in_props_p = &((ecma_extended_object_t *) builtin_object_p)->u.built_in; + } + ecma_extended_object_t *ext_func_obj_p = (ecma_extended_object_t *) func_obj_p; - ext_func_obj_p->u.built_in.id = (uint8_t) builtin_id; + ext_func_obj_p->u.built_in.id = built_in_props_p->id; ext_func_obj_p->u.built_in.routine_id = routine_id; ext_func_obj_p->u.built_in.u.routine_index = (uint8_t) routine_index; ext_func_obj_p->u.built_in.u2.routine_flags = flags; +#if ENABLED (JERRY_BUILTIN_REALMS) + ext_func_obj_p->u.built_in.realm_value = built_in_props_p->realm_value; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + return func_obj_p; } /* ecma_builtin_make_function_object_for_routine */ @@ -586,13 +756,13 @@ ecma_builtin_make_function_object_for_routine (ecma_builtin_id_t builtin_id, /** * @return pointer to constructed accessor getter Function object */ static ecma_object_t * -ecma_builtin_make_function_object_for_getter_accessor (ecma_builtin_id_t builtin_id, /**< id of built-in object */ +ecma_builtin_make_function_object_for_getter_accessor (ecma_object_t *builtin_object_p, /**< builtin object */ uint8_t routine_id, /**< builtin-wide id of the built-in * object's routine property */ uint32_t routine_index) /**< property descriptor index * of routine */ { - return ecma_builtin_make_function_object_for_routine (builtin_id, + return ecma_builtin_make_function_object_for_routine (builtin_object_p, routine_id, routine_index, ECMA_BUILTIN_ROUTINE_GETTER); @@ -604,13 +774,13 @@ ecma_builtin_make_function_object_for_getter_accessor (ecma_builtin_id_t builtin * @return pointer to constructed accessor getter Function object */ static ecma_object_t * -ecma_builtin_make_function_object_for_setter_accessor (ecma_builtin_id_t builtin_id, /**< id of built-in object */ +ecma_builtin_make_function_object_for_setter_accessor (ecma_object_t *builtin_object_p, /**< builtin object */ uint8_t routine_id, /**< builtin-wide id of the built-in * object's routine property */ uint32_t routine_index) /**< property descriptor index * of routine */ { - return ecma_builtin_make_function_object_for_routine (builtin_id, + return ecma_builtin_make_function_object_for_routine (builtin_object_p, routine_id, routine_index, ECMA_BUILTIN_ROUTINE_SETTER); @@ -865,7 +1035,7 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * ecma_object_type_t object_type = ecma_get_object_type (object_p); JERRY_ASSERT (object_type != ECMA_OBJECT_TYPE_FUNCTION || !ecma_builtin_function_is_routine (object_p)); - if (object_type == ECMA_OBJECT_TYPE_CLASS || object_type == ECMA_OBJECT_TYPE_ARRAY) + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { built_in_props_p = &((ecma_extended_built_in_object_t *) object_p)->built_in; } @@ -877,7 +1047,6 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * ecma_builtin_id_t builtin_id = (ecma_builtin_id_t) built_in_props_p->id; JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); - JERRY_ASSERT (ecma_builtin_is (object_p, builtin_id)); const ecma_builtin_property_descriptor_t *property_list_p = ecma_builtin_property_list_references[builtin_id]; @@ -894,6 +1063,14 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * uint32_t index = (uint32_t) (curr_property_p - property_list_p); uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + (index >> 3); + +#if ENABLED (JERRY_BUILTIN_REALMS) + if (index >= 8 * sizeof (uint8_t)) + { + bitset_p += sizeof (ecma_value_t); + } +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + uint8_t bit_for_index = (uint8_t) (1u << (index & 0x7)); if (*bitset_p & bit_for_index) @@ -918,9 +1095,10 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * #if ENABLED (JERRY_ESNEXT) if (value == ECMA_VALUE_GLOBAL_THIS) { - ecma_object_t *global_object_p = ecma_builtin_get_global (); - ecma_ref_object (global_object_p); - value = ecma_make_object_value (global_object_p); + /* Only the global object has globalThis property. */ + JERRY_ASSERT (ecma_builtin_is_global (object_p)); + ecma_ref_object (object_p); + value = ecma_make_object_value (object_p); } #endif /* ENABLED (JERRY_ESNEXT) */ break; @@ -1004,8 +1182,8 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * } case ECMA_BUILTIN_PROPERTY_INTRINSIC_PROPERTY: { - value = ecma_op_object_get_by_magic_id (ecma_builtin_get (ECMA_BUILTIN_ID_INTRINSIC_OBJECT), - (lit_magic_string_id_t) curr_property_p->value); + ecma_object_t *intrinsic_object_p = ecma_builtin_get_from_realm (object_p, ECMA_BUILTIN_ID_INTRINSIC_OBJECT); + value = ecma_op_object_get_by_magic_id (intrinsic_object_p, (lit_magic_string_id_t) curr_property_p->value); break; } case ECMA_BUILTIN_PROPERTY_ACCESSOR_BUILTIN_FUNCTION: @@ -1013,8 +1191,8 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * is_accessor = true; uint16_t getter_id = ECMA_ACCESSOR_READ_WRITE_GET_GETTER_ID (curr_property_p->value); uint16_t setter_id = ECMA_ACCESSOR_READ_WRITE_GET_SETTER_ID (curr_property_p->value); - getter_p = ecma_builtin_get (getter_id); - setter_p = ecma_builtin_get (setter_id); + getter_p = ecma_builtin_get_from_realm (object_p, getter_id); + setter_p = ecma_builtin_get_from_realm (object_p, setter_id); ecma_ref_object (getter_p); ecma_ref_object (setter_p); break; @@ -1022,7 +1200,8 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * #endif /* ENABLED (JERRY_ESNEXT) */ case ECMA_BUILTIN_PROPERTY_OBJECT: { - ecma_object_t *builtin_object_p = ecma_builtin_get ((ecma_builtin_id_t) curr_property_p->value); + ecma_object_t *builtin_object_p; + builtin_object_p = ecma_builtin_get_from_realm (object_p, (ecma_builtin_id_t) curr_property_p->value); ecma_ref_object (builtin_object_p); value = ecma_make_object_value (builtin_object_p); break; @@ -1030,7 +1209,7 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * case ECMA_BUILTIN_PROPERTY_ROUTINE: { ecma_object_t *func_obj_p; - func_obj_p = ecma_builtin_make_function_object_for_routine (builtin_id, + func_obj_p = ecma_builtin_make_function_object_for_routine (object_p, ECMA_GET_ROUTINE_ID (curr_property_p->value), index, ECMA_BUILTIN_ROUTINE_NO_OPTS); @@ -1042,8 +1221,8 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * is_accessor = true; uint8_t getter_id = ECMA_ACCESSOR_READ_WRITE_GET_GETTER_ID (curr_property_p->value); uint8_t setter_id = ECMA_ACCESSOR_READ_WRITE_GET_SETTER_ID (curr_property_p->value); - getter_p = ecma_builtin_make_function_object_for_getter_accessor (builtin_id, getter_id, index); - setter_p = ecma_builtin_make_function_object_for_setter_accessor (builtin_id, setter_id, index); + getter_p = ecma_builtin_make_function_object_for_getter_accessor (object_p, getter_id, index); + setter_p = ecma_builtin_make_function_object_for_setter_accessor (object_p, setter_id, index); break; } default: @@ -1052,7 +1231,7 @@ ecma_builtin_try_to_instantiate_property (ecma_object_t *object_p, /**< object * is_accessor = true; uint8_t getter_id = (uint8_t) curr_property_p->value; - getter_p = ecma_builtin_make_function_object_for_getter_accessor (builtin_id, getter_id, index); + getter_p = ecma_builtin_make_function_object_for_getter_accessor (object_p, getter_id, index); break; } } @@ -1177,13 +1356,13 @@ ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, /**< a built-in ecma_property_counter_t *prop_counter_p) /**< prop counter */ { JERRY_ASSERT (ecma_get_object_is_builtin (object_p)); - JERRY_ASSERT (ecma_get_object_type (object_p) != ECMA_OBJECT_TYPE_FUNCTION + JERRY_ASSERT (ecma_get_object_type (object_p) != ECMA_OBJECT_TYPE_NATIVE_FUNCTION || !ecma_builtin_function_is_routine (object_p)); ecma_built_in_props_t *built_in_props_p; ecma_object_type_t object_type = ecma_get_object_type (object_p); - if (object_type == ECMA_OBJECT_TYPE_CLASS || object_type == ECMA_OBJECT_TYPE_ARRAY) + if (ECMA_BUILTIN_IS_EXTENDED_BUILT_IN (object_type)) { built_in_props_p = &((ecma_extended_built_in_object_t *) object_p)->built_in; } @@ -1195,18 +1374,23 @@ ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, /**< a built-in ecma_builtin_id_t builtin_id = (ecma_builtin_id_t) built_in_props_p->id; JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); - JERRY_ASSERT (ecma_builtin_is (object_p, builtin_id)); const ecma_builtin_property_descriptor_t *curr_property_p = ecma_builtin_property_list_references[builtin_id]; uint32_t index = 0; - uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset; + uint8_t bitset = built_in_props_p->u2.instantiated_bitset[0]; + +#if ENABLED (JERRY_BUILTIN_REALMS) + uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + 1 + sizeof (ecma_value_t); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + uint8_t *bitset_p = built_in_props_p->u2.instantiated_bitset + 1; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ while (curr_property_p->magic_string_id != LIT_MAGIC_STRING__COUNT) { if (index == 8) { - bitset_p++; + bitset = *bitset_p++; index = 0; } @@ -1219,7 +1403,7 @@ ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, /**< a built-in { ecma_string_t *name_p = ecma_op_get_global_symbol (curr_property_p->magic_string_id); - if (!(*bitset_p & bit_for_index) || ecma_op_ordinary_object_has_own_property (object_p, name_p)) + if (!(bitset & bit_for_index) || ecma_op_ordinary_object_has_own_property (object_p, name_p)) { ecma_value_t name = ecma_make_symbol_value (name_p); ecma_collection_push_back (prop_names_p, name); @@ -1236,7 +1420,7 @@ ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, /**< a built-in { ecma_string_t *name_p = ecma_get_magic_string ((lit_magic_string_id_t) curr_property_p->magic_string_id); - if (!(*bitset_p & bit_for_index) || ecma_op_ordinary_object_has_own_property (object_p, name_p)) + if (!(bitset & bit_for_index) || ecma_op_ordinary_object_has_own_property (object_p, name_p)) { ecma_value_t name = ecma_make_magic_string_value ((lit_magic_string_id_t) curr_property_p->magic_string_id); ecma_collection_push_back (prop_names_p, name); @@ -1338,7 +1522,6 @@ ecma_builtin_dispatch_call (ecma_object_t *obj_p, /**< built-in object */ */ ecma_value_t ecma_builtin_dispatch_construct (ecma_object_t *obj_p, /**< built-in object */ - ecma_object_t *new_target_p, /**< new target */ const ecma_value_t *arguments_list_p, /**< arguments list */ uint32_t arguments_list_len) /**< arguments list length */ { @@ -1354,20 +1537,7 @@ ecma_builtin_dispatch_construct (ecma_object_t *obj_p, /**< built-in object */ ecma_builtin_id_t builtin_object_id = ext_obj_p->u.built_in.id; JERRY_ASSERT (builtin_object_id < sizeof (ecma_builtin_construct_functions) / sizeof (ecma_builtin_dispatch_call_t)); -#if ENABLED (JERRY_ESNEXT) - ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target); - JERRY_CONTEXT (current_new_target) = new_target_p; -#else /* !ENABLED (JERRY_ESNEXT) */ - JERRY_UNUSED (new_target_p); -#endif /* ENABLED (JERRY_ESNEXT) */ - - ecma_value_t ret_value = ecma_builtin_construct_functions[builtin_object_id] (arguments_list_p, arguments_list_len); - -#if ENABLED (JERRY_ESNEXT) - JERRY_CONTEXT (current_new_target) = old_new_target; -#endif /* ENABLED (JERRY_ESNEXT) */ - - return ret_value; + return ecma_builtin_construct_functions[builtin_object_id] (arguments_list_p, arguments_list_len); } /* ecma_builtin_dispatch_construct */ /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.h b/jerry-core/ecma/builtin-objects/ecma-builtins.h index ebb12922d..86d17c325 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.h @@ -88,15 +88,38 @@ typedef enum */ #define ECMA_ACCESSOR_READ_WRITE_GET_GETTER_ID(value) ((uint8_t) ((value) >> 8)) +/** + * Number ob built-in objects excluding global object + */ +#define ECMA_BUILTIN_OBJECTS_COUNT (ECMA_BUILTIN_ID__COUNT - 1) + +/** + * Description of built-in global ECMA-object. + */ +typedef struct +{ + ecma_extended_object_t extended_object; /**< extended object part */ + uint32_t extra_instantiated_bitset[1]; /**< extra bit set for instantiated properties */ +#if ENABLED (JERRY_BUILTIN_REALMS) + uint32_t extra_realms_bitset; /**< extra bit set for instantiated properties when realms is enabled */ +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + jmem_cpointer_t global_env_cp; /**< global lexical environment */ +#if ENABLED (JERRY_ESNEXT) + jmem_cpointer_t global_scope_cp; /**< global lexical scope */ +#endif /* ENABLED (JERRY_ESNEXT) */ + jmem_cpointer_t builtin_objects[ECMA_BUILTIN_OBJECTS_COUNT]; /**< pointer to instances of built-in objects */ +} ecma_global_object_t; + /* ecma-builtins.c */ -void ecma_finalize_builtins (void); + +ecma_global_object_t *ecma_builtin_create_global_object (void); ecma_value_t ecma_builtin_dispatch_call (ecma_object_t *obj_p, ecma_value_t this_arg_value, const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); ecma_value_t -ecma_builtin_dispatch_construct (ecma_object_t *obj_p, ecma_object_t *new_target_p, - const ecma_value_t *arguments_list_p, uint32_t arguments_list_len); +ecma_builtin_dispatch_construct (ecma_object_t *obj_p, const ecma_value_t *arguments_list_p, + uint32_t arguments_list_len); ecma_property_t * ecma_builtin_routine_try_to_instantiate_property (ecma_object_t *object_p, ecma_string_t *string_p); ecma_property_t * @@ -110,7 +133,9 @@ ecma_builtin_list_lazy_property_names (ecma_object_t *object_p, ecma_collection_t *prop_names_p, ecma_property_counter_t *prop_counter_p); bool -ecma_builtin_is (ecma_object_t *obj_p, ecma_builtin_id_t builtin_id); +ecma_builtin_is (ecma_object_t *object_p, ecma_builtin_id_t builtin_id); +bool +ecma_builtin_is_global (ecma_object_t *object_p); ecma_object_t * ecma_builtin_get (ecma_builtin_id_t builtin_id); ecma_object_t * diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index 6e9380647..87f93a84a 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -169,6 +169,10 @@ ecma_object_check_constructor (ecma_object_t *obj_p) /**< ecma object */ #if ENABLED (JERRY_ERROR_MESSAGES) switch (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags)) { + case CBC_FUNCTION_SCRIPT: + { + return "Script (global) functions cannot be invoked with 'new'."; + } case CBC_FUNCTION_GENERATOR: { return "Generator functions cannot be invoked with 'new'."; @@ -387,7 +391,7 @@ ecma_op_create_function_object (ecma_object_t *scope_p, /**< function's scope */ #if ENABLED (JERRY_SNAPSHOT_EXEC) if (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION) { - ext_func_p->u.function.bytecode_cp = ECMA_NULL_POINTER; + ext_func_p->u.function.bytecode_cp = JMEM_CP_NULL; ((ecma_static_function_t *) func_p)->bytecode_p = bytecode_data_p; } else @@ -477,7 +481,13 @@ ecma_op_create_dynamic_function (const ecma_value_t *arguments_list_p, /**< argu *func_name_p = ecma_make_magic_string_value (LIT_MAGIC_STRING_ANONYMOUS); #endif /* ENABLED (JERRY_ESNEXT) */ - ecma_object_t *global_env_p = ecma_get_global_environment (); + ecma_object_t *global_object_p = ecma_builtin_get_global (); + +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_ASSERT (global_object_p == ecma_get_object_from_value (ecma_op_function_get_realm (bytecode_p))); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + ecma_object_t *global_env_p = ecma_get_global_environment (global_object_p); ecma_builtin_id_t fallback_proto = ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE; #if ENABLED (JERRY_ESNEXT) @@ -702,6 +712,11 @@ ecma_op_create_native_handler (ecma_native_handler_id_t id, /**< handler id */ ext_func_obj_p->u.built_in.routine_id = (uint8_t) id; ext_func_obj_p->u.built_in.u2.routine_flags = ECMA_NATIVE_HANDLER_FLAGS_NONE; +#if ENABLED (JERRY_BUILTIN_REALMS) + ECMA_SET_INTERNAL_VALUE_POINTER (ext_func_obj_p->u.built_in.realm_value, + ecma_builtin_get_global ()); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + return function_obj_p; } /* ecma_op_create_native_handler */ @@ -716,21 +731,59 @@ inline const ecma_compiled_code_t * JERRY_ATTR_ALWAYS_INLINE ecma_op_function_get_compiled_code (ecma_extended_object_t *function_p) /**< function pointer */ { #if ENABLED (JERRY_SNAPSHOT_EXEC) - if (function_p->u.function.bytecode_cp != ECMA_NULL_POINTER) + if (JERRY_LIKELY (function_p->u.function.bytecode_cp != ECMA_NULL_POINTER)) { return ECMA_GET_INTERNAL_VALUE_POINTER (const ecma_compiled_code_t, function_p->u.function.bytecode_cp); } - else - { - return ((ecma_static_function_t *) function_p)->bytecode_p; - } + + return ((ecma_static_function_t *) function_p)->bytecode_p; #else /* !ENABLED (JERRY_SNAPSHOT_EXEC) */ return ECMA_GET_INTERNAL_VALUE_POINTER (const ecma_compiled_code_t, function_p->u.function.bytecode_cp); #endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ } /* ecma_op_function_get_compiled_code */ +#if ENABLED (JERRY_BUILTIN_REALMS) + +/** + * Get realm from a byte code. + * + * Note: + * Does not increase the reference counter. + * + * @return realm (global) object + */ +inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE +ecma_op_function_get_realm (const ecma_compiled_code_t *bytecode_header_p) /**< byte code header */ +{ + ecma_value_t realm_value; + + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; + realm_value = args_p->realm_value; + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; + realm_value = args_p->realm_value; + } + +#if ENABLED (JERRY_SNAPSHOT_EXEC) + if (JERRY_LIKELY (realm_value != ECMA_VALUE_UNDEFINED)) + { + return realm_value; + } + + return ecma_make_object_value (ecma_builtin_get_global ()); +#else /* !ENABLED (JERRY_SNAPSHOT_EXEC) */ + return realm_value; +#endif /* ENABLED (JERRY_SNAPSHOT_EXEC) */ +} /* ecma_op_function_get_realm */ + +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + /** * 15.3.5.3 implementation of [[HasInstance]] for Function objects * @@ -980,6 +1033,10 @@ ecma_op_function_call_simple (ecma_object_t *func_obj_p, /**< Function object */ shared_args.header.bytecode_header_p = bytecode_data_p; +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_value_t realm_value = ecma_op_function_get_realm (bytecode_data_p); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + /* 1. */ #if ENABLED (JERRY_ESNEXT) if (JERRY_UNLIKELY (CBC_FUNCTION_IS_ARROW (status_flags))) @@ -1007,7 +1064,11 @@ ecma_op_function_call_simple (ecma_object_t *func_obj_p, /**< Function object */ || ecma_is_value_null (this_binding)) { /* 2. */ +#if ENABLED (JERRY_BUILTIN_REALMS) + this_binding = realm_value; +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ this_binding = ecma_make_object_value (ecma_builtin_get_global ()); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } else if (!ecma_is_value_object (this_binding)) { @@ -1052,8 +1113,17 @@ ecma_op_function_call_simple (ecma_object_t *func_obj_p, /**< Function object */ } #endif /* ENABLED (JERRY_ESNEXT) */ +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); + JERRY_CONTEXT (global_object_p) = (ecma_global_object_t *) ecma_get_object_from_value (realm_value); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + ret_value = vm_run (&shared_args.header, this_binding, scope_p); +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_CONTEXT (global_object_p) = saved_global_object_p; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + #if ENABLED (JERRY_ESNEXT) /* ECMAScript v6, 9.2.2.13 */ if (JERRY_UNLIKELY (shared_args.header.status_flags & VM_FRAME_CTX_SHARED_HERITAGE_PRESENT)) @@ -1105,7 +1175,21 @@ ecma_op_function_call_native (ecma_object_t *func_obj_p, /**< Function object */ if (ecma_get_object_is_builtin (func_obj_p)) { - return ecma_builtin_dispatch_call (func_obj_p, this_arg_value, arguments_list_p, arguments_list_len); +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); + ecma_value_t realm_value = ((ecma_extended_object_t *) func_obj_p)->u.built_in.realm_value; + JERRY_CONTEXT (global_object_p) = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, realm_value); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + + ecma_value_t ret_value = ecma_builtin_dispatch_call (func_obj_p, + this_arg_value, + arguments_list_p, + arguments_list_len); + +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_CONTEXT (global_object_p) = saved_global_object_p; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + return ret_value; } JERRY_ASSERT (ext_func_obj_p->u.external_handler_cb != NULL); @@ -1390,7 +1474,27 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ { if (JERRY_UNLIKELY (ecma_get_object_is_builtin (func_obj_p))) { - return ecma_builtin_dispatch_construct (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); + ecma_value_t realm_value = ((ecma_extended_object_t *) func_obj_p)->u.built_in.realm_value; + JERRY_CONTEXT (global_object_p) = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_global_object_t, realm_value); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + +#if ENABLED (JERRY_ESNEXT) + ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target); + JERRY_CONTEXT (current_new_target) = new_target_p; +#endif /* ENABLED (JERRY_ESNEXT) */ + + ecma_value_t ret_value = ecma_builtin_dispatch_construct (func_obj_p, arguments_list_p, arguments_list_len); + +#if ENABLED (JERRY_ESNEXT) + JERRY_CONTEXT (current_new_target) = old_new_target; +#endif /* ENABLED (JERRY_ESNEXT) */ + +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_CONTEXT (global_object_p) = saved_global_object_p; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + return ret_value; } return ecma_op_function_construct_native (func_obj_p, new_target_p, arguments_list_p, arguments_list_len); diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index c75af5389..5223bd627 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -56,6 +56,11 @@ ecma_op_create_external_function_object (ecma_native_handler_t handler_cb); const ecma_compiled_code_t * ecma_op_function_get_compiled_code (ecma_extended_object_t *function_p); +#if ENABLED (JERRY_BUILTIN_REALMS) +ecma_value_t +ecma_op_function_get_realm (const ecma_compiled_code_t *bytecode_header_p); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + ecma_value_t ecma_op_create_dynamic_function (const ecma_value_t *arguments_list_p, uint32_t arguments_list_len, diff --git a/jerry-core/ecma/operations/ecma-get-put-value.c b/jerry-core/ecma/operations/ecma-get-put-value.c index 08a51c733..032a86463 100644 --- a/jerry-core/ecma/operations/ecma-get-put-value.c +++ b/jerry-core/ecma/operations/ecma-get-put-value.c @@ -304,6 +304,8 @@ ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environme lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); } + JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); + if (is_strict) { #if ENABLED (JERRY_ERROR_MESSAGES) @@ -315,7 +317,7 @@ ecma_op_put_value_lex_env_base (ecma_object_t *lex_env_p, /**< lexical environme #endif /* ENABLED (JERRY_ERROR_MESSAGES) */ } - ecma_value_t completion = ecma_op_object_put (ecma_builtin_get_global (), + ecma_value_t completion = ecma_op_object_put (ecma_get_lex_env_binding_object (lex_env_p), name_p, value, false); diff --git a/jerry-core/ecma/operations/ecma-lex-env.c b/jerry-core/ecma/operations/ecma-lex-env.c index 45ed0a0c8..b1d1e8e79 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.c +++ b/jerry-core/ecma/operations/ecma-lex-env.c @@ -39,15 +39,7 @@ void ecma_init_global_environment (void) { - ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); - - ecma_object_t *global_lex_env_p = ecma_create_object_lex_env (NULL, - glob_obj_p, - ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); - ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_env_cp), global_lex_env_p); -#if ENABLED (JERRY_ESNEXT) - ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_scope_cp), global_lex_env_p); -#endif /* ENABLED (JERRY_ESNEXT) */ + JERRY_CONTEXT (global_object_p) = ecma_builtin_create_global_object (); } /* ecma_init_global_environment */ /** @@ -56,15 +48,9 @@ ecma_init_global_environment (void) void ecma_finalize_global_environment (void) { -#if ENABLED (JERRY_ESNEXT) - if (JERRY_CONTEXT (ecma_global_scope_cp) != JERRY_CONTEXT (ecma_global_env_cp)) - { - ecma_deref_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_scope_cp))); - } - JERRY_CONTEXT (ecma_global_scope_cp) = JMEM_CP_NULL; -#endif /* ENABLED (JERRY_ESNEXT) */ - ecma_deref_object (ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_env_cp))); - JERRY_CONTEXT (ecma_global_env_cp) = JMEM_CP_NULL; + /* After this point the gc can free the global object, but the global_object_p pointer + * is not set to NULL because the global object might still be used before the free. */ + ecma_deref_object ((ecma_object_t *) JERRY_CONTEXT (global_object_p)); } /* ecma_finalize_global_environment */ /** @@ -74,10 +60,10 @@ ecma_finalize_global_environment (void) * @return pointer to the object's instance */ ecma_object_t * -ecma_get_global_environment (void) +ecma_get_global_environment (ecma_object_t *global_object_p) /**< global object */ { - JERRY_ASSERT (JERRY_CONTEXT (ecma_global_env_cp) != JMEM_CP_NULL); - return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_env_cp)); + JERRY_ASSERT (global_object_p != NULL && ecma_builtin_is_global (global_object_p)); + return ECMA_GET_NON_NULL_POINTER (ecma_object_t, ((ecma_global_object_t *) global_object_p)->global_env_cp); } /* ecma_get_global_environment */ #if ENABLED (JERRY_ESNEXT) @@ -85,13 +71,18 @@ ecma_get_global_environment (void) * Create the global lexical block on top of the global environment. */ void -ecma_create_global_lexical_block (void) +ecma_create_global_lexical_block (ecma_object_t *global_object_p) /**< global object */ { - if (JERRY_CONTEXT (ecma_global_scope_cp) == JERRY_CONTEXT (ecma_global_env_cp)) + JERRY_ASSERT (global_object_p != NULL && ecma_builtin_is_global (global_object_p)); + + ecma_global_object_t *real_global_object_p = (ecma_global_object_t *) global_object_p; + + if (real_global_object_p->global_scope_cp == real_global_object_p->global_env_cp) { - ecma_object_t *global_scope_p = ecma_create_decl_lex_env (ecma_get_global_environment ()); + ecma_object_t *global_scope_p = ecma_create_decl_lex_env (ecma_get_global_environment (global_object_p)); global_scope_p->type_flags_refs |= (uint16_t) ECMA_OBJECT_FLAG_BLOCK; - ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_global_scope_cp), global_scope_p); + ECMA_SET_NON_NULL_POINTER (real_global_object_p->global_scope_cp, global_scope_p); + ecma_deref_object (global_scope_p); } } /* ecma_create_global_lexical_block */ #endif /* ENABLED (JERRY_ESNEXT) */ @@ -103,13 +94,13 @@ ecma_create_global_lexical_block (void) * @return pointer to the object's instance */ ecma_object_t * -ecma_get_global_scope (void) +ecma_get_global_scope (ecma_object_t *global_object_p) /**< global object */ { #if ENABLED (JERRY_ESNEXT) - JERRY_ASSERT (JERRY_CONTEXT (ecma_global_scope_cp) != JMEM_CP_NULL); - return ECMA_GET_NON_NULL_POINTER (ecma_object_t, JERRY_CONTEXT (ecma_global_scope_cp)); + JERRY_ASSERT (global_object_p != NULL && ecma_builtin_is_global (global_object_p)); + return ECMA_GET_NON_NULL_POINTER (ecma_object_t, ((ecma_global_object_t *) global_object_p)->global_scope_cp); #else /* !ENABLED (JERRY_ESNEXT) */ - return ecma_get_global_environment (); + return ecma_get_global_environment (global_object_p); #endif /* !ENABLED (JERRY_ESNEXT) */ } /* ecma_get_global_scope */ diff --git a/jerry-core/ecma/operations/ecma-lex-env.h b/jerry-core/ecma/operations/ecma-lex-env.h index f83540a8f..f45cbf027 100644 --- a/jerry-core/ecma/operations/ecma-lex-env.h +++ b/jerry-core/ecma/operations/ecma-lex-env.h @@ -32,10 +32,10 @@ void ecma_init_global_environment (void); void ecma_finalize_global_environment (void); -ecma_object_t *ecma_get_global_environment (void); -ecma_object_t *ecma_get_global_scope (void); +ecma_object_t *ecma_get_global_environment (ecma_object_t *global_object_p); +ecma_object_t *ecma_get_global_scope (ecma_object_t *global_object_p); #if ENABLED (JERRY_ESNEXT) -void ecma_create_global_lexical_block (void); +void ecma_create_global_lexical_block (ecma_object_t *global_object_p); #endif /* ENABLED (JERRY_ESNEXT) */ #if ENABLED (JERRY_MODULE_SYSTEM) diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 981b49818..78220af7b 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -2537,7 +2537,7 @@ inline static bool ecma_object_check_class_name_is_object (ecma_object_t *obj_p) /**< object */ { #ifndef JERRY_NDEBUG - return (ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_GLOBAL) + return (ecma_builtin_is_global (obj_p) #if ENABLED (JERRY_BUILTIN_PROMISE) || ecma_builtin_is (obj_p, ECMA_BUILTIN_ID_PROMISE_PROTOTYPE) #endif /* ENABLED (JERRY_BUILTIN_PROMISE) */ diff --git a/jerry-core/ecma/operations/ecma-reference.c b/jerry-core/ecma/operations/ecma-reference.c index 2b87837bd..ef9ab56a9 100644 --- a/jerry-core/ecma/operations/ecma-reference.c +++ b/jerry-core/ecma/operations/ecma-reference.c @@ -79,6 +79,23 @@ ecma_op_resolve_reference_base (ecma_object_t *lex_env_p, /**< starting lexical } /* ecma_op_resolve_reference_base */ #if ENABLED (JERRY_ESNEXT) + +/** + * Check if the passed lexical environment is a global lexical environment + * + * @return true - if the lexical environment is a global lexical environment + * false - otherwise + */ +static inline bool JERRY_ATTR_ALWAYS_INLINE +ecma_op_is_global_environment (ecma_object_t *lex_env_p) /**< lexical environment */ +{ + JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL + || ecma_get_lex_env_binding_object (lex_env_p) == ecma_builtin_get_global ()); + + return lex_env_p->u2.outer_reference_cp == JMEM_CP_NULL; +} /* ecma_op_is_global_environment */ + /** * Perform GetThisEnvironment and GetSuperBase operations * @@ -173,6 +190,7 @@ ecma_op_is_prop_unscopable (ecma_object_t *binding_obj_p, /**< binding object */ return ECMA_VALUE_FALSE; } /* ecma_op_is_prop_unscopable */ + #endif /* ENABLED (JERRY_ESNEXT) */ /** @@ -226,7 +244,7 @@ ecma_op_object_bound_environment_resolve_reference_value (ecma_object_t *lex_env } #if ENABLED (JERRY_ESNEXT) - if (JERRY_LIKELY (lex_env_p == ecma_get_global_scope ())) + if (JERRY_LIKELY (ecma_op_is_global_environment (lex_env_p))) #endif /* ENABLED (JERRY_ESNEXT) */ { return found_binding; @@ -297,7 +315,7 @@ ecma_op_resolve_reference_value (ecma_object_t *lex_env_p, /**< starting lexical else if (lex_env_type == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND) { #if ENABLED (JERRY_ESNEXT) - bool lcache_lookup_allowed = (lex_env_p == ecma_get_global_environment ()); + bool lcache_lookup_allowed = ecma_op_is_global_environment (lex_env_p); #else /* !ENABLED (JERRY_ESNEXT)*/ bool lcache_lookup_allowed = true; #endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 6f28f7d5e..60d3f1c7a 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -105,6 +105,7 @@ typedef enum JERRY_FEATURE_WEAKMAP, /**< WeakMap support */ JERRY_FEATURE_WEAKSET, /**< WeakSet support */ JERRY_FEATURE_BIGINT, /**< BigInt support */ + JERRY_FEATURE_REALM, /**< realm support */ JERRY_FEATURE__COUNT /**< number of features. NOTE: must be at the end of the list */ } jerry_feature_t; @@ -600,6 +601,7 @@ jerry_value_t jerry_create_external_string_sz (const jerry_char_t *str_p, jerry_ jerry_value_t jerry_create_symbol (const jerry_value_t value); jerry_value_t jerry_create_bigint (const uint64_t *digits_p, uint32_t size, bool sign); jerry_value_t jerry_create_undefined (void); +jerry_value_t jerry_create_realm (void); /** * General API functions of JS objects. diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index c87c210b9..a0db21ab0 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (61u) +#define JERRY_SNAPSHOT_VERSION (62u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 11f1a33a0..43f8d4f31 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -106,7 +106,7 @@ typedef struct jerry_context_data_header /** * First non-external member of the jerry context */ -#define JERRY_CONTEXT_FIRST_MEMBER ecma_builtin_objects +#define JERRY_CONTEXT_FIRST_MEMBER global_object_p /** * JerryScript context @@ -125,18 +125,18 @@ struct jerry_context_t #endif /* ENABLED (JERRY_EXTERNAL_CONTEXT) */ /* Update JERRY_CONTEXT_FIRST_MEMBER if the first non-external member changes */ - jmem_cpointer_t ecma_builtin_objects[ECMA_BUILTIN_ID__COUNT]; /**< pointer to instances of built-in objects */ + ecma_global_object_t *global_object_p; /**< current global object */ + jmem_heap_free_t *jmem_heap_list_skip_p; /**< improves deallocation performance */ + jmem_pools_chunk_t *jmem_free_8_byte_chunk_p; /**< list of free eight byte pool chunks */ #if ENABLED (JERRY_BUILTIN_REGEXP) re_compiled_code_t *re_cache[RE_CACHE_SIZE]; /**< regex cache */ #endif /* ENABLED (JERRY_BUILTIN_REGEXP) */ - jmem_cpointer_t ecma_gc_objects_cp; /**< List of currently alive objects. */ - jmem_heap_free_t *jmem_heap_list_skip_p; /**< This is used to speed up deallocation. */ - jmem_pools_chunk_t *jmem_free_8_byte_chunk_p; /**< list of free eight byte pool chunks */ #if ENABLED (JERRY_CPOINTER_32_BIT) jmem_pools_chunk_t *jmem_free_16_byte_chunk_p; /**< list of free sixteen byte pool chunks */ #endif /* ENABLED (JERRY_CPOINTER_32_BIT) */ const lit_utf8_byte_t * const *lit_magic_string_ex_array; /**< array of external magic strings */ const lit_utf8_size_t *lit_magic_string_ex_sizes; /**< external magic string lengths */ + jmem_cpointer_t ecma_gc_objects_cp; /**< List of currently alive objects. */ jmem_cpointer_t string_list_first_cp; /**< first item of the literal string list */ #if ENABLED (JERRY_ESNEXT) jmem_cpointer_t symbol_list_first_cp; /**< first item of the global symbol list */ @@ -145,10 +145,6 @@ struct jerry_context_t #if ENABLED (JERRY_BUILTIN_BIGINT) jmem_cpointer_t bigint_list_first_cp; /**< first item of the literal bigint list */ #endif /* ENABLED (JERRY_BUILTIN_BIGINT) */ - jmem_cpointer_t ecma_global_env_cp; /**< global lexical environment */ -#if ENABLED (JERRY_ESNEXT) - jmem_cpointer_t ecma_global_scope_cp; /**< global lexical scope */ -#endif /* ENABLED (JERRY_ESNEXT) */ #if ENABLED (JERRY_MODULE_SYSTEM) ecma_module_t *ecma_modules_p; /**< list of referenced modules */ diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 2df861152..dfe7a2922 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -852,6 +852,9 @@ typedef struct uint8_t ident_end; /**< end position of the identifier group */ uint8_t const_literal_end; /**< end position of the const literal group */ uint8_t literal_end; /**< end position of the literal group */ +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_value_t realm_value; /**< realm value */ +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } cbc_uint8_arguments_t; /** @@ -867,6 +870,9 @@ typedef struct uint16_t const_literal_end; /**< end position of the const literal group */ uint16_t literal_end; /**< end position of the literal group */ uint16_t padding; /**< an unused value */ +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_value_t realm_value; /**< realm value */ +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ } cbc_uint16_arguments_t; /** @@ -901,6 +907,7 @@ typedef enum CBC_FUNCTION_CONSTRUCTOR, /**< constructor function */ /* The following functions cannot be constructed (see CBC_FUNCTION_IS_CONSTRUCTABLE) */ + CBC_FUNCTION_SCRIPT, /**< script (global) function */ CBC_FUNCTION_GENERATOR, /**< generator function */ CBC_FUNCTION_ASYNC, /**< async function */ CBC_FUNCTION_ASYNC_GENERATOR, /**< async generator function */ diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 0290e64fc..5b6c38000 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -977,6 +977,9 @@ parser_post_processing (parser_context_t *context_p) /**< context */ args_p->ident_end = ident_end; args_p->const_literal_end = const_literal_end; args_p->literal_end = context_p->literal_count; +#if ENABLED (JERRY_BUILTIN_REALMS) + args_p->realm_value = ecma_make_object_value ((ecma_object_t *) JERRY_CONTEXT (global_object_p)); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ compiled_code_p->status_flags |= CBC_CODE_FLAGS_UINT16_ARGUMENTS; byte_code_p += sizeof (cbc_uint16_arguments_t); @@ -991,6 +994,9 @@ parser_post_processing (parser_context_t *context_p) /**< context */ args_p->ident_end = (uint8_t) ident_end; args_p->const_literal_end = (uint8_t) const_literal_end; args_p->literal_end = (uint8_t) context_p->literal_count; +#if ENABLED (JERRY_BUILTIN_REALMS) + args_p->realm_value = ecma_make_object_value ((ecma_object_t *) JERRY_CONTEXT (global_object_p)); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ byte_code_p += sizeof (cbc_uint8_arguments_t); } @@ -1032,6 +1038,10 @@ parser_post_processing (parser_context_t *context_p) /**< context */ { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ACCESSOR); } + else if (!(context_p->status_flags & PARSER_IS_FUNCTION)) + { + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_SCRIPT); + } #if ENABLED (JERRY_ESNEXT) else if (context_p->status_flags & PARSER_IS_ARROW_FUNCTION) { @@ -1067,10 +1077,6 @@ parser_post_processing (parser_context_t *context_p) /**< context */ { function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_METHOD); } - else - { - function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL); - } if (context_p->status_flags & PARSER_LEXICAL_BLOCK_NEEDED) { diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 6c67e3f92..324d85195 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -842,8 +842,18 @@ opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /* ecma_object_t *old_new_target = JERRY_CONTEXT (current_new_target); JERRY_CONTEXT (current_new_target) = NULL; +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_global_object_t *saved_global_object_p = JERRY_CONTEXT (global_object_p); + ecma_value_t realm_value = ecma_op_function_get_realm (bytecode_header_p); + JERRY_CONTEXT (global_object_p) = (ecma_global_object_t *) ecma_get_object_from_value (realm_value); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + ecma_value_t result = vm_execute (&executable_object_p->frame_ctx); +#if ENABLED (JERRY_BUILTIN_REALMS) + JERRY_CONTEXT (global_object_p) = saved_global_object_p; +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + JERRY_CONTEXT (current_new_target) = old_new_target; executable_object_p->extended_object.u.class_prop.extra_info &= (uint16_t) ~ECMA_EXECUTABLE_OBJECT_RUNNING; diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index ffa294b18..dfca952a4 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -301,16 +301,22 @@ vm_run_module (const ecma_compiled_code_t *bytecode_p, /**< pointer to bytecode ecma_value_t vm_run_global (const ecma_compiled_code_t *bytecode_p) /**< pointer to bytecode to run */ { - ecma_object_t *glob_obj_p = ecma_builtin_get_global (); +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_value_t realm_value = ecma_op_function_get_realm (bytecode_p); + ecma_object_t *global_obj_p = ecma_get_object_from_value (realm_value); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ + ecma_object_t *global_obj_p = ecma_builtin_get_global (); + ecma_value_t realm_value = ecma_make_object_value (global_obj_p); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ #if ENABLED (JERRY_ESNEXT) if (bytecode_p->status_flags & CBC_CODE_FLAGS_LEXICAL_BLOCK_NEEDED) { - ecma_create_global_lexical_block (); + ecma_create_global_lexical_block (global_obj_p); } #endif /* ENABLED (JERRY_ESNEXT) */ - ecma_object_t *const global_scope_p = ecma_get_global_scope (); + ecma_object_t *const global_scope_p = ecma_get_global_scope (global_obj_p); #if ENABLED (JERRY_MODULE_SYSTEM) if (JERRY_CONTEXT (module_top_context_p) != NULL) @@ -337,7 +343,7 @@ vm_run_global (const ecma_compiled_code_t *bytecode_p) /**< pointer to bytecode shared.bytecode_header_p = bytecode_p; shared.status_flags = 0; - return vm_run (&shared, ecma_make_object_value (glob_obj_p), global_scope_p); + return vm_run (&shared, realm_value, global_scope_p); } /* vm_run_global */ /** @@ -381,10 +387,16 @@ vm_run_eval (ecma_compiled_code_t *bytecode_data_p, /**< byte-code data */ } else { +#if ENABLED (JERRY_BUILTIN_REALMS) + ecma_value_t realm_value = ecma_op_function_get_realm (bytecode_data_p); + ecma_object_t *global_obj_p = ecma_get_object_from_value (realm_value); +#else /* !ENABLED (JERRY_BUILTIN_REALMS) */ ecma_object_t *global_obj_p = ecma_builtin_get_global (); + ecma_value_t realm_value = ecma_make_object_value (global_obj_p); +#endif /* ENABLED (JERRY_BUILTIN_REALMS) */ + this_binding = realm_value; ecma_ref_object (global_obj_p); - this_binding = ecma_make_object_value (global_obj_p); - lex_env_p = ecma_get_global_scope (); + lex_env_p = ecma_get_global_scope (global_obj_p); } ecma_ref_object (lex_env_p); @@ -1604,7 +1616,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ #if ENABLED (JERRY_ESNEXT) case VM_OC_CHECK_VAR: { - JERRY_ASSERT (ecma_get_global_scope () == frame_ctx_p->lex_env_p); + JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (frame_ctx_p->shared_p->bytecode_header_p->status_flags) + == CBC_FUNCTION_SCRIPT); uint32_t literal_index; READ_LITERAL_INDEX (literal_index); @@ -1627,7 +1640,8 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } case VM_OC_CHECK_LET: { - JERRY_ASSERT (ecma_get_global_scope () == frame_ctx_p->lex_env_p); + JERRY_ASSERT (CBC_FUNCTION_GET_TYPE (frame_ctx_p->shared_p->bytecode_header_p->status_flags) + == CBC_FUNCTION_SCRIPT); uint32_t literal_index; READ_LITERAL_INDEX (literal_index); diff --git a/jerry-main/main-utils.c b/jerry-main/main-utils.c index 6597cc50e..8b6767c35 100644 --- a/jerry-main/main-utils.c +++ b/jerry-main/main-utils.c @@ -44,6 +44,19 @@ main_register_global_function (const char *name_p, /**< name of the function */ jerry_release_value (result_val); } /* main_register_global_function */ +static jerry_value_t +main_create_realm (const jerry_value_t func_obj_val, /**< function object */ + const jerry_value_t this_p, /**< this arg */ + const jerry_value_t args_p[], /**< function arguments */ + const jerry_length_t args_cnt) /**< number of function arguments */ +{ + (void) func_obj_val; /* unused */ + (void) this_p; /* unused */ + (void) args_p; /* unused */ + (void) args_cnt; /* unused */ + return jerry_create_realm (); +} /* main_create_realm */ + /** * Register a method for the $262 object. */ @@ -204,6 +217,7 @@ main_init_engine (main_args_t *arguments_p) /** main arguments */ main_register_global_function ("gc", jerryx_handler_gc); main_register_global_function ("print", jerryx_handler_print); main_register_global_function ("resourceName", jerryx_handler_resource_name); + main_register_global_function ("createRealm", main_create_realm); } /* main_init_engine */ /** diff --git a/tests/debugger/do_variables.expected b/tests/debugger/do_variables.expected index 3d500a71f..977d92862 100644 --- a/tests/debugger/do_variables.expected +++ b/tests/debugger/do_variables.expected @@ -9,6 +9,7 @@ f | Function | addX | Function | z | undefined | undefined c | undefined | undefined +createRealm | Function | resourceName | Function | print | Function | gc | Function | @@ -21,6 +22,7 @@ f | Function | addX | Function | z | undefined | undefined c | undefined | undefined +createRealm | Function | resourceName | Function | print | Function | gc | Function | @@ -48,6 +50,7 @@ f | Function | addX | Function | z | Number | 5 c | Number | 4 +createRealm | Function | resourceName | Function | print | Function | gc | Function | @@ -68,6 +71,7 @@ f | Function | addX | Function | z | Number | 5 c | Number | 4 +createRealm | Function | resourceName | Function | print | Function | gc | Function | diff --git a/tests/jerry/es.next/realms1.js b/tests/jerry/es.next/realms1.js new file mode 100644 index 000000000..daeee9ab5 --- /dev/null +++ b/tests/jerry/es.next/realms1.js @@ -0,0 +1,95 @@ +// 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. + +var realm = createRealm() +var ev = realm.eval + +assert(realm.Math != Math) +assert(typeof realm.Math === "object") +assert(realm.Object != Object) +assert(typeof realm.Object === "function") +assert(realm.eval != eval) +assert(typeof realm.eval === "function") + +// Share our 'assert' function with the realm +realm.assert = assert + +// Test1: var1 and var2 must be different properties + +realm.var1 = 3.5 +assert(realm.var2 === undefined) + +var var1 = "X" +var var2 = "Y" + +ev("assert(var1 === 3.5); \ + try { realm; assert(false) } catch (e) { assert(e instanceof ReferenceError) } \ + assert(this === globalThis); \ + var var2 = this") +assert(realm.var2 === realm) + +assert(var1 === "X") +assert(var2 === "Y") + +// Test2: constructing any objects (including errors) must use the realm + +assert (realm.RangeError != RangeError) +assert (realm.RangeError.prototype != RangeError.prototype) + +realm.RangeError.prototype.myProperty = "XY" +assert(RangeError.prototype.myProperty === undefined) + +try { + var NumberToString = realm.Number.prototype.toString; + NumberToString.call(0, 0) + assert(false) +} catch (e) { + assert(e instanceof realm.RangeError) + assert(!(e instanceof RangeError)) + assert(e.myProperty === "XY") +} + +assert (realm.SyntaxError != SyntaxError) +assert (realm.SyntaxError.prototype != SyntaxError.prototype) + +realm.SyntaxError.prototype.myProperty = "AB" +assert(SyntaxError.prototype.myProperty === undefined) + +try { + ev("5 +") + assert(false) +} catch (e) { + assert(e instanceof realm.SyntaxError) + assert(!(e instanceof SyntaxError)) + assert(e.myProperty === "AB") +} + +// Test3: only the realm corresponding to the function matters + +realm.Boolean.prototype.valueOf.a = Function.prototype.apply +Boolean.prototype.valueOf.a = realm.Function.prototype.apply + +try { + realm.Boolean.prototype.valueOf.a() +} catch (e) { + assert(e instanceof realm.TypeError) + assert(!(e instanceof TypeError)) +} + +try { + Boolean.prototype.valueOf.a() +} catch (e) { + assert(e instanceof TypeError) + assert(!(e instanceof realm.TypeError)) +} diff --git a/tests/jerry/es.next/realms2.js b/tests/jerry/es.next/realms2.js new file mode 100644 index 000000000..3082963ef --- /dev/null +++ b/tests/jerry/es.next/realms2.js @@ -0,0 +1,48 @@ +// 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. + +// Test1: realm cannot be gc-ed until it is referenced + +function f1() { + var ev = createRealm().eval + + gc() + + try { + ev("5 +") + } catch (e) { + assert(e instanceof ev("this").SyntaxError) + assert(!(e instanceof SyntaxError)) + } +} +f1() + +// Test2: built-ins cannot be gc-ed until the realm exists + +function f2() { + var realm = createRealm() + + var str = new realm.String("A"); + // Assign a property to String.prototype + Object.getPrototypeOf(str).myProperty = "XY" + + str = null + // No reference to String.prototype + gc() + + str = new realm.String("A") + assert(Object.getPrototypeOf(str).myProperty === "XY") + assert(realm.String.prototype.myProperty === "XY") +} +f2() diff --git a/tests/jerry/es.next/realms3.js b/tests/jerry/es.next/realms3.js new file mode 100644 index 000000000..1d648d410 --- /dev/null +++ b/tests/jerry/es.next/realms3.js @@ -0,0 +1,198 @@ +// 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. + +// Test1: reading global values from other realms + +var a = 4.5 + +function f1() +{ + var realm = createRealm() + var f = realm.eval("function g() { return a } g") + + realm.a = -6.25 + assert(f() === -6.25) + assert(a === 4.5) + assert(realm.g === f) + + realm.eval("var v1 = 6; eval('var v2 = 7.5')") + assert(realm.v1 === 6) + assert(realm.v2 === 7.5) + + var e = realm.eval + + eval("e('var v3 = -5.25'); var v3 = 1.5; e('v3++')") + assert(realm.v3 === -4.25) + assert(v3 === 1.5) +} +f1() + +// Test2: recursive calls + +var b = "S" + +function f2() +{ + function f(fun) { + return fun() + b; + } + + var realm = createRealm() + + realm.eval("function h() { return b }") + + var g = realm.Function("fun", "return fun(h) + b") + + realm.b = "X" + assert(g(f) === "XSX") + assert(b === "S") +} +f2() + +// Test3: built-in functions + +function f3() +{ + var realm = createRealm() + + realm.f = function () { return eval("/a/") } + + var res = realm.eval("[f(), /a/, f(), /b/]") + + assert(Object.getPrototypeOf(res[0]) === RegExp.prototype) + assert(Object.getPrototypeOf(res[1]) === realm.RegExp.prototype) + assert(Object.getPrototypeOf(res[2]) === RegExp.prototype) + assert(Object.getPrototypeOf(res[3]) === realm.RegExp.prototype) + + realm.g = function () { return non_existent } + + try { + realm.eval("g()") + assert(false) + } catch (e) { + assert(e instanceof ReferenceError) + assert(!(e instanceof realm.ReferenceError)) + } + + try { + realm.eval("non_existent") + assert(false) + } catch (e) { + assert(e instanceof realm.ReferenceError) + assert(!(e instanceof ReferenceError)) + } +} +f3() + +// Test4: generator functions + +function f4() +{ + function f() { + return b; + } + + var realm = createRealm() + + function check_gen_result(result, value, done) + { + assert(Object.getPrototypeOf(result) === realm.Object.prototype) + assert(result.value === value) + assert(result.done === done) + } + + realm.eval("var a = 'P', b = 'Q'") + + var gen = realm.eval("function* gen(f) { yield f() + a; yield f() + b; return a + b + f() }") + + var it = realm.gen(f) + + check_gen_result(it.next(), "SP", false) + check_gen_result(it.next(), "SQ", false) + check_gen_result(it.next(), "PQS", true) +} +f4() + +// Test5: async functions + +var successCount = 0 + +function f5() +{ + function f() { + return b; + } + + var r, p = new Promise(function(resolve, reject) { r = resolve }) + + var realm = createRealm() + + realm.assert = assert + realm.eval("async function asy(p, f) { assert(f() === 'S'); assert(await p === 4.5); return f() + b }") + + realm.b = "X" + + realm.asy(p, f).then(function(v) { + assert(v === "SX") + successCount++ + }, function() { + assert(false) + }) + r(4.5) +} +f5() + +// Test6: async generator functions + +function f6() +{ + function f() { + return b; + } + + var r, p = new Promise(function(resolve, reject) { r = resolve }) + + var realm = createRealm() + + function check_fulfilled(p, value, done) + { + assert(p instanceof realm.Promise) + + p.then(function(v) { + assert(v.value === value) + assert(v.done === done) + successCount++ + }, function() { + assert(false) + }) + } + + realm.assert = assert + realm.eval("async function *asygen(p, f) { assert(f() === 'S'); assert(await p === -1.5); yield f() + a; return f() + b }") + + realm.a = "X" + realm.b = "Y" + + var gen = realm.asygen(p, f) + + check_fulfilled(gen.next(), "SX", false) + check_fulfilled(gen.next(), "SY", true) + + r(-1.5) +} +f6() + +function __checkAsync() { + assert(successCount === 3) +} diff --git a/tests/jerry/regression-test-issue-2105.js b/tests/jerry/regression-test-issue-2105.js index 184566e4a..95c7c2418 100644 --- a/tests/jerry/regression-test-issue-2105.js +++ b/tests/jerry/regression-test-issue-2105.js @@ -51,4 +51,4 @@ try { /* Check properties of a */ assert(Object.keys(a) == "one,two"); /* Check properties of global object */ -assert(Object.keys(this) == "assert,gc,print,resourceName,a,fail,fail_two"); +assert(Object.keys(this) == "assert,gc,print,resourceName,createRealm,a,fail,fail_two");