Created
September 13, 2018 17:36
-
-
Save xeioex/b44c9d876e18a1e59ecbf7f432eca974 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536859188 -10800 | |
| # Thu Sep 13 20:19:48 2018 +0300 | |
| # Node ID ebc9a6f65579c68976a54f88bdeff1248e1d0be8 | |
| # Parent c4f9a49486979285b6b508109d4988b4bc30d35d | |
| Fixed Array.prototype.length setter. | |
| diff --git a/njs/njs_array.c b/njs/njs_array.c | |
| --- a/njs/njs_array.c | |
| +++ b/njs/njs_array.c | |
| @@ -377,7 +377,7 @@ njs_array_prototype_length(njs_vm_t *vm, | |
| njs_value_t *setval, njs_value_t *retval) | |
| { | |
| double num; | |
| - int32_t size; | |
| + int64_t size; | |
| uint32_t length; | |
| njs_ret_t ret; | |
| njs_value_t *val; | |
| @@ -399,7 +399,7 @@ njs_array_prototype_length(njs_vm_t *vm, | |
| return NJS_ERROR; | |
| } | |
| - size = (int32_t) (length - array->length); | |
| + size = (int64_t) length - array->length; | |
| if (size > 0) { | |
| ret = njs_array_expand(vm, array, 0, size); | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -2790,9 +2790,15 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("[].length = {}"), | |
| nxt_string("RangeError: Invalid array length") }, | |
| + { nxt_string("[].length = 2**32 - 1"), | |
| + nxt_string("MemoryError") }, | |
| + | |
| { nxt_string("[].length = 2**32"), | |
| nxt_string("RangeError: Invalid array length") }, | |
| + { nxt_string("[].length = 2**32 + 1"), | |
| + nxt_string("RangeError: Invalid array length") }, | |
| + | |
| { nxt_string("[].length = -1"), | |
| nxt_string("RangeError: Invalid array length") }, | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536859188 -10800 | |
| # Thu Sep 13 20:19:48 2018 +0300 | |
| # Node ID 84d4d83acd928ca222143dbba3ad48c985027262 | |
| # Parent ebc9a6f65579c68976a54f88bdeff1248e1d0be8 | |
| Fixed njs_array_alloc() for length > 2**31. | |
| diff --git a/njs/njs_array.c b/njs/njs_array.c | |
| --- a/njs/njs_array.c | |
| +++ b/njs/njs_array.c | |
| @@ -117,7 +117,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t l | |
| goto memory_error; | |
| } | |
| - size = length + spare; | |
| + size = (size_t) length + spare; | |
| if (nxt_slow_path(size * sizeof(njs_value_t) < size)) { | |
| goto memory_error; | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536859189 -10800 | |
| # Thu Sep 13 20:19:49 2018 +0300 | |
| # Node ID 7baa55f6e48df106e5226bea86949a64804b93d0 | |
| # Parent 84d4d83acd928ca222143dbba3ad48c985027262 | |
| Fixed njs_string_slice(). | |
| dst retval argument was ignored. | |
| diff --git a/njs/njs_string.c b/njs/njs_string.c | |
| --- a/njs/njs_string.c | |
| +++ b/njs/njs_string.c | |
| @@ -1288,7 +1288,7 @@ njs_string_slice_args(njs_slice_prop_t * | |
| nxt_noinline njs_ret_t | |
| njs_string_slice(njs_vm_t *vm, njs_value_t *dst, | |
| - const njs_string_prop_t *string, njs_slice_prop_t *slice) | |
| + const njs_string_prop_t *string, const njs_slice_prop_t *slice) | |
| { | |
| size_t size, n, length; | |
| const u_char *p, *start, *end; | |
| @@ -1325,10 +1325,10 @@ njs_string_slice(njs_vm_t *vm, njs_value | |
| } | |
| if (nxt_fast_path(size != 0)) { | |
| - return njs_string_new(vm, &vm->retval, start, size, length); | |
| + return njs_string_new(vm, dst, start, size, length); | |
| } | |
| - vm->retval = njs_string_empty; | |
| + *dst = njs_string_empty; | |
| return NXT_OK; | |
| } | |
| diff --git a/njs/njs_string.h b/njs/njs_string.h | |
| --- a/njs/njs_string.h | |
| +++ b/njs/njs_string.h | |
| @@ -146,7 +146,7 @@ njs_ret_t njs_string_constructor(njs_vm_ | |
| nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2); | |
| nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2); | |
| njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst, | |
| - const njs_string_prop_t *string, njs_slice_prop_t *slice); | |
| + const njs_string_prop_t *string, const njs_slice_prop_t *slice); | |
| const u_char *njs_string_offset(const u_char *start, const u_char *end, | |
| size_t index); | |
| nxt_noinline uint32_t njs_string_index(njs_string_prop_t *string, | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860013 -10800 | |
| # Thu Sep 13 20:33:33 2018 +0300 | |
| # Node ID ffe3b161805980547f16f3ff1142fc55f3f628ca | |
| # Parent 7baa55f6e48df106e5226bea86949a64804b93d0 | |
| Object property quering is refactored. | |
| njs_property_query() only returns property descriptors and | |
| does not try to invoke PROPERTY_HANDLER for | |
| AccessorDescriptors. | |
| njs_property_query_t.own can be used to query for an object's | |
| OwnProperty. | |
| njs_value_property() is introduced which corresponds to [[Get]] | |
| method from specification. | |
| This fixes #32 issue on Github. | |
| diff --git a/njs/njs_object.c b/njs/njs_object.c | |
| --- a/njs/njs_object.c | |
| +++ b/njs/njs_object.c | |
| @@ -246,13 +246,15 @@ njs_object_property(njs_vm_t *vm, const | |
| njs_ret_t | |
| njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object, | |
| - njs_value_t *property) | |
| + const njs_value_t *property) | |
| { | |
| uint32_t index; | |
| uint32_t (*hash)(const void *, size_t); | |
| njs_ret_t ret; | |
| + njs_array_t *array; | |
| njs_object_t *obj; | |
| njs_function_t *function; | |
| + njs_object_prop_t *prop; | |
| const njs_extern_t *ext_proto; | |
| hash = nxt_djb_hash; | |
| @@ -285,6 +287,34 @@ njs_property_query(njs_vm_t *vm, njs_pro | |
| if (nxt_fast_path(index < NJS_ARRAY_MAX_LENGTH)) { | |
| return njs_array_property_query(vm, pq, object, index); | |
| + | |
| + } else if (pq->query == NJS_PROPERTY_QUERY_GET) { | |
| + | |
| + /* FIXME: length should be a DataProperty */ | |
| + | |
| + njs_primitive_value_to_string(vm, &pq->value, property); | |
| + njs_string_get(&pq->value, &pq->lhq.key); | |
| + | |
| + if (nxt_slow_path(pq->lhq.key.length == 6 | |
| + && memcmp(pq->lhq.key.start, "length", 6) | |
| + == 0)) | |
| + { | |
| + prop = &pq->scratch; | |
| + | |
| + prop->name = (njs_value_t) njs_string("length"); | |
| + | |
| + array = object->data.u.array; | |
| + njs_value_number_set(&prop->value, array->length); | |
| + | |
| + prop->type = NJS_PROPERTY; | |
| + prop->configurable = 0; | |
| + prop->enumerable = 0; | |
| + prop->writable = 1; | |
| + | |
| + pq->lhq.value = prop; | |
| + | |
| + return NXT_OK; | |
| + } | |
| } | |
| } else { | |
| @@ -381,6 +411,7 @@ njs_object_property_query(njs_vm_t *vm, | |
| njs_value_t *value, njs_object_t *object) | |
| { | |
| njs_ret_t ret; | |
| + njs_object_t *proto; | |
| njs_object_prop_t *prop; | |
| pq->lhq.proto = &njs_object_hash_proto; | |
| @@ -392,70 +423,49 @@ njs_object_property_query(njs_vm_t *vm, | |
| } | |
| } | |
| + proto = object; | |
| + | |
| do { | |
| - pq->prototype = object; | |
| + pq->prototype = proto; | |
| - ret = nxt_lvlhsh_find(&object->hash, &pq->lhq); | |
| + /* length and other shared properties should be Own property */ | |
| - if (ret == NXT_OK) { | |
| - prop = pq->lhq.value; | |
| + if (nxt_fast_path(!pq->own || proto == object)) { | |
| + ret = nxt_lvlhsh_find(&proto->hash, &pq->lhq); | |
| - if (prop->type != NJS_WHITEOUT) { | |
| + if (ret == NXT_OK) { | |
| + prop = pq->lhq.value; | |
| pq->shared = 0; | |
| - return ret; | |
| + return (prop->type != NJS_WHITEOUT) ? ret : NXT_DECLINED; | |
| } | |
| - | |
| - goto next; | |
| } | |
| if (pq->query > NJS_PROPERTY_QUERY_IN) { | |
| - /* NXT_DECLINED */ | |
| - return ret; | |
| + return NXT_DECLINED; | |
| } | |
| - ret = nxt_lvlhsh_find(&object->shared_hash, &pq->lhq); | |
| + ret = nxt_lvlhsh_find(&proto->shared_hash, &pq->lhq); | |
| if (ret == NXT_OK) { | |
| pq->shared = 1; | |
| - if (pq->query == NJS_PROPERTY_QUERY_GET) { | |
| - prop = pq->lhq.value; | |
| - | |
| - if (prop->type == NJS_PROPERTY_HANDLER) { | |
| - pq->scratch = *prop; | |
| - prop = &pq->scratch; | |
| - ret = prop->value.data.u.prop_handler(vm, value, NULL, | |
| - &prop->value); | |
| - | |
| - if (nxt_fast_path(ret == NXT_OK)) { | |
| - prop->type = NJS_PROPERTY; | |
| - pq->lhq.value = prop; | |
| - } | |
| - } | |
| - } | |
| - | |
| return ret; | |
| } | |
| if (pq->query > NJS_PROPERTY_QUERY_IN) { | |
| - /* NXT_DECLINED */ | |
| - return ret; | |
| + return NXT_DECLINED; | |
| } | |
| - next: | |
| + proto = proto->__proto__; | |
| - object = object->__proto__; | |
| - | |
| - } while (object != NULL); | |
| + } while (proto != NULL); | |
| if (njs_is_string(value)) { | |
| return NJS_STRING_VALUE; | |
| } | |
| - /* NXT_DECLINED */ | |
| - | |
| - return ret; | |
| + return NXT_DECLINED; | |
| } | |
| @@ -1037,12 +1047,9 @@ njs_object_get_own_property_descriptor(n | |
| lhq.proto = &njs_object_hash_proto; | |
| if (prop == NULL) { | |
| - pq.query = NJS_PROPERTY_QUERY_GET; | |
| - pq.lhq.key.length = 0; | |
| - pq.lhq.key.start = NULL; | |
| + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1); | |
| - ret = njs_property_query(vm, &pq, (njs_value_t *) value, | |
| - (njs_value_t *) property); | |
| + ret = njs_property_query(vm, &pq, (njs_value_t *) value, property); | |
| if (ret != NXT_OK) { | |
| vm->retval = njs_value_void; | |
| @@ -1050,6 +1057,16 @@ njs_object_get_own_property_descriptor(n | |
| } | |
| prop = pq.lhq.value; | |
| + | |
| + if (prop->type == 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; | |
| + } | |
| + } | |
| } | |
| descriptor = njs_object_alloc(vm); | |
| diff --git a/njs/njs_object.h b/njs/njs_object.h | |
| --- a/njs/njs_object.h | |
| +++ b/njs/njs_object.h | |
| @@ -51,9 +51,17 @@ typedef struct { | |
| njs_object_t *prototype; | |
| uint8_t query; | |
| uint8_t shared; | |
| + uint8_t own; | |
| } njs_property_query_t; | |
| +#define njs_property_query_init(pq, _query, _own) \ | |
| + do { \ | |
| + (pq)->lhq.key.length = 0; \ | |
| + (pq)->query = _query; \ | |
| + (pq)->own = _own; \ | |
| + } while (0) | |
| + | |
| struct njs_object_init_s { | |
| nxt_str_t name; | |
| @@ -67,10 +75,12 @@ njs_object_t *njs_object_value_copy(njs_ | |
| njs_object_t *njs_object_value_alloc(njs_vm_t *vm, const njs_value_t *value, | |
| nxt_uint_t type); | |
| njs_array_t *njs_object_keys_array(njs_vm_t *vm, const njs_value_t *object); | |
| +njs_ret_t njs_value_property(njs_vm_t *vm, njs_value_t *value, | |
| + const njs_value_t *property, njs_value_t *retval); | |
| 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, njs_value_t *property); | |
| + 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, | |
| diff --git a/njs/njs_vm.c b/njs/njs_vm.c | |
| --- a/njs/njs_vm.c | |
| +++ b/njs/njs_vm.c | |
| @@ -476,147 +476,13 @@ njs_ret_t | |
| njs_vmcode_property_get(njs_vm_t *vm, njs_value_t *object, | |
| njs_value_t *property) | |
| { | |
| - void *obj; | |
| - int32_t index; | |
| - uintptr_t data; | |
| - njs_ret_t ret; | |
| - njs_value_t *val, ext_val; | |
| - njs_slice_prop_t slice; | |
| - njs_string_prop_t string; | |
| - njs_object_prop_t *prop; | |
| - const njs_value_t *retval; | |
| - const njs_extern_t *ext_proto; | |
| - njs_property_query_t pq; | |
| - | |
| - pq.query = NJS_PROPERTY_QUERY_GET; | |
| - | |
| - ret = njs_property_query(vm, &pq, object, property); | |
| - | |
| - retval = &njs_value_void; | |
| - | |
| - switch (ret) { | |
| - | |
| - case NXT_OK: | |
| - prop = pq.lhq.value; | |
| - | |
| - switch (prop->type) { | |
| - | |
| - case NJS_METHOD: | |
| - if (pq.shared) { | |
| - ret = njs_method_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; | |
| - | |
| - default: | |
| - nxt_thread_log_alert("invalid property get type:%d", prop->type); | |
| - | |
| - return NXT_ERROR; | |
| - } | |
| - | |
| - break; | |
| - | |
| - case NXT_DECLINED: | |
| - case NJS_PRIMITIVE_VALUE: | |
| - break; | |
| - | |
| - case NJS_STRING_VALUE: | |
| - | |
| - /* string[n]. */ | |
| - | |
| - index = (int32_t) njs_value_to_index(property); | |
| - | |
| - if (nxt_fast_path(index >= 0)) { | |
| - 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 vm->retval | |
| - * so the function cannot fail. | |
| - */ | |
| - (void) njs_string_slice(vm, &vm->retval, &string, &slice); | |
| - | |
| - return sizeof(njs_vmcode_prop_get_t); | |
| - } | |
| - } | |
| - | |
| - break; | |
| - | |
| - case NJS_ARRAY_VALUE: | |
| - val = pq.lhq.value; | |
| - | |
| - if (njs_is_valid(val)) { | |
| - retval = val; | |
| - } | |
| - | |
| - break; | |
| - | |
| - case NJS_EXTERNAL_VALUE: | |
| - ext_proto = object->external.proto; | |
| - | |
| - ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); | |
| - | |
| - if (ret == NXT_OK) { | |
| - ext_proto = pq.lhq.value; | |
| - | |
| - ext_val.type = NJS_EXTERNAL; | |
| - ext_val.data.truth = 1; | |
| - ext_val.external.proto = ext_proto; | |
| - ext_val.external.index = object->external.index; | |
| - | |
| - if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) { | |
| - retval = &ext_val; | |
| - break; | |
| - } | |
| - | |
| - data = ext_proto->data; | |
| - | |
| - } else { | |
| - data = (uintptr_t) &pq.lhq.key; | |
| - } | |
| - | |
| - vm->retval = njs_value_void; | |
| - | |
| - if (ext_proto->get != NULL) { | |
| - obj = njs_extern_object(vm, object); | |
| - | |
| - ret = ext_proto->get(vm, &vm->retval, obj, data); | |
| - if (nxt_slow_path(ret != NXT_OK)) { | |
| - return ret; | |
| - } | |
| - | |
| - /* The vm->retval is already retained by ext_proto->get(). */ | |
| - } | |
| - | |
| - if (ext_proto->type == NJS_EXTERN_METHOD) { | |
| - vm->retval.data.u.function = ext_proto->function; | |
| - vm->retval.type = NJS_FUNCTION; | |
| - vm->retval.data.truth = 1; | |
| - } | |
| - | |
| - return sizeof(njs_vmcode_prop_get_t); | |
| - | |
| - case NJS_TRAP: | |
| - case NXT_ERROR: | |
| - default: | |
| - | |
| + njs_ret_t ret; | |
| + | |
| + ret = njs_value_property(vm, object, property, &vm->retval); | |
| + if (ret == NXT_ERROR || ret == NJS_TRAP) { | |
| return ret; | |
| } | |
| - vm->retval = *retval; | |
| - | |
| /* GC: njs_retain(retval) */ | |
| return sizeof(njs_vmcode_prop_get_t); | |
| @@ -646,8 +512,7 @@ njs_vmcode_property_set(njs_vm_t *vm, nj | |
| code = (njs_vmcode_prop_set_t *) vm->current; | |
| value = njs_vmcode_operand(vm, code->value); | |
| - pq.lhq.key.length = 0; | |
| - pq.query = NJS_PROPERTY_QUERY_SET; | |
| + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0); | |
| ret = njs_property_query(vm, &pq, object, property); | |
| @@ -767,7 +632,7 @@ njs_vmcode_property_in(njs_vm_t *vm, njs | |
| retval = &njs_value_false; | |
| - pq.query = NJS_PROPERTY_QUERY_IN; | |
| + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_IN, 0); | |
| ret = njs_property_query(vm, &pq, object, property); | |
| @@ -851,8 +716,7 @@ njs_vmcode_property_delete(njs_vm_t *vm, | |
| retval = &njs_value_false; | |
| - pq.lhq.key.length = 0; | |
| - pq.query = NJS_PROPERTY_QUERY_DELETE; | |
| + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 0); | |
| ret = njs_property_query(vm, &pq, object, property); | |
| @@ -1097,12 +961,10 @@ njs_ret_t | |
| njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object, | |
| njs_value_t *constructor) | |
| { | |
| - nxt_int_t ret; | |
| - njs_value_t *value; | |
| - njs_object_t *prototype, *proto; | |
| - njs_object_prop_t *prop; | |
| - const njs_value_t *retval; | |
| - njs_property_query_t pq; | |
| + nxt_int_t ret; | |
| + njs_value_t *value, val; | |
| + njs_object_t *prototype, *proto; | |
| + const njs_value_t *retval; | |
| static njs_value_t prototype_string = njs_string("prototype"); | |
| @@ -1114,13 +976,10 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs | |
| retval = &njs_value_false; | |
| if (njs_is_object(object)) { | |
| - pq.query = NJS_PROPERTY_QUERY_GET; | |
| - | |
| - ret = njs_property_query(vm, &pq, constructor, &prototype_string); | |
| + ret = njs_value_property(vm, constructor, &prototype_string, &val); | |
| if (nxt_fast_path(ret == NXT_OK)) { | |
| - prop = pq.lhq.value; | |
| - value = &prop->value; | |
| + value = &val; | |
| /* TODO: test prop->value is object. */ | |
| @@ -2123,9 +1982,7 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj | |
| method = (njs_vmcode_method_frame_t *) vm->current; | |
| - pq.lhq.key.length = 0; | |
| - pq.lhq.key.start = NULL; | |
| - pq.query = NJS_PROPERTY_QUERY_GET; | |
| + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); | |
| ret = njs_property_query(vm, &pq, object, name); | |
| @@ -2134,6 +1991,16 @@ njs_vmcode_method_frame(njs_vm_t *vm, nj | |
| case NXT_OK: | |
| prop = pq.lhq.value; | |
| + if (prop->type == NJS_PROPERTY_HANDLER) { | |
| + pq.scratch = *prop; | |
| + prop = &pq.scratch; | |
| + ret = prop->value.data.u.prop_handler(vm, object, NULL, | |
| + &prop->value); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return ret; | |
| + } | |
| + } | |
| + | |
| ret = njs_function_frame_create(vm, &prop->value, object, method->nargs, | |
| method->code.ctor); | |
| break; | |
| @@ -3159,6 +3026,45 @@ njs_vmcode_string_argument(njs_vm_t *vm, | |
| } | |
| +static njs_ret_t | |
| +njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) | |
| +{ | |
| + u_char *restart; | |
| + njs_ret_t ret; | |
| + njs_value_t *retval, *value1; | |
| + njs_native_frame_t *frame; | |
| + njs_vmcode_generic_t *vmcode; | |
| + | |
| + frame = vm->top_frame; | |
| + restart = frame->trap_restart; | |
| + frame->trap_restart = NULL; | |
| + vm->current = restart; | |
| + vmcode = (njs_vmcode_generic_t *) restart; | |
| + | |
| + value1 = &frame->trap_values[0]; | |
| + | |
| + if (frame->trap_reference) { | |
| + value1 = value1->data.u.value; | |
| + } | |
| + | |
| + ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]); | |
| + | |
| + if (nxt_slow_path(ret == NJS_TRAP)) { | |
| + /* Trap handlers are not reentrant. */ | |
| + njs_internal_error(vm, "trap inside restart instruction"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + retval = njs_vmcode_operand(vm, vmcode->operand1); | |
| + | |
| + //njs_release(vm, retval); | |
| + | |
| + *retval = vm->retval; | |
| + | |
| + return ret; | |
| +} | |
| + | |
| + | |
| /* | |
| * A hint value is 0 for numbers and 1 for strings. The value chooses | |
| * method calls order specified by ECMAScript 5.1: "valueOf", "toString" | |
| @@ -3257,42 +3163,165 @@ njs_primitive_value(njs_vm_t *vm, njs_va | |
| } | |
| -static njs_ret_t | |
| -njs_vmcode_restart(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) | |
| +/* | |
| + * 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, njs_value_t *value, | |
| + const njs_value_t *property, njs_value_t *retval) | |
| { | |
| - u_char *restart; | |
| + void *obj; | |
| + int32_t index; | |
| + uintptr_t data; | |
| njs_ret_t ret; | |
| - njs_value_t *retval, *value1; | |
| - njs_native_frame_t *frame; | |
| - njs_vmcode_generic_t *vmcode; | |
| - | |
| - frame = vm->top_frame; | |
| - restart = frame->trap_restart; | |
| - frame->trap_restart = NULL; | |
| - vm->current = restart; | |
| - vmcode = (njs_vmcode_generic_t *) restart; | |
| - | |
| - value1 = &frame->trap_values[0]; | |
| - | |
| - if (frame->trap_reference) { | |
| - value1 = value1->data.u.value; | |
| + njs_value_t *val, ext_val; | |
| + njs_slice_prop_t slice; | |
| + njs_string_prop_t string; | |
| + njs_object_prop_t *prop; | |
| + const njs_extern_t *ext_proto; | |
| + njs_property_query_t pq; | |
| + | |
| + *retval = njs_value_void; | |
| + | |
| + njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0); | |
| + | |
| + ret = njs_property_query(vm, &pq, value, property); | |
| + | |
| + switch (ret) { | |
| + | |
| + case NXT_OK: | |
| + prop = pq.lhq.value; | |
| + | |
| + switch (prop->type) { | |
| + | |
| + case NJS_METHOD: | |
| + if (pq.shared) { | |
| + ret = njs_method_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, value, NULL, | |
| + &prop->value); | |
| + | |
| + if (nxt_fast_path(ret == NXT_OK)) { | |
| + *retval = prop->value; | |
| + } | |
| + | |
| + break; | |
| + | |
| + default: | |
| + njs_internal_error(vm, "invalid property get type:%d", prop->type); | |
| + | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + break; | |
| + | |
| + case NXT_DECLINED: | |
| + case NJS_PRIMITIVE_VALUE: | |
| + break; | |
| + | |
| + case NJS_STRING_VALUE: | |
| + | |
| + /* string[n]. */ | |
| + | |
| + index = (int32_t) njs_value_to_index(property); | |
| + | |
| + if (nxt_fast_path(index >= 0)) { | |
| + slice.start = index; | |
| + slice.length = 1; | |
| + slice.string_length = njs_string_prop(&string, value); | |
| + | |
| + if (slice.start < slice.string_length) { | |
| + /* | |
| + * A single codepoint string fits in retval | |
| + * so the function cannot fail. | |
| + */ | |
| + (void) njs_string_slice(vm, retval, &string, &slice); | |
| + } | |
| + } | |
| + | |
| + break; | |
| + | |
| + case NJS_ARRAY_VALUE: | |
| + val = pq.lhq.value; | |
| + | |
| + if (njs_is_valid(val)) { | |
| + *retval = *val; | |
| + } | |
| + | |
| + break; | |
| + | |
| + case NJS_EXTERNAL_VALUE: | |
| + ext_proto = value->external.proto; | |
| + | |
| + ret = nxt_lvlhsh_find(&ext_proto->hash, &pq.lhq); | |
| + | |
| + if (ret == NXT_OK) { | |
| + ext_proto = pq.lhq.value; | |
| + | |
| + ext_val.type = NJS_EXTERNAL; | |
| + ext_val.data.truth = 1; | |
| + ext_val.external.proto = ext_proto; | |
| + ext_val.external.index = value->external.index; | |
| + | |
| + if ((ext_proto->type & NJS_EXTERN_OBJECT) != 0) { | |
| + *retval = ext_val; | |
| + break; | |
| + } | |
| + | |
| + data = ext_proto->data; | |
| + | |
| + } else { | |
| + data = (uintptr_t) &pq.lhq.key; | |
| + } | |
| + | |
| + if (ext_proto->get != NULL) { | |
| + obj = njs_extern_object(vm, value); | |
| + | |
| + ret = ext_proto->get(vm, retval, obj, data); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return ret; | |
| + } | |
| + } | |
| + | |
| + if (ext_proto->type == NJS_EXTERN_METHOD) { | |
| + retval->data.u.function = ext_proto->function; | |
| + retval->type = NJS_FUNCTION; | |
| + retval->data.truth = 1; | |
| + } | |
| + | |
| + break; | |
| + | |
| + case NJS_TRAP: | |
| + case NXT_ERROR: | |
| + default: | |
| + | |
| + return ret; | |
| } | |
| - ret = vmcode->code.operation(vm, value1, &frame->trap_values[1]); | |
| - | |
| - if (nxt_slow_path(ret == NJS_TRAP)) { | |
| - /* Trap handlers are not reentrant. */ | |
| - njs_internal_error(vm, "trap inside restart instruction"); | |
| - return NXT_ERROR; | |
| - } | |
| - | |
| - retval = njs_vmcode_operand(vm, vmcode->operand1); | |
| - | |
| - //njs_release(vm, retval); | |
| - | |
| - *retval = vm->retval; | |
| - | |
| - return ret; | |
| + return (ret == NXT_OK) ? NXT_OK : NXT_DECLINED; | |
| } | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -7001,9 +7001,18 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("Object.getOwnPropertyDescriptor([3,4], 1).value"), | |
| nxt_string("4") }, | |
| + { nxt_string("Object.getOwnPropertyDescriptor(Object.create({a:1}), 'a')"), | |
| + nxt_string("undefined") }, | |
| + | |
| { nxt_string("Object.getOwnPropertyDescriptor([], 'length').value"), | |
| nxt_string("0") }, | |
| + { nxt_string("Object.getOwnPropertyDescriptor(Array.of, 'length').value"), | |
| + nxt_string("0") }, | |
| + | |
| + { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor(new String('abc'), 'length'))"), | |
| + nxt_string("{\"value\":3,\"configurable\":false,\"enumerable\":false,\"writable\":false}") }, | |
| + | |
| { nxt_string("JSON.stringify(Object.getOwnPropertyDescriptor([3,4], 'length'))"), | |
| nxt_string("{\"value\":2,\"configurable\":false,\"enumerable\":false,\"writable\":true}") }, | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860015 -10800 | |
| # Thu Sep 13 20:33:35 2018 +0300 | |
| # Node ID b9a1326c940d11f303c3d6328dcd1557d20d507f | |
| # Parent ffe3b161805980547f16f3ff1142fc55f3f628ca | |
| Fixed delete operator. | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860015 -10800 | |
| # Thu Sep 13 20:33:35 2018 +0300 | |
| # Node ID b85d3a6cf1eafd0f6d8b43d61f8e6421282a55ef | |
| # Parent b9a1326c940d11f303c3d6328dcd1557d20d507f | |
| Added njs_primitive_value_to_number(). | |
| diff --git a/njs/njs_number.c b/njs/njs_number.c | |
| --- a/njs/njs_number.c | |
| +++ b/njs/njs_number.c | |
| @@ -64,6 +64,24 @@ njs_value_to_index(const njs_value_t *va | |
| double | |
| +njs_primitive_value_to_number(const njs_value_t *value) | |
| +{ | |
| + if (nxt_fast_path(njs_is_numeric(value))) { | |
| + return value->data.u.number; | |
| + } | |
| + | |
| + return njs_string_to_number(value, 1); | |
| +} | |
| + | |
| + | |
| +uint32_t | |
| +njs_primitive_value_to_uint32(const njs_value_t *value) | |
| +{ | |
| + return njs_number_to_integer(njs_primitive_value_to_number(value)); | |
| +} | |
| + | |
| + | |
| +double | |
| njs_number_dec_parse(const u_char **start, const u_char *end) | |
| { | |
| return nxt_strtod(start, end); | |
| diff --git a/njs/njs_number.h b/njs/njs_number.h | |
| --- a/njs/njs_number.h | |
| +++ b/njs/njs_number.h | |
| @@ -13,6 +13,8 @@ | |
| uint32_t njs_value_to_index(const njs_value_t *value); | |
| +double njs_primitive_value_to_number(const njs_value_t *value); | |
| +uint32_t njs_primitive_value_to_uint32(const njs_value_t *value); | |
| double njs_number_dec_parse(const u_char **start, const u_char *end); | |
| uint64_t njs_number_oct_parse(const u_char **start, const u_char *end); | |
| uint64_t njs_number_bin_parse(const u_char **start, const u_char *end); | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860016 -10800 | |
| # Thu Sep 13 20:33:36 2018 +0300 | |
| # Node ID b87565896b6efc8ac59da011665ed1acbc23a1e6 | |
| # Parent b85d3a6cf1eafd0f6d8b43d61f8e6421282a55ef | |
| Fixed String.slice() for undefined arguments. | |
| diff --git a/njs/njs_string.c b/njs/njs_string.c | |
| --- a/njs/njs_string.c | |
| +++ b/njs/njs_string.c | |
| @@ -1239,45 +1239,43 @@ njs_string_slice_args(njs_slice_prop_t * | |
| ssize_t start, end, length; | |
| length = slice->string_length; | |
| - start = 0; | |
| - | |
| - if (nargs > 1) { | |
| - start = args[1].data.u.number; | |
| + | |
| + start = njs_arg(args, nargs, 1)->data.u.number; | |
| + | |
| + if (start < 0) { | |
| + start += length; | |
| if (start < 0) { | |
| - start += length; | |
| - | |
| - if (start < 0) { | |
| - start = 0; | |
| - } | |
| + start = 0; | |
| } | |
| - | |
| - if (start >= length) { | |
| - start = 0; | |
| - length = 0; | |
| + } | |
| + | |
| + if (start >= length) { | |
| + start = 0; | |
| + length = 0; | |
| + | |
| + } else { | |
| + if (!njs_is_void(njs_arg(args, nargs, 2))) { | |
| + end = njs_arg(args, nargs, 2)->data.u.number; | |
| } else { | |
| end = length; | |
| - | |
| - if (nargs > 2) { | |
| - end = args[2].data.u.number; | |
| - | |
| - if (end < 0) { | |
| - end += length; | |
| - } | |
| + } | |
| + | |
| + if (end < 0) { | |
| + end += length; | |
| + } | |
| + | |
| + if (length >= end) { | |
| + length = end - start; | |
| + | |
| + if (length < 0) { | |
| + start = 0; | |
| + length = 0; | |
| } | |
| - if (length >= end) { | |
| - length = end - start; | |
| - | |
| - if (length < 0) { | |
| - start = 0; | |
| - length = 0; | |
| - } | |
| - | |
| - } else { | |
| - length -= start; | |
| - } | |
| + } else { | |
| + length -= start; | |
| } | |
| } | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -3800,6 +3800,18 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("'abcdefgh'.slice(3)"), | |
| nxt_string("defgh") }, | |
| + { nxt_string("'abcdefgh'.slice(undefined, undefined)"), | |
| + nxt_string("abcdefgh") }, | |
| + | |
| + { nxt_string("'abcdefgh'.slice(undefined)"), | |
| + nxt_string("abcdefgh") }, | |
| + | |
| + { nxt_string("'abcdefgh'.slice(undefined, 1)"), | |
| + nxt_string("a") }, | |
| + | |
| + { nxt_string("'abcdefgh'.slice(3, undefined)"), | |
| + nxt_string("defgh") }, | |
| + | |
| { nxt_string("'abcde'.slice(50)"), | |
| nxt_string("") }, | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860016 -10800 | |
| # Thu Sep 13 20:33:36 2018 +0300 | |
| # Node ID 96e1d261f0b2c36a797c139b4fb689fe535d0ea8 | |
| # Parent b87565896b6efc8ac59da011665ed1acbc23a1e6 | |
| Added njs_string_slice_string_prop(). | |
| diff --git a/njs/njs_string.c b/njs/njs_string.c | |
| --- a/njs/njs_string.c | |
| +++ b/njs/njs_string.c | |
| @@ -1284,46 +1284,60 @@ njs_string_slice_args(njs_slice_prop_t * | |
| } | |
| +nxt_noinline void | |
| +njs_string_slice_string_prop(njs_string_prop_t *dst, | |
| + const njs_string_prop_t *string, const njs_slice_prop_t *slice) | |
| +{ | |
| + size_t size, n, length; | |
| + const u_char *p, *start, *end; | |
| + | |
| + length = slice->length; | |
| + start = string->start; | |
| + | |
| + if (string->size == slice->string_length) { | |
| + /* Byte or ASCII string. */ | |
| + start += slice->start; | |
| + size = slice->length; | |
| + | |
| + if (string->length == 0) { | |
| + /* Byte string. */ | |
| + length = 0; | |
| + } | |
| + | |
| + } else { | |
| + /* UTF-8 string. */ | |
| + end = start + string->size; | |
| + start = njs_string_offset(start, end, slice->start); | |
| + | |
| + /* Evaluate size of the slice in bytes and ajdust length. */ | |
| + p = start; | |
| + n = length; | |
| + | |
| + do { | |
| + p = nxt_utf8_next(p, end); | |
| + n--; | |
| + } while (n != 0 && p < end); | |
| + | |
| + size = p - start; | |
| + length -= n; | |
| + } | |
| + | |
| + dst->start = (u_char *) start; | |
| + dst->length = length; | |
| + dst->size = size; | |
| +} | |
| + | |
| + | |
| nxt_noinline njs_ret_t | |
| njs_string_slice(njs_vm_t *vm, njs_value_t *dst, | |
| const njs_string_prop_t *string, const njs_slice_prop_t *slice) | |
| { | |
| - size_t size, n, length; | |
| - const u_char *p, *start, *end; | |
| - | |
| - length = slice->length; | |
| - start = string->start; | |
| - | |
| - if (string->size == slice->string_length) { | |
| - /* Byte or ASCII string. */ | |
| - start += slice->start; | |
| - size = slice->length; | |
| - | |
| - if (string->length == 0) { | |
| - /* Byte string. */ | |
| - length = 0; | |
| - } | |
| - | |
| - } else { | |
| - /* UTF-8 string. */ | |
| - end = start + string->size; | |
| - start = njs_string_offset(start, end, slice->start); | |
| - | |
| - /* Evaluate size of the slice in bytes and ajdust length. */ | |
| - p = start; | |
| - n = length; | |
| - | |
| - do { | |
| - p = nxt_utf8_next(p, end); | |
| - n--; | |
| - } while (n != 0 && p < end); | |
| - | |
| - size = p - start; | |
| - length -= n; | |
| - } | |
| - | |
| - if (nxt_fast_path(size != 0)) { | |
| - return njs_string_new(vm, dst, start, size, length); | |
| + njs_string_prop_t prop; | |
| + | |
| + njs_string_slice_string_prop(&prop, string, slice); | |
| + | |
| + if (nxt_fast_path(prop.size != 0)) { | |
| + return njs_string_new(vm, dst, prop.start, prop.size, prop.length); | |
| } | |
| *dst = njs_string_empty; | |
| diff --git a/njs/njs_string.h b/njs/njs_string.h | |
| --- a/njs/njs_string.h | |
| +++ b/njs/njs_string.h | |
| @@ -145,6 +145,8 @@ njs_ret_t njs_string_constructor(njs_vm_ | |
| nxt_uint_t nargs, njs_index_t unused); | |
| nxt_bool_t njs_string_eq(const njs_value_t *val1, const njs_value_t *val2); | |
| nxt_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2); | |
| +nxt_noinline void njs_string_slice_string_prop(njs_string_prop_t *dst, | |
| + const njs_string_prop_t *string, const njs_slice_prop_t *slice); | |
| njs_ret_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst, | |
| const njs_string_prop_t *string, const njs_slice_prop_t *slice); | |
| const u_char *njs_string_offset(const u_char *start, const u_char *end, | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860017 -10800 | |
| # Thu Sep 13 20:33:37 2018 +0300 | |
| # Node ID 0b601d903078774410f9c44ee3554c7e9b7db6ee | |
| # Parent 96e1d261f0b2c36a797c139b4fb689fe535d0ea8 | |
| Hanling non-array values properly in Array.prototype.slice. | |
| diff --git a/njs/njs_array.c b/njs/njs_array.c | |
| --- a/njs/njs_array.c | |
| +++ b/njs/njs_array.c | |
| @@ -9,6 +9,19 @@ | |
| typedef struct { | |
| + union { | |
| + njs_continuation_t cont; | |
| + u_char padding[NJS_CONTINUATION_SIZE]; | |
| + } u; | |
| + /* | |
| + * This retval value must be aligned so the continuation is padded | |
| + * to aligned size. | |
| + */ | |
| + njs_value_t length; | |
| +} njs_array_slice_t; | |
| + | |
| + | |
| +typedef struct { | |
| njs_continuation_t cont; | |
| njs_value_t *values; | |
| uint32_t max; | |
| @@ -67,6 +80,8 @@ typedef struct { | |
| } njs_array_sort_t; | |
| +static njs_ret_t njs_array_prototype_slice_continuation(njs_vm_t *vm, | |
| + njs_value_t *args, nxt_uint_t nargs, njs_index_t unused); | |
| static njs_ret_t njs_array_prototype_to_string_continuation(njs_vm_t *vm, | |
| njs_value_t *args, nxt_uint_t nargs, njs_index_t retval); | |
| static njs_ret_t njs_array_prototype_join_continuation(njs_vm_t *vm, | |
| @@ -434,55 +449,87 @@ static njs_ret_t | |
| njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
| njs_index_t unused) | |
| { | |
| - int32_t start, end, length; | |
| - uint32_t n; | |
| - njs_array_t *array; | |
| - njs_value_t *value; | |
| + njs_ret_t ret; | |
| + njs_array_slice_t *slice; | |
| + | |
| + static const njs_value_t njs_string_length = njs_string("length"); | |
| + | |
| + slice = njs_vm_continuation(vm); | |
| + slice->u.cont.function = njs_array_prototype_slice_continuation; | |
| + | |
| + ret = njs_value_property(vm, &args[0], &njs_string_length, &slice->length); | |
| + if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) { | |
| + return ret; | |
| + } | |
| + | |
| + return njs_array_prototype_slice_continuation(vm, args, nargs, unused); | |
| +} | |
| + | |
| + | |
| +static njs_ret_t | |
| +njs_array_prototype_slice_continuation(njs_vm_t *vm, njs_value_t *args, | |
| + nxt_uint_t nargs, njs_index_t unused) | |
| +{ | |
| + size_t size; | |
| + u_char *dst; | |
| + int32_t start, end; | |
| + int64_t length; | |
| + uint32_t n; | |
| + njs_ret_t ret; | |
| + njs_array_t *array; | |
| + njs_value_t *value, name, retval, *this; | |
| + const u_char *src, *last; | |
| + njs_slice_prop_t string_slice; | |
| + njs_array_slice_t *slice; | |
| + njs_string_prop_t string; | |
| start = 0; | |
| - length = 0; | |
| - | |
| - if (njs_is_array(&args[0])) { | |
| - length = args[0].data.u.array->length; | |
| - | |
| - if (nargs > 1) { | |
| - start = args[1].data.u.number; | |
| - | |
| - if (start < 0) { | |
| - start += length; | |
| - | |
| - if (start < 0) { | |
| - start = 0; | |
| - } | |
| - } | |
| - | |
| - if (start >= length) { | |
| + | |
| + slice = njs_vm_continuation(vm); | |
| + | |
| + if (nxt_slow_path(!njs_is_primitive(&slice->length))) { | |
| + njs_vm_trap_value(vm, &slice->length); | |
| + return njs_trap(vm, NJS_TRAP_NUMBER_ARG); | |
| + } | |
| + | |
| + length = njs_primitive_value_to_uint32(&slice->length); | |
| + | |
| + start = njs_arg(args, nargs, 1)->data.u.number; | |
| + | |
| + if (start < 0) { | |
| + start += length; | |
| + | |
| + if (start < 0) { | |
| + start = 0; | |
| + } | |
| + } | |
| + | |
| + if (start >= length) { | |
| + start = 0; | |
| + length = 0; | |
| + | |
| + } else { | |
| + if (!njs_is_void(njs_arg(args, nargs, 2))) { | |
| + end = njs_arg(args, nargs, 2)->data.u.number; | |
| + | |
| + } else { | |
| + end = length; | |
| + } | |
| + | |
| + if (end < 0) { | |
| + end += length; | |
| + } | |
| + | |
| + if (length >= end) { | |
| + length = end - start; | |
| + | |
| + if (length < 0) { | |
| start = 0; | |
| length = 0; | |
| - | |
| - } else { | |
| - end = length; | |
| - | |
| - if (nargs > 2) { | |
| - end = args[2].data.u.number; | |
| - | |
| - if (end < 0) { | |
| - end += length; | |
| - } | |
| - } | |
| - | |
| - if (length >= end) { | |
| - length = end - start; | |
| - | |
| - if (length < 0) { | |
| - start = 0; | |
| - length = 0; | |
| - } | |
| - | |
| - } else { | |
| - length -= start; | |
| - } | |
| } | |
| + | |
| + } else { | |
| + length -= start; | |
| } | |
| } | |
| @@ -496,14 +543,56 @@ njs_array_prototype_slice(njs_vm_t *vm, | |
| vm->retval.data.truth = 1; | |
| if (length != 0) { | |
| - value = args[0].data.u.array->start; | |
| n = 0; | |
| - | |
| - do { | |
| - /* GC: retain long string and object in values[start]. */ | |
| - array->start[n++] = value[start++]; | |
| - length--; | |
| - } while (length != 0); | |
| + this = &args[0]; | |
| + | |
| + if (nxt_fast_path(njs_is_array(this))) { | |
| + value = this->data.u.array->start; | |
| + | |
| + do { | |
| + /* GC: retain long string and object in values[start]. */ | |
| + array->start[n++] = value[start++]; | |
| + length--; | |
| + } while (length != 0); | |
| + | |
| + } else if (njs_is_string(this) || this->type == NJS_OBJECT_STRING) { | |
| + | |
| + if (this->type == NJS_OBJECT_STRING) { | |
| + this = &this->data.u.object_value->value; | |
| + } | |
| + | |
| + string_slice.start = start; | |
| + string_slice.length = length; | |
| + string_slice.string_length = njs_string_prop(&string, this); | |
| + | |
| + njs_string_slice_string_prop(&string, &string, &string_slice); | |
| + | |
| + src = string.start; | |
| + last = src + string.size; | |
| + | |
| + do { | |
| + value = &array->start[n++]; | |
| + dst = njs_string_short_start(value); | |
| + dst = nxt_utf8_copy(dst, &src, last); | |
| + size = dst - njs_string_short_start(value); | |
| + njs_string_short_set(value, size, (size == 1) ? 0 : 1); | |
| + | |
| + length--; | |
| + } while (length != 0); | |
| + | |
| + | |
| + } else if (njs_is_object(this)) { | |
| + | |
| + do { | |
| + njs_uint32_to_string(&name, start++); | |
| + | |
| + ret = njs_value_property(vm, this, &name, &retval); | |
| + | |
| + array->start[n++] = (ret == NXT_OK) ? retval | |
| + : njs_value_invalid; | |
| + length--; | |
| + } while (length != 0); | |
| + } | |
| } | |
| return NXT_OK; | |
| @@ -2101,7 +2190,8 @@ static const njs_object_prop_t njs_arra | |
| { | |
| .type = NJS_METHOD, | |
| .name = njs_string("slice"), | |
| - .value = njs_native_function(njs_array_prototype_slice, 0, | |
| + .value = njs_native_function(njs_array_prototype_slice, | |
| + njs_continuation_size(njs_array_slice_t), | |
| NJS_OBJECT_ARG, NJS_INTEGER_ARG, NJS_INTEGER_ARG), | |
| }, | |
| diff --git a/njs/njs_object_hash.h b/njs/njs_object_hash.h | |
| --- a/njs/njs_object_hash.h | |
| +++ b/njs/njs_object_hash.h | |
| @@ -108,6 +108,16 @@ | |
| 'j'), 'o'), 'i'), 'n') | |
| +#define NJS_LENGTH_HASH \ | |
| + nxt_djb_hash_add( \ | |
| + nxt_djb_hash_add( \ | |
| + nxt_djb_hash_add( \ | |
| + nxt_djb_hash_add( \ | |
| + nxt_djb_hash_add( \ | |
| + nxt_djb_hash_add(NXT_DJB_HASH_INIT, \ | |
| + 'l'), 'e'), 'n'), 'g'), 't'), 'h') | |
| + | |
| + | |
| #define NJS_NAME_HASH \ | |
| nxt_djb_hash_add( \ | |
| nxt_djb_hash_add( \ | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -2981,12 +2981,93 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("Array.prototype.slice(1,2)"), | |
| nxt_string("") }, | |
| + { nxt_string("Array.prototype.slice.call(undefined)"), | |
| + nxt_string("TypeError: cannot convert void to object") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call(1)"), | |
| + nxt_string("") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call(false)"), | |
| + nxt_string("") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:1})"), | |
| + nxt_string("a") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2})"), | |
| + nxt_string("a,b") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:4})"), | |
| + nxt_string("a,b,,") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1)"), | |
| + nxt_string("b") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1, 2)"), | |
| + nxt_string("b") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({length:'2'})"), | |
| + nxt_string(",") }, | |
| + | |
| + { nxt_string("njs.dump(Array.prototype.slice.call({length: 3, 1: undefined }))"), | |
| + nxt_string("[<empty>,undefined,<empty>]") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({length:new Number(3)})"), | |
| + nxt_string(",,") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({length: { valueOf: function() { return 2; } }})"), | |
| + nxt_string(",") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({ length: Object.create(null) })"), | |
| + nxt_string("TypeError: Cannot convert object to primitive value") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call({length:-1})"), | |
| + nxt_string("MemoryError") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ')"), | |
| + nxt_string("α,β,Z,γ") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ', 1)"), | |
| + nxt_string("β,Z,γ") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ', 2)"), | |
| + nxt_string("Z,γ") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ', 3)"), | |
| + nxt_string("γ") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ', 4)"), | |
| + nxt_string("") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ', 0, 1)"), | |
| + nxt_string("α") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ', 1, 2)"), | |
| + nxt_string("β") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ').length"), | |
| + nxt_string("4") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call('αβZγ')[1].length"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("Array.prototype.slice.call(new String('αβZγ'))"), | |
| + nxt_string("α,β,Z,γ") }, | |
| + | |
| { nxt_string("Array.prototype.pop()"), | |
| nxt_string("undefined") }, | |
| { nxt_string("Array.prototype.shift()"), | |
| nxt_string("undefined") }, | |
| + { nxt_string("[0,1].slice()"), | |
| + nxt_string("0,1") }, | |
| + | |
| + { nxt_string("[0,1].slice(undefined)"), | |
| + nxt_string("0,1") }, | |
| + | |
| + { nxt_string("[0,1].slice(undefined, undefined)"), | |
| + nxt_string("0,1") }, | |
| + | |
| { nxt_string("[0,1,2,3,4].slice(1,4)"), | |
| nxt_string("1,2,3") }, | |
| @@ -9442,6 +9523,9 @@ static njs_unit_test_t njs_test[] = | |
| { nxt_string("njs.dump({a:1, b:[1,,2,{c:new Boolean(1)}]})"), | |
| nxt_string("{a:1,b:[1,<empty>,2,{c:[Boolean: true]}]}") }, | |
| + { nxt_string("njs.dump(Array.prototype.slice.call({'1':'b', length:2}))"), | |
| + nxt_string("[<empty>,'b']") }, | |
| + | |
| { nxt_string("njs.dump($r.props)"), | |
| nxt_string("{a:{type:\"property\",props:[\"getter\"]},b:{type:\"property\",props:[\"getter\"]}}") }, | |
| # HG changeset patch | |
| # User Dmitry Volyntsev <[email protected]> | |
| # Date 1536860117 -10800 | |
| # Thu Sep 13 20:35:17 2018 +0300 | |
| # Node ID fb2b4c6ac8b022a4530428ad744e9297a342906a | |
| # Parent 0b601d903078774410f9c44ee3554c7e9b7db6ee | |
| Added arguments object. | |
| This closes #19 issue on Github. | |
| diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c | |
| --- a/njs/njs_disassembler.c | |
| +++ b/njs/njs_disassembler.c | |
| @@ -24,6 +24,8 @@ static njs_code_name_t code_names[] = { | |
| nxt_string("OBJECT ") }, | |
| { njs_vmcode_function, sizeof(njs_vmcode_function_t), | |
| nxt_string("FUNCTION ") }, | |
| + { njs_vmcode_arguments, sizeof(njs_vmcode_arguments_t), | |
| + nxt_string("ARGUMENTS ") }, | |
| { njs_vmcode_regexp, sizeof(njs_vmcode_regexp_t), | |
| nxt_string("REGEXP ") }, | |
| { njs_vmcode_object_copy, sizeof(njs_vmcode_object_copy_t), | |
| diff --git a/njs/njs_function.c b/njs/njs_function.c | |
| --- a/njs/njs_function.c | |
| +++ b/njs/njs_function.c | |
| @@ -8,6 +8,8 @@ | |
| #include <string.h> | |
| +static njs_ret_t njs_function_arguments_thrower(njs_vm_t *vm, | |
| + njs_value_t *value, njs_value_t *setval, njs_value_t *retval); | |
| static njs_ret_t njs_function_activate(njs_vm_t *vm, njs_function_t *function, | |
| njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, njs_index_t retval); | |
| @@ -95,6 +97,137 @@ njs_function_value_copy(njs_vm_t *vm, nj | |
| } | |
| +/* | |
| + * ES5.1, 10.6: CreateArgumentsObject. | |
| + */ | |
| +njs_ret_t | |
| +njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame) | |
| +{ | |
| + nxt_int_t ret; | |
| + nxt_uint_t nargs, n; | |
| + njs_value_t val, *value; | |
| + njs_object_t *obj; | |
| + njs_object_prop_t *prop; | |
| + nxt_lvlhsh_query_t lhq; | |
| + | |
| + static const njs_value_t njs_string_length = njs_string("length"); | |
| + static const njs_value_t njs_string_callee = njs_string("callee"); | |
| + static const njs_value_t njs_string_caller = njs_string("caller"); | |
| + | |
| + obj = njs_object_alloc(vm); | |
| + if (nxt_slow_path(obj == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + nargs = frame->nargs; | |
| + | |
| + value = &val; | |
| + njs_value_number_set(value, nargs); | |
| + | |
| + prop = njs_object_prop_alloc(vm, &njs_string_length, value, 1); | |
| + if (nxt_slow_path(prop == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + prop->enumerable = 0; | |
| + | |
| + lhq.value = prop; | |
| + lhq.key_hash = NJS_LENGTH_HASH; | |
| + njs_string_get(&prop->name, &lhq.key); | |
| + | |
| + lhq.replace = 0; | |
| + lhq.pool = vm->mem_cache_pool; | |
| + lhq.proto = &njs_object_hash_proto; | |
| + | |
| + ret = nxt_lvlhsh_insert(&obj->hash, &lhq); | |
| + | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + njs_internal_error(vm, "lvlhsh insert failed"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + prop = njs_object_prop_handler_alloc(vm, &njs_string_callee, | |
| + njs_function_arguments_thrower, 0); | |
| + if (nxt_slow_path(prop == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + lhq.value = prop; | |
| + njs_string_get(&prop->name, &lhq.key); | |
| + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); | |
| + | |
| + lhq.replace = 0; | |
| + lhq.pool = vm->mem_cache_pool; | |
| + lhq.proto = &njs_object_hash_proto; | |
| + | |
| + ret = nxt_lvlhsh_insert(&obj->hash, &lhq); | |
| + | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + njs_internal_error(vm, "lvlhsh insert failed"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + prop = njs_object_prop_handler_alloc(vm, &njs_string_caller, | |
| + njs_function_arguments_thrower, 0); | |
| + if (nxt_slow_path(prop == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + lhq.value = prop; | |
| + njs_string_get(&prop->name, &lhq.key); | |
| + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); | |
| + | |
| + lhq.replace = 0; | |
| + lhq.pool = vm->mem_cache_pool; | |
| + lhq.proto = &njs_object_hash_proto; | |
| + | |
| + ret = nxt_lvlhsh_insert(&obj->hash, &lhq); | |
| + | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + njs_internal_error(vm, "lvlhsh insert failed"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + for (n = 0; n < nargs; n++) { | |
| + njs_uint32_to_string(&val, n); | |
| + | |
| + prop = njs_object_prop_alloc(vm, &val, &frame->arguments[n + 1], 1); | |
| + if (nxt_slow_path(prop == NULL)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + lhq.value = prop; | |
| + njs_string_get(&prop->name, &lhq.key); | |
| + lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length); | |
| + | |
| + lhq.replace = 0; | |
| + lhq.pool = vm->mem_cache_pool; | |
| + lhq.proto = &njs_object_hash_proto; | |
| + | |
| + ret = nxt_lvlhsh_insert(&obj->hash, &lhq); | |
| + | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + njs_internal_error(vm, "lvlhsh insert failed"); | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + } | |
| + | |
| + frame->arguments_object = obj; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +static njs_ret_t | |
| +njs_function_arguments_thrower(njs_vm_t *vm, njs_value_t *value, | |
| + njs_value_t *setval, njs_value_t *retval) | |
| +{ | |
| + njs_type_error(vm, "'caller', 'callee' properties may not be accessed"); | |
| + return NXT_ERROR; | |
| +} | |
| + | |
| + | |
| njs_ret_t | |
| njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, | |
| const njs_value_t *this, njs_value_t *args, nxt_uint_t nargs, | |
| @@ -315,6 +448,7 @@ nxt_noinline njs_ret_t | |
| njs_function_call(njs_vm_t *vm, njs_index_t retval, size_t advance) | |
| { | |
| size_t size; | |
| + njs_ret_t ret; | |
| nxt_uint_t n, nesting; | |
| njs_frame_t *frame; | |
| njs_value_t *value; | |
| @@ -393,6 +527,13 @@ njs_function_call(njs_vm_t *vm, njs_inde | |
| vm->scopes[NJS_SCOPE_CLOSURE + n] = &closure->u.values; | |
| } | |
| + if (lambda->arguments_object) { | |
| + ret = njs_function_arguments_object_init(vm, &frame->native); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NXT_ERROR; | |
| + } | |
| + } | |
| + | |
| vm->active_frame = frame; | |
| return NJS_APPLIED; | |
| diff --git a/njs/njs_function.h b/njs/njs_function.h | |
| --- a/njs/njs_function.h | |
| +++ b/njs/njs_function.h | |
| @@ -30,6 +30,8 @@ struct njs_function_lambda_s { | |
| /* Function internal block closures levels. */ | |
| uint8_t block_closures; /* 4 bits */ | |
| + uint8_t arguments_object;/* 1 bit */ | |
| + | |
| /* Initial values of local scope. */ | |
| njs_value_t *local_scope; | |
| @@ -102,7 +104,9 @@ struct njs_native_frame_s { | |
| njs_function_t *function; | |
| njs_native_frame_t *previous; | |
| + | |
| njs_value_t *arguments; | |
| + njs_object_t *arguments_object; | |
| njs_exception_t exception; | |
| @@ -147,6 +151,8 @@ struct njs_frame_s { | |
| njs_function_t *njs_function_alloc(njs_vm_t *vm); | |
| njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value); | |
| njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size); | |
| +njs_ret_t njs_function_arguments_object_init(njs_vm_t *vm, | |
| + njs_native_frame_t *frame); | |
| njs_ret_t njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value, | |
| njs_value_t *setval, njs_value_t *retval); | |
| njs_value_t *njs_function_property_prototype_create(njs_vm_t *vm, | |
| diff --git a/njs/njs_generator.c b/njs/njs_generator.c | |
| --- a/njs/njs_generator.c | |
| +++ b/njs/njs_generator.c | |
| @@ -14,6 +14,8 @@ static nxt_int_t njs_generate_name(njs_v | |
| njs_parser_node_t *node); | |
| static nxt_int_t njs_generate_builtin_object(njs_vm_t *vm, njs_parser_t *parser, | |
| njs_parser_node_t *node); | |
| +static nxt_int_t njs_generate_arguments_object(njs_vm_t *vm, | |
| + njs_parser_t *parser, njs_parser_node_t *node); | |
| static nxt_int_t njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser, | |
| njs_parser_node_t *node); | |
| static nxt_int_t njs_generate_var_statement(njs_vm_t *vm, njs_parser_t *parser, | |
| @@ -308,6 +310,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t | |
| case NJS_TOKEN_CLEAR_TIMEOUT: | |
| return njs_generate_builtin_object(vm, parser, node); | |
| + case NJS_TOKEN_ARGUMENTS: | |
| + return njs_generate_arguments_object(vm, parser, node); | |
| + | |
| case NJS_TOKEN_FUNCTION: | |
| return njs_generate_function_declaration(vm, parser, node); | |
| @@ -396,6 +401,29 @@ njs_generate_builtin_object(njs_vm_t *vm | |
| static nxt_int_t | |
| +njs_generate_arguments_object(njs_vm_t *vm, njs_parser_t *parser, | |
| + njs_parser_node_t *node) | |
| +{ | |
| + njs_vmcode_arguments_t *gen; | |
| + | |
| + parser->arguments_object = 1; | |
| + | |
| + node->index = njs_generator_object_dest_index(vm, parser, node); | |
| + if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) { | |
| + return NXT_ERROR; | |
| + } | |
| + | |
| + njs_generate_code(parser, njs_vmcode_arguments_t, gen); | |
| + gen->code.operation = njs_vmcode_arguments; | |
| + gen->code.operands = NJS_VMCODE_1OPERAND; | |
| + gen->code.retval = NJS_VMCODE_RETVAL; | |
| + gen->retval = node->index; | |
| + | |
| + return NXT_OK; | |
| +} | |
| + | |
| + | |
| +static nxt_int_t | |
| njs_generate_variable(njs_vm_t *vm, njs_parser_t *parser, | |
| njs_parser_node_t *node) | |
| { | |
| @@ -2010,6 +2038,7 @@ njs_generate_function_scope(njs_vm_t *vm | |
| parser->code_size += node->scope->argument_closures | |
| * sizeof(njs_vmcode_move_t); | |
| + parser->arguments_object = 0; | |
| ret = njs_generate_scope(vm, parser, node); | |
| if (nxt_fast_path(ret == NXT_OK)) { | |
| @@ -2023,6 +2052,7 @@ njs_generate_function_scope(njs_vm_t *vm | |
| lambda->nesting = node->scope->nesting; | |
| lambda->closure_size = size; | |
| + lambda->arguments_object = parser->arguments_object; | |
| lambda->local_size = parser->scope_size; | |
| lambda->local_scope = parser->local_scope; | |
| diff --git a/njs/njs_lexer_keyword.c b/njs/njs_lexer_keyword.c | |
| --- a/njs/njs_lexer_keyword.c | |
| +++ b/njs/njs_lexer_keyword.c | |
| @@ -53,6 +53,7 @@ static const njs_keyword_t njs_keywords | |
| /* Builtin objects. */ | |
| { nxt_string("this"), NJS_TOKEN_THIS, 0 }, | |
| + { nxt_string("arguments"), NJS_TOKEN_ARGUMENTS, 0 }, | |
| { nxt_string("njs"), NJS_TOKEN_NJS, 0 }, | |
| { nxt_string("Math"), NJS_TOKEN_MATH, 0 }, | |
| { nxt_string("JSON"), NJS_TOKEN_JSON, 0 }, | |
| diff --git a/njs/njs_object.c b/njs/njs_object.c | |
| --- a/njs/njs_object.c | |
| +++ b/njs/njs_object.c | |
| @@ -203,6 +203,36 @@ njs_object_prop_alloc(njs_vm_t *vm, cons | |
| nxt_noinline njs_object_prop_t * | |
| +njs_object_prop_handler_alloc(njs_vm_t *vm, const njs_value_t *name, | |
| + njs_prop_handler_t handler, uint8_t attributes) | |
| +{ | |
| + njs_object_prop_t *prop; | |
| + | |
| + prop = nxt_mem_cache_align(vm->mem_cache_pool, sizeof(njs_value_t), | |
| + sizeof(njs_object_prop_t)); | |
| + | |
| + if (nxt_fast_path(prop != NULL)) { | |
| + /* GC: retain. */ | |
| + njs_set_invalid(&prop->value); | |
| + prop->value.data.u.prop_handler = handler; | |
| + | |
| + /* GC: retain. */ | |
| + prop->name = *name; | |
| + | |
| + prop->type = NJS_PROPERTY_HANDLER; | |
| + prop->enumerable = attributes; | |
| + prop->writable = 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) | |
| { | |
| diff --git a/njs/njs_object.h b/njs/njs_object.h | |
| --- a/njs/njs_object.h | |
| +++ b/njs/njs_object.h | |
| @@ -87,6 +87,8 @@ njs_ret_t njs_object_constructor(njs_vm_ | |
| 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); | |
| +nxt_noinline njs_object_prop_t *njs_object_prop_handler_alloc(njs_vm_t *vm, | |
| + const njs_value_t *name, njs_prop_handler_t handler, 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, | |
| diff --git a/njs/njs_parser.c b/njs/njs_parser.c | |
| --- a/njs/njs_parser.c | |
| +++ b/njs/njs_parser.c | |
| @@ -455,6 +455,13 @@ njs_parser_function_declaration(njs_vm_t | |
| } | |
| if (token != NJS_TOKEN_NAME) { | |
| + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { | |
| + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" " | |
| + "is forbidden in function declaration", | |
| + (int) parser->lexer->text.length, | |
| + parser->lexer->text.start); | |
| + } | |
| + | |
| return NJS_TOKEN_ILLEGAL; | |
| } | |
| @@ -821,6 +828,13 @@ njs_parser_var_statement(njs_vm_t *vm, n | |
| } | |
| if (token != NJS_TOKEN_NAME) { | |
| + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { | |
| + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" " | |
| + "is forbidden in var declaration", | |
| + (int) parser->lexer->text.length, | |
| + parser->lexer->text.start); | |
| + } | |
| + | |
| return NJS_TOKEN_ILLEGAL; | |
| } | |
| @@ -1306,6 +1320,13 @@ njs_parser_for_var_statement(njs_vm_t *v | |
| } | |
| if (token != NJS_TOKEN_NAME) { | |
| + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { | |
| + njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" " | |
| + "is forbidden in for-in var declaration", | |
| + (int) parser->lexer->text.length, | |
| + parser->lexer->text.start); | |
| + } | |
| + | |
| return NJS_TOKEN_ILLEGAL; | |
| } | |
| @@ -1973,6 +1994,24 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa | |
| case NJS_TOKEN_JSON: | |
| return njs_parser_builtin_object(vm, parser, node); | |
| + case NJS_TOKEN_ARGUMENTS: | |
| + nxt_thread_log_debug("JS: arguments"); | |
| + | |
| + if (parser->scope->type <= NJS_SCOPE_GLOBAL) { | |
| + njs_parser_syntax_error(vm, parser, "\"%.*s\" object " | |
| + "in global scope", | |
| + (int) parser->lexer->text.length, | |
| + parser->lexer->text.start); | |
| + | |
| + return NJS_TOKEN_ILLEGAL; | |
| + } | |
| + | |
| + node->token = NJS_TOKEN_ARGUMENTS; | |
| + | |
| + parser->code_size += sizeof(njs_vmcode_arguments_t); | |
| + | |
| + break; | |
| + | |
| case NJS_TOKEN_OBJECT_CONSTRUCTOR: | |
| node->index = NJS_INDEX_OBJECT; | |
| break; | |
| diff --git a/njs/njs_parser.h b/njs/njs_parser.h | |
| --- a/njs/njs_parser.h | |
| +++ b/njs/njs_parser.h | |
| @@ -161,6 +161,7 @@ typedef enum { | |
| NJS_TOKEN_THROW, | |
| NJS_TOKEN_THIS, | |
| + NJS_TOKEN_ARGUMENTS, | |
| #define NJS_TOKEN_FIRST_OBJECT NJS_TOKEN_GLOBAL_THIS | |
| @@ -346,6 +347,8 @@ struct njs_parser_s { | |
| u_char *code_end; | |
| njs_parser_t *parent; | |
| + | |
| + nxt_uint_t arguments_object; | |
| }; | |
| diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c | |
| --- a/njs/njs_parser_expression.c | |
| +++ b/njs/njs_parser_expression.c | |
| @@ -414,8 +414,19 @@ njs_parser_assignment_expression(njs_vm_ | |
| } | |
| if (!njs_parser_is_lvalue(parser->node)) { | |
| - njs_parser_ref_error(vm, parser, | |
| - "Invalid left-hand side in assignment"); | |
| + token = parser->node->token; | |
| + | |
| + if (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) { | |
| + njs_parser_syntax_error(vm, parser, "Identifier \"%s\" " | |
| + "is forbidden as left-hand in assignment", | |
| + (token == NJS_TOKEN_EVAL) ? "eval" | |
| + : "arguments"); | |
| + | |
| + } else { | |
| + njs_parser_ref_error(vm, parser, | |
| + "Invalid left-hand side in assignment"); | |
| + } | |
| + | |
| return NJS_TOKEN_ILLEGAL; | |
| } | |
| diff --git a/njs/njs_vm.c b/njs/njs_vm.c | |
| --- a/njs/njs_vm.c | |
| +++ b/njs/njs_vm.c | |
| @@ -415,6 +415,29 @@ njs_vmcode_function(njs_vm_t *vm, njs_va | |
| njs_ret_t | |
| +njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) | |
| +{ | |
| + njs_ret_t ret; | |
| + njs_frame_t *frame; | |
| + | |
| + frame = (njs_frame_t *) vm->active_frame; | |
| + | |
| + if (frame->native.arguments_object == NULL) { | |
| + ret = njs_function_arguments_object_init(vm, &frame->native); | |
| + if (nxt_slow_path(ret != NXT_OK)) { | |
| + return NXT_ERROR; | |
| + } | |
| + } | |
| + | |
| + vm->retval.data.u.object = frame->native.arguments_object; | |
| + vm->retval.type = NJS_OBJECT; | |
| + vm->retval.data.truth = 1; | |
| + | |
| + return sizeof(njs_vmcode_arguments_t); | |
| +} | |
| + | |
| + | |
| +njs_ret_t | |
| njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *invld1, njs_value_t *invld2) | |
| { | |
| njs_regexp_t *regexp; | |
| diff --git a/njs/njs_vm.h b/njs/njs_vm.h | |
| --- a/njs/njs_vm.h | |
| +++ b/njs/njs_vm.h | |
| @@ -644,6 +644,12 @@ typedef struct { | |
| typedef struct { | |
| njs_vmcode_t code; | |
| njs_index_t retval; | |
| +} njs_vmcode_arguments_t; | |
| + | |
| + | |
| +typedef struct { | |
| + njs_vmcode_t code; | |
| + njs_index_t retval; | |
| uintptr_t length; | |
| } njs_vmcode_array_t; | |
| @@ -1112,6 +1118,8 @@ njs_ret_t njs_vmcode_array(njs_vm_t *vm, | |
| njs_value_t *inlvd2); | |
| njs_ret_t njs_vmcode_function(njs_vm_t *vm, njs_value_t *inlvd1, | |
| njs_value_t *invld2); | |
| +njs_ret_t njs_vmcode_arguments(njs_vm_t *vm, njs_value_t *inlvd1, | |
| + njs_value_t *invld2); | |
| njs_ret_t njs_vmcode_regexp(njs_vm_t *vm, njs_value_t *inlvd1, | |
| njs_value_t *invld2); | |
| njs_ret_t njs_vmcode_object_copy(njs_vm_t *vm, njs_value_t *value, | |
| diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c | |
| --- a/njs/test/njs_unit_test.c | |
| +++ b/njs/test/njs_unit_test.c | |
| @@ -5574,6 +5574,77 @@ static njs_unit_test_t njs_test[] = | |
| "var b = a(); b(2)"), | |
| nxt_string("3") }, | |
| + /* arguments object. */ | |
| + | |
| + { nxt_string("var arguments"), | |
| + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") }, | |
| + | |
| + { nxt_string("for (var arguments in []) {}"), | |
| + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in for-in var declaration in 1") }, | |
| + | |
| + { nxt_string("function arguments(){}"), | |
| + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden in function declaration in 1") }, | |
| + | |
| + { nxt_string("(function () {arguments = [];})"), | |
| + nxt_string("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") }, | |
| + | |
| + { nxt_string("(function(){return arguments[0];})(1,2,3)"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("(function(){return arguments[2];})(1,2,3)"), | |
| + nxt_string("3") }, | |
| + | |
| + { nxt_string("(function(){return arguments[3];})(1,2,3)"), | |
| + nxt_string("undefined") }, | |
| + | |
| + { nxt_string("(function(a,b,c){return a;})(1,2,3)"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("(function(a,b,c){arguments[0] = 4; return a;})(1,2,3)"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("(function(a,b,c){a = 4; return arguments[0];})(1,2,3)"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; " | |
| + "function f() {check(arguments.length > 1); return 1}; f()"), | |
| + nxt_string("TypeError: Too few arguments") }, | |
| + | |
| + { nxt_string("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; " | |
| + "function f() {check(arguments.length > 1); return 1}; f(1,2)"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("(function(a,b){delete arguments[0]; return arguments[0]})(1,1)"), | |
| + nxt_string("undefined") }, | |
| + | |
| + { nxt_string("(function(){return arguments.length;})()"), | |
| + nxt_string("0") }, | |
| + | |
| + { nxt_string("(function(){return arguments.length;})(1,2,3)"), | |
| + nxt_string("3") }, | |
| + | |
| + { nxt_string("(function(){arguments.length = 1; return arguments.length;})(1,2,3)"), | |
| + nxt_string("1") }, | |
| + | |
| + { nxt_string("(function(){return arguments.callee;})()"), | |
| + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") }, | |
| + | |
| + { nxt_string("(function(){return arguments.caller;})()"), | |
| + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") }, | |
| + | |
| + { nxt_string("(function(){return arguments.callee;})()"), | |
| + nxt_string("TypeError: 'caller', 'callee' properties may not be accessed") }, | |
| + | |
| + { nxt_string("function sum() { var args = Array.prototype.slice.call(arguments); " | |
| + "return args.reduce(function(prev, curr) {return prev + curr})};" | |
| + "[sum(1), sum(1,2), sum(1,2,3), sum(1,2,3,4)]"), | |
| + nxt_string("1,3,6,10") }, | |
| + | |
| + { nxt_string("function concat(sep) { var args = Array.prototype.slice.call(arguments, 1); " | |
| + "return args.join(sep)};" | |
| + "[concat('.',1,2,3), concat('+',1,2,3,4)]"), | |
| + nxt_string("1.2.3,1+2+3+4") }, | |
| + | |
| /* Scopes. */ | |
| { nxt_string("function f(x) { a = x } var a; f(5); a"), |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment