Skip to content

Instantly share code, notes, and snippets.

@lexborisov
Created April 5, 2019 12:16
Show Gist options
  • Save lexborisov/e9f6ed57579ce773b86dedb325679c0f to your computer and use it in GitHub Desktop.
Save lexborisov/e9f6ed57579ce773b86dedb325679c0f to your computer and use it in GitHub Desktop.
# 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