jerryscript/src/libecmaobjects/ecma-helpers.c

574 lines
18 KiB
C

/* Copyright 2014 Samsung Electronics Co., Ltd.
*
* 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.
*/
/** \addtogroup ecma ---TODO---
* @{
*
* \addtogroup ecmahelpers Helpers for operations with ECMA data types
* @{
*/
#include "ecma-alloc.h"
#include "ecma-gc.h"
#include "ecma-globals.h"
#include "ecma-helpers.h"
/**
* 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) */
bool is_extensible, /**< value of extensible attribute */
ecma_object_type_t type) /**< object type */
{
ecma_object_t *object_p = ecma_alloc_object ();
ecma_init_gc_info (object_p);
object_p->properties_p = ECMA_NULL_POINTER;
object_p->is_lexical_environment = false;
object_p->u.object.extensible = is_extensible;
ECMA_SET_POINTER(object_p->u.object.prototype_object_p, prototype_object_p);
object_p->u.object.type = type;
return 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 ();
ecma_init_gc_info (new_lexical_environment_p);
new_lexical_environment_p->is_lexical_environment = true;
new_lexical_environment_p->u.lexical_environment.type = ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE;
new_lexical_environment_p->properties_p = ECMA_NULL_POINTER;
ECMA_SET_POINTER(new_lexical_environment_p->u.lexical_environment.outer_reference_p, 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), binding object and provideThis flag.
*
* 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 */
bool provide_this) /**< provideThis flag */
{
JERRY_ASSERT(binding_obj_p != NULL);
ecma_object_t *new_lexical_environment_p = ecma_alloc_object ();
ecma_init_gc_info (new_lexical_environment_p);
new_lexical_environment_p->is_lexical_environment = true;
new_lexical_environment_p->u.lexical_environment.type = ECMA_LEXICAL_ENVIRONMENT_OBJECTBOUND;
new_lexical_environment_p->properties_p = ECMA_NULL_POINTER;
ECMA_SET_POINTER(new_lexical_environment_p->u.lexical_environment.outer_reference_p, outer_lexical_environment_p);
ecma_property_t *provide_this_prop_p = ecma_create_internal_property (new_lexical_environment_p,
ECMA_INTERNAL_PROPERTY_PROVIDE_THIS);
provide_this_prop_p->u.internal_property.value = provide_this;
ecma_property_t *binding_object_prop_p = ecma_create_internal_property (new_lexical_environment_p,
ECMA_INTERNAL_PROPERTY_BINDING_OBJECT);
ECMA_SET_POINTER(binding_object_prop_p->u.internal_property.value, binding_obj_p);
ecma_gc_update_may_ref_younger_object_flag_by_object (new_lexical_environment_p, binding_obj_p);
return new_lexical_environment_p;
} /* ecma_create_object_lex_env */
/**
* Create internal property in an object and link it into
* the object's properties' linked-list (at start of the list).
*
* @return pointer to newly created property
*/
ecma_property_t*
ecma_create_internal_property (ecma_object_t *object_p, /**< the object */
ecma_internal_property_id_t property_id) /**< internal property identifier */
{
ecma_property_t *new_property_p = ecma_alloc_property ();
new_property_p->type = ECMA_PROPERTY_INTERNAL;
ecma_property_t *list_head_p = ECMA_GET_POINTER(object_p->properties_p);
ECMA_SET_POINTER(new_property_p->next_property_p, list_head_p);
ECMA_SET_NON_NULL_POINTER(object_p->properties_p, new_property_p);
new_property_p->u.internal_property.type = property_id;
new_property_p->u.internal_property.value = ECMA_NULL_POINTER;
return new_property_p;
} /* ecma_create_internal_property */
/**
* Find internal property in the object's property set.
*
* @return pointer to the property, if it is found,
* NULL - otherwise.
*/
ecma_property_t*
ecma_find_internal_property (ecma_object_t *object_p, /**< object descriptor */
ecma_internal_property_id_t property_id) /**< internal property identifier */
{
JERRY_ASSERT(object_p != NULL);
JERRY_ASSERT(property_id != ECMA_INTERNAL_PROPERTY_PROTOTYPE
&& property_id != ECMA_INTERNAL_PROPERTY_EXTENSIBLE);
for (ecma_property_t *property_p = ECMA_GET_POINTER(object_p->properties_p);
property_p != NULL;
property_p = ECMA_GET_POINTER(property_p->next_property_p))
{
if (property_p->type == ECMA_PROPERTY_INTERNAL)
{
if (property_p->u.internal_property.type == property_id)
{
return property_p;
}
}
}
return NULL;
} /* ecma_find_internal_property */
/**
* Get an internal property.
*
* Warning:
* the property must exist
*
* @return pointer to the property
*/
ecma_property_t*
ecma_get_internal_property (ecma_object_t *object_p, /**< object descriptor */
ecma_internal_property_id_t property_id) /**< internal property identifier */
{
ecma_property_t *property_p = ecma_find_internal_property (object_p, property_id);
JERRY_ASSERT(property_p != NULL);
return property_p;
} /* ecma_get_internal_property */
/**
* Create named data property with given name, attributes and undefined value
* in the specified object.
*
* @return pointer to newly created property
*/
ecma_property_t*
ecma_create_named_data_property (ecma_object_t *obj_p, /**< object */
ecma_string_t *name_p, /**< property name */
ecma_property_writable_value_t writable, /**< 'writable' attribute */
ecma_property_enumerable_value_t enumerable, /**< 'enumerable' attribute */
ecma_property_configurable_value_t configurable) /**< 'configurable' attribute */
{
JERRY_ASSERT(obj_p != NULL && name_p != NULL);
ecma_property_t *prop_p = ecma_alloc_property ();
prop_p->type = ECMA_PROPERTY_NAMEDDATA;
ecma_ref_ecma_string (name_p);
ECMA_SET_NON_NULL_POINTER(prop_p->u.named_data_property.name_p, name_p);
prop_p->u.named_data_property.writable = writable;
prop_p->u.named_data_property.enumerable = enumerable;
prop_p->u.named_data_property.configurable = configurable;
prop_p->u.named_data_property.value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED);
ecma_property_t *list_head_p = ECMA_GET_POINTER(obj_p->properties_p);
ECMA_SET_POINTER(prop_p->next_property_p, list_head_p);
ECMA_SET_NON_NULL_POINTER(obj_p->properties_p, prop_p);
return prop_p;
} /* ecma_create_named_data_property */
/**
* Create named accessor property with given name, attributes, getter and setter.
*
* @return pointer to newly created property
*/
ecma_property_t*
ecma_create_named_accessor_property (ecma_object_t *obj_p, /**< object */
ecma_string_t *name_p, /**< property name */
ecma_object_t *get_p, /**< getter */
ecma_object_t *set_p, /**< setter */
ecma_property_enumerable_value_t enumerable, /**< 'enumerable' attribute */
ecma_property_configurable_value_t configurable) /**< 'configurable' attribute */
{
JERRY_ASSERT(obj_p != NULL && name_p != NULL);
ecma_property_t *prop_p = ecma_alloc_property ();
prop_p->type = ECMA_PROPERTY_NAMEDACCESSOR;
ecma_ref_ecma_string (name_p);
ECMA_SET_NON_NULL_POINTER(prop_p->u.named_accessor_property.name_p, name_p);
ECMA_SET_POINTER(prop_p->u.named_accessor_property.get_p, get_p);
ecma_gc_update_may_ref_younger_object_flag_by_object (obj_p, get_p);
ECMA_SET_POINTER(prop_p->u.named_accessor_property.set_p, set_p);
ecma_gc_update_may_ref_younger_object_flag_by_object (obj_p, set_p);
prop_p->u.named_accessor_property.enumerable = enumerable;
prop_p->u.named_accessor_property.configurable = configurable;
ecma_property_t *list_head_p = ECMA_GET_POINTER(obj_p->properties_p);
ECMA_SET_POINTER(prop_p->next_property_p, list_head_p);
ECMA_SET_NON_NULL_POINTER(obj_p->properties_p, prop_p);
return prop_p;
} /* ecma_create_named_accessor_property */
/**
* Find named data property or named access property in 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 */
const ecma_string_t *name_p) /**< property's name */
{
JERRY_ASSERT(obj_p != NULL);
JERRY_ASSERT(name_p != NULL);
for (ecma_property_t *property_p = ECMA_GET_POINTER(obj_p->properties_p);
property_p != NULL;
property_p = ECMA_GET_POINTER(property_p->next_property_p))
{
ecma_string_t *property_name_p;
if (property_p->type == ECMA_PROPERTY_NAMEDDATA)
{
property_name_p = ECMA_GET_POINTER(property_p->u.named_data_property.name_p);
}
else if (property_p->type == ECMA_PROPERTY_NAMEDACCESSOR)
{
property_name_p = ECMA_GET_POINTER(property_p->u.named_accessor_property.name_p);
}
else
{
continue;
}
JERRY_ASSERT(property_name_p != NULL);
if (ecma_compare_ecma_string_to_ecma_string (name_p, property_name_p))
{
return property_p;
}
}
return NULL;
} /* 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_t*
ecma_get_named_property (ecma_object_t *obj_p, /**< object to find property in */
const ecma_string_t *name_p) /**< property's name */
{
JERRY_ASSERT(obj_p != NULL);
JERRY_ASSERT(name_p != NULL);
ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
JERRY_ASSERT(property_p != NULL);
return property_p;
} /* ecma_get_named_property */
/**
* Get named data property in specified object.
*
* Warning:
* the property must exist and be named data property
*
* @return pointer to the property, if it is found,
* NULL - otherwise.
*/
ecma_property_t*
ecma_get_named_data_property (ecma_object_t *obj_p, /**< object to find property in */
const ecma_string_t *name_p) /**< property's name */
{
JERRY_ASSERT(obj_p != NULL);
JERRY_ASSERT(name_p != NULL);
ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
JERRY_ASSERT(property_p != NULL && property_p->type == ECMA_PROPERTY_NAMEDDATA);
return property_p;
} /* ecma_get_named_data_property */
/**
* Free the named data property and values it references.
*/
void
ecma_free_named_data_property (ecma_property_t *property_p) /**< the property */
{
JERRY_ASSERT(property_p->type == ECMA_PROPERTY_NAMEDDATA);
ecma_deref_ecma_string (ECMA_GET_POINTER (property_p->u.named_data_property.name_p));
ecma_free_value (property_p->u.named_data_property.value, false);
ecma_dealloc_property (property_p);
} /* ecma_free_named_data_property */
/**
* Free the named accessor property and values it references.
*/
void
ecma_free_named_accessor_property (ecma_property_t *property_p) /**< the property */
{
JERRY_ASSERT(property_p->type == ECMA_PROPERTY_NAMEDACCESSOR);
ecma_deref_ecma_string (ECMA_GET_POINTER (property_p->u.named_accessor_property.name_p));
ecma_dealloc_property (property_p);
} /* ecma_free_named_accessor_property */
/**
* Free the internal property and values it references.
*/
void
ecma_free_internal_property (ecma_property_t *property_p) /**< the property */
{
JERRY_ASSERT(property_p->type == ECMA_PROPERTY_INTERNAL);
ecma_internal_property_id_t property_id = property_p->u.internal_property.type;
uint32_t property_value = property_p->u.internal_property.value;
switch (property_id)
{
case ECMA_INTERNAL_PROPERTY_NUMBER_INDEXED_ARRAY_VALUES: /* a collection */
case ECMA_INTERNAL_PROPERTY_STRING_INDEXED_ARRAY_VALUES: /* a collection */
{
ecma_free_values_collection (ECMA_GET_POINTER(property_value), true);
break;
}
case ECMA_INTERNAL_PROPERTY_FORMAL_PARAMETERS: /* a strings' collection */
{
if (property_value != ECMA_NULL_POINTER)
{
ecma_free_values_collection (ECMA_GET_POINTER(property_value), false);
}
break;
}
case ECMA_INTERNAL_PROPERTY_SCOPE: /* a lexical environment */
case ECMA_INTERNAL_PROPERTY_PARAMETERS_MAP: /* an object */
case ECMA_INTERNAL_PROPERTY_BINDING_OBJECT: /* an object */
case ECMA_INTERNAL_PROPERTY_PROTOTYPE: /* the property's value is located in ecma_object_t */
case ECMA_INTERNAL_PROPERTY_EXTENSIBLE: /* the property's value is located in ecma_object_t */
case ECMA_INTERNAL_PROPERTY_PROVIDE_THIS: /* a boolean */
case ECMA_INTERNAL_PROPERTY_CLASS: /* an enum */
case ECMA_INTERNAL_PROPERTY_CODE: /* an integer */
{
break;
}
}
ecma_dealloc_property (property_p);
} /* ecma_free_internal_property */
/**
* Free the property and values it references.
*/
void
ecma_free_property (ecma_property_t *prop_p) /**< property */
{
switch ((ecma_property_type_t) prop_p->type)
{
case ECMA_PROPERTY_NAMEDDATA:
{
ecma_free_named_data_property (prop_p);
break;
}
case ECMA_PROPERTY_NAMEDACCESSOR:
{
ecma_free_named_accessor_property (prop_p);
break;
}
case ECMA_PROPERTY_INTERNAL:
{
ecma_free_internal_property (prop_p);
break;
}
}
} /* ecma_free_property */
/**
* Delete the object's property.
*
* Warning: specified property must be owned by specified object.
*/
void
ecma_delete_property (ecma_object_t *obj_p, /**< object */
ecma_property_t *prop_p) /**< property */
{
for (ecma_property_t *cur_prop_p = ECMA_GET_POINTER(obj_p->properties_p), *prev_prop_p = NULL, *next_prop_p;
cur_prop_p != NULL;
prev_prop_p = cur_prop_p, cur_prop_p = next_prop_p)
{
next_prop_p = ECMA_GET_POINTER(cur_prop_p->next_property_p);
if (cur_prop_p == prop_p)
{
ecma_free_property (prop_p);
if (prev_prop_p == NULL)
{
ECMA_SET_POINTER(obj_p->properties_p, next_prop_p);
}
else
{
ECMA_SET_POINTER(prev_prop_p->next_property_p, next_prop_p);
}
return;
}
}
JERRY_UNREACHABLE();
} /* ecma_delete_property */
/**
* Get property's 'Enumerable' attribute value
*
* @return true - property is enumerable,
* false - otherwise.
*/
bool
ecma_is_property_enumerable (ecma_property_t* prop_p) /**< property */
{
if (prop_p->type == ECMA_PROPERTY_NAMEDDATA)
{
return (prop_p->u.named_data_property.enumerable == ECMA_PROPERTY_ENUMERABLE);
}
else
{
JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDACCESSOR);
return (prop_p->u.named_accessor_property.enumerable == ECMA_PROPERTY_ENUMERABLE);
}
} /* ecma_is_property_enumerable */
/**
* Get property's 'Configurable' attribute value
*
* @return true - property is configurable,
* false - otherwise.
*/
bool
ecma_is_property_configurable (ecma_property_t* prop_p) /**< property */
{
if (prop_p->type == ECMA_PROPERTY_NAMEDDATA)
{
return (prop_p->u.named_data_property.configurable == ECMA_PROPERTY_CONFIGURABLE);
}
else
{
JERRY_ASSERT (prop_p->type == ECMA_PROPERTY_NAMEDACCESSOR);
return (prop_p->u.named_accessor_property.configurable == ECMA_PROPERTY_CONFIGURABLE);
}
} /* ecma_is_property_configurable */
/**
* Construct empty property descriptor, i.e.:
* property descriptor with all is_defined flags set to false and the rest - to default value.
*/
ecma_property_descriptor_t
ecma_make_empty_property_descriptor (void)
{
ecma_property_descriptor_t prop_desc = (ecma_property_descriptor_t)
{
.is_value_defined = false,
.value = ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED),
.is_writable_defined = false,
.writable = ECMA_PROPERTY_NOT_WRITABLE,
.is_enumerable_defined = false,
.enumerable = ECMA_PROPERTY_NOT_ENUMERABLE,
.is_configurable_defined = false,
.configurable = ECMA_PROPERTY_NOT_CONFIGURABLE,
.is_get_defined = false,
.get_p = NULL,
.is_set_defined = false,
.set_p = NULL
};
return prop_desc;
} /* ecma_make_empty_property_descriptor */
/**
* @}
* @}
*/