details: https://hg.nginx.org/njs/rev/6923d5ac84bc branches: changeset: 1140:6923d5ac84bc user: Dmitry Volyntsev <xei...@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") }, _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel