]> git.kaiwu.me - njs.git/commitdiff
Making "prototype" property of function instances writable.
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 22 Aug 2019 15:27:34 +0000 (18:27 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Thu, 22 Aug 2019 15:27:34 +0000 (18:27 +0300)
This closes #40 issue on Github.

src/njs_function.c
src/njs_function.h
src/njs_vmcode.c
src/test/njs_unit_test.c

index 5a3af5312f035e1c6367d9bec2552c3b54b3a282..8a1f97da722b3a07ce9ec44a3d12b57e8859a355 100644 (file)
@@ -780,6 +780,42 @@ njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native)
 }
 
 
+static njs_value_t *
+njs_function_property_prototype_create(njs_vm_t *vm, njs_lvlhsh_t *hash,
+    njs_value_t *prototype)
+{
+    njs_int_t           ret;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
+
+    const njs_value_t  proto_string = njs_string("prototype");
+
+    prop = njs_object_prop_alloc(vm, &proto_string, prototype, 0);
+    if (njs_slow_path(prop == NULL)) {
+        return NULL;
+    }
+
+    prop->writable = 1;
+
+    lhq.value = prop;
+    lhq.key_hash = NJS_PROTOTYPE_HASH;
+    lhq.key = njs_str_value("prototype");
+    lhq.replace = 1;
+    lhq.pool = vm->mem_pool;
+    lhq.proto = &njs_object_hash_proto;
+
+    ret = njs_lvlhsh_insert(hash, &lhq);
+
+    if (njs_fast_path(ret == NJS_OK)) {
+        return &prop->value;
+    }
+
+    njs_internal_error(vm, "lvlhsh insert failed");
+
+    return NULL;
+}
+
+
 /*
  * The "prototype" property of user defined functions is created on
  * demand in private hash of the functions by the "prototype" getter.
@@ -794,49 +830,43 @@ njs_int_t
 njs_function_prototype_create(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *setval, njs_value_t *retval)
 {
-    njs_value_t  *proto;
-
-    proto = njs_function_property_prototype_create(vm, value);
-
-    if (njs_fast_path(proto != NULL)) {
-        *retval = *proto;
-        return NJS_OK;
-    }
-
-    return NJS_ERROR;
-}
-
-
-njs_value_t *
-njs_function_property_prototype_create(njs_vm_t *vm, njs_value_t *value)
-{
-    njs_value_t     *proto, *cons;
+    njs_value_t     *proto, proto_value, *cons;
     njs_object_t    *prototype;
     njs_function_t  *function;
 
-    prototype = njs_object_alloc(vm);
-    if (njs_slow_path(prototype == NULL)) {
-        return NULL;
+    if (setval == NULL) {
+        prototype = njs_object_alloc(vm);
+        if (njs_slow_path(prototype == NULL)) {
+            return NJS_ERROR;
+        }
+
+        njs_set_object(&proto_value, prototype);
+
+        setval = &proto_value;
     }
 
     function = njs_function_value_copy(vm, value);
     if (njs_slow_path(function == NULL)) {
-        return NULL;
+        return NJS_ERROR;
     }
 
-    proto = njs_property_prototype_create(vm, &function->object.hash,
-                                          prototype);
+    proto = njs_function_property_prototype_create(vm, &function->object.hash,
+                                                   setval);
     if (njs_slow_path(proto == NULL)) {
-        return NULL;
+        return NJS_ERROR;
     }
 
-    cons = njs_property_constructor_create(vm, &prototype->hash, value);
-
-    if (njs_fast_path(cons != NULL)) {
-        return proto;
+    if (njs_is_object(proto)) {
+        cons = njs_property_constructor_create(vm, njs_object_hash(proto),
+                                               value);
+        if (njs_slow_path(cons == NULL)) {
+            return NJS_ERROR;
+        }
     }
 
-    return NULL;
+    *retval = *proto;
+
+    return NJS_OK;
 }
 
 
@@ -1197,6 +1227,7 @@ const njs_object_prop_t  njs_function_instance_properties[] =
         .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("prototype"),
         .value = njs_prop_handler(njs_function_prototype_create),
+        .writable = 1
     },
 };
 
index 983d220b9cd2d62bd731defd2d02d42bd7d3b5b8..29decd01f416a65407c5164928d4dcf81ea2d197 100644 (file)
@@ -118,8 +118,6 @@ njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm,
     njs_native_frame_t *frame);
 njs_int_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,
-    njs_value_t *value);
 njs_int_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 njs_int_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
index 7c86ba0e521f15d530c24a78bebba595e430446d..5aaf925fafed85ecdb769a4cc46d35626e1fb571 100644 (file)
@@ -70,7 +70,8 @@ static njs_jump_off_t njs_primitive_values_compare(njs_vm_t *vm,
 static njs_jump_off_t njs_function_frame_create(njs_vm_t *vm,
     njs_value_t *value, const njs_value_t *this, uintptr_t nargs,
     njs_bool_t ctor);
-static njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *value);
+static njs_object_t *njs_function_new_object(njs_vm_t *vm,
+    njs_value_t *constructor);
 
 /*
  * The nJSVM is optimized for an ABIs where the first several arguments
@@ -1458,10 +1459,10 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
     njs_jump_off_t     ret;
     const njs_value_t  *retval;
 
-    static njs_value_t prototype_string = njs_string("prototype");
+    const njs_value_t prototype_string = njs_string("prototype");
 
     if (!njs_is_function(constructor)) {
-        njs_type_error(vm, "right argument is not a function");
+        njs_type_error(vm, "right argument is not callable");
         return NJS_ERROR;
     }
 
@@ -1469,16 +1470,17 @@ njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object,
 
     if (njs_is_object(object)) {
         njs_set_undefined(&value);
-        ret = njs_value_property(vm, constructor, &prototype_string, &value);
+        ret = njs_value_property(vm, constructor,
+                                 njs_value_arg(&prototype_string), &value);
 
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
 
         if (njs_fast_path(ret == NJS_OK)) {
-
             if (njs_slow_path(!njs_is_object(&value))) {
-                njs_internal_error(vm, "prototype is not an object");
+                njs_type_error(vm, "Function has non-object prototype "
+                               "in instanceof");
                 return NJS_ERROR;
             }
 
@@ -1737,41 +1739,31 @@ njs_function_frame_create(njs_vm_t *vm, njs_value_t *value,
 
 
 static njs_object_t *
-njs_function_new_object(njs_vm_t *vm, njs_value_t *value)
+njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor)
 {
-    njs_value_t         *proto;
-    njs_object_t        *object;
-    njs_jump_off_t      ret;
-    njs_function_t      *function;
-    njs_object_prop_t   *prop;
-    njs_lvlhsh_query_t  lhq;
-
-    object = njs_object_alloc(vm);
+    njs_value_t     proto;
+    njs_object_t    *object;
+    njs_jump_off_t  ret;
 
-    if (njs_fast_path(object != NULL)) {
+    const njs_value_t prototype_string = njs_string("prototype");
 
-        lhq.key_hash = NJS_PROTOTYPE_HASH;
-        lhq.key = njs_str_value("prototype");
-        lhq.proto = &njs_object_hash_proto;
-        function = njs_function(value);
-
-        ret = njs_lvlhsh_find(&function->object.hash, &lhq);
+    object = njs_object_alloc(vm);
+    if (njs_slow_path(object == NULL)) {
+        return NULL;
+    }
 
-        if (ret == NJS_OK) {
-            prop = lhq.value;
-            proto = &prop->value;
+    ret = njs_value_property(vm, constructor, njs_value_arg(&prototype_string),
+                             &proto);
 
-        } else {
-            proto = njs_function_property_prototype_create(vm, value);
-        }
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return NULL;
+    }
 
-        if (njs_fast_path(proto != NULL)) {
-            object->__proto__ = njs_object(proto);
-            return object;
-        }
-   }
+    if (njs_fast_path(njs_is_object(&proto))) {
+        object->__proto__ = njs_object(&proto);
+    }
 
-   return NULL;
+    return object;
 }
 
 
index 43ec8640d2e9b467f9408b784123e96054060eb2..5206092bd1307cf0077b71ac710a66b47703c267 100644 (file)
@@ -7233,6 +7233,41 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("var F = function (){}; typeof F.prototype"),
       njs_str("object") },
 
+    { njs_str("var F = function (){}; F.prototype = NaN; ({}) instanceof F"),
+      njs_str("TypeError: Function has non-object prototype in instanceof") },
+
+    { njs_str("var F = function() {};"
+              "[F, F].map((x)=>Object.getOwnPropertyDescriptor(x, 'prototype').writable)"
+              ".every((x)=> x === true)"),
+      njs_str("true") },
+
+    { njs_str("var F = function() {}, a = {t: 1}, b = {t: 2}, x, y; "
+              "F.prototype = a; x = new F();"
+              "F.prototype = b; y = new F();"
+              "x.t == 1 && y.t == 2"),
+      njs_str("true") },
+
+    { njs_str("var x = {}, y = function() {}, z; y.prototype = x; z = new y();"
+              "(z instanceof y) && (z.__proto__ == y.prototype) && (x.isPrototypeOf(z))"),
+      njs_str("true") },
+
+    { njs_str("var x = {}, y = function() {}, z; y.prototype = x; z = new y();"
+              "(z instanceof y) && (z.__proto__ == y.prototype) && (x.isPrototypeOf(z))"),
+      njs_str("true") },
+
+    { njs_str("[undefined, null, false, NaN, '']"
+              ".map((x) => { var f = function() {}; f.prototype = x; "
+              "              return Object.getPrototypeOf(new f()); })"
+              ".every((x) => x == Object.prototype)"),
+      njs_str("true") },
+
+    { njs_str("[undefined, null, false, NaN, '']"
+              ".map((x) => { var f = function() {}; f.prototype = x; return f; })"
+              ".map((x) => { try { return ({} instanceof x) ? 1 : 2; } "
+              "              catch (e) { return (e instanceof TypeError) ? 3 : 4; } })"
+              ".every((x) => x == 3)"),
+      njs_str("true")},
+
     { njs_str("new decodeURI('%00')"),
       njs_str("TypeError: function is not a constructor")},
 
@@ -8579,7 +8614,7 @@ static njs_unit_test_t  njs_test[] =
       njs_str("true") },
 
     { njs_str("[] instanceof []"),
-      njs_str("TypeError: right argument is not a function") },
+      njs_str("TypeError: right argument is not callable") },
 
     { njs_str("[] instanceof Array"),
       njs_str("true") },
@@ -8651,7 +8686,7 @@ static njs_unit_test_t  njs_test[] =
       njs_str("TypeError: object is not a function") },
 
     { njs_str("var ex; try {({}) instanceof this} catch (e) {ex = e}; ex"),
-      njs_str("TypeError: right argument is not a function") },
+      njs_str("TypeError: right argument is not callable") },
 
     { njs_str("Function.call(this, 'var x / = 1;')"),
       njs_str("InternalError: Not implemented") },