jerryscript/jerry-core/ecma/base/ecma-helpers.c
Dániel Bátyai ac1c48eeff
Update jerry-port and jerry-ext (#4907)
Notable changes:
  - Updated and the port API interface, new functions have been added
    and some have been changed. The port library is now cleaned up to
    not have any dependency on jerry-core, as it should be. The port library
    is now strictly a collection of functions that implement
    embedding/platform specific behavior.
  - The default port implementation has been split for windows and unix.
    Implemented port functions have been categorized and reorganized,
    and marked with attribute((weak)) for better reusability.
  - External context allocation has been moved to the port API instead
    of a core API callback. The iterface has also been extended with a
    function to free the allocated context. When external context is
    enabled, jerry_init now automatically calls the port implementation
    to allocate the context and jerry_cleanup automatically calls the port
    to free the context.
  - jerry_port_log has been changed to no longer require formatting to
    be implemented by the port. The reason beind this is that it was vague what
    format specifiers were used by the engine, and in what manner. The port
    function now takes a zero-terminated string, and should only implement
    how the string should be logged.
  - Logging and log message formatting is now handled by the core jerry library
    where it can be implemented as necessary. Logging can be done through a new
    core API function, which uses the port to output the final log message.
  - Log level has been moved into jerry-core, and an API function has
    been added to set the log level. It should be the library that
    filters log messages based on the requested log level, instead of
    logging everything and requiring the user to do so.
  - Module resolving logic has been moved into jerry-core. There's no
    reason to have it in the port library and requiring embedders to
    duplicate the code. It also added an unnecessary dependency on
    jerry-core to the port. Platform specific behavior is still used through
    the port API, like resolving module specifiers, and reading source file
    contents. If necessary, the resolving logic can still be overridden as
    previously.
  - The jerry-ext library has also been cleaned up, and many utility
    functions have been added that previously were implemented in
    jerry-main. This allows easier reusability for some common operations,
    like printing unhandled exceptions or providing a repl console.
  - Debugger interaction with logged/printed messages has been fixed, so
    that it's no longer the port implementations responsibility to send
    the output to the debugger, as the port should have no notion of what a
    debugger is.  The printing and logging functions will now pass the
    result message to the debugger, if connected.
  - Cleaned up TZA handling in the date port implementation, and simplified
    the API function prototype.
  - Moved property access helper functions that use ASCII strings as
    keys from jerry-ext to the core API.

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
2022-01-20 13:53:47 +01:00

1818 lines
58 KiB
C

/* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecma-helpers.h"
#include "ecma-alloc.h"
#include "ecma-array-object.h"
#include "ecma-builtins.h"
#include "ecma-function-object.h"
#include "ecma-gc.h"
#include "ecma-globals.h"
#include "ecma-lcache.h"
#include "ecma-line-info.h"
#include "ecma-property-hashmap.h"
#include "byte-code.h"
#include "jcontext.h"
#include "jrt-bit-fields.h"
#include "re-compiler.h"
#if JERRY_DEBUGGER
#include "debugger.h"
#endif /* JERRY_DEBUGGER */
/** \addtogroup ecma ECMA
* @{
*
* \addtogroup ecmahelpers Helpers for operations with ECMA data types
* @{
*/
JERRY_STATIC_ASSERT (ECMA_OBJECT_TYPE_MASK >= ECMA_OBJECT_TYPE__MAX - 1,
ecma_object_types_must_be_lower_than_the_container_mask);
JERRY_STATIC_ASSERT (ECMA_OBJECT_TYPE_MASK >= ECMA_LEXICAL_ENVIRONMENT_TYPE__MAX,
ecma_lexical_environment_types_must_be_lower_than_the_container_mask);
JERRY_STATIC_ASSERT (ECMA_OBJECT_FLAG_EXTENSIBLE == ECMA_OBJECT_TYPE_MASK + 1,
ecma_extensible_flag_must_follow_the_object_type);
JERRY_STATIC_ASSERT (ECMA_OBJECT_REF_ONE == (ECMA_OBJECT_FLAG_EXTENSIBLE << 1),
ecma_object_ref_one_must_follow_the_extensible_flag);
JERRY_STATIC_ASSERT ((ECMA_OBJECT_MAX_REF + ECMA_OBJECT_REF_ONE) == ECMA_OBJECT_REF_MASK,
ecma_object_max_ref_does_not_fill_the_remaining_bits);
JERRY_STATIC_ASSERT ((ECMA_OBJECT_REF_MASK & (ECMA_OBJECT_TYPE_MASK | ECMA_OBJECT_FLAG_EXTENSIBLE)) == 0,
ecma_object_ref_mask_overlaps_with_object_type_or_extensible);
JERRY_STATIC_ASSERT (ECMA_PROPERTY_FLAGS_MASK == ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
ecma_property_flags_mask_must_use_the_configurable_enumerable_writable_flags);
/* These checks are needed by ecma_get_object_base_type. */
JERRY_STATIC_ASSERT ((int) ECMA_OBJECT_TYPE_BUILT_IN_GENERAL == ((int) ECMA_OBJECT_TYPE_GENERAL | 0x1)
&& ((int) ECMA_OBJECT_TYPE_GENERAL & 0x1) == 0,
ecma_object_type_built_in_general_has_unexpected_value);
JERRY_STATIC_ASSERT ((int) ECMA_OBJECT_TYPE_BUILT_IN_CLASS == ((int) ECMA_OBJECT_TYPE_CLASS | 0x1)
&& ((int) ECMA_OBJECT_TYPE_CLASS & 0x1) == 0,
ecma_object_type_built_in_class_has_unexpected_value);
JERRY_STATIC_ASSERT ((int) ECMA_OBJECT_TYPE_BUILT_IN_ARRAY == ((int) ECMA_OBJECT_TYPE_ARRAY | 0x1)
&& ((int) ECMA_OBJECT_TYPE_ARRAY & 0x1) == 0,
ecma_object_type_built_in_array_has_unexpected_value);
/**
* Create an object with specified prototype object
* (or NULL prototype if there is not prototype for the object)
* and value of 'Extensible' attribute.
*
* Reference counter's value will be set to one.
*
* @return pointer to the object's descriptor
*/
ecma_object_t *
ecma_create_object (ecma_object_t *prototype_object_p, /**< pointer to prototybe of the object (or NULL) */
size_t ext_object_size, /**< size of extended objects */
ecma_object_type_t type) /**< object type */
{
ecma_object_t *new_object_p;
if (ext_object_size > 0)
{
new_object_p = (ecma_object_t *) ecma_alloc_extended_object (ext_object_size);
}
else
{
new_object_p = ecma_alloc_object ();
}
new_object_p->type_flags_refs = (ecma_object_descriptor_t) (type | ECMA_OBJECT_FLAG_EXTENSIBLE);
ecma_init_gc_info (new_object_p);
new_object_p->u1.property_list_cp = JMEM_CP_NULL;
ECMA_SET_POINTER (new_object_p->u2.prototype_cp, prototype_object_p);
return new_object_p;
} /* ecma_create_object */
/**
* Create a declarative lexical environment with specified outer lexical environment
* (or NULL if the environment is not nested).
*
* See also: ECMA-262 v5, 10.2.1.1
*
* Reference counter's value will be set to one.
*
* @return pointer to the descriptor of lexical environment
*/
ecma_object_t *
ecma_create_decl_lex_env (ecma_object_t *outer_lexical_environment_p) /**< outer lexical environment */
{
ecma_object_t *new_lexical_environment_p = ecma_alloc_object ();
new_lexical_environment_p->type_flags_refs = ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE;
ecma_init_gc_info (new_lexical_environment_p);
new_lexical_environment_p->u1.property_list_cp = JMEM_CP_NULL;
ECMA_SET_POINTER (new_lexical_environment_p->u2.outer_reference_cp, outer_lexical_environment_p);
return new_lexical_environment_p;
} /* ecma_create_decl_lex_env */
/**
* Create a object lexical environment with specified outer lexical environment
* (or NULL if the environment is not nested), and binding object.
*
* See also: ECMA-262 v5, 10.2.1.2
*
* Reference counter's value will be set to one.
*
* @return pointer to the descriptor of lexical environment
*/
ecma_object_t *
ecma_create_object_lex_env (ecma_object_t *outer_lexical_environment_p, /**< outer lexical environment */
ecma_object_t *binding_obj_p) /**< binding object */
{
JERRY_ASSERT (binding_obj_p != NULL && !ecma_is_lexical_environment (binding_obj_p));
ecma_object_t *new_lexical_environment_p = ecma_alloc_object ();
new_lexical_environment_p->type_flags_refs = ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND;
ecma_init_gc_info (new_lexical_environment_p);
ECMA_SET_NON_NULL_POINTER (new_lexical_environment_p->u1.bound_object_cp, binding_obj_p);
ECMA_SET_POINTER (new_lexical_environment_p->u2.outer_reference_cp, outer_lexical_environment_p);
return new_lexical_environment_p;
} /* ecma_create_object_lex_env */
#if JERRY_ESNEXT
/**
* Create a lexical environment with a specified size.
*
* @return pointer to the descriptor of the lexical environment
*/
ecma_object_t *
ecma_create_lex_env_class (ecma_object_t *outer_lexical_environment_p, /**< outer lexical environment */
size_t lexical_env_size) /**< size of the lexical environment */
{
ecma_object_t *new_lexical_environment_p;
ecma_object_descriptor_t type_flags_refs = ECMA_LEXICAL_ENVIRONMENT_CLASS;
if (lexical_env_size > 0)
{
new_lexical_environment_p = (ecma_object_t *) ecma_alloc_extended_object (lexical_env_size);
type_flags_refs |= ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA;
}
else
{
new_lexical_environment_p = ecma_alloc_object ();
}
new_lexical_environment_p->type_flags_refs = type_flags_refs;
ecma_init_gc_info (new_lexical_environment_p);
new_lexical_environment_p->u1.property_list_cp = JMEM_CP_NULL;
ECMA_SET_POINTER (new_lexical_environment_p->u2.outer_reference_cp, outer_lexical_environment_p);
return new_lexical_environment_p;
} /* ecma_create_lex_env_class */
#endif /* JERRY_ESNEXT */
/**
* Check if the object is lexical environment.
*
* @return true - if object is a lexical environment
* false - otherwise
*/
extern inline bool JERRY_ATTR_PURE
ecma_is_lexical_environment (const ecma_object_t *object_p) /**< object or lexical environment */
{
JERRY_ASSERT (object_p != NULL);
return (object_p->type_flags_refs & ECMA_OBJECT_TYPE_MASK) >= ECMA_LEXICAL_ENVIRONMENT_TYPE_START;
} /* ecma_is_lexical_environment */
/**
* Set value of [[Extensible]] object's internal property.
*/
extern inline void
ecma_op_ordinary_object_set_extensible (ecma_object_t *object_p) /**< object */
{
JERRY_ASSERT (object_p != NULL);
JERRY_ASSERT (!ecma_is_lexical_environment (object_p));
object_p->type_flags_refs |= ECMA_OBJECT_FLAG_EXTENSIBLE;
} /* ecma_op_ordinary_object_set_extensible */
/**
* Get the internal type of an object.
*
* @return type of the object (ecma_object_type_t)
*/
extern inline ecma_object_type_t JERRY_ATTR_PURE
ecma_get_object_type (const ecma_object_t *object_p) /**< object */
{
JERRY_ASSERT (object_p != NULL);
JERRY_ASSERT (!ecma_is_lexical_environment (object_p));
return (ecma_object_type_t) (object_p->type_flags_refs & ECMA_OBJECT_TYPE_MASK);
} /* ecma_get_object_type */
/**
* Get the internal base type of an object.
*
* @return base type of the object (ecma_object_base_type_t)
*/
extern inline ecma_object_base_type_t JERRY_ATTR_PURE
ecma_get_object_base_type (const ecma_object_t *object_p) /**< object */
{
JERRY_ASSERT (object_p != NULL);
JERRY_ASSERT (!ecma_is_lexical_environment (object_p));
return (ecma_object_base_type_t) (object_p->type_flags_refs & (ECMA_OBJECT_TYPE_MASK - 0x1));
} /* ecma_get_object_base_type */
/**
* Get value of an object if the class matches
*
* @return value of the object if the class matches
* ECMA_VALUE_NOT_FOUND otherwise
*/
extern inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_object_class_is (ecma_object_t *object_p, /**< object */
ecma_object_class_type_t class_id) /**< class id */
{
if (ecma_get_object_base_type (object_p) == ECMA_OBJECT_BASE_TYPE_CLASS)
{
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
if (ext_object_p->u.cls.type == (uint8_t) class_id)
{
return true;
}
}
return false;
} /* ecma_object_class_is */
/**
* Get type of lexical environment.
*
* @return type of the lexical environment (ecma_lexical_environment_type_t)
*/
extern inline ecma_lexical_environment_type_t JERRY_ATTR_PURE
ecma_get_lex_env_type (const ecma_object_t *object_p) /**< lexical environment */
{
JERRY_ASSERT (object_p != NULL);
JERRY_ASSERT (ecma_is_lexical_environment (object_p));
return (ecma_lexical_environment_type_t) (object_p->type_flags_refs & ECMA_OBJECT_TYPE_MASK);
} /* ecma_get_lex_env_type */
/**
* Get lexical environment's bound object.
*
* @return pointer to ecma object
*/
extern inline ecma_object_t *JERRY_ATTR_PURE
ecma_get_lex_env_binding_object (const ecma_object_t *object_p) /**< object-bound lexical environment */
{
JERRY_ASSERT (object_p != NULL);
JERRY_ASSERT (ecma_is_lexical_environment (object_p));
#if JERRY_ESNEXT
JERRY_ASSERT (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND
|| (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS
&& !ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)));
#else /* !JERRY_ESNEXT */
JERRY_ASSERT (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND);
#endif /* JERRY_ESNEXT */
return ECMA_GET_NON_NULL_POINTER (ecma_object_t, object_p->u1.bound_object_cp);
} /* ecma_get_lex_env_binding_object */
/**
* Create a new lexical environment with the same property list as the passed lexical environment
*
* @return pointer to the newly created lexical environment
*/
ecma_object_t *
ecma_clone_decl_lexical_environment (ecma_object_t *lex_env_p, /**< declarative lexical environment */
bool copy_values) /**< copy property values as well */
{
JERRY_ASSERT (ecma_get_lex_env_type (lex_env_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE);
JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL);
ecma_object_t *outer_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp);
ecma_object_t *new_lex_env_p = ecma_create_decl_lex_env (outer_lex_env_p);
jmem_cpointer_t prop_iter_cp = lex_env_p->u1.property_list_cp;
ecma_property_header_t *prop_iter_p;
JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL);
#if JERRY_PROPERTY_HASHMAP
prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
prop_iter_cp = prop_iter_p->next_property_cp;
}
JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL);
#endif /* JERRY_PROPERTY_HASHMAP */
do
{
prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
{
if (prop_iter_p->types[i] != ECMA_PROPERTY_TYPE_DELETED)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_RAW_DATA (prop_iter_p->types[i]));
uint8_t prop_attributes = (uint8_t) (prop_iter_p->types[i] & ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
ecma_string_t *name_p = ecma_string_from_property_name (prop_iter_p->types[i], prop_pair_p->names_cp[i]);
ecma_property_value_t *property_value_p;
property_value_p = ecma_create_named_data_property (new_lex_env_p, name_p, prop_attributes, NULL);
ecma_deref_ecma_string (name_p);
JERRY_ASSERT (property_value_p->value == ECMA_VALUE_UNDEFINED);
if (copy_values)
{
property_value_p->value = ecma_copy_value_if_not_object (prop_pair_p->values[i].value);
}
else
{
property_value_p->value = ECMA_VALUE_UNINITIALIZED;
}
}
}
prop_iter_cp = prop_iter_p->next_property_cp;
} while (prop_iter_cp != JMEM_CP_NULL);
ecma_deref_object (lex_env_p);
return new_lex_env_p;
} /* ecma_clone_decl_lexical_environment */
/**
* Create a property in an object and link it into
* the object's properties' linked-list (at start of the list).
*
* @return pointer to the newly created property value
*/
static ecma_property_value_t *
ecma_create_property (ecma_object_t *object_p, /**< the object */
ecma_string_t *name_p, /**< property name */
uint8_t type_and_flags, /**< type and flags, see ecma_property_info_t */
ecma_property_value_t value, /**< property value */
ecma_property_t **out_prop_p) /**< [out] the property is also returned
* if this field is non-NULL */
{
JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2);
JERRY_ASSERT (name_p != NULL);
JERRY_ASSERT (object_p != NULL);
jmem_cpointer_t *property_list_head_p = &object_p->u1.property_list_cp;
if (*property_list_head_p != ECMA_NULL_POINTER)
{
/* If the first entry is free (deleted), it is reused. */
ecma_property_header_t *first_property_p =
ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, *property_list_head_p);
#if JERRY_PROPERTY_HASHMAP
bool has_hashmap = false;
if (first_property_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
property_list_head_p = &first_property_p->next_property_cp;
first_property_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, *property_list_head_p);
has_hashmap = true;
}
#endif /* JERRY_PROPERTY_HASHMAP */
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (first_property_p));
if (first_property_p->types[0] == ECMA_PROPERTY_TYPE_DELETED)
{
ecma_property_pair_t *first_property_pair_p = (ecma_property_pair_t *) first_property_p;
ecma_property_t name_type;
first_property_pair_p->names_cp[0] = ecma_string_to_property_name (name_p, &name_type);
first_property_p->types[0] = (ecma_property_t) (type_and_flags | name_type);
ecma_property_t *property_p = first_property_p->types + 0;
JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p) == first_property_pair_p->values + 0);
if (out_prop_p != NULL)
{
*out_prop_p = property_p;
}
first_property_pair_p->values[0] = value;
#if JERRY_PROPERTY_HASHMAP
/* The property must be fully initialized before ecma_property_hashmap_insert
* is called, because the insert operation may reallocate the hashmap, and
* that triggers garbage collection which scans all properties of all objects.
* A not fully initialized but queued property may cause a crash. */
if (has_hashmap)
{
ecma_property_hashmap_insert (object_p, name_p, first_property_pair_p, 0);
}
#endif /* JERRY_PROPERTY_HASHMAP */
return first_property_pair_p->values + 0;
}
}
/* Otherwise we create a new property pair and use its second value. */
ecma_property_pair_t *first_property_pair_p = ecma_alloc_property_pair ();
/* Need to query property_list_head_p again and recheck the existennce
* of property hasmap, because ecma_alloc_property_pair may delete them. */
property_list_head_p = &object_p->u1.property_list_cp;
#if JERRY_PROPERTY_HASHMAP
bool has_hashmap = false;
if (*property_list_head_p != ECMA_NULL_POINTER)
{
ecma_property_header_t *first_property_p =
ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, *property_list_head_p);
if (first_property_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
property_list_head_p = &first_property_p->next_property_cp;
has_hashmap = true;
}
}
#endif /* JERRY_PROPERTY_HASHMAP */
/* Just copy the previous value (no need to decompress, compress). */
first_property_pair_p->header.next_property_cp = *property_list_head_p;
first_property_pair_p->header.types[0] = ECMA_PROPERTY_TYPE_DELETED;
first_property_pair_p->names_cp[0] = LIT_INTERNAL_MAGIC_STRING_DELETED;
ecma_property_t name_type;
first_property_pair_p->names_cp[1] = ecma_string_to_property_name (name_p, &name_type);
first_property_pair_p->header.types[1] = (ecma_property_t) (type_and_flags | name_type);
ECMA_SET_NON_NULL_POINTER (*property_list_head_p, &first_property_pair_p->header);
ecma_property_t *property_p = first_property_pair_p->header.types + 1;
JERRY_ASSERT (ECMA_PROPERTY_VALUE_PTR (property_p) == first_property_pair_p->values + 1);
if (out_prop_p != NULL)
{
*out_prop_p = property_p;
}
first_property_pair_p->values[1] = value;
#if JERRY_PROPERTY_HASHMAP
/* See the comment before the other ecma_property_hashmap_insert above. */
if (has_hashmap)
{
ecma_property_hashmap_insert (object_p, name_p, first_property_pair_p, 1);
}
#endif /* JERRY_PROPERTY_HASHMAP */
return first_property_pair_p->values + 1;
} /* ecma_create_property */
/**
* Create named data property with given name, attributes and undefined value
* in the specified object.
*
* @return pointer to the newly created property value
*/
ecma_property_value_t *
ecma_create_named_data_property (ecma_object_t *object_p, /**< object */
ecma_string_t *name_p, /**< property name */
uint8_t prop_attributes, /**< property attributes (See: ecma_property_flags_t) */
ecma_property_t **out_prop_p) /**< [out] the property is also returned
* if this field is non-NULL */
{
JERRY_ASSERT (object_p != NULL && name_p != NULL);
JERRY_ASSERT (ecma_is_lexical_environment (object_p) || !ecma_op_object_is_fast_array (object_p));
JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL);
JERRY_ASSERT ((prop_attributes & ~ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE_WRITABLE) == 0);
uint8_t type_and_flags = ECMA_PROPERTY_FLAG_DATA | prop_attributes;
ecma_property_value_t value;
value.value = ECMA_VALUE_UNDEFINED;
return ecma_create_property (object_p, name_p, type_and_flags, value, out_prop_p);
} /* ecma_create_named_data_property */
/**
* Create named accessor property with given name, attributes, getter and setter.
*
* @return pointer to the newly created property value
*/
ecma_property_value_t *
ecma_create_named_accessor_property (ecma_object_t *object_p, /**< object */
ecma_string_t *name_p, /**< property name */
ecma_object_t *get_p, /**< getter */
ecma_object_t *set_p, /**< setter */
uint8_t prop_attributes, /**< property attributes */
ecma_property_t **out_prop_p) /**< [out] the property is also returned
* if this field is non-NULL */
{
JERRY_ASSERT (object_p != NULL && name_p != NULL);
JERRY_ASSERT (ecma_is_lexical_environment (object_p) || !ecma_op_object_is_fast_array (object_p));
JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL);
JERRY_ASSERT ((prop_attributes & ~ECMA_PROPERTY_BUILT_IN_CONFIGURABLE_ENUMERABLE) == 0);
uint8_t type_and_flags = prop_attributes;
ecma_property_value_t value;
#if JERRY_CPOINTER_32_BIT
ecma_getter_setter_pointers_t *getter_setter_pair_p;
getter_setter_pair_p = jmem_pools_alloc (sizeof (ecma_getter_setter_pointers_t));
ECMA_SET_POINTER (getter_setter_pair_p->getter_cp, get_p);
ECMA_SET_POINTER (getter_setter_pair_p->setter_cp, set_p);
ECMA_SET_NON_NULL_POINTER (value.getter_setter_pair_cp, getter_setter_pair_p);
#else /* !JERRY_CPOINTER_32_BIT */
ECMA_SET_POINTER (value.getter_setter_pair.getter_cp, get_p);
ECMA_SET_POINTER (value.getter_setter_pair.setter_cp, set_p);
#endif /* JERRY_CPOINTER_32_BIT */
return ecma_create_property (object_p, name_p, type_and_flags, value, out_prop_p);
} /* ecma_create_named_accessor_property */
#if JERRY_MODULE_SYSTEM
/**
* Create property reference
*/
void
ecma_create_named_reference_property (ecma_object_t *object_p, /**< object */
ecma_string_t *name_p, /**< property name */
ecma_value_t reference) /**< property reference */
{
JERRY_ASSERT (object_p != NULL && name_p != NULL);
JERRY_ASSERT (ecma_find_named_property (object_p, name_p) == NULL);
JERRY_ASSERT ((ecma_is_lexical_environment (object_p)
&& ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS
&& ECMA_LEX_ENV_CLASS_IS_MODULE (object_p))
|| ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_MODULE_NAMESPACE));
uint8_t type_and_flags = ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE;
ecma_property_value_t value;
value.value = reference;
ecma_create_property (object_p, name_p, type_and_flags, value, NULL);
} /* ecma_create_named_reference_property */
#endif /* JERRY_MODULE_SYSTEM */
/**
* Find named data property or named accessor property in a specified object.
*
* @return pointer to the property, if it is found,
* NULL - otherwise.
*/
ecma_property_t *
ecma_find_named_property (ecma_object_t *obj_p, /**< object to find property in */
ecma_string_t *name_p) /**< property's name */
{
JERRY_ASSERT (obj_p != NULL);
JERRY_ASSERT (name_p != NULL);
JERRY_ASSERT (ecma_is_lexical_environment (obj_p) || !ecma_op_object_is_fast_array (obj_p));
#if JERRY_LCACHE
ecma_property_t *property_p = ecma_lcache_lookup (obj_p, name_p);
if (property_p != NULL)
{
return property_p;
}
#else /* !JERRY_LCACHE */
ecma_property_t *property_p = NULL;
#endif /* JERRY_LCACHE */
jmem_cpointer_t prop_iter_cp = obj_p->u1.property_list_cp;
#if JERRY_PROPERTY_HASHMAP
if (prop_iter_cp != JMEM_CP_NULL)
{
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
jmem_cpointer_t property_real_name_cp;
property_p = ecma_property_hashmap_find ((ecma_property_hashmap_t *) prop_iter_p, name_p, &property_real_name_cp);
#if JERRY_LCACHE
if (property_p != NULL && !ecma_is_property_lcached (property_p))
{
ecma_lcache_insert (obj_p, property_real_name_cp, property_p);
}
#endif /* JERRY_LCACHE */
return property_p;
}
}
#endif /* JERRY_PROPERTY_HASHMAP */
#if JERRY_PROPERTY_HASHMAP
uint32_t steps = 0;
#endif /* JERRY_PROPERTY_HASHMAP */
jmem_cpointer_t property_name_cp = ECMA_NULL_POINTER;
if (ECMA_IS_DIRECT_STRING (name_p))
{
ecma_property_t prop_name_type = (ecma_property_t) ECMA_GET_DIRECT_STRING_TYPE (name_p);
property_name_cp = (jmem_cpointer_t) ECMA_GET_DIRECT_STRING_VALUE (name_p);
JERRY_ASSERT (prop_name_type > 0);
while (prop_iter_cp != JMEM_CP_NULL)
{
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
if (prop_pair_p->names_cp[0] == property_name_cp
&& ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[0]) == prop_name_type)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[0]));
property_p = prop_iter_p->types + 0;
break;
}
if (prop_pair_p->names_cp[1] == property_name_cp
&& ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[1]) == prop_name_type)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (prop_iter_p->types[1]));
property_p = prop_iter_p->types + 1;
break;
}
#if JERRY_PROPERTY_HASHMAP
steps++;
#endif /* JERRY_PROPERTY_HASHMAP */
prop_iter_cp = prop_iter_p->next_property_cp;
}
}
else
{
while (prop_iter_cp != JMEM_CP_NULL)
{
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
if (ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[0]) == ECMA_DIRECT_STRING_PTR)
{
property_name_cp = prop_pair_p->names_cp[0];
ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, property_name_cp);
if (ecma_compare_ecma_non_direct_strings (name_p, prop_name_p))
{
property_p = prop_iter_p->types + 0;
break;
}
}
if (ECMA_PROPERTY_GET_NAME_TYPE (prop_iter_p->types[1]) == ECMA_DIRECT_STRING_PTR)
{
property_name_cp = prop_pair_p->names_cp[1];
ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, property_name_cp);
if (ecma_compare_ecma_non_direct_strings (name_p, prop_name_p))
{
property_p = prop_iter_p->types + 1;
break;
}
}
#if JERRY_PROPERTY_HASHMAP
steps++;
#endif /* JERRY_PROPERTY_HASHMAP */
prop_iter_cp = prop_iter_p->next_property_cp;
}
}
#if JERRY_PROPERTY_HASHMAP
if (steps >= (ECMA_PROPERTY_HASMAP_MINIMUM_SIZE / 2))
{
ecma_property_hashmap_create (obj_p);
}
#endif /* JERRY_PROPERTY_HASHMAP */
#if JERRY_LCACHE
if (property_p != NULL && !ecma_is_property_lcached (property_p))
{
ecma_lcache_insert (obj_p, property_name_cp, property_p);
}
#endif /* JERRY_LCACHE */
return property_p;
} /* ecma_find_named_property */
/**
* Get named data property or named access property in specified object.
*
* Warning:
* the property must exist
*
* @return pointer to the property, if it is found,
* NULL - otherwise.
*/
ecma_property_value_t *
ecma_get_named_data_property (ecma_object_t *obj_p, /**< object to find property in */
ecma_string_t *name_p) /**< property's name */
{
JERRY_ASSERT (obj_p != NULL);
JERRY_ASSERT (name_p != NULL);
JERRY_ASSERT (ecma_is_lexical_environment (obj_p) || !ecma_op_object_is_fast_array (obj_p));
ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
JERRY_ASSERT (property_p != NULL && ECMA_PROPERTY_IS_RAW_DATA (*property_p));
return ECMA_PROPERTY_VALUE_PTR (property_p);
} /* ecma_get_named_data_property */
/**
* Delete the object's property referenced by its value pointer.
*
* Note: specified property must be owned by specified object.
*/
void
ecma_delete_property (ecma_object_t *object_p, /**< object */
ecma_property_value_t *prop_value_p) /**< property value reference */
{
jmem_cpointer_t cur_prop_cp = object_p->u1.property_list_cp;
ecma_property_header_t *prev_prop_p = NULL;
#if JERRY_PROPERTY_HASHMAP
ecma_property_hashmap_delete_status hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_NO_HASHMAP;
if (cur_prop_cp != JMEM_CP_NULL)
{
ecma_property_header_t *cur_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, cur_prop_cp);
if (cur_prop_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
prev_prop_p = cur_prop_p;
cur_prop_cp = cur_prop_p->next_property_cp;
hashmap_status = ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP;
}
}
#endif /* JERRY_PROPERTY_HASHMAP */
while (cur_prop_cp != JMEM_CP_NULL)
{
ecma_property_header_t *cur_prop_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, cur_prop_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (cur_prop_p));
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) cur_prop_p;
for (uint32_t i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
{
if ((prop_pair_p->values + i) == prop_value_p)
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (cur_prop_p->types[i]));
#if JERRY_PROPERTY_HASHMAP
if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_HAS_HASHMAP)
{
hashmap_status = ecma_property_hashmap_delete (object_p, prop_pair_p->names_cp[i], cur_prop_p->types + i);
}
#endif /* JERRY_PROPERTY_HASHMAP */
ecma_gc_free_property (object_p, prop_pair_p, i);
cur_prop_p->types[i] = ECMA_PROPERTY_TYPE_DELETED;
prop_pair_p->names_cp[i] = LIT_INTERNAL_MAGIC_STRING_DELETED;
JERRY_ASSERT (ECMA_PROPERTY_PAIR_ITEM_COUNT == 2);
if (cur_prop_p->types[1 - i] != ECMA_PROPERTY_TYPE_DELETED)
{
#if JERRY_PROPERTY_HASHMAP
/* The other property is still valid. */
if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP)
{
ecma_property_hashmap_free (object_p);
ecma_property_hashmap_create (object_p);
}
#endif /* JERRY_PROPERTY_HASHMAP */
return;
}
JERRY_ASSERT (cur_prop_p->types[i] == ECMA_PROPERTY_TYPE_DELETED);
if (prev_prop_p == NULL)
{
object_p->u1.property_list_cp = cur_prop_p->next_property_cp;
}
else
{
prev_prop_p->next_property_cp = cur_prop_p->next_property_cp;
}
ecma_dealloc_property_pair ((ecma_property_pair_t *) cur_prop_p);
#if JERRY_PROPERTY_HASHMAP
if (hashmap_status == ECMA_PROPERTY_HASHMAP_DELETE_RECREATE_HASHMAP)
{
ecma_property_hashmap_free (object_p);
ecma_property_hashmap_create (object_p);
}
#endif /* JERRY_PROPERTY_HASHMAP */
return;
}
}
prev_prop_p = cur_prop_p;
cur_prop_cp = cur_prop_p->next_property_cp;
}
} /* ecma_delete_property */
/**
* Check whether the object contains a property
*/
static void
ecma_assert_object_contains_the_property (const ecma_object_t *object_p, /**< ecma-object */
const ecma_property_value_t *prop_value_p, /**< property value */
bool is_data) /**< property should be data property */
{
#ifndef JERRY_NDEBUG
jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp;
JERRY_ASSERT (prop_iter_cp != JMEM_CP_NULL);
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
prop_iter_cp = prop_iter_p->next_property_cp;
}
while (prop_iter_cp != JMEM_CP_NULL)
{
prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p;
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
{
if ((prop_pair_p->values + i) == prop_value_p)
{
JERRY_ASSERT (is_data == ((prop_pair_p->header.types[i] & ECMA_PROPERTY_FLAG_DATA) != 0));
return;
}
}
prop_iter_cp = prop_iter_p->next_property_cp;
}
#else /* JERRY_NDEBUG */
JERRY_UNUSED (object_p);
JERRY_UNUSED (prop_value_p);
JERRY_UNUSED (is_data);
#endif /* !JERRY_NDEBUG */
} /* ecma_assert_object_contains_the_property */
/**
* Assign value to named data property
*
* Note:
* value previously stored in the property is freed
*/
extern inline void JERRY_ATTR_ALWAYS_INLINE
ecma_named_data_property_assign_value (ecma_object_t *obj_p, /**< object */
ecma_property_value_t *prop_value_p, /**< property value reference */
ecma_value_t value) /**< value to assign */
{
ecma_assert_object_contains_the_property (obj_p, prop_value_p, true);
ecma_value_assign_value (&prop_value_p->value, value);
} /* ecma_named_data_property_assign_value */
/**
* Get named accessor property getter-setter-pair
*
* @return pointer to object's getter-setter pair
*/
ecma_getter_setter_pointers_t *
ecma_get_named_accessor_property (const ecma_property_value_t *prop_value_p) /**< property value reference */
{
#if JERRY_CPOINTER_32_BIT
return ECMA_GET_NON_NULL_POINTER (ecma_getter_setter_pointers_t, prop_value_p->getter_setter_pair_cp);
#else /* !JERRY_CPOINTER_32_BIT */
return (ecma_getter_setter_pointers_t *) &prop_value_p->getter_setter_pair;
#endif /* JERRY_CPOINTER_32_BIT */
} /* ecma_get_named_accessor_property */
/**
* Set getter of named accessor property
*/
void
ecma_set_named_accessor_property_getter (ecma_object_t *object_p, /**< the property's container */
ecma_property_value_t *prop_value_p, /**< property value reference */
ecma_object_t *getter_p) /**< getter object */
{
ecma_assert_object_contains_the_property (object_p, prop_value_p, false);
#if JERRY_CPOINTER_32_BIT
ecma_getter_setter_pointers_t *getter_setter_pair_p;
getter_setter_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_getter_setter_pointers_t, prop_value_p->getter_setter_pair_cp);
ECMA_SET_POINTER (getter_setter_pair_p->getter_cp, getter_p);
#else /* !JERRY_CPOINTER_32_BIT */
ECMA_SET_POINTER (prop_value_p->getter_setter_pair.getter_cp, getter_p);
#endif /* JERRY_CPOINTER_32_BIT */
} /* ecma_set_named_accessor_property_getter */
/**
* Set setter of named accessor property
*/
void
ecma_set_named_accessor_property_setter (ecma_object_t *object_p, /**< the property's container */
ecma_property_value_t *prop_value_p, /**< property value reference */
ecma_object_t *setter_p) /**< setter object */
{
ecma_assert_object_contains_the_property (object_p, prop_value_p, false);
#if JERRY_CPOINTER_32_BIT
ecma_getter_setter_pointers_t *getter_setter_pair_p;
getter_setter_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_getter_setter_pointers_t, prop_value_p->getter_setter_pair_cp);
ECMA_SET_POINTER (getter_setter_pair_p->setter_cp, setter_p);
#else /* !JERRY_CPOINTER_32_BIT */
ECMA_SET_POINTER (prop_value_p->getter_setter_pair.setter_cp, setter_p);
#endif /* JERRY_CPOINTER_32_BIT */
} /* ecma_set_named_accessor_property_setter */
#if JERRY_MODULE_SYSTEM
/**
* Construct a reference to a given property
*
* @return property reference
*/
ecma_value_t
ecma_property_to_reference (ecma_property_t *property_p) /**< data or reference property */
{
ecma_property_value_t *referenced_value_p = ECMA_PROPERTY_VALUE_PTR (property_p);
if (!(*property_p & ECMA_PROPERTY_FLAG_DATA))
{
return referenced_value_p->value;
}
jmem_cpointer_tag_t offset = (jmem_cpointer_tag_t) (((uintptr_t) property_p) & 0x1);
#if JERRY_CPOINTER_32_BIT
if (offset != 0)
{
--referenced_value_p;
}
#else /* !JERRY_CPOINTER_32_BIT */
if (offset == 0)
{
++referenced_value_p;
}
#endif /* JERRY_CPOINTER_32_BIT */
JERRY_ASSERT ((((uintptr_t) referenced_value_p) & (((uintptr_t) 1 << JMEM_ALIGNMENT_LOG) - 1)) == 0);
ecma_value_t result;
ECMA_SET_NON_NULL_POINTER_TAG (result, referenced_value_p, offset);
return result;
} /* ecma_property_to_reference */
/**
* Gets the referenced property value
*
* @return pointer to the value
*/
extern inline ecma_property_value_t *JERRY_ATTR_ALWAYS_INLINE
ecma_get_property_value_from_named_reference (ecma_property_value_t *reference_p) /**< data property reference */
{
ecma_value_t value = reference_p->value;
reference_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_property_value_t, value);
#if JERRY_CPOINTER_32_BIT
if (ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (value))
{
++reference_p;
}
#else /* !JERRY_CPOINTER_32_BIT */
if (!ECMA_GET_FIRST_BIT_FROM_POINTER_TAG (value))
{
--reference_p;
}
#endif /* JERRY_CPOINTER_32_BIT */
return reference_p;
} /* ecma_get_property_value_from_named_reference */
#endif /* JERRY_MODULE_SYSTEM */
/**
* Get property's 'Writable' attribute value
*
* @return true - property is writable,
* false - otherwise
*/
extern inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_is_property_writable (ecma_property_t property) /**< property */
{
JERRY_ASSERT (property & ECMA_PROPERTY_FLAG_DATA);
return (property & ECMA_PROPERTY_FLAG_WRITABLE) != 0;
} /* ecma_is_property_writable */
/**
* Set property's 'Writable' attribute value
*/
void
ecma_set_property_writable_attr (ecma_property_t *property_p, /**< [in,out] property */
bool is_writable) /**< new value for writable flag */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_RAW_DATA (*property_p));
if (is_writable)
{
*property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_WRITABLE);
}
else
{
*property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_WRITABLE);
}
} /* ecma_set_property_writable_attr */
/**
* Get property's 'Enumerable' attribute value
*
* @return true - property is enumerable,
* false - otherwise
*/
extern inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_is_property_enumerable (ecma_property_t property) /**< property */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (property));
return (property & ECMA_PROPERTY_FLAG_ENUMERABLE) != 0;
} /* ecma_is_property_enumerable */
/**
* Set property's 'Enumerable' attribute value
*/
void
ecma_set_property_enumerable_attr (ecma_property_t *property_p, /**< [in,out] property */
bool is_enumerable) /**< new value for enumerable flag */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p));
if (is_enumerable)
{
*property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_ENUMERABLE);
}
else
{
*property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_ENUMERABLE);
}
} /* ecma_set_property_enumerable_attr */
/**
* Get property's 'Configurable' attribute value
*
* @return true - property is configurable,
* false - otherwise
*/
extern inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_is_property_configurable (ecma_property_t property) /**< property */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (property));
return (property & ECMA_PROPERTY_FLAG_CONFIGURABLE) != 0;
} /* ecma_is_property_configurable */
/**
* Set property's 'Configurable' attribute value
*/
void
ecma_set_property_configurable_attr (ecma_property_t *property_p, /**< [in,out] property */
bool is_configurable) /**< new value for configurable flag */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_RAW (*property_p));
if (is_configurable)
{
*property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_CONFIGURABLE);
}
else
{
*property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_CONFIGURABLE);
}
} /* ecma_set_property_configurable_attr */
#if JERRY_LCACHE
/**
* Check whether the property is registered in LCache
*
* @return true / false
*/
extern inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_is_property_lcached (ecma_property_t *property_p) /**< property */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p));
return (*property_p & ECMA_PROPERTY_FLAG_LCACHED) != 0;
} /* ecma_is_property_lcached */
/**
* Set value of flag indicating whether the property is registered in LCache
*/
extern inline void JERRY_ATTR_ALWAYS_INLINE
ecma_set_property_lcached (ecma_property_t *property_p, /**< property */
bool is_lcached) /**< new value for lcached flag */
{
JERRY_ASSERT (ECMA_PROPERTY_IS_NAMED_PROPERTY (*property_p));
if (is_lcached)
{
*property_p = (uint8_t) (*property_p | ECMA_PROPERTY_FLAG_LCACHED);
}
else
{
*property_p = (uint8_t) (*property_p & ~ECMA_PROPERTY_FLAG_LCACHED);
}
} /* ecma_set_property_lcached */
#endif /* JERRY_LCACHE */
/**
* Construct empty property descriptor, i.e.:
* property descriptor with all is_defined flags set to false and the rest - to default value.
*
* @return empty property descriptor
*/
ecma_property_descriptor_t
ecma_make_empty_property_descriptor (void)
{
ecma_property_descriptor_t prop_desc;
prop_desc.flags = 0;
prop_desc.value = ECMA_VALUE_UNDEFINED;
prop_desc.get_p = NULL;
prop_desc.set_p = NULL;
return prop_desc;
} /* ecma_make_empty_property_descriptor */
/**
* Free values contained in the property descriptor
* and make it empty property descriptor
*/
void
ecma_free_property_descriptor (ecma_property_descriptor_t *prop_desc_p) /**< property descriptor */
{
if (prop_desc_p->flags & JERRY_PROP_IS_VALUE_DEFINED)
{
ecma_free_value (prop_desc_p->value);
}
if ((prop_desc_p->flags & JERRY_PROP_IS_GET_DEFINED) && prop_desc_p->get_p != NULL)
{
ecma_deref_object (prop_desc_p->get_p);
}
if ((prop_desc_p->flags & JERRY_PROP_IS_SET_DEFINED) && prop_desc_p->set_p != NULL)
{
ecma_deref_object (prop_desc_p->set_p);
}
*prop_desc_p = ecma_make_empty_property_descriptor ();
} /* ecma_free_property_descriptor */
/**
* Increase ref count of an extended primitve value.
*/
void
ecma_ref_extended_primitive (ecma_extended_primitive_t *primitve_p) /**< extended primitve value */
{
if (JERRY_LIKELY (primitve_p->refs_and_type < ECMA_EXTENDED_PRIMITIVE_MAX_REF))
{
primitve_p->refs_and_type += ECMA_EXTENDED_PRIMITIVE_REF_ONE;
}
else
{
jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT);
}
} /* ecma_ref_extended_primitive */
/**
* Decrease ref count of an error reference.
*/
void
ecma_deref_exception (ecma_extended_primitive_t *error_ref_p) /**< error reference */
{
JERRY_ASSERT (error_ref_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE);
error_ref_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE;
if (error_ref_p->refs_and_type < ECMA_EXTENDED_PRIMITIVE_REF_ONE)
{
ecma_free_value (error_ref_p->u.value);
jmem_pools_free (error_ref_p, sizeof (ecma_extended_primitive_t));
}
} /* ecma_deref_exception */
#if JERRY_BUILTIN_BIGINT
/**
* Decrease ref count of a bigint value.
*/
void
ecma_deref_bigint (ecma_extended_primitive_t *bigint_p) /**< bigint value */
{
JERRY_ASSERT (bigint_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE);
bigint_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE;
if (bigint_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE)
{
return;
}
uint32_t size = ECMA_BIGINT_GET_SIZE (bigint_p);
JERRY_ASSERT (size > 0);
size_t mem_size = ECMA_BIGINT_GET_BYTE_SIZE (size) + sizeof (ecma_extended_primitive_t);
jmem_heap_free_block (bigint_p, mem_size);
} /* ecma_deref_bigint */
#endif /* JERRY_BUILTIN_BIGINT */
/**
* Create an error reference from a given value.
*
* Note:
* Reference of the value is taken.
*
* @return error reference value
*/
ecma_value_t
ecma_create_exception (ecma_value_t value, /**< referenced value */
uint32_t options) /**< ECMA_ERROR_API_* options */
{
ecma_extended_primitive_t *error_ref_p;
error_ref_p = (ecma_extended_primitive_t *) jmem_pools_alloc (sizeof (ecma_extended_primitive_t));
error_ref_p->refs_and_type = ECMA_EXTENDED_PRIMITIVE_REF_ONE | options;
error_ref_p->u.value = value;
return ecma_make_extended_primitive_value (error_ref_p, ECMA_TYPE_ERROR);
} /* ecma_create_exception */
/**
* Create an error reference from the currently thrown error value.
*
* @return error reference value
*/
ecma_value_t
ecma_create_exception_from_context (void)
{
uint32_t options = 0;
uint32_t status_flags = JERRY_CONTEXT (status_flags);
if (status_flags & ECMA_STATUS_ABORT)
{
options |= ECMA_ERROR_API_FLAG_ABORT;
}
#if JERRY_VM_THROW
if (status_flags & ECMA_STATUS_ERROR_THROWN)
{
options |= ECMA_ERROR_API_FLAG_THROW_CAPTURED;
}
#endif /* JERRY_VM_THROW */
return ecma_create_exception (jcontext_take_exception (), options);
} /* ecma_create_exception_from_context */
/**
* Create an exception from a given object.
*
* Note:
* Reference of the object is taken.
*
* @return exception value
*/
extern inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE
ecma_create_exception_from_object (ecma_object_t *object_p) /**< referenced object */
{
return ecma_create_exception (ecma_make_object_value (object_p), 0);
} /* ecma_create_exception_from_object */
/**
* Raise a new exception from the argument exception value.
*
* Note: the argument exceptions reference count is decreased
*/
void
ecma_throw_exception (ecma_value_t value) /**< error reference */
{
JERRY_ASSERT (!jcontext_has_pending_exception () && !jcontext_has_pending_abort ());
ecma_extended_primitive_t *error_ref_p = ecma_get_extended_primitive_from_value (value);
JERRY_ASSERT (error_ref_p->refs_and_type >= ECMA_EXTENDED_PRIMITIVE_REF_ONE);
ecma_value_t referenced_value = error_ref_p->u.value;
uint32_t status_flags = JERRY_CONTEXT (status_flags);
status_flags |= (ECMA_STATUS_EXCEPTION
#if JERRY_VM_THROW
| ECMA_STATUS_ERROR_THROWN
#endif /* JERRY_VM_THROW */
| ECMA_STATUS_ABORT);
if (!(error_ref_p->refs_and_type & ECMA_ERROR_API_FLAG_ABORT))
{
status_flags &= ~(uint32_t) ECMA_STATUS_ABORT;
}
#if JERRY_VM_THROW
if (!(error_ref_p->refs_and_type & ECMA_ERROR_API_FLAG_THROW_CAPTURED))
{
status_flags &= ~(uint32_t) ECMA_STATUS_ERROR_THROWN;
}
#endif /* JERRY_VM_THROW */
JERRY_CONTEXT (status_flags) = status_flags;
if (error_ref_p->refs_and_type >= 2 * ECMA_EXTENDED_PRIMITIVE_REF_ONE)
{
error_ref_p->refs_and_type -= ECMA_EXTENDED_PRIMITIVE_REF_ONE;
referenced_value = ecma_copy_value (referenced_value);
}
else
{
jmem_pools_free (error_ref_p, sizeof (ecma_extended_primitive_t));
}
JERRY_CONTEXT (error_value) = referenced_value;
} /* ecma_throw_exception */
/**
* Decrease the reference counter of a script value.
*/
void
ecma_script_deref (ecma_value_t script_value) /**< script value */
{
cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value);
script_p->refs_and_type -= CBC_SCRIPT_REF_ONE;
if (script_p->refs_and_type >= CBC_SCRIPT_REF_ONE)
{
return;
}
size_t script_size = sizeof (cbc_script_t);
uint32_t type = script_p->refs_and_type;
if (type & CBC_SCRIPT_HAS_USER_VALUE)
{
script_size += sizeof (ecma_value_t);
if (!(type & CBC_SCRIPT_USER_VALUE_IS_OBJECT))
{
ecma_value_t user_value = CBC_SCRIPT_GET_USER_VALUE (script_p);
JERRY_ASSERT (!ecma_is_value_object (user_value));
ecma_free_value (user_value);
}
}
#if JERRY_SOURCE_NAME
ecma_deref_ecma_string (ecma_get_string_from_value (script_p->source_name));
#endif /* JERRY_SOURCE_NAME */
#if JERRY_MODULE_SYSTEM
if (type & CBC_SCRIPT_HAS_IMPORT_META)
{
JERRY_ASSERT (!(type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS));
JERRY_ASSERT (ecma_is_value_object (CBC_SCRIPT_GET_IMPORT_META (script_p, type)));
script_size += sizeof (ecma_value_t);
}
#endif /* JERRY_MODULE_SYSTEM */
#if JERRY_FUNCTION_TO_STRING
ecma_deref_ecma_string (ecma_get_string_from_value (script_p->source_code));
if (type & CBC_SCRIPT_HAS_FUNCTION_ARGUMENTS)
{
ecma_deref_ecma_string (ecma_get_string_from_value (CBC_SCRIPT_GET_FUNCTION_ARGUMENTS (script_p, type)));
script_size += sizeof (ecma_value_t);
}
#endif /* JERRY_FUNCTION_TO_STRING */
jmem_heap_free_block (script_p, script_size);
} /* ecma_script_deref */
/**
* Increase reference counter of Compact
* Byte Code or regexp byte code.
*/
void
ecma_bytecode_ref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
{
/* Abort program if maximum reference number is reached. */
if (bytecode_p->refs >= UINT16_MAX)
{
jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT);
}
bytecode_p->refs++;
} /* ecma_bytecode_ref */
/**
* Decrease reference counter of Compact
* Byte Code or regexp byte code.
*/
void
ecma_bytecode_deref (ecma_compiled_code_t *bytecode_p) /**< byte code pointer */
{
JERRY_ASSERT (bytecode_p->refs > 0);
JERRY_ASSERT (!CBC_IS_FUNCTION (bytecode_p->status_flags)
|| !(bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION));
bytecode_p->refs--;
if (bytecode_p->refs > 0)
{
/* Non-zero reference counter. */
return;
}
if (CBC_IS_FUNCTION (bytecode_p->status_flags))
{
ecma_value_t *literal_start_p = NULL;
uint32_t literal_end;
uint32_t const_literal_end;
if (bytecode_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)
{
cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_p;
literal_end = args_p->literal_end;
const_literal_end = args_p->const_literal_end;
literal_start_p = (ecma_value_t *) ((uint8_t *) bytecode_p + sizeof (cbc_uint16_arguments_t));
literal_start_p -= args_p->register_end;
}
else
{
cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_p;
literal_end = args_p->literal_end;
const_literal_end = args_p->const_literal_end;
literal_start_p = (ecma_value_t *) ((uint8_t *) bytecode_p + sizeof (cbc_uint8_arguments_t));
literal_start_p -= args_p->register_end;
}
for (uint32_t i = const_literal_end; i < literal_end; i++)
{
ecma_compiled_code_t *bytecode_literal_p =
ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, literal_start_p[i]);
/* Self references are ignored. */
if (bytecode_literal_p != bytecode_p)
{
ecma_bytecode_deref (bytecode_literal_p);
}
}
ecma_script_deref (((cbc_uint8_arguments_t *) bytecode_p)->script_value);
#if JERRY_ESNEXT
if (bytecode_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS)
{
ecma_collection_t *collection_p = ecma_compiled_code_get_tagged_template_collection (bytecode_p);
/* Since the objects in the tagged template collection are not strong referenced anymore by the compiled code
we can treat them as 'new' objects. */
JERRY_CONTEXT (ecma_gc_new_objects) += collection_p->item_count * 2;
ecma_collection_free_template_literal (collection_p);
}
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
if (bytecode_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO)
{
ecma_line_info_free (ecma_compiled_code_get_line_info (bytecode_p));
}
#endif /* JERRY_LINE_INFO */
#if JERRY_DEBUGGER
if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED)
&& !(bytecode_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE)
&& jerry_debugger_send_function_cp (JERRY_DEBUGGER_RELEASE_BYTE_CODE_CP, bytecode_p))
{
/* Delay the byte code free until the debugger client is notified.
* If the connection is aborted the pointer is still freed by
* jerry_debugger_close_connection(). */
jerry_debugger_byte_code_free_t *byte_code_free_p = (jerry_debugger_byte_code_free_t *) bytecode_p;
jmem_cpointer_t byte_code_free_head = JERRY_CONTEXT (debugger_byte_code_free_head);
byte_code_free_p->prev_cp = ECMA_NULL_POINTER;
jmem_cpointer_t byte_code_free_cp;
JMEM_CP_SET_NON_NULL_POINTER (byte_code_free_cp, byte_code_free_p);
if (byte_code_free_head == ECMA_NULL_POINTER)
{
JERRY_CONTEXT (debugger_byte_code_free_tail) = byte_code_free_cp;
}
else
{
jerry_debugger_byte_code_free_t *first_byte_code_free_p;
first_byte_code_free_p = JMEM_CP_GET_NON_NULL_POINTER (jerry_debugger_byte_code_free_t, byte_code_free_head);
first_byte_code_free_p->prev_cp = byte_code_free_cp;
}
JERRY_CONTEXT (debugger_byte_code_free_head) = byte_code_free_cp;
return;
}
#endif /* JERRY_DEBUGGER */
#if JERRY_MEM_STATS
jmem_stats_free_byte_code_bytes (((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG);
#endif /* JERRY_MEM_STATS */
}
else
{
#if JERRY_BUILTIN_REGEXP
re_compiled_code_t *re_bytecode_p = (re_compiled_code_t *) bytecode_p;
ecma_deref_ecma_string (ecma_get_string_from_value (re_bytecode_p->source));
#endif /* JERRY_BUILTIN_REGEXP */
}
jmem_heap_free_block (bytecode_p, ((size_t) bytecode_p->size) << JMEM_ALIGNMENT_LOG);
} /* ecma_bytecode_deref */
/**
* Gets the script data asigned to a script / module / function
*
* @return script data - if available, JMEM_CP_NULL - otherwise
*/
ecma_value_t
ecma_script_get_from_value (ecma_value_t value) /**< compiled code */
{
if (!ecma_is_value_object (value))
{
return JMEM_CP_NULL;
}
ecma_object_t *object_p = ecma_get_object_from_value (value);
const ecma_compiled_code_t *bytecode_p = NULL;
while (true)
{
switch (ecma_get_object_type (object_p))
{
case ECMA_OBJECT_TYPE_CLASS:
{
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
if (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_SCRIPT)
{
bytecode_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value);
break;
}
#if JERRY_MODULE_SYSTEM
if (ext_object_p->u.cls.type == ECMA_OBJECT_CLASS_MODULE)
{
ecma_module_t *module_p = (ecma_module_t *) object_p;
if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE))
{
bytecode_p = module_p->u.compiled_code_p;
break;
}
}
#endif /* JERRY_MODULE_SYSTEM */
return JMEM_CP_NULL;
}
case ECMA_OBJECT_TYPE_FUNCTION:
{
bytecode_p = ecma_op_function_get_compiled_code ((ecma_extended_object_t *) object_p);
break;
}
case ECMA_OBJECT_TYPE_BOUND_FUNCTION:
{
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
object_p =
ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_object_p->u.bound_function.target_function);
continue;
}
#if JERRY_ESNEXT
case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION:
{
return ((ecma_extended_object_t *) object_p)->u.constructor_function.script_value;
}
#endif /* JERRY_ESNEXT */
default:
{
return JMEM_CP_NULL;
}
}
JERRY_ASSERT (bytecode_p != NULL);
return ((cbc_uint8_arguments_t *) bytecode_p)->script_value;
}
} /* ecma_script_get_from_value */
/**
* Resolve the position of the arguments list start of the compiled code
*
* @return start position of the arguments list start of the compiled code
*/
ecma_value_t *
ecma_compiled_code_resolve_arguments_start (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */
{
JERRY_ASSERT (bytecode_header_p != NULL);
uint8_t *byte_p = (uint8_t *) bytecode_header_p;
byte_p += ((size_t) bytecode_header_p->size) << JMEM_ALIGNMENT_LOG;
if (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_MAPPED_ARGUMENTS_NEEDED))
{
return ((ecma_value_t *) byte_p);
}
if (JERRY_LIKELY (!(bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS)))
{
return ((ecma_value_t *) byte_p) - ((cbc_uint8_arguments_t *) bytecode_header_p)->argument_end;
}
return ((ecma_value_t *) byte_p) - ((cbc_uint16_arguments_t *) bytecode_header_p)->argument_end;
} /* ecma_compiled_code_resolve_arguments_start */
#if JERRY_ESNEXT
/**
* Resolve the position of the function name of the compiled code
*
* @return position of the function name of the compiled code
*/
extern inline ecma_value_t *JERRY_ATTR_ALWAYS_INLINE
ecma_compiled_code_resolve_function_name (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */
{
JERRY_ASSERT (bytecode_header_p != NULL);
ecma_value_t *base_p = ecma_compiled_code_resolve_arguments_start (bytecode_header_p);
if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR)
{
base_p--;
}
return base_p;
} /* ecma_compiled_code_resolve_function_name */
/**
* Get the tagged template collection of the compiled code
*
* @return pointer to the tagged template collection
*/
ecma_collection_t *
ecma_compiled_code_get_tagged_template_collection (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */
{
JERRY_ASSERT (bytecode_header_p != NULL);
JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS);
ecma_value_t *base_p = ecma_compiled_code_resolve_function_name (bytecode_header_p);
return ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, base_p[-1]);
} /* ecma_compiled_code_get_tagged_template_collection */
#endif /* JERRY_ESNEXT */
#if JERRY_LINE_INFO
/**
* Get the line info data from the byte code
*
* @return pointer to the line info data
*/
uint8_t *
ecma_compiled_code_get_line_info (const ecma_compiled_code_t *bytecode_header_p) /**< compiled code */
{
JERRY_ASSERT (bytecode_header_p != NULL);
JERRY_ASSERT (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_LINE_INFO);
ecma_value_t *base_p = ecma_compiled_code_resolve_arguments_start (bytecode_header_p);
#if JERRY_ESNEXT
if (CBC_FUNCTION_GET_TYPE (bytecode_header_p->status_flags) != CBC_FUNCTION_CONSTRUCTOR)
{
base_p--;
}
if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_HAS_TAGGED_LITERALS)
{
base_p--;
}
#endif /* JERRY_ESNEXT */
return ECMA_GET_INTERNAL_VALUE_POINTER (uint8_t, base_p[-1]);
} /* ecma_compiled_code_get_line_info */
#endif /* JERRY_LINE_INFO */
/**
* Get the source name of a compiled code.
*
* @return source name value
*/
ecma_value_t
ecma_get_source_name (const ecma_compiled_code_t *bytecode_p) /**< compiled code */
{
#if JERRY_SOURCE_NAME
#if JERRY_SNAPSHOT_EXEC
if (JERRY_UNLIKELY (bytecode_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION))
{
return ecma_make_magic_string_value (LIT_MAGIC_STRING_SOURCE_NAME_ANON);
}
#endif /* JERRY_SNAPSHOT_EXEC */
ecma_value_t script_value = ((cbc_uint8_arguments_t *) bytecode_p)->script_value;
return ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value)->source_name;
#else /* !JERRY_SOURCE_NAME */
JERRY_UNUSED (bytecode_p);
return ecma_make_magic_string_value (LIT_MAGIC_STRING_SOURCE_NAME_ANON);
#endif /* !JERRY_SOURCE_NAME */
} /* ecma_get_source_name */
#if (JERRY_STACK_LIMIT != 0)
/**
* Check the current stack usage by calculating the difference from the initial stack base.
*
* @return current stack usage in bytes
*/
uintptr_t JERRY_ATTR_NOINLINE
ecma_get_current_stack_usage (void)
{
volatile int __sp;
return (uintptr_t) (JERRY_CONTEXT (stack_base) - (uintptr_t) &__sp);
} /* ecma_get_current_stack_usage */
#endif /* (JERRY_STACK_LIMIT != 0) */
/**
* @}
* @}
*/