[njs] Making "prototype" property of function instances writable.
Dmitry Volyntsev
xeioex at nginx.com
Thu Aug 22 15:34:36 UTC 2019
details: https://hg.nginx.org/njs/rev/6923d5ac84bc
branches:
changeset: 1140:6923d5ac84bc
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Thu Aug 22 18:27:34 2019 +0300
description:
Making "prototype" property of function instances writable.
This closes #40 issue on Github.
diffstat:
src/njs_function.c | 93 ++++++++++++++++++++++++++++++++----------------
src/njs_function.h | 2 -
src/njs_vmcode.c | 60 +++++++++++++-----------------
src/test/njs_unit_test.c | 39 +++++++++++++++++++-
4 files changed, 125 insertions(+), 69 deletions(-)
diffs (311 lines):
diff -r 91e3f7345e47 -r 6923d5ac84bc src/njs_function.c
--- a/src/njs_function.c Tue Aug 20 23:03:26 2019 -0400
+++ b/src/njs_function.c Thu Aug 22 18:27:34 2019 +0300
@@ -780,6 +780,42 @@ njs_function_frame_free(njs_vm_t *vm, nj
}
+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_function_property_prototype_create(vm, &function->object.hash,
+ setval);
+ if (njs_slow_path(proto == NULL)) {
+ return NJS_ERROR;
}
- proto = njs_property_prototype_create(vm, &function->object.hash,
- prototype);
- if (njs_slow_path(proto == NULL)) {
- return NULL;
+ 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;
+ }
}
- cons = njs_property_constructor_create(vm, &prototype->hash, value);
+ *retval = *proto;
- if (njs_fast_path(cons != NULL)) {
- return proto;
- }
-
- return NULL;
+ return NJS_OK;
}
@@ -1197,6 +1227,7 @@ const njs_object_prop_t njs_function_in
.type = NJS_PROPERTY_HANDLER,
.name = njs_string("prototype"),
.value = njs_prop_handler(njs_function_prototype_create),
+ .writable = 1
},
};
diff -r 91e3f7345e47 -r 6923d5ac84bc src/njs_function.h
--- a/src/njs_function.h Tue Aug 20 23:03:26 2019 -0400
+++ b/src/njs_function.h Thu Aug 22 18:27:34 2019 +0300
@@ -118,8 +118,6 @@ njs_int_t njs_function_rest_parameters_i
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,
diff -r 91e3f7345e47 -r 6923d5ac84bc src/njs_vmcode.c
--- a/src/njs_vmcode.c Tue Aug 20 23:03:26 2019 -0400
+++ b/src/njs_vmcode.c Thu Aug 22 18:27:34 2019 +0300
@@ -70,7 +70,8 @@ static njs_jump_off_t njs_primitive_valu
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
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
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,
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;
+ njs_value_t proto;
+ njs_object_t *object;
+ njs_jump_off_t ret;
+
+ const njs_value_t prototype_string = njs_string("prototype");
object = njs_object_alloc(vm);
-
- if (njs_fast_path(object != NULL)) {
+ if (njs_slow_path(object == NULL)) {
+ return NULL;
+ }
- 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);
+ ret = njs_value_property(vm, constructor, njs_value_arg(&prototype_string),
+ &proto);
- if (ret == NJS_OK) {
- prop = lhq.value;
- proto = &prop->value;
-
- } 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;
}
diff -r 91e3f7345e47 -r 6923d5ac84bc src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c Tue Aug 20 23:03:26 2019 -0400
+++ b/src/test/njs_unit_test.c Thu Aug 22 18:27:34 2019 +0300
@@ -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") },
More information about the nginx-devel
mailing list