]> git.kaiwu.me - njs.git/commitdiff
Implemented Array.from().
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 19 May 2023 01:33:36 +0000 (18:33 -0700)
committerDmitry Volyntsev <xeioex@nginx.com>
Fri, 19 May 2023 01:33:36 +0000 (18:33 -0700)
src/njs_array.c
src/njs_typed_array.c
src/njs_value.c
src/njs_value.h
src/njs_vmcode.c
src/test/njs_unit_test.c

index b4e9f9742633f01f2d970b09e3cb59be3f270d61..b45dbfa5e25c7bbafc4567a7f70f69e76f00683e 100644 (file)
@@ -482,6 +482,95 @@ njs_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
+static njs_int_t
+njs_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused, njs_value_t *retval)
+{
+    int64_t         length, i;
+    njs_int_t       ret;
+    njs_array_t     *array;
+    njs_value_t     *this, *items, *mapfn;
+    njs_value_t     arguments[3], value, result;
+    njs_function_t  *function;
+
+    mapfn = njs_arg(args, nargs, 2);
+
+    if (njs_slow_path(!njs_is_function_or_undefined(mapfn))) {
+        njs_type_error(vm, "\"mapfn\" argument is not callable");
+        return NJS_ERROR;
+    }
+
+    function = NULL;
+    if (njs_is_function(mapfn)) {
+        function = njs_function(mapfn);
+    }
+
+    items = njs_arg(args, nargs, 1);
+
+    ret = njs_value_to_object(vm, items);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    ret = njs_object_length(vm, items, &length);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
+
+    this = njs_argument(args, 0);
+
+    if (njs_is_constructor(this)) {
+        njs_set_number(&arguments[0], length);
+
+        ret = njs_value_construct(vm, this, arguments, 1, &value);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+
+    } else {
+        array = njs_array_alloc(vm, 1, length, 0);
+        if (njs_slow_path(array == NULL)) {
+            return NJS_ERROR;
+        }
+
+        njs_set_array(&value, array);
+    }
+
+    arguments[0] = *njs_arg(args, nargs, 3);
+
+    for (i = 0; i < length; i++) {
+        ret = njs_value_property_i64(vm, items, i, &result);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return NJS_ERROR;
+        }
+
+        if (function != NULL) {
+            njs_value_assign(&arguments[1], &result);
+            njs_set_number(&arguments[2], i);
+
+            ret = njs_function_apply(vm, function, arguments, 3, &result);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+        }
+
+        ret = njs_value_create_data_prop_i64(vm, &value, i, &result, 0);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
+    ret = njs_object_length_set(vm, &value, length);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
+
+    njs_value_assign(retval, &value);
+
+    return NJS_OK;
+}
+
+
 static njs_int_t
 njs_array_is_array(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval)
@@ -526,6 +615,8 @@ static const njs_object_prop_t  njs_array_constructor_properties[] =
 
     NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0),
 
+    NJS_DECLARE_PROP_NATIVE("from", njs_array_from, 1, 0),
+
     NJS_DECLARE_PROP_NATIVE("isArray", njs_array_is_array, 1, 0),
 
     NJS_DECLARE_PROP_NATIVE("of", njs_array_of, 0, 0),
index 1c5265da24d2381598697a222bc45f78567a6959..8c535b5798b52438fbbcc604d0eb5fce76f7d2c9 100644 (file)
@@ -222,19 +222,9 @@ njs_typed_array_create(njs_vm_t *vm, njs_value_t *constructor,
     njs_value_t *args, njs_uint_t nargs, njs_value_t *retval)
 {
     njs_int_t          ret;
-    njs_value_t        this;
-    njs_object_t       *object;
     njs_typed_array_t  *array;
 
-    object = njs_function_new_object(vm, constructor);
-    if (njs_slow_path(object == NULL)) {
-        return NJS_ERROR;
-    }
-
-    njs_set_object(&this, object);
-
-    ret = njs_function_call2(vm, njs_function(constructor), &this, args,
-                             nargs, retval, 1);
+    ret = njs_value_construct(vm, constructor, args, nargs, retval);
     if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
index a5fec855c926c358ac6f8abbb5b8810af88ed74f..05dcf9e8a2ba2d1a9c5c42403e6e4336137648e0 100644 (file)
@@ -1680,6 +1680,25 @@ njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string)
 }
 
 
+njs_int_t
+njs_value_construct(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *args,
+    njs_uint_t nargs, njs_value_t *retval)
+{
+    njs_value_t   this;
+    njs_object_t  *object;
+
+    object = njs_function_new_object(vm, constructor);
+    if (njs_slow_path(object == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_object(&this, object);
+
+    return njs_function_call2(vm, njs_function(constructor), &this, args,
+                              nargs, retval, 1);
+}
+
+
 njs_int_t
 njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *default_constructor, njs_value_t *dst)
index 9f19d0195e94a4f79652051ee6310777743d9e8b..570c84ff21caf230240fc91964b010e51ac0cc3a 100644 (file)
@@ -1090,6 +1090,8 @@ njs_int_t njs_value_to_object(njs_vm_t *vm, njs_value_t *value);
 
 void njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string);
 
+njs_int_t njs_value_construct(njs_vm_t *vm, njs_value_t *constructor,
+    njs_value_t *args, njs_uint_t nargs, njs_value_t *retval);
 njs_int_t njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object,
     njs_value_t *default_constructor, njs_value_t *dst);
 
index b6e28ef51782009265bb36174a1a070002a0611d..80a45f0f51188676025322b6456802e8dd6c0caf 100644 (file)
@@ -2536,6 +2536,8 @@ njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor)
         return NULL;
     }
 
+    njs_assert(njs_is_function(constructor));
+
     function = njs_function(constructor);
 
     if (function->bound != NULL) {
index c8a70d2cf0d330894b9654bbdbaddfd52035ccfd..4be4338b91ac0f0f139803f86911b5d8373df376 100644 (file)
@@ -4561,6 +4561,38 @@ static njs_unit_test_t  njs_test[] =
     { njs_str("Array.isArray([]) ? 'true' : 'false'"),
       njs_str("true") },
 
+    { njs_str("["
+              "  [undefined],"
+              "  [null],"
+              "  ['foo'],"
+              "  ['foo', c => c.toUpperCase()],"
+              "  [{length: 3, 1:'a', 2:'b'}],"
+              "  [[7,,9], v => v*2],"
+              "].map(args => { try { return Array.from.apply(Array,args) }"
+              "                catch (e) {return e.toString()}})"),
+      njs_str("TypeError: cannot convert null or undefined to object,"
+              "TypeError: cannot convert null or undefined to object,"
+              "f,o,o,"
+              "F,O,O,"
+              ",a,b,"
+              "14,NaN,18"
+              ) },
+
+    { njs_str("function f() {return Array.from(arguments);}; f(1,2,3)"),
+      njs_str("1,2,3") },
+
+    { njs_str("Array.from({ length: 5 }, (v, i) => i)"),
+      njs_str("0,1,2,3,4") },
+
+    { njs_str("const range = (start, stop, step) =>"
+              "Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);"
+              "range(1, 10, 2)"),
+      njs_str("1,3,5,7,9") },
+
+    { njs_str("var a = Array.from.call(Object, { length: 2, 0:7, 1:9 });"
+              "[a[0], a[1], Array.isArray(a)]"),
+      njs_str("7,9,false") },
+
     { njs_str("Array.of()"),
       njs_str("") },
 
@@ -15282,7 +15314,7 @@ static njs_unit_test_t  njs_test[] =
       njs_str("length,name,prototype") },
 
     { njs_str("Object.getOwnPropertyNames(Array)"),
-      njs_str("name,length,prototype,isArray,of") },
+      njs_str("name,length,prototype,from,isArray,of") },
 
     { njs_str("Object.getOwnPropertyNames(Array.isArray)"),
       njs_str("name,length") },