From 19cc91a443bd75aae9fbfe2969a08761b3ba62f2 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Tue, 2 Apr 2019 17:45:38 +0300 Subject: [PATCH] Making __proto__ accessor descriptor of Object mutable. --- njs/njs_builtin.c | 2 +- njs/njs_math.c | 2 +- njs/njs_object.c | 71 ++++++++++++++++++++++++++++++++++++---- njs/njs_object.h | 2 +- njs/test/njs_unit_test.c | 14 +++++++- 5 files changed, 81 insertions(+), 10 deletions(-) diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 6d715a0e..62241c94 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -414,7 +414,7 @@ njs_prototype_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, * Object(), * Object.__proto__ -> Function_Prototype, * Object_Prototype.__proto__ -> null, - * the null value is handled by njs_object_prototype_get_proto(), + * the null value is handled by njs_object_prototype_proto(), * * Array(), * Array.__proto__ -> Function_Prototype, diff --git a/njs/njs_math.c b/njs/njs_math.c index e96f562e..027d0b9c 100644 --- a/njs/njs_math.c +++ b/njs/njs_math.c @@ -819,7 +819,7 @@ static const njs_object_prop_t njs_math_object_properties[] = { .type = NJS_PROPERTY_HANDLER, .name = njs_string("__proto__"), - .value = njs_prop_handler(njs_object_prototype_get_proto), + .value = njs_prop_handler(njs_object_prototype_proto), }, { diff --git a/njs/njs_object.c b/njs/njs_object.c index d124f655..f2a0f38e 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -1889,8 +1889,8 @@ njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, value = njs_arg(args, nargs, 1); if (njs_is_object(value)) { - njs_object_prototype_get_proto(vm, (njs_value_t *) value, NULL, - &vm->retval); + njs_object_prototype_proto(vm, (njs_value_t *) value, NULL, + &vm->retval); return NXT_OK; } @@ -2393,13 +2393,71 @@ const njs_object_init_t njs_object_constructor_init = { }; +/* + * ES6, 9.1.2: [[SetPrototypeOf]]. + */ +static nxt_bool_t +njs_object_set_prototype_of(njs_vm_t *vm, njs_object_t *object, + const njs_value_t *value) +{ + const njs_object_t *proto; + + proto = njs_is_object(value) ? value->data.u.object->__proto__ + : NULL; + + if (nxt_slow_path(object->__proto__ == proto)) { + return 1; + } + + if (nxt_slow_path(proto == NULL)) { + object->__proto__ = NULL; + return 1; + } + + do { + if (proto == object) { + return 0; + } + + proto = proto->__proto__; + + } while (proto != NULL); + + object->__proto__ = value->data.u.object; + + return 1; +} + + njs_ret_t -njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value, +njs_object_prototype_proto(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { - njs_object_t *proto; + nxt_bool_t ret; + njs_object_t *proto, *object; + + if (!njs_is_object(value)) { + *retval = *value; + return NJS_OK; + } + + object = value->data.u.object; + + if (setval != NULL) { + if (njs_is_object(setval) || njs_is_null(setval)) { + ret = njs_object_set_prototype_of(vm, object, setval); + if (nxt_slow_path(!ret)) { + njs_type_error(vm, "Cyclic __proto__ value"); + return NXT_ERROR; + } + } + + *retval = njs_value_undefined; + + return NJS_OK; + } - proto = value->data.u.object->__proto__; + proto = object->__proto__; if (nxt_fast_path(proto != NULL)) { retval->data.u.object = proto; @@ -2722,7 +2780,8 @@ static const njs_object_prop_t njs_object_prototype_properties[] = { .type = NJS_PROPERTY_HANDLER, .name = njs_string("__proto__"), - .value = njs_prop_handler(njs_object_prototype_get_proto), + .value = njs_prop_handler(njs_object_prototype_proto), + .writable = 1, }, { diff --git a/njs/njs_object.h b/njs/njs_object.h index c0ebb00b..f27205aa 100644 --- a/njs/njs_object.h +++ b/njs/njs_object.h @@ -106,7 +106,7 @@ njs_ret_t njs_object_prototype_create(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_value_t *njs_property_prototype_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, njs_object_t *prototype); -njs_ret_t njs_object_prototype_get_proto(njs_vm_t *vm, njs_value_t *value, +njs_ret_t njs_object_prototype_proto(njs_vm_t *vm, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_value_t *njs_property_constructor_create(njs_vm_t *vm, nxt_lvlhsh_t *hash, njs_value_t *constructor); diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 533ba1a0..01b44e5e 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -7507,6 +7507,12 @@ static njs_unit_test_t njs_test[] = { nxt_string("Object.prototype.__proto__ === null"), nxt_string("true") }, + { nxt_string("Object.prototype.__proto__ = {}"), + nxt_string("TypeError: Cyclic __proto__ value") }, + + { nxt_string("var o = {}; var o2 = Object.create(o); o.__proto__ = o2"), + nxt_string("TypeError: Cyclic __proto__ value") }, + { nxt_string("Object.prototype.__proto__.f()"), nxt_string("TypeError: cannot get property \"f\" of undefined") }, @@ -7529,7 +7535,13 @@ static njs_unit_test_t njs_test[] = nxt_string("true") }, { nxt_string("({}).__proto__ = 1"), - nxt_string("TypeError: Cannot assign to read-only property \"__proto__\" of object") }, + nxt_string("1") }, + + { nxt_string("({}).__proto__ = null"), + nxt_string("null") }, + + { nxt_string("({__proto__: []}) instanceof Array"), + nxt_string("true") }, { nxt_string("({}).__proto__.constructor === Object"), nxt_string("true") }, -- 2.47.3