]> git.kaiwu.me - njs.git/commitdiff
Moving object property methods to njs_object_property.c.
authorhongzhidao <hongzhidao@gmail.com>
Tue, 11 Jun 2019 01:30:29 +0000 (21:30 -0400)
committerhongzhidao <hongzhidao@gmail.com>
Tue, 11 Jun 2019 01:30:29 +0000 (21:30 -0400)
auto/sources
njs/njs_object.c
njs/njs_object.h
njs/njs_object_property.c [new file with mode: 0644]
njs/njs_vm.c

index 4b925fac85bbf84279a1a97b88d6ba90f260b10e..b24d1a419a91c54b6e8ceb2d82c6e207b9a46a7c 100644 (file)
@@ -35,6 +35,7 @@ NJS_LIB_SRCS=" \
    njs/njs_number.c \
    njs/njs_string.c \
    njs/njs_object.c \
+   njs/njs_object_property.c \
    njs/njs_array.c \
    njs/njs_json.c \
    njs/njs_function.c \
index 27d6212cfdce38ff7dfe18d07a7d4cfee535ba62..ded41480afd44883b1bd857cb5629cef22ad9812 100644 (file)
@@ -9,22 +9,6 @@
 
 
 static nxt_int_t njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
-static njs_ret_t njs_object_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_object_t *object,
-    const njs_value_t *property);
-static njs_ret_t njs_array_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_array_t *array, uint32_t index);
-static njs_ret_t njs_string_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_value_t *object, uint32_t index);
-static njs_ret_t njs_external_property_query(njs_vm_t *vm,
-    njs_property_query_t *pq, njs_value_t *object);
-static njs_ret_t njs_external_property_set(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *setval, njs_value_t *retval);
-static njs_ret_t njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *setval, njs_value_t *retval);
-static njs_ret_t njs_define_property(njs_vm_t *vm, njs_value_t *object,
-    const njs_value_t *name, const njs_object_t *descriptor);
-
 static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin,
     const njs_object_t *end, nxt_lvlhsh_query_t *lhq);
 static uint32_t njs_object_enumerate_array_length(const njs_object_t *object);
@@ -179,603 +163,30 @@ njs_object_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
     u_char             *start;
     njs_object_prop_t  *prop;
 
-    prop = data;
-
-    size = prop->name.short_string.size;
-
-    if (size != NJS_STRING_LONG) {
-        if (lhq->key.length != size) {
-            return NXT_DECLINED;
-        }
-
-        start = prop->name.short_string.start;
-
-    } else {
-        if (lhq->key.length != prop->name.long_string.size) {
-            return NXT_DECLINED;
-        }
-
-        start = prop->name.long_string.data->start;
-    }
-
-    if (memcmp(start, lhq->key.start, lhq->key.length) == 0) {
-        return NXT_OK;
-    }
-
-    return NXT_DECLINED;
-}
-
-
-nxt_noinline njs_object_prop_t *
-njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
-    const njs_value_t *value, uint8_t attributes)
-{
-    njs_object_prop_t  *prop;
-
-    prop = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t),
-                        sizeof(njs_object_prop_t));
-
-    if (nxt_fast_path(prop != NULL)) {
-        /* GC: retain. */
-        prop->value = *value;
-
-        /* GC: retain. */
-        prop->name = *name;
-
-        prop->type = NJS_PROPERTY;
-        prop->writable = attributes;
-        prop->enumerable = attributes;
-        prop->configurable = attributes;
-
-        return prop;
-    }
-
-    njs_memory_error(vm);
-
-    return NULL;
-}
-
-
-nxt_noinline njs_object_prop_t *
-njs_object_property(njs_vm_t *vm, const njs_object_t *object,
-    nxt_lvlhsh_query_t *lhq)
-{
-    nxt_int_t  ret;
-
-    lhq->proto = &njs_object_hash_proto;
-
-    do {
-        ret = nxt_lvlhsh_find(&object->hash, lhq);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-            return lhq->value;
-        }
-
-        ret = nxt_lvlhsh_find(&object->shared_hash, lhq);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-            return lhq->value;
-        }
-
-        object = object->__proto__;
-
-    } while (object != NULL);
-
-    return NULL;
-}
-
-
-/*
- * ES5.1, 8.12.1: [[GetOwnProperty]], [[GetProperty]].
- * The njs_property_query() returns values
- *   NXT_OK               property has been found in object,
- *     retval of type njs_object_prop_t * is in pq->lhq.value.
- *     in NJS_PROPERTY_QUERY_GET
- *       prop->type is NJS_PROPERTY, NJS_METHOD or NJS_PROPERTY_HANDLER.
- *     in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE
- *       prop->type is NJS_PROPERTY, NJS_PROPERTY_REF, NJS_METHOD or
- *       NJS_PROPERTY_HANDLER.
- *   NXT_DECLINED         property was not found in object,
- *     if pq->lhq.value != NULL it contains retval of type
- *     njs_object_prop_t * where prop->type is NJS_WHITEOUT
- *   NJS_TRAP             the property trap must be called,
- *   NXT_ERROR            exception has been thrown.
- *
- *   TODO:
- *     Object.defineProperty([1,2], '1', {configurable:false})
- */
-
-njs_ret_t
-njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
-    const njs_value_t *property)
-{
-    uint32_t        index;
-    uint32_t        (*hash)(const void *, size_t);
-    njs_ret_t       ret;
-    njs_object_t    *obj;
-    njs_function_t  *function;
-
-    if (nxt_slow_path(!njs_is_primitive(property))) {
-        return njs_trap(vm, NJS_TRAP_PROPERTY);
-    }
-
-    hash = nxt_djb_hash;
-
-    switch (object->type) {
-
-    case NJS_BOOLEAN:
-    case NJS_NUMBER:
-        index = njs_primitive_prototype_index(object->type);
-        obj = &vm->prototypes[index].object;
-        break;
-
-    case NJS_STRING:
-        if (nxt_fast_path(!njs_is_null_or_undefined_or_boolean(property))) {
-            index = njs_value_to_index(property);
-
-            if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
-                return njs_string_property_query(vm, pq, object, index);
-            }
-        }
-
-        obj = &vm->string_object;
-        break;
-
-    case NJS_OBJECT:
-    case NJS_ARRAY:
-    case NJS_OBJECT_BOOLEAN:
-    case NJS_OBJECT_NUMBER:
-    case NJS_OBJECT_STRING:
-    case NJS_REGEXP:
-    case NJS_DATE:
-    case NJS_OBJECT_ERROR:
-    case NJS_OBJECT_EVAL_ERROR:
-    case NJS_OBJECT_INTERNAL_ERROR:
-    case NJS_OBJECT_RANGE_ERROR:
-    case NJS_OBJECT_REF_ERROR:
-    case NJS_OBJECT_SYNTAX_ERROR:
-    case NJS_OBJECT_TYPE_ERROR:
-    case NJS_OBJECT_URI_ERROR:
-    case NJS_OBJECT_VALUE:
-        obj = object->data.u.object;
-        break;
-
-    case NJS_FUNCTION:
-        function = njs_function_value_copy(vm, object);
-        if (nxt_slow_path(function == NULL)) {
-            return NXT_ERROR;
-        }
-
-        obj = &function->object;
-        break;
-
-    case NJS_EXTERNAL:
-        obj = NULL;
-        break;
-
-    case NJS_UNDEFINED:
-    case NJS_NULL:
-    default:
-        ret = njs_primitive_value_to_string(vm, &pq->value, property);
-
-        if (nxt_fast_path(ret == NXT_OK)) {
-            njs_string_get(&pq->value, &pq->lhq.key);
-            njs_type_error(vm, "cannot get property \"%V\" of undefined",
-                           &pq->lhq.key);
-            return NXT_ERROR;
-        }
-
-        njs_type_error(vm, "cannot get property \"unknown\" of undefined");
-
-        return NXT_ERROR;
-    }
-
-    ret = njs_primitive_value_to_string(vm, &pq->value, property);
-
-    if (nxt_fast_path(ret == NXT_OK)) {
-
-        njs_string_get(&pq->value, &pq->lhq.key);
-        pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
-
-        if (obj == NULL) {
-            pq->own = 1;
-            return njs_external_property_query(vm, pq, object);
-        }
-
-        return njs_object_property_query(vm, pq, obj, property);
-    }
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_object_t *object, const njs_value_t *property)
-{
-    uint32_t            index;
-    njs_ret_t           ret;
-    nxt_bool_t          own;
-    njs_array_t         *array;
-    njs_object_t        *proto;
-    njs_object_prop_t   *prop;
-    njs_object_value_t  *ov;
-
-    pq->lhq.proto = &njs_object_hash_proto;
-
-    own = pq->own;
-    pq->own = 1;
-
-    proto = object;
-
-    do {
-        pq->prototype = proto;
-
-        if (!njs_is_null_or_undefined_or_boolean(property)) {
-            switch (proto->type) {
-            case NJS_ARRAY:
-                index = njs_value_to_index(property);
-                if (nxt_fast_path(index < NJS_ARRAY_MAX_INDEX)) {
-                    array = (njs_array_t *) proto;
-                    return njs_array_property_query(vm, pq, array, index);
-                }
-
-                break;
-
-            case NJS_OBJECT_STRING:
-                index = njs_value_to_index(property);
-                if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
-                    ov = (njs_object_value_t *) proto;
-                    ret = njs_string_property_query(vm, pq, &ov->value, index);
-
-                    if (nxt_fast_path(ret != NXT_DECLINED)) {
-                        return ret;
-                    }
-                }
-
-            default:
-                break;
-            }
-        }
-
-        ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
-
-        if (ret == NXT_OK) {
-            prop = pq->lhq.value;
-
-            if (prop->type != NJS_WHITEOUT) {
-                return ret;
-            }
-
-            if (pq->own) {
-                pq->own_whiteout = prop;
-            }
-
-        } else {
-            ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
-
-            if (ret == NXT_OK) {
-                pq->shared = 1;
-
-                return ret;
-            }
-        }
-
-        if (own) {
-            return NXT_DECLINED;
-        }
-
-        pq->own = 0;
-        proto = proto->__proto__;
-
-    } while (proto != NULL);
-
-    return NXT_DECLINED;
-}
-
-
-static njs_ret_t
-njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_array_t *array, uint32_t index)
-{
-    uint32_t           size;
-    njs_ret_t          ret;
-    njs_value_t        *value;
-    njs_object_prop_t  *prop;
-
-    if (index >= array->length) {
-        if (pq->query != NJS_PROPERTY_QUERY_SET) {
-            return NXT_DECLINED;
-        }
-
-        size = index - array->length;
-
-        ret = njs_array_expand(vm, array, 0, size + 1);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-
-        value = &array->start[array->length];
-
-        while (size != 0) {
-            njs_set_invalid(value);
-            value++;
-            size--;
-        }
-
-        array->length = index + 1;
-    }
-
-    prop = &pq->scratch;
-
-    if (pq->query == NJS_PROPERTY_QUERY_GET) {
-        if (!njs_is_valid(&array->start[index])) {
-            return NXT_DECLINED;
-        }
-
-        prop->value = array->start[index];
-        prop->type = NJS_PROPERTY;
-
-    } else {
-        prop->value.data.u.value = &array->start[index];
-        prop->type = NJS_PROPERTY_REF;
-    }
-
-    prop->writable = 1;
-    prop->enumerable = 1;
-    prop->configurable = 1;
-
-    pq->lhq.value = prop;
-
-    return NXT_OK;
-}
-
-
-static njs_ret_t
-njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_value_t *object, uint32_t index)
-{
-    njs_slice_prop_t   slice;
-    njs_object_prop_t  *prop;
-    njs_string_prop_t  string;
-
-    prop = &pq->scratch;
-
-    slice.start = index;
-    slice.length = 1;
-    slice.string_length = njs_string_prop(&string, object);
-
-    if (slice.start < slice.string_length) {
-        /*
-         * A single codepoint string fits in retval
-         * so the function cannot fail.
-         */
-        (void) njs_string_slice(vm, &prop->value, &string, &slice);
-
-        prop->type = NJS_PROPERTY;
-        prop->writable = 0;
-        prop->enumerable = 1;
-        prop->configurable = 0;
-
-        pq->lhq.value = prop;
-
-        if (pq->query != NJS_PROPERTY_QUERY_GET) {
-            /* pq->lhq.key is used by njs_vmcode_property_set for TypeError */
-            njs_uint32_to_string(&pq->value, index);
-            njs_string_get(&pq->value, &pq->lhq.key);
-        }
-
-        return NXT_OK;
-    }
-
-    return NXT_DECLINED;
-}
-
-
-static njs_ret_t
-njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_value_t *object)
-{
-    void                *obj;
-    njs_ret_t           ret;
-    uintptr_t           data;
-    njs_object_prop_t   *prop;
-    const njs_extern_t  *ext_proto;
-
-    prop = &pq->scratch;
-
-    prop->type = NJS_PROPERTY;
-    prop->writable = 0;
-    prop->enumerable = 1;
-    prop->configurable = 0;
-
-    ext_proto = object->external.proto;
-
-    pq->lhq.proto = &njs_extern_hash_proto;
-    ret = nxt_lvlhsh_find(&ext_proto->hash, &pq->lhq);
-
-    if (ret == NXT_OK) {
-        ext_proto = pq->lhq.value;
-
-        prop->value.type = NJS_EXTERNAL;
-        prop->value.data.truth = 1;
-        prop->value.external.proto = ext_proto;
-        prop->value.external.index = object->external.index;
-
-        if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
-            goto done;
-        }
-
-        data = ext_proto->data;
-
-    } else {
-        data = (uintptr_t) &pq->lhq.key;
-    }
-
-    switch (pq->query) {
-
-    case NJS_PROPERTY_QUERY_GET:
-        if (ext_proto->get != NULL) {
-            obj = njs_extern_object(vm, object);
-            ret = ext_proto->get(vm, &prop->value, obj, data);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return ret;
-            }
-        }
-
-        break;
-
-    case NJS_PROPERTY_QUERY_SET:
-    case NJS_PROPERTY_QUERY_DELETE:
-
-        prop->type = NJS_PROPERTY_HANDLER;
-        prop->name = *object;
-
-        if (pq->query == NJS_PROPERTY_QUERY_SET) {
-            prop->writable = (ext_proto->set != NULL);
-            prop->value.data.u.prop_handler = njs_external_property_set;
-
-        } else {
-            prop->configurable = (ext_proto->find != NULL);
-            prop->value.data.u.prop_handler = njs_external_property_delete;
-        }
-
-        pq->ext_data = data;
-        pq->ext_proto = ext_proto;
-        pq->ext_index = object->external.index;
-
-        pq->lhq.value = prop;
-
-        vm->stash = (uintptr_t) pq;
-
-        return NXT_OK;
-    }
-
-done:
-
-    if (ext_proto->type == NJS_EXTERN_METHOD) {
-        prop->value.type = NJS_FUNCTION;
-        prop->value.data.u.function = ext_proto->function;
-        prop->value.data.truth = 1;
-    }
-
-    pq->lhq.value = prop;
-
-    return ret;
-}
-
-
-static njs_ret_t
-njs_external_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval,
-    njs_value_t *retval)
-{
-    void                  *obj;
-    njs_ret_t             ret;
-    nxt_str_t             s;
-    njs_property_query_t  *pq;
-
-    pq = (njs_property_query_t *) vm->stash;
-
-    if (!njs_is_null_or_undefined(setval)) {
-        ret = njs_vm_value_to_ext_string(vm, &s, setval, 0);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-
-    } else {
-        s = nxt_string_value("");
-    }
-
-    *retval = *setval;
-
-    obj = njs_extern_index(vm, pq->ext_index);
-
-    return pq->ext_proto->set(vm, obj, pq->ext_data, &s);
-}
-
-
-static njs_ret_t
-njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *unused, njs_value_t *unused2)
-{
-    void                  *obj;
-    njs_property_query_t  *pq;
-
-    pq = (njs_property_query_t *) vm->stash;
-
-    obj = njs_extern_index(vm, pq->ext_index);
-
-    return pq->ext_proto->find(vm, obj, pq->ext_data, 1);
-}
-
-
-njs_ret_t
-njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
-{
-    nxt_int_t           ret;
-    njs_function_t      *function;
-    njs_object_prop_t   *prop, *shared, *name;
-    nxt_lvlhsh_query_t  lhq;
-
-    static const njs_value_t  name_string = njs_string("name");
-
-    prop = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t),
-                        sizeof(njs_object_prop_t));
-    if (nxt_slow_path(prop == NULL)) {
-        njs_memory_error(vm);
-        return NXT_ERROR;
-    }
-
-    shared = pq->lhq.value;
-    *prop = *shared;
-
-    pq->lhq.replace = 0;
-    pq->lhq.value = prop;
-    pq->lhq.pool = vm->mem_pool;
-
-    ret = nxt_lvlhsh_insert(&pq->prototype->hash, &pq->lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
-    }
+    prop = data;
 
-    if (!njs_is_function(&prop->value)) {
-        return NXT_OK;
-    }
+    size = prop->name.short_string.size;
 
-    function = njs_function_value_copy(vm, &prop->value);
-    if (nxt_slow_path(function == NULL)) {
-        return NXT_ERROR;
-    }
+    if (size != NJS_STRING_LONG) {
+        if (lhq->key.length != size) {
+            return NXT_DECLINED;
+        }
 
-    if (function->ctor) {
-        function->object.shared_hash = vm->shared->function_instance_hash;
+        start = prop->name.short_string.start;
 
     } else {
-        function->object.shared_hash = vm->shared->arrow_instance_hash;
-    }
+        if (lhq->key.length != prop->name.long_string.size) {
+            return NXT_DECLINED;
+        }
 
-    name = njs_object_prop_alloc(vm, &name_string, &prop->name, 0);
-    if (nxt_slow_path(name == NULL)) {
-        return NXT_ERROR;
+        start = prop->name.long_string.data->start;
     }
 
-    name->configurable = 1;
-
-    lhq.key_hash = NJS_NAME_HASH;
-    lhq.key = nxt_string_value("name");
-    lhq.replace = 0;
-    lhq.value = name;
-    lhq.proto = &njs_object_hash_proto;
-    lhq.pool = vm->mem_pool;
-
-    ret = nxt_lvlhsh_insert(&function->object.hash, &lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
+    if (memcmp(start, lhq->key.start, lhq->key.length) == 0) {
+        return NXT_OK;
     }
 
-    return NXT_OK;
+    return NXT_DECLINED;
 }
 
 
@@ -902,7 +313,7 @@ njs_object_keys(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 static njs_ret_t
 njs_object_values(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
- {
+{
     njs_array_t        *array;
     const njs_value_t  *value;
 
@@ -931,7 +342,7 @@ njs_object_values(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 static njs_ret_t
 njs_object_entries(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
- {
+{
     njs_array_t        *array;
     const njs_value_t  *value;
 
@@ -1800,385 +1211,6 @@ njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
 }
 
 
-static njs_object_prop_t *
-njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name,
-    const njs_object_t *descriptor)
-{
-    njs_object_prop_t   *prop, *pr;
-    nxt_lvlhsh_query_t  pq;
-
-    prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
-                                 NJS_ATTRIBUTE_UNSET);
-    if (nxt_slow_path(prop == NULL)) {
-        return NULL;
-    }
-
-    pq.key = nxt_string_value("value");
-    pq.key_hash = NJS_VALUE_HASH;
-
-    pr = njs_object_property(vm, descriptor, &pq);
-    if (pr != NULL) {
-        prop->value = pr->value;
-    }
-
-    pq.key = nxt_string_value("writable");
-    pq.key_hash = NJS_WRITABABLE_HASH;
-
-    pr = njs_object_property(vm, descriptor, &pq);
-    if (pr != NULL) {
-        prop->writable = pr->value.data.truth;
-    }
-
-    pq.key = nxt_string_value("enumerable");
-    pq.key_hash = NJS_ENUMERABLE_HASH;
-
-    pr = njs_object_property(vm, descriptor, &pq);
-    if (pr != NULL) {
-        prop->enumerable = pr->value.data.truth;
-    }
-
-    pq.key = nxt_string_value("configurable");
-    pq.key_hash = NJS_CONFIGURABLE_HASH;
-
-    pr = njs_object_property(vm, descriptor, &pq);
-    if (pr != NULL) {
-        prop->configurable = pr->value.data.truth;
-    }
-
-    return prop;
-}
-
-
-/*
- * ES5.1, 8.12.9: [[DefineOwnProperty]]
- *   Limited support of special descriptors like length and array index
- *   (values can be set, but without property flags support).
- */
-static njs_ret_t
-njs_define_property(njs_vm_t *vm, njs_value_t *object, const njs_value_t *name,
-    const njs_object_t *descriptor)
-{
-    nxt_int_t             ret;
-    njs_object_prop_t     *desc, *current;
-    njs_property_query_t  pq;
-
-    njs_string_get(name, &pq.lhq.key);
-    pq.lhq.key_hash = nxt_djb_hash(pq.lhq.key.start, pq.lhq.key.length);
-    pq.lhq.proto = &njs_object_hash_proto;
-
-    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
-
-    ret = njs_property_query(vm, &pq, object, name);
-
-    if (ret != NXT_OK && ret != NXT_DECLINED) {
-        return ret;
-    }
-
-    desc = njs_descriptor_prop(vm, name, descriptor);
-    if (nxt_slow_path(desc == NULL)) {
-        return NXT_ERROR;
-    }
-
-    if (nxt_fast_path(ret == NXT_DECLINED)) {
-
-        if (!njs_is_valid(&desc->value)) {
-            desc->value = njs_value_undefined;
-        }
-
-        if (desc->writable == NJS_ATTRIBUTE_UNSET) {
-            desc->writable = 0;
-        }
-
-        if (desc->enumerable == NJS_ATTRIBUTE_UNSET) {
-            desc->enumerable = 0;
-        }
-
-        if (desc->configurable == NJS_ATTRIBUTE_UNSET) {
-            desc->configurable = 0;
-        }
-
-        if (nxt_slow_path(pq.lhq.value != NULL)) {
-            current = pq.lhq.value;
-
-            if (nxt_slow_path(current->type == NJS_WHITEOUT)) {
-                /* Previously deleted property.  */
-                *current = *desc;
-            }
-
-        } else {
-            pq.lhq.value = desc;
-            pq.lhq.replace = 0;
-            pq.lhq.pool = vm->mem_pool;
-
-            ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
-            if (nxt_slow_path(ret != NXT_OK)) {
-                njs_internal_error(vm, "lvlhsh insert failed");
-                return NXT_ERROR;
-            }
-        }
-
-        return NXT_OK;
-    }
-
-    /* Updating existing prop. */
-
-    if (nxt_slow_path(pq.shared)) {
-        ret = njs_prop_private_copy(vm, &pq);
-
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-    }
-
-    current = pq.lhq.value;
-
-    switch (current->type) {
-    case NJS_PROPERTY:
-    case NJS_METHOD:
-        break;
-
-    case NJS_PROPERTY_REF:
-        if (njs_is_valid(&desc->value)) {
-            *current->value.data.u.value = desc->value;
-        } else {
-            *current->value.data.u.value = njs_value_undefined;
-        }
-
-        return NXT_OK;
-
-    case NJS_PROPERTY_HANDLER:
-        if (current->writable && njs_is_valid(&desc->value)) {
-            ret = current->value.data.u.prop_handler(vm, object, &desc->value,
-                                                     &vm->retval);
-
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return ret;
-            }
-        }
-
-        return NXT_OK;
-
-    default:
-        njs_internal_error(vm, "unexpected property type \"%s\" "
-                           "while defining property",
-                           njs_prop_type_string(current->type));
-
-        return NXT_ERROR;
-    }
-
-    if (!current->configurable) {
-
-        if (njs_is_valid(&desc->value)
-            && current->writable == NJS_ATTRIBUTE_FALSE
-            && !njs_values_strict_equal(&desc->value, &current->value))
-        {
-            goto exception;
-        }
-
-        if (desc->writable == NJS_ATTRIBUTE_TRUE
-            && current->writable == NJS_ATTRIBUTE_FALSE)
-        {
-            goto exception;
-        }
-
-        if (desc->enumerable != NJS_ATTRIBUTE_UNSET
-            && current->enumerable != desc->enumerable)
-        {
-            goto exception;
-        }
-
-        if (desc->configurable == NJS_ATTRIBUTE_TRUE) {
-            goto exception;
-        }
-    }
-
-    if (njs_is_valid(&desc->value)) {
-        current->value = desc->value;
-    }
-
-    if (desc->writable != NJS_ATTRIBUTE_UNSET) {
-        current->writable = desc->writable;
-    }
-
-    if (desc->enumerable != NJS_ATTRIBUTE_UNSET) {
-        current->enumerable = desc->enumerable;
-    }
-
-    if (desc->configurable != NJS_ATTRIBUTE_UNSET) {
-        current->configurable = desc->configurable;
-    }
-
-    return NXT_OK;
-
-exception:
-
-    njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.lhq.key);
-
-    return NXT_ERROR;
-}
-
-
-static const njs_value_t  njs_object_value_string = njs_string("value");
-static const njs_value_t  njs_object_writable_string =
-                                                    njs_string("writable");
-static const njs_value_t  njs_object_enumerable_string =
-                                                    njs_string("enumerable");
-static const njs_value_t  njs_object_configurable_string =
-                                                    njs_string("configurable");
-
-
-static njs_ret_t
-njs_object_property_descriptor(njs_vm_t *vm, njs_value_t *dest,
-    const njs_value_t *value, const njs_value_t *property)
-{
-    nxt_int_t             ret;
-    njs_object_t          *descriptor;
-    njs_object_prop_t     *pr, *prop;
-    const njs_value_t     *setval;
-    nxt_lvlhsh_query_t    lhq;
-    njs_property_query_t  pq;
-
-    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
-
-    ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
-
-    switch (ret) {
-    case NXT_OK:
-        break;
-
-    case NXT_DECLINED:
-        *dest = njs_value_undefined;
-        return NXT_OK;
-
-    case NJS_TRAP:
-    case NXT_ERROR:
-    default:
-        return ret;
-    }
-
-    prop = pq.lhq.value;
-
-    switch (prop->type) {
-    case NJS_PROPERTY:
-        break;
-
-    case NJS_PROPERTY_HANDLER:
-        pq.scratch = *prop;
-        prop = &pq.scratch;
-        ret = prop->value.data.u.prop_handler(vm, (njs_value_t *) value,
-                                              NULL, &prop->value);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return ret;
-        }
-
-        break;
-
-    case NJS_METHOD:
-        if (pq.shared) {
-            ret = njs_prop_private_copy(vm, &pq);
-
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return ret;
-            }
-
-            prop = pq.lhq.value;
-        }
-
-        break;
-
-    default:
-        njs_type_error(vm, "unexpected property type: %s",
-                       njs_prop_type_string(prop->type));
-        return NXT_ERROR;
-    }
-
-    descriptor = njs_object_alloc(vm);
-    if (nxt_slow_path(descriptor == NULL)) {
-        return NXT_ERROR;
-    }
-
-    lhq.proto = &njs_object_hash_proto;
-    lhq.replace = 0;
-    lhq.pool = vm->mem_pool;
-
-    lhq.key = nxt_string_value("value");
-    lhq.key_hash = NJS_VALUE_HASH;
-
-    pr = njs_object_prop_alloc(vm, &njs_object_value_string, &prop->value, 1);
-    if (nxt_slow_path(pr == NULL)) {
-        return NXT_ERROR;
-    }
-
-    lhq.value = pr;
-
-    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
-    }
-
-    lhq.key = nxt_string_value("writable");
-    lhq.key_hash = NJS_WRITABABLE_HASH;
-
-    setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false;
-
-    pr = njs_object_prop_alloc(vm, &njs_object_writable_string, setval, 1);
-    if (nxt_slow_path(pr == NULL)) {
-        return NXT_ERROR;
-    }
-
-    lhq.value = pr;
-
-    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
-    }
-
-    lhq.key = nxt_string_value("enumerable");
-    lhq.key_hash = NJS_ENUMERABLE_HASH;
-
-    setval = (prop->enumerable == 1) ? &njs_value_true : &njs_value_false;
-
-    pr = njs_object_prop_alloc(vm, &njs_object_enumerable_string, setval, 1);
-    if (nxt_slow_path(pr == NULL)) {
-        return NXT_ERROR;
-    }
-
-    lhq.value = pr;
-
-    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
-    }
-
-    lhq.key = nxt_string_value("configurable");
-    lhq.key_hash = NJS_CONFIGURABLE_HASH;
-
-    setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false;
-
-    pr = njs_object_prop_alloc(vm, &njs_object_configurable_string, setval, 1);
-    if (nxt_slow_path(pr == NULL)) {
-        return NXT_ERROR;
-    }
-
-    lhq.value = pr;
-
-    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
-    }
-
-    dest->data.u.object = descriptor;
-    dest->type = NJS_OBJECT;
-    dest->data.truth = 1;
-
-    return NXT_OK;
-}
-
-
 static njs_ret_t
 njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused)
@@ -3297,28 +2329,3 @@ const njs_object_init_t  njs_object_prototype_init = {
     njs_object_prototype_properties,
     nxt_nitems(njs_object_prototype_properties),
 };
-
-
-const char *
-njs_prop_type_string(njs_object_property_type_t type)
-{
-    switch (type) {
-    case NJS_PROPERTY_REF:
-        return "property_ref";
-
-    case NJS_METHOD:
-        return "method";
-
-    case NJS_PROPERTY_HANDLER:
-        return "property handler";
-
-    case NJS_WHITEOUT:
-        return "whiteout";
-
-    case NJS_PROPERTY:
-        return "property";
-
-    default:
-        return "unknown";
-    }
-}
index 2196aca038e101579fed1a146347e5837075ace7..115637c4f9f0f04de485254771a868822c22c216 100644 (file)
@@ -87,20 +87,10 @@ njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
     njs_object_enum_t kind, nxt_bool_t all);
 njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
     njs_object_enum_t kind, nxt_bool_t all);
-njs_ret_t njs_value_property(njs_vm_t *vm, const njs_value_t *value,
-    const njs_value_t *property, njs_value_t *retval);
-njs_ret_t njs_value_property_set(njs_vm_t *vm, njs_value_t *object,
-    const njs_value_t *property, njs_value_t *value);
-njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
-    nxt_lvlhsh_query_t *lhq);
-njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq,
-    njs_value_t *object, const njs_value_t *property);
 nxt_int_t njs_object_hash_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
     const njs_object_prop_t *prop, nxt_uint_t n);
 njs_ret_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
-njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
-    const njs_value_t *value, uint8_t attributes);
 njs_ret_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *setval, njs_value_t *retval);
 njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value,
@@ -116,6 +106,20 @@ njs_value_t *njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash,
 njs_ret_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
 
+njs_ret_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_value_t *object, const njs_value_t *property);
+njs_ret_t njs_value_property(njs_vm_t *vm, const njs_value_t *value,
+    const njs_value_t *property, njs_value_t *retval);
+njs_ret_t njs_value_property_set(njs_vm_t *vm, njs_value_t *object,
+    const njs_value_t *property, njs_value_t *value);
+njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
+    const njs_value_t *value, uint8_t attributes);
+njs_object_prop_t *njs_object_property(njs_vm_t *vm, const njs_object_t *obj,
+    nxt_lvlhsh_query_t *lhq);
+njs_ret_t njs_define_property(njs_vm_t *vm, njs_value_t *object,
+    const njs_value_t *name, const njs_object_t *descriptor);
+njs_ret_t njs_object_property_descriptor(njs_vm_t *vm, njs_value_t *dest,
+    const njs_value_t *value, const njs_value_t *property);
 njs_ret_t njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq);
 const char *njs_prop_type_string(njs_object_property_type_t type);
 
diff --git a/njs/njs_object_property.c b/njs/njs_object_property.c
new file mode 100644 (file)
index 0000000..60ff3d4
--- /dev/null
@@ -0,0 +1,1216 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <njs_core.h>
+#include <string.h>
+
+
+static njs_ret_t njs_object_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_object_t *object,
+    const njs_value_t *property);
+static njs_ret_t njs_array_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_array_t *array, uint32_t index);
+static njs_ret_t njs_string_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_value_t *object, uint32_t index);
+static njs_ret_t njs_external_property_query(njs_vm_t *vm,
+    njs_property_query_t *pq, njs_value_t *object);
+static njs_ret_t njs_external_property_set(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval);
+static njs_ret_t njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *setval, njs_value_t *retval);
+static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm,
+    const njs_value_t *name, const njs_object_t *descriptor);
+
+
+/*
+ * ES5.1, 8.12.1: [[GetOwnProperty]], [[GetProperty]].
+ * The njs_property_query() returns values
+ *   NXT_OK               property has been found in object,
+ *     retval of type njs_object_prop_t * is in pq->lhq.value.
+ *     in NJS_PROPERTY_QUERY_GET
+ *       prop->type is NJS_PROPERTY, NJS_METHOD or NJS_PROPERTY_HANDLER.
+ *     in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE
+ *       prop->type is NJS_PROPERTY, NJS_PROPERTY_REF, NJS_METHOD or
+ *       NJS_PROPERTY_HANDLER.
+ *   NXT_DECLINED         property was not found in object,
+ *     if pq->lhq.value != NULL it contains retval of type
+ *     njs_object_prop_t * where prop->type is NJS_WHITEOUT
+ *   NJS_TRAP             the property trap must be called,
+ *   NXT_ERROR            exception has been thrown.
+ *
+ *   TODO:
+ *     Object.defineProperty([1,2], '1', {configurable:false})
+ */
+
+njs_ret_t
+njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
+    const njs_value_t *property)
+{
+    uint32_t        index;
+    uint32_t        (*hash)(const void *, size_t);
+    njs_ret_t       ret;
+    njs_object_t    *obj;
+    njs_function_t  *function;
+
+    if (nxt_slow_path(!njs_is_primitive(property))) {
+        return njs_trap(vm, NJS_TRAP_PROPERTY);
+    }
+
+    hash = nxt_djb_hash;
+
+    switch (object->type) {
+
+    case NJS_BOOLEAN:
+    case NJS_NUMBER:
+        index = njs_primitive_prototype_index(object->type);
+        obj = &vm->prototypes[index].object;
+        break;
+
+    case NJS_STRING:
+        if (nxt_fast_path(!njs_is_null_or_undefined_or_boolean(property))) {
+            index = njs_value_to_index(property);
+
+            if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+                return njs_string_property_query(vm, pq, object, index);
+            }
+        }
+
+        obj = &vm->string_object;
+        break;
+
+    case NJS_OBJECT:
+    case NJS_ARRAY:
+    case NJS_OBJECT_BOOLEAN:
+    case NJS_OBJECT_NUMBER:
+    case NJS_OBJECT_STRING:
+    case NJS_REGEXP:
+    case NJS_DATE:
+    case NJS_OBJECT_ERROR:
+    case NJS_OBJECT_EVAL_ERROR:
+    case NJS_OBJECT_INTERNAL_ERROR:
+    case NJS_OBJECT_RANGE_ERROR:
+    case NJS_OBJECT_REF_ERROR:
+    case NJS_OBJECT_SYNTAX_ERROR:
+    case NJS_OBJECT_TYPE_ERROR:
+    case NJS_OBJECT_URI_ERROR:
+    case NJS_OBJECT_VALUE:
+        obj = object->data.u.object;
+        break;
+
+    case NJS_FUNCTION:
+        function = njs_function_value_copy(vm, object);
+        if (nxt_slow_path(function == NULL)) {
+            return NXT_ERROR;
+        }
+
+        obj = &function->object;
+        break;
+
+    case NJS_EXTERNAL:
+        obj = NULL;
+        break;
+
+    case NJS_UNDEFINED:
+    case NJS_NULL:
+    default:
+        ret = njs_primitive_value_to_string(vm, &pq->value, property);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+            njs_string_get(&pq->value, &pq->lhq.key);
+            njs_type_error(vm, "cannot get property \"%V\" of undefined",
+                           &pq->lhq.key);
+            return NXT_ERROR;
+        }
+
+        njs_type_error(vm, "cannot get property \"unknown\" of undefined");
+
+        return NXT_ERROR;
+    }
+
+    ret = njs_primitive_value_to_string(vm, &pq->value, property);
+
+    if (nxt_fast_path(ret == NXT_OK)) {
+
+        njs_string_get(&pq->value, &pq->lhq.key);
+        pq->lhq.key_hash = hash(pq->lhq.key.start, pq->lhq.key.length);
+
+        if (obj == NULL) {
+            pq->own = 1;
+            return njs_external_property_query(vm, pq, object);
+        }
+
+        return njs_object_property_query(vm, pq, obj, property);
+    }
+
+    return ret;
+}
+
+
+static njs_ret_t
+njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_object_t *object, const njs_value_t *property)
+{
+    uint32_t            index;
+    njs_ret_t           ret;
+    nxt_bool_t          own;
+    njs_array_t         *array;
+    njs_object_t        *proto;
+    njs_object_prop_t   *prop;
+    njs_object_value_t  *ov;
+
+    pq->lhq.proto = &njs_object_hash_proto;
+
+    own = pq->own;
+    pq->own = 1;
+
+    proto = object;
+
+    do {
+        pq->prototype = proto;
+
+        if (!njs_is_null_or_undefined_or_boolean(property)) {
+            switch (proto->type) {
+            case NJS_ARRAY:
+                index = njs_value_to_index(property);
+                if (nxt_fast_path(index < NJS_ARRAY_MAX_INDEX)) {
+                    array = (njs_array_t *) proto;
+                    return njs_array_property_query(vm, pq, array, index);
+                }
+
+                break;
+
+            case NJS_OBJECT_STRING:
+                index = njs_value_to_index(property);
+                if (nxt_fast_path(index < NJS_STRING_MAX_LENGTH)) {
+                    ov = (njs_object_value_t *) proto;
+                    ret = njs_string_property_query(vm, pq, &ov->value, index);
+
+                    if (nxt_fast_path(ret != NXT_DECLINED)) {
+                        return ret;
+                    }
+                }
+
+            default:
+                break;
+            }
+        }
+
+        ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq);
+
+        if (ret == NXT_OK) {
+            prop = pq->lhq.value;
+
+            if (prop->type != NJS_WHITEOUT) {
+                return ret;
+            }
+
+            if (pq->own) {
+                pq->own_whiteout = prop;
+            }
+
+        } else {
+            ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq);
+
+            if (ret == NXT_OK) {
+                pq->shared = 1;
+
+                return ret;
+            }
+        }
+
+        if (own) {
+            return NXT_DECLINED;
+        }
+
+        pq->own = 0;
+        proto = proto->__proto__;
+
+    } while (proto != NULL);
+
+    return NXT_DECLINED;
+}
+
+
+static njs_ret_t
+njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_array_t *array, uint32_t index)
+{
+    uint32_t           size;
+    njs_ret_t          ret;
+    njs_value_t        *value;
+    njs_object_prop_t  *prop;
+
+    if (index >= array->length) {
+        if (pq->query != NJS_PROPERTY_QUERY_SET) {
+            return NXT_DECLINED;
+        }
+
+        size = index - array->length;
+
+        ret = njs_array_expand(vm, array, 0, size + 1);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+
+        value = &array->start[array->length];
+
+        while (size != 0) {
+            njs_set_invalid(value);
+            value++;
+            size--;
+        }
+
+        array->length = index + 1;
+    }
+
+    prop = &pq->scratch;
+
+    if (pq->query == NJS_PROPERTY_QUERY_GET) {
+        if (!njs_is_valid(&array->start[index])) {
+            return NXT_DECLINED;
+        }
+
+        prop->value = array->start[index];
+        prop->type = NJS_PROPERTY;
+
+    } else {
+        prop->value.data.u.value = &array->start[index];
+        prop->type = NJS_PROPERTY_REF;
+    }
+
+    prop->writable = 1;
+    prop->enumerable = 1;
+    prop->configurable = 1;
+
+    pq->lhq.value = prop;
+
+    return NXT_OK;
+}
+
+
+static njs_ret_t
+njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_value_t *object, uint32_t index)
+{
+    njs_slice_prop_t   slice;
+    njs_object_prop_t  *prop;
+    njs_string_prop_t  string;
+
+    prop = &pq->scratch;
+
+    slice.start = index;
+    slice.length = 1;
+    slice.string_length = njs_string_prop(&string, object);
+
+    if (slice.start < slice.string_length) {
+        /*
+         * A single codepoint string fits in retval
+         * so the function cannot fail.
+         */
+        (void) njs_string_slice(vm, &prop->value, &string, &slice);
+
+        prop->type = NJS_PROPERTY;
+        prop->writable = 0;
+        prop->enumerable = 1;
+        prop->configurable = 0;
+
+        pq->lhq.value = prop;
+
+        if (pq->query != NJS_PROPERTY_QUERY_GET) {
+            /* pq->lhq.key is used by njs_vmcode_property_set for TypeError */
+            njs_uint32_to_string(&pq->value, index);
+            njs_string_get(&pq->value, &pq->lhq.key);
+        }
+
+        return NXT_OK;
+    }
+
+    return NXT_DECLINED;
+}
+
+
+static njs_ret_t
+njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
+    njs_value_t *object)
+{
+    void                *obj;
+    njs_ret_t           ret;
+    uintptr_t           data;
+    njs_object_prop_t   *prop;
+    const njs_extern_t  *ext_proto;
+
+    prop = &pq->scratch;
+
+    prop->type = NJS_PROPERTY;
+    prop->writable = 0;
+    prop->enumerable = 1;
+    prop->configurable = 0;
+
+    ext_proto = object->external.proto;
+
+    pq->lhq.proto = &njs_extern_hash_proto;
+    ret = nxt_lvlhsh_find(&ext_proto->hash, &pq->lhq);
+
+    if (ret == NXT_OK) {
+        ext_proto = pq->lhq.value;
+
+        prop->value.type = NJS_EXTERNAL;
+        prop->value.data.truth = 1;
+        prop->value.external.proto = ext_proto;
+        prop->value.external.index = object->external.index;
+
+        if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) {
+            goto done;
+        }
+
+        data = ext_proto->data;
+
+    } else {
+        data = (uintptr_t) &pq->lhq.key;
+    }
+
+    switch (pq->query) {
+
+    case NJS_PROPERTY_QUERY_GET:
+        if (ext_proto->get != NULL) {
+            obj = njs_extern_object(vm, object);
+            ret = ext_proto->get(vm, &prop->value, obj, data);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+        }
+
+        break;
+
+    case NJS_PROPERTY_QUERY_SET:
+    case NJS_PROPERTY_QUERY_DELETE:
+
+        prop->type = NJS_PROPERTY_HANDLER;
+        prop->name = *object;
+
+        if (pq->query == NJS_PROPERTY_QUERY_SET) {
+            prop->writable = (ext_proto->set != NULL);
+            prop->value.data.u.prop_handler = njs_external_property_set;
+
+        } else {
+            prop->configurable = (ext_proto->find != NULL);
+            prop->value.data.u.prop_handler = njs_external_property_delete;
+        }
+
+        pq->ext_data = data;
+        pq->ext_proto = ext_proto;
+        pq->ext_index = object->external.index;
+
+        pq->lhq.value = prop;
+
+        vm->stash = (uintptr_t) pq;
+
+        return NXT_OK;
+    }
+
+done:
+
+    if (ext_proto->type == NJS_EXTERN_METHOD) {
+        prop->value.type = NJS_FUNCTION;
+        prop->value.data.u.function = ext_proto->function;
+        prop->value.data.truth = 1;
+    }
+
+    pq->lhq.value = prop;
+
+    return ret;
+}
+
+
+static njs_ret_t
+njs_external_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval)
+{
+    void                  *obj;
+    njs_ret_t             ret;
+    nxt_str_t             s;
+    njs_property_query_t  *pq;
+
+    pq = (njs_property_query_t *) vm->stash;
+
+    if (!njs_is_null_or_undefined(setval)) {
+        ret = njs_vm_value_to_ext_string(vm, &s, setval, 0);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+
+    } else {
+        s = nxt_string_value("");
+    }
+
+    *retval = *setval;
+
+    obj = njs_extern_index(vm, pq->ext_index);
+
+    return pq->ext_proto->set(vm, obj, pq->ext_data, &s);
+}
+
+
+static njs_ret_t
+njs_external_property_delete(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *unused, njs_value_t *unused2)
+{
+    void                  *obj;
+    njs_property_query_t  *pq;
+
+    pq = (njs_property_query_t *) vm->stash;
+
+    obj = njs_extern_index(vm, pq->ext_index);
+
+    return pq->ext_proto->find(vm, obj, pq->ext_data, 1);
+}
+
+
+/*
+ * ES5.1, 8.12.3: [[Get]].
+ *   NXT_OK               property has been found in object,
+ *      retval will contain the property's value
+ *
+ *   NXT_DECLINED         property was not found in object,
+ *   NJS_TRAP             the property trap must be called,
+ *   NXT_ERROR            exception has been thrown.
+ *      retval will contain undefined
+ */
+njs_ret_t
+njs_value_property(njs_vm_t *vm, const njs_value_t *value,
+    const njs_value_t *property, njs_value_t *retval)
+{
+    njs_ret_t             ret;
+    njs_object_prop_t     *prop;
+    njs_property_query_t  pq;
+
+    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
+
+    ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
+
+    switch (ret) {
+
+    case NXT_OK:
+        prop = pq.lhq.value;
+
+        switch (prop->type) {
+
+        case NJS_METHOD:
+            if (pq.shared) {
+                ret = njs_prop_private_copy(vm, &pq);
+
+                if (nxt_slow_path(ret != NXT_OK)) {
+                    return ret;
+                }
+
+                prop = pq.lhq.value;
+            }
+
+            /* Fall through. */
+
+        case NJS_PROPERTY:
+            *retval = prop->value;
+            break;
+
+        case NJS_PROPERTY_HANDLER:
+            pq.scratch = *prop;
+            prop = &pq.scratch;
+            ret = prop->value.data.u.prop_handler(vm, (njs_value_t *) value,
+                                                  NULL, &prop->value);
+
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
+            *retval = prop->value;
+            break;
+
+        default:
+            njs_internal_error(vm, "unexpected property type \"%s\" "
+                               "while getting",
+                               njs_prop_type_string(prop->type));
+
+            return NXT_ERROR;
+        }
+
+        break;
+
+    case NXT_DECLINED:
+        *retval = njs_value_undefined;
+
+        return NXT_DECLINED;
+
+    case NJS_TRAP:
+    case NXT_ERROR:
+    default:
+
+        return ret;
+    }
+
+    return NXT_OK;
+}
+
+
+/*
+ *   NXT_OK               property has been set successfully
+ *   NJS_TRAP             the property trap must be called
+ *   NXT_ERROR            exception has been thrown.
+ */
+njs_ret_t
+njs_value_property_set(njs_vm_t *vm, njs_value_t *object,
+    const njs_value_t *property, njs_value_t *value)
+{
+    njs_ret_t             ret;
+    njs_object_prop_t     *prop, *shared;
+    njs_property_query_t  pq;
+
+    if (njs_is_primitive(object)) {
+        njs_type_error(vm, "property set on primitive %s type",
+                       njs_type_string(object->type));
+        return NXT_ERROR;
+    }
+
+    shared = NULL;
+
+    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
+
+    ret = njs_property_query(vm, &pq, object, property);
+
+    switch (ret) {
+
+    case NXT_OK:
+        prop = pq.lhq.value;
+
+        if (nxt_slow_path(!prop->writable)) {
+            njs_type_error(vm,
+                           "Cannot assign to read-only property \"%V\" of %s",
+                           &pq.lhq.key, njs_type_string(object->type));
+            return NXT_ERROR;
+        }
+
+        if (prop->type == NJS_PROPERTY_HANDLER) {
+            ret = prop->value.data.u.prop_handler(vm, object, value,
+                                                  &vm->retval);
+            if (ret != NXT_DECLINED) {
+                return ret;
+            }
+        }
+
+        if (pq.own) {
+            switch (prop->type) {
+            case NJS_PROPERTY:
+            case NJS_METHOD:
+                if (nxt_slow_path(pq.shared)) {
+                    shared = prop;
+                    break;
+                }
+
+                goto found;
+
+            case NJS_PROPERTY_REF:
+                *prop->value.data.u.value = *value;
+                return NXT_OK;
+
+            default:
+                njs_internal_error(vm, "unexpected property type \"%s\" "
+                                   "while setting",
+                                   njs_prop_type_string(prop->type));
+
+                return NXT_ERROR;
+            }
+
+            break;
+        }
+
+        /* Fall through. */
+
+    case NXT_DECLINED:
+        if (nxt_slow_path(pq.own_whiteout != NULL)) {
+            /* Previously deleted property. */
+            prop = pq.own_whiteout;
+
+            prop->type = NJS_PROPERTY;
+            prop->enumerable = 1;
+            prop->configurable = 1;
+            prop->writable = 1;
+
+            goto found;
+        }
+
+        break;
+
+    case NJS_TRAP:
+    case NXT_ERROR:
+    default:
+
+        return ret;
+    }
+
+    if (nxt_slow_path(!object->data.u.object->extensible)) {
+        njs_type_error(vm, "Cannot add property \"%V\", "
+                       "object is not extensible", &pq.lhq.key);
+        return NXT_ERROR;
+    }
+
+    prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_undefined, 1);
+    if (nxt_slow_path(prop == NULL)) {
+        return NXT_ERROR;
+    }
+
+    if (nxt_slow_path(shared != NULL)) {
+        prop->enumerable = shared->enumerable;
+        prop->configurable = shared->configurable;
+    }
+
+    pq.lhq.replace = 0;
+    pq.lhq.value = prop;
+    pq.lhq.pool = vm->mem_pool;
+
+    ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+found:
+
+    prop->value = *value;
+
+    return NXT_OK;
+}
+
+
+nxt_noinline njs_object_prop_t *
+njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
+    const njs_value_t *value, uint8_t attributes)
+{
+    njs_object_prop_t  *prop;
+
+    prop = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t),
+                        sizeof(njs_object_prop_t));
+
+    if (nxt_fast_path(prop != NULL)) {
+        /* GC: retain. */
+        prop->value = *value;
+
+        /* GC: retain. */
+        prop->name = *name;
+
+        prop->type = NJS_PROPERTY;
+        prop->writable = attributes;
+        prop->enumerable = attributes;
+        prop->configurable = attributes;
+
+        return prop;
+    }
+
+    njs_memory_error(vm);
+
+    return NULL;
+}
+
+
+nxt_noinline njs_object_prop_t *
+njs_object_property(njs_vm_t *vm, const njs_object_t *object,
+    nxt_lvlhsh_query_t *lhq)
+{
+    nxt_int_t  ret;
+
+    lhq->proto = &njs_object_hash_proto;
+
+    do {
+        ret = nxt_lvlhsh_find(&object->hash, lhq);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+            return lhq->value;
+        }
+
+        ret = nxt_lvlhsh_find(&object->shared_hash, lhq);
+
+        if (nxt_fast_path(ret == NXT_OK)) {
+            return lhq->value;
+        }
+
+        object = object->__proto__;
+
+    } while (object != NULL);
+
+    return NULL;
+}
+
+
+/*
+ * ES5.1, 8.12.9: [[DefineOwnProperty]]
+ *   Limited support of special descriptors like length and array index
+ *   (values can be set, but without property flags support).
+ */
+njs_ret_t
+njs_define_property(njs_vm_t *vm, njs_value_t *object, const njs_value_t *name,
+    const njs_object_t *descriptor)
+{
+    nxt_int_t             ret;
+    njs_object_prop_t     *desc, *current;
+    njs_property_query_t  pq;
+
+    njs_string_get(name, &pq.lhq.key);
+    pq.lhq.key_hash = nxt_djb_hash(pq.lhq.key.start, pq.lhq.key.length);
+    pq.lhq.proto = &njs_object_hash_proto;
+
+    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
+
+    ret = njs_property_query(vm, &pq, object, name);
+
+    if (ret != NXT_OK && ret != NXT_DECLINED) {
+        return ret;
+    }
+
+    desc = njs_descriptor_prop(vm, name, descriptor);
+    if (nxt_slow_path(desc == NULL)) {
+        return NXT_ERROR;
+    }
+
+    if (nxt_fast_path(ret == NXT_DECLINED)) {
+
+        if (!njs_is_valid(&desc->value)) {
+            desc->value = njs_value_undefined;
+        }
+
+        if (desc->writable == NJS_ATTRIBUTE_UNSET) {
+            desc->writable = 0;
+        }
+
+        if (desc->enumerable == NJS_ATTRIBUTE_UNSET) {
+            desc->enumerable = 0;
+        }
+
+        if (desc->configurable == NJS_ATTRIBUTE_UNSET) {
+            desc->configurable = 0;
+        }
+
+        if (nxt_slow_path(pq.lhq.value != NULL)) {
+            current = pq.lhq.value;
+
+            if (nxt_slow_path(current->type == NJS_WHITEOUT)) {
+                /* Previously deleted property.  */
+                *current = *desc;
+            }
+
+        } else {
+            pq.lhq.value = desc;
+            pq.lhq.replace = 0;
+            pq.lhq.pool = vm->mem_pool;
+
+            ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                njs_internal_error(vm, "lvlhsh insert failed");
+                return NXT_ERROR;
+            }
+        }
+
+        return NXT_OK;
+    }
+
+    /* Updating existing prop. */
+
+    if (nxt_slow_path(pq.shared)) {
+        ret = njs_prop_private_copy(vm, &pq);
+
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+    }
+
+    current = pq.lhq.value;
+
+    switch (current->type) {
+    case NJS_PROPERTY:
+    case NJS_METHOD:
+        break;
+
+    case NJS_PROPERTY_REF:
+        if (njs_is_valid(&desc->value)) {
+            *current->value.data.u.value = desc->value;
+        } else {
+            *current->value.data.u.value = njs_value_undefined;
+        }
+
+        return NXT_OK;
+
+    case NJS_PROPERTY_HANDLER:
+        if (current->writable && njs_is_valid(&desc->value)) {
+            ret = current->value.data.u.prop_handler(vm, object, &desc->value,
+                                                     &vm->retval);
+
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+        }
+
+        return NXT_OK;
+
+    default:
+        njs_internal_error(vm, "unexpected property type \"%s\" "
+                           "while defining property",
+                           njs_prop_type_string(current->type));
+
+        return NXT_ERROR;
+    }
+
+    if (!current->configurable) {
+
+        if (njs_is_valid(&desc->value)
+            && current->writable == NJS_ATTRIBUTE_FALSE
+            && !njs_values_strict_equal(&desc->value, &current->value))
+        {
+            goto exception;
+        }
+
+        if (desc->writable == NJS_ATTRIBUTE_TRUE
+            && current->writable == NJS_ATTRIBUTE_FALSE)
+        {
+            goto exception;
+        }
+
+        if (desc->enumerable != NJS_ATTRIBUTE_UNSET
+            && current->enumerable != desc->enumerable)
+        {
+            goto exception;
+        }
+
+        if (desc->configurable == NJS_ATTRIBUTE_TRUE) {
+            goto exception;
+        }
+    }
+
+    if (njs_is_valid(&desc->value)) {
+        current->value = desc->value;
+    }
+
+    if (desc->writable != NJS_ATTRIBUTE_UNSET) {
+        current->writable = desc->writable;
+    }
+
+    if (desc->enumerable != NJS_ATTRIBUTE_UNSET) {
+        current->enumerable = desc->enumerable;
+    }
+
+    if (desc->configurable != NJS_ATTRIBUTE_UNSET) {
+        current->configurable = desc->configurable;
+    }
+
+    return NXT_OK;
+
+exception:
+
+    njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.lhq.key);
+
+    return NXT_ERROR;
+}
+
+
+static njs_object_prop_t *
+njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name,
+    const njs_object_t *descriptor)
+{
+    njs_object_prop_t   *prop, *pr;
+    nxt_lvlhsh_query_t  pq;
+
+    prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
+                                 NJS_ATTRIBUTE_UNSET);
+    if (nxt_slow_path(prop == NULL)) {
+        return NULL;
+    }
+
+    pq.key = nxt_string_value("value");
+    pq.key_hash = NJS_VALUE_HASH;
+
+    pr = njs_object_property(vm, descriptor, &pq);
+    if (pr != NULL) {
+        prop->value = pr->value;
+    }
+
+    pq.key = nxt_string_value("writable");
+    pq.key_hash = NJS_WRITABABLE_HASH;
+
+    pr = njs_object_property(vm, descriptor, &pq);
+    if (pr != NULL) {
+        prop->writable = pr->value.data.truth;
+    }
+
+    pq.key = nxt_string_value("enumerable");
+    pq.key_hash = NJS_ENUMERABLE_HASH;
+
+    pr = njs_object_property(vm, descriptor, &pq);
+    if (pr != NULL) {
+        prop->enumerable = pr->value.data.truth;
+    }
+
+    pq.key = nxt_string_value("configurable");
+    pq.key_hash = NJS_CONFIGURABLE_HASH;
+
+    pr = njs_object_property(vm, descriptor, &pq);
+    if (pr != NULL) {
+        prop->configurable = pr->value.data.truth;
+    }
+
+    return prop;
+}
+
+
+static const njs_value_t  njs_object_value_string = njs_string("value");
+static const njs_value_t  njs_object_writable_string =
+                                                    njs_string("writable");
+static const njs_value_t  njs_object_enumerable_string =
+                                                    njs_string("enumerable");
+static const njs_value_t  njs_object_configurable_string =
+                                                    njs_string("configurable");
+
+
+njs_ret_t
+njs_object_property_descriptor(njs_vm_t *vm, njs_value_t *dest,
+    const njs_value_t *value, const njs_value_t *property)
+{
+    nxt_int_t             ret;
+    njs_object_t          *descriptor;
+    njs_object_prop_t     *pr, *prop;
+    const njs_value_t     *setval;
+    nxt_lvlhsh_query_t    lhq;
+    njs_property_query_t  pq;
+
+    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
+
+    ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
+
+    switch (ret) {
+    case NXT_OK:
+        break;
+
+    case NXT_DECLINED:
+        *dest = njs_value_undefined;
+        return NXT_OK;
+
+    case NJS_TRAP:
+    case NXT_ERROR:
+    default:
+        return ret;
+    }
+
+    prop = pq.lhq.value;
+
+    switch (prop->type) {
+    case NJS_PROPERTY:
+        break;
+
+    case NJS_PROPERTY_HANDLER:
+        pq.scratch = *prop;
+        prop = &pq.scratch;
+        ret = prop->value.data.u.prop_handler(vm, (njs_value_t *) value,
+                                              NULL, &prop->value);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+
+        break;
+
+    case NJS_METHOD:
+        if (pq.shared) {
+            ret = njs_prop_private_copy(vm, &pq);
+
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
+            prop = pq.lhq.value;
+        }
+
+        break;
+
+    default:
+        njs_type_error(vm, "unexpected property type: %s",
+                       njs_prop_type_string(prop->type));
+        return NXT_ERROR;
+    }
+
+    descriptor = njs_object_alloc(vm);
+    if (nxt_slow_path(descriptor == NULL)) {
+        return NXT_ERROR;
+    }
+
+    lhq.proto = &njs_object_hash_proto;
+    lhq.replace = 0;
+    lhq.pool = vm->mem_pool;
+
+    lhq.key = nxt_string_value("value");
+    lhq.key_hash = NJS_VALUE_HASH;
+
+    pr = njs_object_prop_alloc(vm, &njs_object_value_string, &prop->value, 1);
+    if (nxt_slow_path(pr == NULL)) {
+        return NXT_ERROR;
+    }
+
+    lhq.value = pr;
+
+    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+    lhq.key = nxt_string_value("writable");
+    lhq.key_hash = NJS_WRITABABLE_HASH;
+
+    setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false;
+
+    pr = njs_object_prop_alloc(vm, &njs_object_writable_string, setval, 1);
+    if (nxt_slow_path(pr == NULL)) {
+        return NXT_ERROR;
+    }
+
+    lhq.value = pr;
+
+    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+    lhq.key = nxt_string_value("enumerable");
+    lhq.key_hash = NJS_ENUMERABLE_HASH;
+
+    setval = (prop->enumerable == 1) ? &njs_value_true : &njs_value_false;
+
+    pr = njs_object_prop_alloc(vm, &njs_object_enumerable_string, setval, 1);
+    if (nxt_slow_path(pr == NULL)) {
+        return NXT_ERROR;
+    }
+
+    lhq.value = pr;
+
+    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+    lhq.key = nxt_string_value("configurable");
+    lhq.key_hash = NJS_CONFIGURABLE_HASH;
+
+    setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false;
+
+    pr = njs_object_prop_alloc(vm, &njs_object_configurable_string, setval, 1);
+    if (nxt_slow_path(pr == NULL)) {
+        return NXT_ERROR;
+    }
+
+    lhq.value = pr;
+
+    ret = nxt_lvlhsh_insert(&descriptor->hash, &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+    dest->data.u.object = descriptor;
+    dest->type = NJS_OBJECT;
+    dest->data.truth = 1;
+
+    return NXT_OK;
+}
+
+
+njs_ret_t
+njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
+{
+    nxt_int_t           ret;
+    njs_function_t      *function;
+    njs_object_prop_t   *prop, *shared, *name;
+    nxt_lvlhsh_query_t  lhq;
+
+    static const njs_value_t  name_string = njs_string("name");
+
+    prop = nxt_mp_align(vm->mem_pool, sizeof(njs_value_t),
+                        sizeof(njs_object_prop_t));
+    if (nxt_slow_path(prop == NULL)) {
+        njs_memory_error(vm);
+        return NXT_ERROR;
+    }
+
+    shared = pq->lhq.value;
+    *prop = *shared;
+
+    pq->lhq.replace = 0;
+    pq->lhq.value = prop;
+    pq->lhq.pool = vm->mem_pool;
+
+    ret = nxt_lvlhsh_insert(&pq->prototype->hash, &pq->lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+    if (!njs_is_function(&prop->value)) {
+        return NXT_OK;
+    }
+
+    function = njs_function_value_copy(vm, &prop->value);
+    if (nxt_slow_path(function == NULL)) {
+        return NXT_ERROR;
+    }
+
+    if (function->ctor) {
+        function->object.shared_hash = vm->shared->function_instance_hash;
+
+    } else {
+        function->object.shared_hash = vm->shared->arrow_instance_hash;
+    }
+
+    name = njs_object_prop_alloc(vm, &name_string, &prop->name, 0);
+    if (nxt_slow_path(name == NULL)) {
+        return NXT_ERROR;
+    }
+
+    name->configurable = 1;
+
+    lhq.key_hash = NJS_NAME_HASH;
+    lhq.key = nxt_string_value("name");
+    lhq.replace = 0;
+    lhq.value = name;
+    lhq.proto = &njs_object_hash_proto;
+    lhq.pool = vm->mem_pool;
+
+    ret = nxt_lvlhsh_insert(&function->object.hash, &lhq);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        njs_internal_error(vm, "lvlhsh insert failed");
+        return NXT_ERROR;
+    }
+
+    return NXT_OK;
+}
+
+
+const char *
+njs_prop_type_string(njs_object_property_type_t type)
+{
+    switch (type) {
+    case NJS_PROPERTY_REF:
+        return "property_ref";
+
+    case NJS_METHOD:
+        return "method";
+
+    case NJS_PROPERTY_HANDLER:
+        return "property handler";
+
+    case NJS_WHITEOUT:
+        return "whiteout";
+
+    case NJS_PROPERTY:
+        return "property";
+
+    default:
+        return "unknown";
+    }
+}
index 7488617867c4c8415387378ef21af027777650e8..2c0ecb15d09e19e2513d9d63c5bddff5d116acf2 100644 (file)
@@ -3038,220 +3038,6 @@ njs_primitive_value(njs_vm_t *vm, njs_value_t *value, nxt_uint_t hint)
 }
 
 
-/*
- * ES5.1, 8.12.3: [[Get]].
- *   NXT_OK               property has been found in object,
- *      retval will contain the property's value
- *
- *   NXT_DECLINED         property was not found in object,
- *   NJS_TRAP             the property trap must be called,
- *   NXT_ERROR            exception has been thrown.
- *      retval will contain undefined
- */
-njs_ret_t
-njs_value_property(njs_vm_t *vm, const njs_value_t *value,
-    const njs_value_t *property, njs_value_t *retval)
-{
-    njs_ret_t             ret;
-    njs_object_prop_t     *prop;
-    njs_property_query_t  pq;
-
-    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
-
-    ret = njs_property_query(vm, &pq, (njs_value_t *) value, property);
-
-    switch (ret) {
-
-    case NXT_OK:
-        prop = pq.lhq.value;
-
-        switch (prop->type) {
-
-        case NJS_METHOD:
-            if (pq.shared) {
-                ret = njs_prop_private_copy(vm, &pq);
-
-                if (nxt_slow_path(ret != NXT_OK)) {
-                    return ret;
-                }
-
-                prop = pq.lhq.value;
-            }
-
-            /* Fall through. */
-
-        case NJS_PROPERTY:
-            *retval = prop->value;
-            break;
-
-        case NJS_PROPERTY_HANDLER:
-            pq.scratch = *prop;
-            prop = &pq.scratch;
-            ret = prop->value.data.u.prop_handler(vm, (njs_value_t *) value,
-                                                  NULL, &prop->value);
-
-            if (nxt_slow_path(ret != NXT_OK)) {
-                return ret;
-            }
-
-            *retval = prop->value;
-            break;
-
-        default:
-            njs_internal_error(vm, "unexpected property type \"%s\" "
-                               "while getting",
-                               njs_prop_type_string(prop->type));
-
-            return NXT_ERROR;
-        }
-
-        break;
-
-    case NXT_DECLINED:
-        *retval = njs_value_undefined;
-
-        return NXT_DECLINED;
-
-    case NJS_TRAP:
-    case NXT_ERROR:
-    default:
-
-        return ret;
-    }
-
-    return NXT_OK;
-}
-
-
-/*
- *   NXT_OK               property has been set successfully
- *   NJS_TRAP             the property trap must be called
- *   NXT_ERROR            exception has been thrown.
- */
-njs_ret_t
-njs_value_property_set(njs_vm_t *vm, njs_value_t *object,
-    const njs_value_t *property, njs_value_t *value)
-{
-    njs_ret_t             ret;
-    njs_object_prop_t     *prop, *shared;
-    njs_property_query_t  pq;
-
-    if (njs_is_primitive(object)) {
-        njs_type_error(vm, "property set on primitive %s type",
-                       njs_type_string(object->type));
-        return NXT_ERROR;
-    }
-
-    shared = NULL;
-
-    njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
-
-    ret = njs_property_query(vm, &pq, object, property);
-
-    switch (ret) {
-
-    case NXT_OK:
-        prop = pq.lhq.value;
-
-        if (nxt_slow_path(!prop->writable)) {
-            njs_type_error(vm,
-                           "Cannot assign to read-only property \"%V\" of %s",
-                           &pq.lhq.key, njs_type_string(object->type));
-            return NXT_ERROR;
-        }
-
-        if (prop->type == NJS_PROPERTY_HANDLER) {
-            ret = prop->value.data.u.prop_handler(vm, object, value,
-                                                  &vm->retval);
-            if (ret != NXT_DECLINED) {
-                return ret;
-            }
-        }
-
-        if (pq.own) {
-            switch (prop->type) {
-            case NJS_PROPERTY:
-            case NJS_METHOD:
-                if (nxt_slow_path(pq.shared)) {
-                    shared = prop;
-                    break;
-                }
-
-                goto found;
-
-            case NJS_PROPERTY_REF:
-                *prop->value.data.u.value = *value;
-                return NXT_OK;
-
-            default:
-                njs_internal_error(vm, "unexpected property type \"%s\" "
-                                   "while setting",
-                                   njs_prop_type_string(prop->type));
-
-                return NXT_ERROR;
-            }
-
-            break;
-        }
-
-        /* Fall through. */
-
-    case NXT_DECLINED:
-        if (nxt_slow_path(pq.own_whiteout != NULL)) {
-            /* Previously deleted property. */
-            prop = pq.own_whiteout;
-
-            prop->type = NJS_PROPERTY;
-            prop->enumerable = 1;
-            prop->configurable = 1;
-            prop->writable = 1;
-
-            goto found;
-        }
-
-        break;
-
-    case NJS_TRAP:
-    case NXT_ERROR:
-    default:
-
-        return ret;
-    }
-
-    if (nxt_slow_path(!object->data.u.object->extensible)) {
-        njs_type_error(vm, "Cannot add property \"%V\", "
-                       "object is not extensible", &pq.lhq.key);
-        return NXT_ERROR;
-    }
-
-    prop = njs_object_prop_alloc(vm, &pq.value, &njs_value_undefined, 1);
-    if (nxt_slow_path(prop == NULL)) {
-        return NXT_ERROR;
-    }
-
-    if (nxt_slow_path(shared != NULL)) {
-        prop->enumerable = shared->enumerable;
-        prop->configurable = shared->configurable;
-    }
-
-    pq.lhq.replace = 0;
-    pq.lhq.value = prop;
-    pq.lhq.pool = vm->mem_pool;
-
-    ret = nxt_lvlhsh_insert(&object->data.u.object->hash, &pq.lhq);
-    if (nxt_slow_path(ret != NXT_OK)) {
-        njs_internal_error(vm, "lvlhsh insert failed");
-        return NXT_ERROR;
-    }
-
-found:
-
-    prop->value = *value;
-
-    return NXT_OK;
-}
-
-
 njs_array_t *
 njs_value_enumerate(njs_vm_t *vm, const njs_value_t *value,
     njs_object_enum_t kind, nxt_bool_t all)