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
This commit is contained in:
Zoltan Herczeg
2020-12-08 14:36:36 +01:00
committed by GitHub
parent 7cb9f808f7
commit df92c86ecf
32 changed files with 1128 additions and 188 deletions
@@ -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);
@@ -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,
@@ -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);
+20 -29
View File
@@ -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 */
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -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) */
+20 -2
View File
@@ -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) */