[njs] Making __proto__ accessor descriptor of Object mutable.

Dmitry Volyntsev xeioex at nginx.com
Tue Apr 2 14:46:21 UTC 2019


details:   https://hg.nginx.org/njs/rev/8d9585e9cc8b
branches:  
changeset: 864:8d9585e9cc8b
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Tue Apr 02 17:45:38 2019 +0300
description:
Making __proto__ accessor descriptor of Object mutable.

diffstat:

 njs/njs_builtin.c        |   2 +-
 njs/njs_math.c           |   2 +-
 njs/njs_object.c         |  73 +++++++++++++++++++++++++++++++++++++++++++----
 njs/njs_object.h         |   2 +-
 njs/test/njs_unit_test.c |  14 ++++++++-
 5 files changed, 82 insertions(+), 11 deletions(-)

diffs (167 lines):

diff -r 61f2616e21c7 -r 8d9585e9cc8b njs/njs_builtin.c
--- a/njs/njs_builtin.c	Tue Apr 02 17:17:49 2019 +0300
+++ b/njs/njs_builtin.c	Tue Apr 02 17:45:38 2019 +0300
@@ -414,7 +414,7 @@ njs_prototype_function(njs_vm_t *vm, njs
  * 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 -r 61f2616e21c7 -r 8d9585e9cc8b njs/njs_math.c
--- a/njs/njs_math.c	Tue Apr 02 17:17:49 2019 +0300
+++ b/njs/njs_math.c	Tue Apr 02 17:45:38 2019 +0300
@@ -819,7 +819,7 @@ static const njs_object_prop_t  njs_math
     {
         .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 -r 61f2616e21c7 -r 8d9585e9cc8b njs/njs_object.c
--- a/njs/njs_object.c	Tue Apr 02 17:17:49 2019 +0300
+++ b/njs/njs_object.c	Tue Apr 02 17:45:38 2019 +0300
@@ -1889,8 +1889,8 @@ njs_object_get_prototype_of(njs_vm_t *vm
     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_cons
 };
 
 
+/*
+ * 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;
-
-    proto = value->data.u.object->__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 = object->__proto__;
 
     if (nxt_fast_path(proto != NULL)) {
         retval->data.u.object = proto;
@@ -2722,7 +2780,8 @@ static const njs_object_prop_t  njs_obje
     {
         .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 -r 61f2616e21c7 -r 8d9585e9cc8b njs/njs_object.h
--- a/njs/njs_object.h	Tue Apr 02 17:17:49 2019 +0300
+++ b/njs/njs_object.h	Tue Apr 02 17:45:38 2019 +0300
@@ -106,7 +106,7 @@ njs_ret_t njs_object_prototype_create(nj
     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 -r 61f2616e21c7 -r 8d9585e9cc8b njs/test/njs_unit_test.c
--- a/njs/test/njs_unit_test.c	Tue Apr 02 17:17:49 2019 +0300
+++ b/njs/test/njs_unit_test.c	Tue Apr 02 17:45:38 2019 +0300
@@ -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") },


More information about the nginx-devel mailing list