Created
April 5, 2019 12:16
-
-
Save lexborisov/e9f6ed57579ce773b86dedb325679c0f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User Alexander Borisov <alexander.borisov@nginx.com> | |
# Date 1554459514 -10800 | |
# Fri Apr 05 13:18:34 2019 +0300 | |
# Node ID 9728332c666c3ee2c425579cb69747a83c3ec1a7 | |
# Parent d1cedbc86bc223167e6f2b95a34acdc692e343d3 | |
Added support array-like objects for Function.prototype.apply(). | |
This closes #51 issue on Github. | |
diff -r d1cedbc86bc2 -r 9728332c666c njs/njs_function.c | |
--- a/njs/njs_function.c Wed Apr 03 11:27:05 2019 +0800 | |
+++ b/njs/njs_function.c Fri Apr 05 13:18:34 2019 +0300 | |
@@ -917,40 +917,71 @@ | |
njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs, | |
njs_index_t retval) | |
{ | |
- njs_ret_t ret; | |
- njs_array_t *array; | |
- njs_value_t *this; | |
- njs_function_t *function; | |
+ uint32_t idx; | |
+ njs_ret_t ret; | |
+ nxt_uint_t act_nargs; | |
+ njs_value_t *act_args, *arr_like, length, idx_name; | |
+ njs_array_t *arr; | |
+ njs_function_t *func; | |
+ const njs_value_t *this; | |
- if (!njs_is_function(&args[0])) { | |
+ static const njs_value_t njs_string_length = njs_string("length"); | |
+ | |
+ if (!njs_is_function(njs_arg(args, nargs, 0))) { | |
njs_type_error(vm, "\"this\" argument is not a function"); | |
return NXT_ERROR; | |
} | |
- function = args[0].data.u.function; | |
- this = &args[1]; | |
+ func = (njs_argument(args, 0))->data.u.function; | |
+ this = njs_arg(args, nargs, 1); | |
+ arr_like = (njs_value_t *) njs_arg(args, nargs, 2); | |
- if (nargs > 2) { | |
- if (!njs_is_array(&args[2])) { | |
- njs_type_error(vm, "second argument is not an array"); | |
- return NXT_ERROR; | |
- } | |
+ if (njs_is_null_or_undefined(arr_like)) { | |
+ act_args = NULL; | |
+ act_nargs = 0; | |
+ | |
+ goto activate; | |
- array = args[2].data.u.array; | |
- args = array->start; | |
- nargs = array->length; | |
+ } else if (njs_is_array(arr_like)) { | |
+ arr = arr_like->data.u.array; | |
+ | |
+ act_args = arr->start; | |
+ act_nargs = arr->length; | |
- } else { | |
- if (nargs == 1) { | |
- this = (njs_value_t *) &njs_value_undefined; | |
- } | |
+ goto activate; | |
- nargs = 0; | |
+ } else if (nxt_slow_path(!njs_is_object(arr_like))) { | |
+ njs_type_error(vm, "second argument is not an array-like object"); | |
+ return NXT_ERROR; | |
} | |
- ret = njs_function_activate(vm, function, this, args, nargs, retval, | |
+ ret = njs_value_property(vm, arr_like, &njs_string_length, &length); | |
+ if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) { | |
+ return ret; | |
+ } | |
+ | |
+ act_nargs = njs_primitive_value_to_number(&length); | |
+ | |
+ arr = njs_array_alloc(vm, act_nargs, NJS_ARRAY_SPARE); | |
+ if (nxt_slow_path(arr == NULL)) { | |
+ return NXT_ERROR; | |
+ } | |
+ | |
+ act_args = arr->start; | |
+ | |
+ for (idx = 0; idx < act_nargs; idx++) { | |
+ njs_uint32_to_string(&idx_name, idx); | |
+ | |
+ ret = njs_value_property(vm, arr_like, &idx_name, &act_args[idx]); | |
+ if (nxt_slow_path(ret == NXT_ERROR || ret == NJS_TRAP)) { | |
+ return ret; | |
+ } | |
+ } | |
+ | |
+activate: | |
+ | |
+ ret = njs_function_activate(vm, func, this, act_args, act_nargs, retval, | |
sizeof(njs_vmcode_function_call_t)); | |
- | |
if (nxt_slow_path(ret == NXT_ERROR)) { | |
return ret; | |
} | |
diff -r d1cedbc86bc2 -r 9728332c666c njs/test/njs_unit_test.c | |
--- a/njs/test/njs_unit_test.c Wed Apr 03 11:27:05 2019 +0800 | |
+++ b/njs/test/njs_unit_test.c Fri Apr 05 13:18:34 2019 +0300 | |
@@ -6148,7 +6148,7 @@ | |
nxt_string("5") }, | |
{ nxt_string("var f = function(a) { return this + a }; f.apply(5, 1)"), | |
- nxt_string("TypeError: second argument is not an array") }, | |
+ nxt_string("TypeError: second argument is not an array-like object") }, | |
{ nxt_string("var f = function(a, b) { return this + a + b };" | |
"f.apply(5, [1, 2])"), | |
@@ -6158,6 +6158,46 @@ | |
"f.apply(5, [1, 2], 3)"), | |
nxt_string("8") }, | |
+ { nxt_string("var f = function(a, b) { return this + a + b };" | |
+ "f.apply(5, {'length':2, '0':1, '1':2})"), | |
+ nxt_string("8") }, | |
+ | |
+ { nxt_string("var f = function(a, b) { return this + a + b };" | |
+ "f.apply(5, {'length':2, '0':1, '1':2, '2':3})"), | |
+ nxt_string("8") }, | |
+ | |
+ { nxt_string("var f = function(a, b, c) { return this + a + b + c};" | |
+ "f.apply(\"a\", {'length':2, '0':1, '1':2, '2':3})"), | |
+ nxt_string("a12undefined") }, | |
+ | |
+ { nxt_string("var f = function(a, b) { return this + a + b };" | |
+ "f.apply(5, {'length':3, '0':1, '1':2, '2':3})"), | |
+ nxt_string("8") }, | |
+ | |
+ { nxt_string("var f = function(a) { return this + a };" | |
+ "f.apply(5, {'nolength':3, '0':1, '1':2})"), | |
+ nxt_string("NaN") }, | |
+ | |
+ { nxt_string("var f = function(a) { return this };" | |
+ "f.apply(5, {'nolength':3, '0':1, '1':2})"), | |
+ nxt_string("5") }, | |
+ | |
+ { nxt_string("var f = function(a, b, c) { return this + a + b + c };" | |
+ "f.apply(\"a\", {'length':3, '0':1, '1':2})"), | |
+ nxt_string("a12undefined") }, | |
+ | |
+ { nxt_string("var f = function(a, b) { return this + a + b };" | |
+ "f.apply(\"a\", {'length':2, '0':undefined, '1':null})"), | |
+ nxt_string("aundefinednull") }, | |
+ | |
+ { nxt_string("var f = function() { return this };" | |
+ "f.apply(123, {})"), | |
+ nxt_string("123") }, | |
+ | |
+ { nxt_string("String.prototype.concat.apply('a', " | |
+ "{length:2, 0:{toString:function() {return 'b'}}, 1:'c'})"), | |
+ nxt_string("abc") }, | |
+ | |
{ nxt_string("var a = function() { return 1 } + ''; a"), | |
nxt_string("[object Function]") }, | |
@@ -6183,7 +6223,7 @@ | |
nxt_string("a") }, | |
{ nxt_string("''.concat.apply('a', 'b')"), | |
- nxt_string("TypeError: second argument is not an array") }, | |
+ nxt_string("TypeError: second argument is not an array-like object") }, | |
{ nxt_string("''.concat.apply('a', [ 'b', 'c' ])"), | |
nxt_string("abc") }, |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment