]> git.kaiwu.me - njs.git/commitdiff
Refactor modules using external prototypes.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 21 Dec 2021 17:42:26 +0000 (17:42 +0000)
committerDmitry Volyntsev <xeioex@nginx.com>
Tue, 21 Dec 2021 17:42:26 +0000 (17:42 +0000)
34 files changed:
auto/init
auto/make
auto/module [new file with mode: 0644]
auto/modules [new file with mode: 0644]
auto/options
auto/sources
configure
external/njs_crypto.c [moved from src/njs_crypto.c with 61% similarity]
external/njs_fs.c [moved from src/njs_fs.c with 77% similarity]
external/njs_query_string.c [moved from src/njs_query_string.c with 90% similarity]
external/njs_webcrypto.c
external/njs_webcrypto.h [deleted file]
nginx/config
nginx/config.make
nginx/ngx_http_js_module.c
nginx/ngx_js.c
nginx/ngx_js.h
nginx/ngx_stream_js_module.c
src/njs.h
src/njs_buffer.c
src/njs_buffer.h
src/njs_builtin.c
src/njs_crypto.h [deleted file]
src/njs_fs.h [deleted file]
src/njs_main.h
src/njs_module.c
src/njs_module.h
src/njs_query_string.h [deleted file]
src/njs_shell.c
src/njs_value.h
src/njs_vm.c
src/njs_vm.h
src/test/njs_externals_test.c
src/test/njs_unit_test.c

index 648ec2615dd335450c8d2450e818eef53319173c..4edea8b93ae7fdfb80788c8e63fc13153cc4e80c 100644 (file)
--- a/auto/init
+++ b/auto/init
@@ -20,6 +20,9 @@ NJS_AUTOCONF_ERR=$NJS_BUILD_DIR/autoconf.err
 NJS_AUTO_CONFIG_H=$NJS_BUILD_DIR/njs_auto_config.h
 NJS_MAKEFILE=$NJS_BUILD_DIR/Makefile
 
+NJS_LIB_MODULES=
+NJS_LIB_INCS="src $NJS_BUILD_DIR"
+
 test -d $NJS_BUILD_DIR || mkdir $NJS_BUILD_DIR
 
 > $NJS_AUTOCONF_ERR
index 2aaafeac293b440a67e82183eed14ba6589caed5..dae8416656dd3b7b2760fd7c28479ed4ed11e28d 100644 (file)
--- a/auto/make
+++ b/auto/make
@@ -7,11 +7,44 @@
 echo "creating $NJS_MAKEFILE"
 
 mkdir -p $NJS_BUILD_DIR/src
+mkdir -p $NJS_BUILD_DIR/build
+mkdir -p $NJS_BUILD_DIR/external
 mkdir -p $NJS_BUILD_DIR/test
 
+njs_modules_c=$NJS_BUILD_DIR/njs_modules.c
+
+NJS_LIB_SRCS="$NJS_LIB_SRCS $njs_modules_c"
+
+njs_incs=`echo $NJS_LIB_INCS \
+        | sed -e "s# *\([^ ]*\)#$njs_regex_cont-I\1#g"`
 njs_objs=`echo $NJS_LIB_SRCS \
         | sed -e "s# *\([^ ]*\.\)c#$NJS_BUILD_DIR/\1o$njs_regex_cont#g"`
 
+cat << END                                    > $njs_modules_c
+
+#include <njs_main.h>
+
+END
+
+for mod in $NJS_LIB_MODULES
+do
+    echo "extern njs_module_t  $mod;"         >> $njs_modules_c
+done
+
+echo                                          >> $njs_modules_c
+echo 'njs_module_t *njs_modules[] = {'        >> $njs_modules_c
+
+for mod in $NJS_LIB_MODULES
+do
+    echo "    &$mod,"                         >> $njs_modules_c
+done
+
+cat << END                                    >> $njs_modules_c
+    NULL
+};
+
+END
+
 cat << END > $NJS_MAKEFILE
 
 # This file is auto-generated by configure
@@ -28,8 +61,7 @@ NPM = npm
 
 default: njs
 
-NJS_LIB_INCS = -Isrc -I$NJS_BUILD_DIR
-
+NJS_LIB_INCS = $njs_incs
 NJS_LIB_OBJS = $njs_objs
 
 libnjs: $NJS_BUILD_DIR/libnjs.a
@@ -46,9 +78,8 @@ END
 
 for njs_src in $NJS_LIB_SRCS
 do
-    fname=$(basename $njs_src)
-    njs_obj="src/${fname%.c}.o"
-    njs_dep="src/${fname%.c}.dep"
+    njs_obj="${njs_src%.c}.o"
+    njs_dep="${njs_src%.c}.dep"
     njs_dep_flags=`njs_gen_dep_flags $njs_dep $njs_obj`
     njs_dep_post=`njs_gen_dep_post $njs_dep $njs_obj`
     cat << END >> $NJS_MAKEFILE
@@ -73,7 +104,7 @@ cat << END >> $NJS_MAKEFILE
 
 $NJS_BUILD_DIR/njs: \\
        $NJS_BUILD_DIR/libnjs.a \\
-       src/njs_shell.c external/njs_webcrypto.h external/njs_webcrypto.c
+       src/njs_shell.c
        \$(NJS_LINK) -o $NJS_BUILD_DIR/njs \$(NJS_CFLAGS) \\
                $NJS_LIB_AUX_CFLAGS \$(NJS_LIB_INCS) -Injs \\
                src/njs_shell.c \\
@@ -143,7 +174,7 @@ njs_dep_post=`njs_gen_dep_post $njs_dep $njs_externals_obj`
 cat << END >> $NJS_MAKEFILE
 
 $NJS_BUILD_DIR/$njs_externals_obj: \\
-    $njs_src external/njs_webcrypto.h external/njs_webcrypto.c
+    $njs_src external/njs_webcrypto.c
        \$(NJS_CC) -c \$(NJS_CFLAGS) $NJS_LIB_AUX_CFLAGS \\
                \$(NJS_LIB_INCS) -Injs \\
                -o $NJS_BUILD_DIR/$njs_externals_obj \\
diff --git a/auto/module b/auto/module
new file mode 100644 (file)
index 0000000..b830879
--- /dev/null
@@ -0,0 +1,6 @@
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) NGINX, Inc.
+
+NJS_LIB_MODULES="$NJS_LIB_MODULES $njs_module_name"
+NJS_LIB_SRCS="$NJS_LIB_SRCS $njs_module_srcs"
+NJS_LIB_INCS="$NJS_LIB_INCS $njs_module_incs"
diff --git a/auto/modules b/auto/modules
new file mode 100644 (file)
index 0000000..4d46c66
--- /dev/null
@@ -0,0 +1,34 @@
+# Copyright (C) Dmitry Volyntsev
+# Copyright (C) NGINX, Inc.
+
+njs_module_name=njs_buffer_module
+njs_module_incs=
+njs_module_srcs=src/njs_buffer.c
+
+. auto/module
+
+njs_module_name=njs_crypto_module
+njs_module_incs=
+njs_module_srcs=external/njs_crypto.c
+
+. auto/module
+
+if [ $NJS_WEBCRYPTO = YES -a $NJS_HAVE_OPENSSL = YES ]; then
+       njs_module_name=njs_webcrypto_module
+       njs_module_incs=
+       njs_module_srcs=external/njs_webcrypto.c
+
+       . auto/module
+fi
+
+njs_module_name=njs_fs_module
+njs_module_incs=
+njs_module_srcs=external/njs_fs.c
+
+. auto/module
+
+njs_module_name=njs_query_string_module
+njs_module_incs=
+njs_module_srcs=external/njs_query_string.c
+
+. auto/module
index f4b3cad1f6055885df85a08b44222d4d4db79f91..11c1ff0c1b83d38fa8fd3cd93ff257db7db5da94 100644 (file)
@@ -11,6 +11,7 @@ NJS_DEBUG_MEMORY=NO
 NJS_ADDRESS_SANITIZER=NO
 NJS_TEST262=YES
 
+NJS_WEBCRYPTO=YES
 NJS_TRY_PCRE2=YES
 
 NJS_CONFIGURE_OPTIONS=
@@ -33,6 +34,7 @@ do
         --debug-memory=*)                NJS_DEBUG_MEMORY="$value"           ;;
         --test262=*)                     NJS_TEST262="$value"                ;;
 
+        --no-webcrypto)                  NJS_WEBCRYPTO=NO                    ;;
         --no-pcre2)                      NJS_TRY_PCRE2=NO                    ;;
 
         --help)
index 18f5688312c293cb092a3eea81bcc2c8c1d4ff54..21ca33451db90c52197c493ba9f72e242214b532 100644 (file)
@@ -42,8 +42,6 @@ NJS_LIB_SRCS=" \
    src/njs_timer.c \
    src/njs_module.c \
    src/njs_event.c \
-   src/njs_fs.c \
-   src/njs_crypto.c \
    src/njs_extern.c \
    src/njs_variable.c \
    src/njs_builtin.c \
@@ -55,9 +53,7 @@ NJS_LIB_SRCS=" \
    src/njs_array_buffer.c \
    src/njs_typed_array.c \
    src/njs_promise.c \
-   src/njs_query_string.c \
    src/njs_encoding.c \
-   src/njs_buffer.c \
    src/njs_iterator.c \
    src/njs_scope.c \
    src/njs_async.c \
index 5ff6738f05ed018beac034bfa263fbe4bd345e1c..5fb5af005df3c1392ed6ec0543a0aec9fda42a73 100755 (executable)
--- a/configure
+++ b/configure
@@ -35,6 +35,7 @@ NJS_LIB_AUX_CFLAGS="$NJS_PCRE_CFLAGS"
 NJS_LIBS="$NJS_LIBRT"
 NJS_LIB_AUX_LIBS="$NJS_PCRE_LIB $NJS_OPENSSL_LIB"
 
+. auto/modules
 . auto/make
 
 . auto/expect
similarity index 61%
rename from src/njs_crypto.c
rename to external/njs_crypto.c
index 0b65f0ed049a34a7108cfb7684136a3608db83de..28b0868b5eb3180fbc44049d1f445f49ece0a7ad 100644 (file)
@@ -61,6 +61,16 @@ static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm,
     const njs_value_t *value);
 static njs_int_t njs_buffer_digest(njs_vm_t *vm, njs_value_t *value,
     const njs_str_t *src);
+static njs_int_t njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
+static njs_int_t njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t hmac);
+static njs_int_t njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t hmac);
+static njs_int_t njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
+
+static njs_int_t njs_crypto_init(njs_vm_t *vm);
 
 
 static njs_hash_alg_t njs_hash_algorithms[] = {
@@ -129,24 +139,145 @@ static njs_crypto_enc_t njs_encodings[] = {
 };
 
 
+static njs_external_t  njs_ext_crypto_hash[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Hash",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("update"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_hash_prototype_update,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("digest"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_hash_prototype_digest,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("constructor"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_crypto_create_hash,
+        }
+    },
+};
+
+
+static njs_external_t  njs_ext_crypto_hmac[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Hmac",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("update"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_hash_prototype_update,
+            .magic8 = 1,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("digest"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_hash_prototype_digest,
+            .magic8 = 1,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("constructor"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_crypto_create_hmac,
+            .magic8 = 0,
+        }
+    },
+};
+
+
+static njs_external_t  njs_ext_crypto_crypto[] = {
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("createHash"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_crypto_create_hash,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("createHmac"),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 1,
+        .u.method = {
+            .native = njs_crypto_create_hmac,
+            .magic8 = 0,
+        }
+    },
+};
+
+
+static njs_int_t    njs_crypto_hash_proto_id;
+static njs_int_t    njs_crypto_hmac_proto_id;
+
+
+njs_module_t  njs_crypto_module = {
+    .name = njs_str("crypto"),
+    .init = njs_crypto_init,
+};
+
+
 static njs_int_t
 njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    njs_digest_t        *dgst;
-    njs_hash_alg_t      *alg;
-    njs_object_value_t  *hash;
+    njs_digest_t    *dgst;
+    njs_hash_alg_t  *alg;
 
     alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
     if (njs_slow_path(alg == NULL)) {
         return NJS_ERROR;
     }
 
-    hash = njs_object_value_alloc(vm, NJS_OBJ_TYPE_CRYPTO_HASH, 0, NULL);
-    if (njs_slow_path(hash == NULL)) {
-        return NJS_ERROR;
-    }
-
     dgst = njs_mp_alloc(vm->mem_pool, sizeof(njs_digest_t));
     if (njs_slow_path(dgst == NULL)) {
         njs_memory_error(vm);
@@ -157,16 +288,14 @@ njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     alg->init(&dgst->u);
 
-    njs_set_data(&hash->value, dgst, NJS_DATA_TAG_CRYPTO_HASH);
-    njs_set_object_value(&vm->retval, hash);
-
-    return NJS_OK;
+    return njs_vm_external_create(vm, &vm->retval, njs_crypto_hash_proto_id,
+                                  dgst, 0);
 }
 
 
 static njs_int_t
 njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t tag)
+    njs_index_t hmac)
 {
     njs_str_t                    data;
     njs_int_t                    ret;
@@ -179,9 +308,34 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     const njs_buffer_encoding_t  *encoding;
 
     this = njs_argument(args, 0);
-    if (njs_slow_path(!njs_is_object_data(this, tag))) {
-        njs_type_error(vm, "\"this\" is not a hash object");
-        return NJS_ERROR;
+
+    if (!hmac) {
+        dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, this);
+        if (njs_slow_path(dgst == NULL)) {
+            njs_type_error(vm, "\"this\" is not a hash object");
+            return NJS_ERROR;
+        }
+
+        if (njs_slow_path(dgst->alg == NULL)) {
+            njs_error(vm, "Digest already called");
+            return NJS_ERROR;
+        }
+
+        ctx = NULL;
+
+    } else {
+        ctx = njs_vm_external(vm, njs_crypto_hmac_proto_id, this);
+        if (njs_slow_path(ctx == NULL)) {
+            njs_type_error(vm, "\"this\" is not a hmac object");
+            return NJS_ERROR;
+        }
+
+        if (njs_slow_path(ctx->alg == NULL)) {
+            njs_error(vm, "Digest already called");
+            return NJS_ERROR;
+        }
+
+        dgst = NULL;
     }
 
     value = njs_arg(args, nargs, 1);
@@ -221,22 +375,10 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    if (tag == NJS_DATA_TAG_CRYPTO_HASH) {
-        dgst = njs_object_data(this);
-        if (njs_slow_path(dgst->alg == NULL)) {
-            njs_error(vm, "Digest already called");
-            return NJS_ERROR;
-        }
-
+    if (!hmac) {
         dgst->alg->update(&dgst->u, data.start, data.length);
 
     } else {
-        ctx = njs_object_data(this);
-        if (njs_slow_path(ctx->alg == NULL)) {
-            njs_error(vm, "Digest already called");
-            return NJS_ERROR;
-        }
-
         ctx->alg->update(&ctx->u, data.start, data.length);
     }
 
@@ -248,7 +390,7 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 static njs_int_t
 njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t tag)
+    njs_index_t hmac)
 {
     njs_str_t         str;
     njs_hmac_t        *ctx;
@@ -259,32 +401,45 @@ njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     u_char            hash1[32], digest[32];
 
     this = njs_argument(args, 0);
-    if (njs_slow_path(!njs_is_object_data(this, tag))) {
-        njs_type_error(vm, "\"this\" is not a hash object");
-        return NJS_ERROR;
-    }
 
-    enc = njs_crypto_encoding(vm, njs_arg(args, nargs, 1));
-    if (njs_slow_path(enc == NULL)) {
-        return NJS_ERROR;
-    }
+    if (!hmac) {
+        dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, this);
+        if (njs_slow_path(dgst == NULL)) {
+            njs_type_error(vm, "\"this\" is not a hash object");
+            return NJS_ERROR;
+        }
 
-    if (tag == NJS_DATA_TAG_CRYPTO_HASH) {
-        dgst = njs_object_data(this);
         if (njs_slow_path(dgst->alg == NULL)) {
             goto exception;
         }
 
-        alg = dgst->alg;
-        alg->final(digest, &dgst->u);
-        dgst->alg = NULL;
+        ctx = NULL;
 
     } else {
-        ctx = njs_object_data(this);
+        ctx = njs_vm_external(vm, njs_crypto_hmac_proto_id, this);
+        if (njs_slow_path(ctx == NULL)) {
+            njs_type_error(vm, "\"this\" is not a hmac object");
+            return NJS_ERROR;
+        }
+
         if (njs_slow_path(ctx->alg == NULL)) {
             goto exception;
         }
 
+        dgst = NULL;
+    }
+
+    enc = njs_crypto_encoding(vm, njs_arg(args, nargs, 1));
+    if (njs_slow_path(enc == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (!hmac) {
+        alg = dgst->alg;
+        alg->final(digest, &dgst->u);
+        dgst->alg = NULL;
+
+    } else {
         alg = ctx->alg;
         alg->final(hash1, &ctx->u);
 
@@ -307,103 +462,6 @@ exception:
 }
 
 
-static const njs_object_prop_t  njs_hash_prototype_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("Hash"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
-        .value = njs_string("Hash"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("update"),
-        .value = njs_native_function2(njs_hash_prototype_update, 0,
-                                      NJS_DATA_TAG_CRYPTO_HASH),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("digest"),
-        .value = njs_native_function2(njs_hash_prototype_digest, 0,
-                                      NJS_DATA_TAG_CRYPTO_HASH),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("constructor"),
-        .value = njs_prop_handler(njs_object_prototype_create_constructor),
-        .writable = 1,
-        .configurable = 1,
-    },
-};
-
-
-const njs_object_init_t  njs_hash_prototype_init = {
-    njs_hash_prototype_properties,
-    njs_nitems(njs_hash_prototype_properties),
-};
-
-
-static njs_int_t
-njs_hash_constructor(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
-{
-    return njs_crypto_create_hash(vm, args, nargs, unused);
-}
-
-
-static const njs_object_prop_t  njs_hash_constructor_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("Hash"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("length"),
-        .value = njs_value(NJS_NUMBER, 1, 2.0),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("prototype"),
-        .value = njs_prop_handler(njs_object_prototype_create),
-    },
-};
-
-
-const njs_object_init_t  njs_hash_constructor_init = {
-    njs_hash_constructor_properties,
-    njs_nitems(njs_hash_constructor_properties),
-};
-
-
-const njs_object_type_init_t  njs_hash_type_init = {
-    .constructor = njs_native_ctor(njs_hash_constructor, 2, 0),
-    .constructor_props = &njs_hash_constructor_init,
-    .prototype_props = &njs_hash_prototype_init,
-    .prototype_value = { .object_value = { .value = njs_value(NJS_DATA, 0, 0.0),
-                                           .object = { .type = NJS_OBJECT } } },
-};
-
-
 static njs_int_t
 njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
@@ -415,7 +473,6 @@ njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_typed_array_t   *array;
     const njs_value_t   *value;
     njs_array_buffer_t  *buffer;
-    njs_object_value_t  *hmac;
     u_char              digest[32], key_buf[64];
 
     alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
@@ -483,155 +540,11 @@ njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     alg->init(&ctx->u);
     alg->update(&ctx->u, key_buf, 64);
 
-    hmac = njs_object_value_alloc(vm, NJS_OBJ_TYPE_CRYPTO_HMAC, 0, NULL);
-    if (njs_slow_path(hmac == NULL)) {
-        return NJS_ERROR;
-    }
-
-    njs_set_data(&hmac->value, ctx, NJS_DATA_TAG_CRYPTO_HMAC);
-    njs_set_object_value(&vm->retval, hmac);
-
-    return NJS_OK;
-}
-
-
-static const njs_object_prop_t  njs_hmac_prototype_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("Hmac"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
-        .value = njs_string("Hmac"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("update"),
-        .value = njs_native_function2(njs_hash_prototype_update, 0,
-                                      NJS_DATA_TAG_CRYPTO_HMAC),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("digest"),
-        .value = njs_native_function2(njs_hash_prototype_digest, 0,
-                                      NJS_DATA_TAG_CRYPTO_HMAC),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("constructor"),
-        .value = njs_prop_handler(njs_object_prototype_create_constructor),
-        .writable = 1,
-        .configurable = 1,
-    },
-};
-
-
-const njs_object_init_t  njs_hmac_prototype_init = {
-    njs_hmac_prototype_properties,
-    njs_nitems(njs_hmac_prototype_properties),
-};
-
-
-static njs_int_t
-njs_hmac_constructor(njs_vm_t *vm, njs_value_t *args,
-    njs_uint_t nargs, njs_index_t unused)
-{
-    return njs_crypto_create_hmac(vm, args, nargs, unused);
+    return njs_vm_external_create(vm, &vm->retval, njs_crypto_hmac_proto_id,
+                                  ctx, 0);
 }
 
 
-static const njs_object_prop_t  njs_hmac_constructor_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("Hmac"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("length"),
-        .value = njs_value(NJS_NUMBER, 1, 3.0),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("prototype"),
-        .value = njs_prop_handler(njs_object_prototype_create),
-    },
-};
-
-
-const njs_object_init_t  njs_hmac_constructor_init = {
-    njs_hmac_constructor_properties,
-    njs_nitems(njs_hmac_constructor_properties),
-};
-
-
-static const njs_object_prop_t  njs_crypto_object_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("crypto"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("sandbox"),
-        .value = njs_value(NJS_BOOLEAN, 1, 1.0),
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("createHash"),
-        .value = njs_native_function(njs_crypto_create_hash, 0),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("createHmac"),
-        .value = njs_native_function(njs_crypto_create_hmac, 0),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-};
-
-
-const njs_object_init_t  njs_crypto_object_init = {
-    njs_crypto_object_properties,
-    njs_nitems(njs_crypto_object_properties),
-};
-
-
-const njs_object_type_init_t  njs_hmac_type_init = {
-    .constructor = njs_native_ctor(njs_hmac_constructor, 3, 0),
-    .constructor_props = &njs_hmac_constructor_init,
-    .prototype_props = &njs_hmac_prototype_init,
-    .prototype_value = { .object_value = { .value = njs_value(NJS_DATA, 0, 0.0),
-                                           .object = { .type = NJS_OBJECT } } },
-};
-
-
 static njs_hash_alg_t *
 njs_crypto_algorithm(njs_vm_t *vm, const njs_value_t *value)
 {
@@ -691,3 +604,47 @@ njs_buffer_digest(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src)
 {
     return njs_buffer_new(vm, value, src->start, src->length);
 }
+
+
+static njs_int_t
+njs_crypto_init(njs_vm_t *vm)
+{
+    njs_int_t           ret, proto_id;
+    njs_mod_t           *module;
+    njs_opaque_value_t  value;
+
+    njs_crypto_hash_proto_id =
+                     njs_vm_external_prototype(vm, njs_ext_crypto_hash,
+                                               njs_nitems(njs_ext_crypto_hash));
+    if (njs_slow_path(njs_crypto_hash_proto_id < 0)) {
+        return NJS_ERROR;
+    }
+
+    njs_crypto_hmac_proto_id =
+                     njs_vm_external_prototype(vm, njs_ext_crypto_hmac,
+                                               njs_nitems(njs_ext_crypto_hmac));
+    if (njs_slow_path(njs_crypto_hmac_proto_id < 0)) {
+        return NJS_ERROR;
+    }
+
+    proto_id = njs_vm_external_prototype(vm, njs_ext_crypto_crypto,
+                                         njs_nitems(njs_ext_crypto_crypto));
+    if (njs_slow_path(proto_id < 0)) {
+        return NJS_ERROR;
+    }
+
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    module = njs_module_add(vm, &njs_str_value("crypto"), 1);
+    if (njs_slow_path(module == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_value_assign(&module->value, &value);
+    module->function.native = 1;
+
+    return NJS_OK;
+}
similarity index 77%
rename from src/njs_fs.c
rename to external/njs_fs.c
index 129978f88d4b30377b8605095bdd3b8352012000..e76ac1549e6ffc0896adcdfe7cf80688fd2843d9 100644 (file)
@@ -36,6 +36,9 @@
 #define njs_fs_magic(calltype, mode)                                         \
     (((mode) << 2) | calltype)
 
+#define njs_fs_magic2(field, type)                                           \
+    (((type) << 4) | field)
+
 
 typedef enum {
     NJS_FS_DIRECT,
@@ -126,6 +129,46 @@ typedef njs_int_t (*njs_file_tree_walk_cb_t)(const char *, const struct stat *,
      njs_ftw_type_t);
 
 
+static njs_int_t njs_fs_access(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_read(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_readdir(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_rename(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_stat(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_symlink(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_unlink(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+static njs_int_t njs_fs_write(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t calltype);
+
+static njs_int_t njs_fs_constants(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *unused, njs_value_t *retval);
+static njs_int_t njs_fs_promises(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *unused, njs_value_t *retval);
+
+static njs_int_t njs_fs_dirent_constructor(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
+static njs_int_t njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t testtype);
+
+static njs_int_t njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t testtype);
+static njs_int_t njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop,
+    njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+static njs_int_t njs_fs_stats_create(njs_vm_t *vm, struct stat *st,
+    njs_value_t *retval);
+
 static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data);
 
 static njs_int_t njs_fs_error(njs_vm_t *vm, const char *syscall,
@@ -153,8 +196,16 @@ static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
 static njs_int_t njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name,
     njs_value_t *type, njs_value_t *retval);
 
-static njs_int_t njs_fs_stats_create(njs_vm_t *vm, struct stat *st,
-    njs_value_t *retval);
+
+static njs_int_t njs_fs_init(njs_vm_t *vm);
+
+
+static const njs_value_t  string_flag = njs_string("flag");
+static const njs_value_t  string_mode = njs_string("mode");
+static const njs_value_t  string_buffer = njs_string("buffer");
+static const njs_value_t  string_encoding = njs_string("encoding");
+static const njs_value_t  string_recursive = njs_string("recursive");
+
 
 static njs_fs_entry_t njs_flags_table[] = {
     { njs_str("a"),   O_APPEND | O_CREAT | O_WRONLY },
@@ -174,162 +225,714 @@ static njs_fs_entry_t njs_flags_table[] = {
 };
 
 
-static const njs_value_t  string_flag = njs_string("flag");
-static const njs_value_t  string_mode = njs_string("mode");
-static const njs_value_t  string_buffer = njs_string("buffer");
-static const njs_value_t  string_encoding = njs_string("encoding");
-static const njs_value_t  string_recursive = njs_string("recursive");
+static njs_external_t  njs_ext_fs[] = {
 
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("access"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_access,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-static njs_int_t
-njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t calltype)
-{
-    int                          fd, flags;
-    njs_str_t                    data;
-    njs_int_t                    ret;
-    const char                   *path;
-    njs_value_t                  flag, encode, retval, *callback, *options;
-    struct stat                  sb;
-    const njs_buffer_encoding_t  *encoding;
-    char                         path_buf[NJS_MAX_PATH + 1];
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("accessSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_access,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
-    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
-    if (njs_slow_path(path == NULL)) {
-        return NJS_ERROR;
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("appendFile"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_write,
+            .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_APPEND),
+        }
+    },
 
-    callback = NULL;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("appendFileSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_write,
+            .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_APPEND),
+        }
+    },
 
-    options = njs_arg(args, nargs, 2);
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("constants"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_constants,
+        }
+    },
 
-    if (calltype == NJS_FS_CALLBACK) {
-        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
-        if (!njs_is_function(callback)) {
-            njs_type_error(vm, "\"callback\" must be a function");
-            return NJS_ERROR;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("Dirent"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_constructor,
+            .ctor = 1,
         }
+    },
 
-        if (options == callback) {
-            options = njs_value_arg(&njs_value_undefined);
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("lstat"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stat,
+            .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_LSTAT),
         }
-    }
+    },
 
-    njs_set_undefined(&flag);
-    njs_set_undefined(&encode);
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("lstatSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stat,
+            .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_LSTAT),
+        }
+    },
 
-    switch (options->type) {
-    case NJS_STRING:
-        encode = *options;
-        break;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("mkdir"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_mkdir,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-    case NJS_UNDEFINED:
-        break;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("mkdirSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_mkdir,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
-    default:
-        if (!njs_is_object(options)) {
-            njs_type_error(vm, "Unknown options type: \"%s\" "
-                           "(a string or object required)",
-                           njs_type_string(options->type));
-            return NJS_ERROR;
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("promises"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_promises,
         }
+    },
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_flag),
-                                 &flag);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("readdir"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_readdir,
+            .magic8 = NJS_FS_CALLBACK,
         }
+    },
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encode);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("readdirSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_readdir,
+            .magic8 = NJS_FS_DIRECT,
         }
-    }
+    },
 
-    flags = njs_fs_flags(vm, &flag, O_RDONLY);
-    if (njs_slow_path(flags == -1)) {
-        return NJS_ERROR;
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("readFile"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_read,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-    encoding = NULL;
-    if (njs_is_defined(&encode)) {
-        encoding = njs_buffer_encoding(vm, &encode);
-        if (njs_slow_path(encoding == NULL)) {
-            return NJS_ERROR;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("readFileSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_read,
+            .magic8 = NJS_FS_DIRECT,
         }
-    }
+    },
 
-    fd = open(path, flags);
-    if (njs_slow_path(fd < 0)) {
-        ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
-        goto done;
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("realpath"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_realpath,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-    ret = fstat(fd, &sb);
-    if (njs_slow_path(ret == -1)) {
-        ret = njs_fs_error(vm, "stat", strerror(errno), path, errno, &retval);
-        goto done;
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("realpathSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_realpath,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
-    if (njs_slow_path(!S_ISREG(sb.st_mode))) {
-        ret = njs_fs_error(vm, "stat", "File is not regular", path, 0, &retval);
-        goto done;
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("rename"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_rename,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-    data.start = NULL;
-    data.length = sb.st_size;
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("renameSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_rename,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
-    ret = njs_fs_fd_read(vm, fd, &data);
-    if (njs_slow_path(ret != NJS_OK)) {
-        if (ret == NJS_DECLINED) {
-            ret = njs_fs_error(vm, "read", strerror(errno), path, errno,
-                               &retval);
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("rmdir"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_rmdir,
+            .magic8 = NJS_FS_CALLBACK,
         }
+    },
 
-        goto done;
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("rmdirSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_rmdir,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
-    if (encoding == NULL) {
-        ret = njs_buffer_set(vm, &retval, data.start, data.length);
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("stat"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stat,
+            .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_STAT),
+        }
+    },
 
-    } else {
-        ret = encoding->encode(vm, &retval, &data);
-        njs_mp_free(vm->mem_pool, data.start);
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("statSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stat,
+            .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_STAT),
+        }
+    },
 
-done:
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("symlink"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_symlink,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-    if (fd != -1) {
-        (void) close(fd);
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("symlinkSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_symlink,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
-    if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 2);
-    }
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("unlink"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_unlink,
+            .magic8 = NJS_FS_CALLBACK,
+        }
+    },
 
-    return NJS_ERROR;
-}
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("unlinkSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_unlink,
+            .magic8 = NJS_FS_DIRECT,
+        }
+    },
 
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("writeFile"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_write,
+            .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_TRUNC),
+        }
+    },
 
-static njs_int_t
-njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t magic)
-{
-    int                          fd, flags;
-    u_char                       *p, *end;
-    mode_t                       md;
-    ssize_t                      n;
-    njs_str_t                    content;
-    njs_int_t                    ret;
-    const char                   *path;
-    njs_value_t                  flag, mode, encode, retval, *data, *callback,
-                                 *options;
-    njs_typed_array_t            *array;
-    njs_fs_calltype_t            calltype;
-    njs_array_buffer_t           *buffer;
-    const njs_buffer_encoding_t  *encoding;
-    char                         path_buf[NJS_MAX_PATH + 1];
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("writeFileSync"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_write,
+            .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_TRUNC),
+        }
+    },
+
+};
+
+
+static njs_external_t  njs_ext_dirent[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Dirent",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("constructor"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_constructor,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isBlockDevice"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_BLK,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isCharacterDevice"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_CHR,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isDirectory"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_DIR,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isFIFO"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_FIFO,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isFile"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_REG,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isSocket"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_SOCK,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isSymbolicLink"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_dirent_test,
+            .magic8 = DT_LNK,
+        }
+    },
+};
+
+
+static njs_external_t  njs_ext_stats[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL,
+        .name.symbol = NJS_SYMBOL_TO_STRING_TAG,
+        .u.property = {
+            .value = "Stats",
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("atime"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_ATIME, NJS_DATE),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("atimeMs"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_ATIME, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("birthtime"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_BIRTHTIME, NJS_DATE),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("birthtimeMs"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_BIRTHTIME, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("ctime"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_CTIME, NJS_DATE),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("ctimeMs"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_CTIME, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("blksize"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_BLKSIZE, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("blocks"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_BLOCKS, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("dev"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_DEV, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("gid"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_GID, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("ino"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_INO, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("mode"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_MODE, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("mtime"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_MTIME, NJS_DATE),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("mtimeMs"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_MTIME, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("nlink"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_NLINK, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("rdev"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_RDEV, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("size"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_SIZE, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("uid"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_fs_stats_prop,
+            .magic32 = njs_fs_magic2(NJS_FS_STAT_UID, NJS_NUMBER),
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isBlockDevice"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_BLK,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isCharacterDevice"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_CHR,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isDirectory"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_DIR,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isFIFO"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_FIFO,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isFile"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_REG,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isSocket"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_SOCK,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("isSymbolicLink"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_fs_stats_test,
+            .magic8 = DT_LNK,
+        }
+    },
+
+};
+
+
+static njs_int_t    njs_fs_stats_proto_id;
+static njs_int_t    njs_fs_dirent_proto_id;
+
+
+njs_module_t  njs_fs_module = {
+    .name = njs_str("fs"),
+    .init = njs_fs_init,
+};
+
+
+static njs_int_t
+njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t calltype)
+{
+    int          md;
+    njs_int_t    ret;
+    const char  *path;
+    njs_value_t  retval, *callback, *mode;
+    char         path_buf[NJS_MAX_PATH + 1];
 
     path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
     if (njs_slow_path(path == NULL)) {
@@ -337,28 +940,84 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     callback = NULL;
-    calltype = magic & 3;
-    options = njs_arg(args, nargs, 3);
+    mode = njs_arg(args, nargs, 2);
 
     if (calltype == NJS_FS_CALLBACK) {
-        callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
         if (!njs_is_function(callback)) {
             njs_type_error(vm, "\"callback\" must be a function");
             return NJS_ERROR;
         }
 
+        if (mode == callback) {
+            mode = njs_value_arg(&njs_value_undefined);
+        }
+    }
+
+    switch (mode->type) {
+    case NJS_UNDEFINED:
+        md = F_OK;
+        break;
+
+    case NJS_NUMBER:
+        md = njs_number(mode);
+        break;
+
+    default:
+        njs_type_error(vm, "\"mode\" must be a number");
+        return NJS_ERROR;
+    }
+
+    njs_set_undefined(&retval);
+
+    ret = access(path, md);
+    if (njs_slow_path(ret != 0)) {
+        ret = njs_fs_error(vm, "access", strerror(errno), path, errno, &retval);
+    }
+
+    if (ret == NJS_OK) {
+        return njs_fs_result(vm, &retval, calltype, callback, 1);
+    }
+
+    return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t calltype)
+{
+    char         *path;
+    mode_t       md;
+    njs_int_t    ret;
+    njs_value_t  mode, recursive, retval, *callback, *options;
+    char         path_buf[NJS_MAX_PATH + 1];
+
+    path = (char *) njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
+    if (njs_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    callback = NULL;
+    options = njs_arg(args, nargs, 2);
+
+    if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
+            return NJS_ERROR;
+        }
         if (options == callback) {
             options = njs_value_arg(&njs_value_undefined);
         }
     }
 
-    njs_set_undefined(&flag);
     njs_set_undefined(&mode);
-    njs_set_undefined(&encode);
+    njs_set_false(&recursive);
 
     switch (options->type) {
-    case NJS_STRING:
-        encode = *options;
+    case NJS_NUMBER:
+        mode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -367,158 +1026,164 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     default:
         if (!njs_is_object(options)) {
             njs_type_error(vm, "Unknown options type: \"%s\" "
-                           "(a string or object required)",
+                           "(a number or object required)",
                            njs_type_string(options->type));
             return NJS_ERROR;
         }
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_flag),
-                                 &flag);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
-        }
-
         ret = njs_value_property(vm, options, njs_value_arg(&string_mode),
                                  &mode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encode);
+        ret = njs_value_property(vm, options, njs_value_arg(&string_recursive),
+                                 &recursive);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
     }
 
-    data = njs_arg(args, nargs, 2);
+    md = njs_fs_mode(vm, &mode, 0777);
+    if (njs_slow_path(md == (mode_t) -1)) {
+        return NJS_ERROR;
+    }
 
-    switch (data->type) {
-    case NJS_TYPED_ARRAY:
-    case NJS_DATA_VIEW:
-        array = njs_typed_array(data);
-        buffer = array->buffer;
-        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
-            njs_type_error(vm, "detached buffer");
+    ret = njs_fs_make_path(vm, path, md, njs_is_true(&recursive), &retval);
+
+    if (ret == NJS_OK) {
+        return njs_fs_result(vm, &retval, calltype, callback, 1);
+    }
+
+    return NJS_ERROR;
+}
+
+
+static njs_int_t
+njs_fs_read(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t calltype)
+{
+    int                          fd, flags;
+    njs_str_t                    data;
+    njs_int_t                    ret;
+    const char                   *path;
+    njs_value_t                  flag, encode, retval, *callback, *options;
+    struct stat                  sb;
+    const njs_buffer_encoding_t  *encoding;
+    char                         path_buf[NJS_MAX_PATH + 1];
+
+    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
+    if (njs_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    callback = NULL;
+
+    options = njs_arg(args, nargs, 2);
+
+    if (calltype == NJS_FS_CALLBACK) {
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
             return NJS_ERROR;
         }
 
-        content.start = &buffer->u.u8[array->offset];
-        content.length = array->byte_length;
-        break;
+        if (options == callback) {
+            options = njs_value_arg(&njs_value_undefined);
+        }
+    }
 
+    njs_set_undefined(&flag);
+    njs_set_undefined(&encode);
+
+    switch (options->type) {
     case NJS_STRING:
+        encode = *options;
+        break;
+
+    case NJS_UNDEFINED:
+        break;
+
     default:
-        encoding = njs_buffer_encoding(vm, &encode);
-        if (njs_slow_path(encoding == NULL)) {
+        if (!njs_is_object(options)) {
+            njs_type_error(vm, "Unknown options type: \"%s\" "
+                           "(a string or object required)",
+                           njs_type_string(options->type));
             return NJS_ERROR;
         }
 
-        ret = njs_value_to_string(vm, &retval, data);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_ERROR;
+        ret = njs_value_property(vm, options, njs_value_arg(&string_flag),
+                                 &flag);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
         }
 
-        ret = njs_buffer_decode_string(vm, &retval, &retval, encoding);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_ERROR;
+        ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
+                                 &encode);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
         }
-
-        njs_string_get(&retval, &content);
-        break;
     }
 
-    flags = njs_fs_flags(vm, &flag, O_CREAT | O_WRONLY);
+    flags = njs_fs_flags(vm, &flag, O_RDONLY);
     if (njs_slow_path(flags == -1)) {
         return NJS_ERROR;
     }
 
-    flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC;
-
-    md = njs_fs_mode(vm, &mode, 0666);
-    if (njs_slow_path(md == (mode_t) -1)) {
-        return NJS_ERROR;
+    encoding = NULL;
+    if (njs_is_defined(&encode)) {
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
     }
 
-    fd = open(path, flags, md);
+    fd = open(path, flags);
     if (njs_slow_path(fd < 0)) {
         ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
         goto done;
     }
 
-    p = content.start;
-    end = p + content.length;
-
-    while (p < end) {
-        n = write(fd, p, end - p);
-        if (njs_slow_path(n == -1)) {
-            if (errno == EINTR) {
-                continue;
-            }
-
-            ret = njs_fs_error(vm, "write", strerror(errno), path, errno,
-                               &retval);
-            goto done;
-        }
-
-        p += n;
-    }
-
-    ret = NJS_OK;
-    njs_set_undefined(&retval);
-
-done:
-
-    if (fd != -1) {
-        (void) close(fd);
+    ret = fstat(fd, &sb);
+    if (njs_slow_path(ret == -1)) {
+        ret = njs_fs_error(vm, "stat", strerror(errno), path, errno, &retval);
+        goto done;
     }
 
-    if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 1);
+    if (njs_slow_path(!S_ISREG(sb.st_mode))) {
+        ret = njs_fs_error(vm, "stat", "File is not regular", path, 0, &retval);
+        goto done;
     }
 
-    return NJS_ERROR;
-}
-
-
-static njs_int_t
-njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t calltype)
-{
-    njs_int_t    ret;
-    const char   *path, *newpath;
-    njs_value_t  retval, *callback;
-    char         path_buf[NJS_MAX_PATH + 1], newpath_buf[NJS_MAX_PATH + 1];
-
-    callback = NULL;
+    data.start = NULL;
+    data.length = sb.st_size;
 
-    if (calltype == NJS_FS_CALLBACK) {
-        callback = njs_arg(args, nargs, 3);
-        if (!njs_is_function(callback)) {
-            njs_type_error(vm, "\"callback\" must be a function");
-            return NJS_ERROR;
+    ret = njs_fs_fd_read(vm, fd, &data);
+    if (njs_slow_path(ret != NJS_OK)) {
+        if (ret == NJS_DECLINED) {
+            ret = njs_fs_error(vm, "read", strerror(errno), path, errno,
+                               &retval);
         }
-    }
 
-    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "oldPath");
-    if (njs_slow_path(path == NULL)) {
-        return NJS_ERROR;
+        goto done;
     }
 
-    newpath = njs_fs_path(vm, newpath_buf, njs_arg(args, nargs, 2), "newPath");
-    if (njs_slow_path(newpath == NULL)) {
-        return NJS_ERROR;
+    if (encoding == NULL) {
+        ret = njs_buffer_set(vm, &retval, data.start, data.length);
+
+    } else {
+        ret = encoding->encode(vm, &retval, &data);
+        njs_mp_free(vm->mem_pool, data.start);
     }
 
-    njs_set_undefined(&retval);
+done:
 
-    ret = rename(path, newpath);
-    if (njs_slow_path(ret != 0)) {
-        ret = njs_fs_error(vm, "rename", strerror(errno), NULL, errno, &retval);
+    if (fd != -1) {
+        (void) close(fd);
     }
 
     if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 1);
+        return njs_fs_result(vm, &retval, calltype, callback, 2);
     }
 
     return NJS_ERROR;
@@ -526,14 +1191,21 @@ njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static njs_int_t
-njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t calltype)
 {
-    int          md;
-    njs_int_t    ret;
-    const char  *path;
-    njs_value_t  retval, *callback, *mode;
-    char         path_buf[NJS_MAX_PATH + 1];
+    DIR                          *dir;
+    njs_str_t                    s;
+    njs_int_t                    ret;
+    const char                   *path;
+    njs_value_t                  encode, types, ename, etype, retval,
+                                 *callback, *options, *value;
+    njs_array_t                  *results;
+    struct dirent                *entry;
+    const njs_buffer_encoding_t  *encoding;
+    char                         path_buf[NJS_MAX_PATH + 1];
+
+    static const njs_value_t  string_types = njs_string("withFileTypes");
 
     path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
     if (njs_slow_path(path == NULL)) {
@@ -541,137 +1213,133 @@ njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     callback = NULL;
-    mode = njs_arg(args, nargs, 2);
+    options = njs_arg(args, nargs, 2);
 
-    if (calltype == NJS_FS_CALLBACK) {
+    if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
         callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
         if (!njs_is_function(callback)) {
             njs_type_error(vm, "\"callback\" must be a function");
             return NJS_ERROR;
         }
-
-        if (mode == callback) {
-            mode = njs_value_arg(&njs_value_undefined);
+        if (options == callback) {
+            options = njs_value_arg(&njs_value_undefined);
         }
     }
 
-    switch (mode->type) {
-    case NJS_UNDEFINED:
-        md = F_OK;
+    njs_set_false(&types);
+    njs_set_undefined(&encode);
+
+    switch (options->type) {
+    case NJS_STRING:
+        encode = *options;
         break;
 
-    case NJS_NUMBER:
-        md = njs_number(mode);
+    case NJS_UNDEFINED:
         break;
 
     default:
-        njs_type_error(vm, "\"mode\" must be a number");
-        return NJS_ERROR;
-    }
+        if (!njs_is_object(options)) {
+            njs_type_error(vm, "Unknown options type: \"%s\" "
+                           "(a string or object required)",
+                           njs_type_string(options->type));
+            return NJS_ERROR;
+        }
 
-    njs_set_undefined(&retval);
+        ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
+                                 &encode);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
 
-    ret = access(path, md);
-    if (njs_slow_path(ret != 0)) {
-        ret = njs_fs_error(vm, "access", strerror(errno), path, errno, &retval);
+        ret = njs_value_property(vm, options, njs_value_arg(&string_types),
+                                 &types);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
     }
 
-    if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 1);
+    encoding = NULL;
+    if (!njs_is_string(&encode) || !njs_string_eq(&encode, &string_buffer)) {
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
     }
 
-    return NJS_ERROR;
-}
-
-
-static njs_int_t
-njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t calltype)
-{
-    njs_int_t    ret;
-    const char  *target, *path;
-    njs_value_t  retval, *callback, *type;
-    char         target_buf[NJS_MAX_PATH + 1], path_buf[NJS_MAX_PATH + 1];
-
-    target = njs_fs_path(vm, target_buf, njs_arg(args, nargs, 1), "target");
-    if (njs_slow_path(target == NULL)) {
+    results = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
+    if (njs_slow_path(results == NULL)) {
         return NJS_ERROR;
     }
 
-    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 2), "path");
-    if (njs_slow_path(path == NULL)) {
-        return NJS_ERROR;
+    njs_set_array(&retval, results);
+
+    dir = opendir(path);
+    if (njs_slow_path(dir == NULL)) {
+        ret = njs_fs_error(vm, "opendir", strerror(errno), path, errno,
+                           &retval);
+        goto done;
     }
 
-    callback = NULL;
-    type = njs_arg(args, nargs, 3);
+    ret = NJS_OK;
 
-    if (calltype == NJS_FS_CALLBACK) {
-        callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
-        if (!njs_is_function(callback)) {
-            njs_type_error(vm, "\"callback\" must be a function");
-            return NJS_ERROR;
-        }
+    for ( ;; ) {
+        errno = 0;
+        entry = readdir(dir);
+        if (njs_slow_path(entry == NULL)) {
+            if (errno != 0) {
+                ret = njs_fs_error(vm, "readdir", strerror(errno), path, errno,
+                                   &retval);
+            }
 
-        if (type == callback) {
-            type = njs_value_arg(&njs_value_undefined);
+            goto done;
         }
-    }
-
-    if (njs_slow_path(!njs_is_undefined(type) && !njs_is_string(type))) {
-        njs_type_error(vm, "\"type\" must be a string");
-        return NJS_ERROR;
-    }
 
-    njs_set_undefined(&retval);
+        s.start = (u_char *) entry->d_name;
+        s.length = njs_strlen(s.start);
 
-    ret = symlink(target, path);
-    if (njs_slow_path(ret != 0)) {
-        ret = njs_fs_error(vm, "symlink", strerror(errno), path, errno,
-                           &retval);
-    }
+        if ((s.length == 1 && s.start[0] == '.')
+            || (s.length == 2 && (s.start[0] == '.' && s.start[1] == '.')))
+        {
+            continue;
+        }
 
-    if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 1);
-    }
+        value = njs_array_push(vm, results);
+        if (njs_slow_path(value == NULL)) {
+            goto done;
+        }
 
-    return NJS_ERROR;
-}
+        if (encoding == NULL) {
+            ret = njs_buffer_set(vm, &ename, s.start, s.length);
 
+        } else {
+            ret = encoding->encode(vm, &ename, &s);
+        }
 
-static njs_int_t
-njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t calltype)
-{
-    njs_int_t    ret;
-    const char   *path;
-    njs_value_t  retval, *callback;
-    char         path_buf[NJS_MAX_PATH + 1];
+        if (njs_slow_path(ret != NJS_OK)) {
+            goto done;
+        }
 
-    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
-    if (njs_slow_path(path == NULL)) {
-        return NJS_ERROR;
-    }
+        if (njs_fast_path(!njs_is_true(&types))) {
+            *value = ename;
+            continue;
+        }
 
-    callback = NULL;
+        njs_set_number(&etype, njs_dentry_type(entry));
 
-    if (calltype == NJS_FS_CALLBACK) {
-        callback = njs_arg(args, nargs, 2);
-        if (!njs_is_function(callback)) {
-            njs_type_error(vm, "\"callback\" must be a function");
-            return NJS_ERROR;
+        ret = njs_fs_dirent_create(vm, &ename, &etype, value);
+        if (njs_slow_path(ret != NJS_OK)) {
+            goto done;
         }
     }
 
-    njs_set_undefined(&retval);
+done:
 
-    ret = unlink(path);
-    if (njs_slow_path(ret != 0)) {
-        ret = njs_fs_error(vm, "unlink", strerror(errno), path, errno, &retval);
+    if (dir != NULL) {
+        (void) closedir(dir);
     }
 
     if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 1);
+        return njs_fs_result(vm, &retval, calltype, callback, 2);
     }
 
     return NJS_ERROR;
@@ -769,73 +1437,41 @@ done:
 }
 
 
-static njs_int_t
-njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t calltype)
-{
-    char         *path;
-    mode_t       md;
-    njs_int_t    ret;
-    njs_value_t  mode, recursive, retval, *callback, *options;
-    char         path_buf[NJS_MAX_PATH + 1];
-
-    path = (char *) njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
-    if (njs_slow_path(path == NULL)) {
-        return NJS_ERROR;
-    }
-
-    callback = NULL;
-    options = njs_arg(args, nargs, 2);
-
-    if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
-        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
-        if (!njs_is_function(callback)) {
-            njs_type_error(vm, "\"callback\" must be a function");
-            return NJS_ERROR;
-        }
-        if (options == callback) {
-            options = njs_value_arg(&njs_value_undefined);
-        }
-    }
-
-    njs_set_undefined(&mode);
-    njs_set_false(&recursive);
-
-    switch (options->type) {
-    case NJS_NUMBER:
-        mode = *options;
-        break;
+static njs_int_t
+njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t calltype)
+{
+    njs_int_t    ret;
+    const char   *path, *newpath;
+    njs_value_t  retval, *callback;
+    char         path_buf[NJS_MAX_PATH + 1], newpath_buf[NJS_MAX_PATH + 1];
 
-    case NJS_UNDEFINED:
-        break;
+    callback = NULL;
 
-    default:
-        if (!njs_is_object(options)) {
-            njs_type_error(vm, "Unknown options type: \"%s\" "
-                           "(a number or object required)",
-                           njs_type_string(options->type));
+    if (calltype == NJS_FS_CALLBACK) {
+        callback = njs_arg(args, nargs, 3);
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
             return NJS_ERROR;
         }
+    }
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_mode),
-                                 &mode);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
-        }
-
-        ret = njs_value_property(vm, options, njs_value_arg(&string_recursive),
-                                 &recursive);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
-        }
+    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "oldPath");
+    if (njs_slow_path(path == NULL)) {
+        return NJS_ERROR;
     }
 
-    md = njs_fs_mode(vm, &mode, 0777);
-    if (njs_slow_path(md == (mode_t) -1)) {
+    newpath = njs_fs_path(vm, newpath_buf, njs_arg(args, nargs, 2), "newPath");
+    if (njs_slow_path(newpath == NULL)) {
         return NJS_ERROR;
     }
 
-    ret = njs_fs_make_path(vm, path, md, njs_is_true(&recursive), &retval);
+    njs_set_undefined(&retval);
+
+    ret = rename(path, newpath);
+    if (njs_slow_path(ret != 0)) {
+        ret = njs_fs_error(vm, "rename", strerror(errno), NULL, errno, &retval);
+    }
 
     if (ret == NJS_OK) {
         return njs_fs_result(vm, &retval, calltype, callback, 1);
@@ -905,21 +1541,19 @@ njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static njs_int_t
-njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t calltype)
+njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t magic)
 {
-    DIR                          *dir;
-    njs_str_t                    s;
-    njs_int_t                    ret;
-    const char                   *path;
-    njs_value_t                  encode, types, ename, etype, retval,
-                                 *callback, *options, *value;
-    njs_array_t                  *results;
-    struct dirent                *entry;
-    const njs_buffer_encoding_t  *encoding;
-    char                         path_buf[NJS_MAX_PATH + 1];
+    njs_int_t          ret;
+    njs_bool_t         throw;
+    struct stat        sb;
+    const char         *path;
+    njs_value_t        retval, *callback, *options;
+    njs_fs_calltype_t  calltype;
+    char               path_buf[NJS_MAX_PATH + 1];
 
-    static const njs_value_t  string_types = njs_string("withFileTypes");
+    static const njs_value_t  string_bigint = njs_string("bigint");
+    static const njs_value_t  string_throw = njs_string("throwIfNoEntry");
 
     path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
     if (njs_slow_path(path == NULL)) {
@@ -927,6 +1561,7 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     callback = NULL;
+    calltype = magic & 3;
     options = njs_arg(args, nargs, 2);
 
     if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
@@ -940,120 +1575,155 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    njs_set_false(&types);
-    njs_set_undefined(&encode);
+    throw = 1;
 
     switch (options->type) {
-    case NJS_STRING:
-        encode = *options;
-        break;
-
     case NJS_UNDEFINED:
         break;
 
     default:
         if (!njs_is_object(options)) {
             njs_type_error(vm, "Unknown options type: \"%s\" "
-                           "(a string or object required)",
+                           "(an object required)",
                            njs_type_string(options->type));
             return NJS_ERROR;
         }
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encode);
+        ret = njs_value_property(vm, options, njs_value_arg(&string_bigint),
+                                 &retval);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_types),
-                                 &types);
-        if (njs_slow_path(ret == NJS_ERROR)) {
-            return ret;
+        if (njs_bool(&retval)) {
+            njs_type_error(vm, "\"bigint\" is not supported");
+            return NJS_ERROR;
+        }
+
+        if (calltype == NJS_FS_DIRECT) {
+            ret = njs_value_property(vm, options, njs_value_arg(&string_throw),
+                                     &retval);
+            if (njs_slow_path(ret == NJS_ERROR)) {
+                return ret;
+            }
+
+            throw = njs_bool(&retval);
         }
     }
 
-    encoding = NULL;
-    if (!njs_is_string(&encode) || !njs_string_eq(&encode, &string_buffer)) {
-        encoding = njs_buffer_encoding(vm, &encode);
-        if (njs_slow_path(encoding == NULL)) {
-            return NJS_ERROR;
+    ret = ((magic >> 2) == NJS_FS_STAT) ? stat(path, &sb) : lstat(path, &sb);
+    if (njs_slow_path(ret != 0)) {
+        if (errno != ENOENT || throw) {
+            ret = njs_fs_error(vm,
+                               ((magic >> 2) == NJS_FS_STAT) ? "stat" : "lstat",
+                               strerror(errno), path, errno, &retval);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NJS_ERROR;
+            }
+        } else {
+            njs_set_undefined(&retval);
         }
+
+        return njs_fs_result(vm, &retval, calltype, callback, 2);
     }
 
-    results = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
-    if (njs_slow_path(results == NULL)) {
+    ret = njs_fs_stats_create(vm, &sb, &retval);
+    if (njs_slow_path(ret != NJS_OK)) {
         return NJS_ERROR;
     }
 
-    njs_set_array(&retval, results);
+    return njs_fs_result(vm, &retval, calltype, callback, 2);
+}
 
-    dir = opendir(path);
-    if (njs_slow_path(dir == NULL)) {
-        ret = njs_fs_error(vm, "opendir", strerror(errno), path, errno,
-                           &retval);
-        goto done;
-    }
 
-    ret = NJS_OK;
+static njs_int_t
+njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t calltype)
+{
+    njs_int_t    ret;
+    const char  *target, *path;
+    njs_value_t  retval, *callback, *type;
+    char         target_buf[NJS_MAX_PATH + 1], path_buf[NJS_MAX_PATH + 1];
 
-    for ( ;; ) {
-        errno = 0;
-        entry = readdir(dir);
-        if (njs_slow_path(entry == NULL)) {
-            if (errno != 0) {
-                ret = njs_fs_error(vm, "readdir", strerror(errno), path, errno,
-                                   &retval);
-            }
+    target = njs_fs_path(vm, target_buf, njs_arg(args, nargs, 1), "target");
+    if (njs_slow_path(target == NULL)) {
+        return NJS_ERROR;
+    }
 
-            goto done;
-        }
+    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 2), "path");
+    if (njs_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
 
-        s.start = (u_char *) entry->d_name;
-        s.length = njs_strlen(s.start);
+    callback = NULL;
+    type = njs_arg(args, nargs, 3);
 
-        if ((s.length == 1 && s.start[0] == '.')
-            || (s.length == 2 && (s.start[0] == '.' && s.start[1] == '.')))
-        {
-            continue;
+    if (calltype == NJS_FS_CALLBACK) {
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
+            return NJS_ERROR;
         }
 
-        value = njs_array_push(vm, results);
-        if (njs_slow_path(value == NULL)) {
-            goto done;
+        if (type == callback) {
+            type = njs_value_arg(&njs_value_undefined);
         }
+    }
 
-        if (encoding == NULL) {
-            ret = njs_buffer_set(vm, &ename, s.start, s.length);
+    if (njs_slow_path(!njs_is_undefined(type) && !njs_is_string(type))) {
+        njs_type_error(vm, "\"type\" must be a string");
+        return NJS_ERROR;
+    }
 
-        } else {
-            ret = encoding->encode(vm, &ename, &s);
-        }
+    njs_set_undefined(&retval);
 
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto done;
-        }
+    ret = symlink(target, path);
+    if (njs_slow_path(ret != 0)) {
+        ret = njs_fs_error(vm, "symlink", strerror(errno), path, errno,
+                           &retval);
+    }
 
-        if (njs_fast_path(!njs_is_true(&types))) {
-            *value = ename;
-            continue;
-        }
+    if (ret == NJS_OK) {
+        return njs_fs_result(vm, &retval, calltype, callback, 1);
+    }
 
-        njs_set_number(&etype, njs_dentry_type(entry));
+    return NJS_ERROR;
+}
 
-        ret = njs_fs_dirent_create(vm, &ename, &etype, value);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto done;
+
+static njs_int_t
+njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t calltype)
+{
+    njs_int_t    ret;
+    const char   *path;
+    njs_value_t  retval, *callback;
+    char         path_buf[NJS_MAX_PATH + 1];
+
+    path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
+    if (njs_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    callback = NULL;
+
+    if (calltype == NJS_FS_CALLBACK) {
+        callback = njs_arg(args, nargs, 2);
+        if (!njs_is_function(callback)) {
+            njs_type_error(vm, "\"callback\" must be a function");
+            return NJS_ERROR;
         }
     }
 
-done:
+    njs_set_undefined(&retval);
 
-    if (dir != NULL) {
-        (void) closedir(dir);
+    ret = unlink(path);
+    if (njs_slow_path(ret != 0)) {
+        ret = njs_fs_error(vm, "unlink", strerror(errno), path, errno, &retval);
     }
 
     if (ret == NJS_OK) {
-        return njs_fs_result(vm, &retval, calltype, callback, 2);
+        return njs_fs_result(vm, &retval, calltype, callback, 1);
     }
 
     return NJS_ERROR;
@@ -1061,19 +1731,23 @@ done:
 
 
 static njs_int_t
-njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+njs_fs_write(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t magic)
 {
-    njs_int_t          ret;
-    njs_bool_t         throw;
-    struct stat        sb;
-    const char         *path;
-    njs_value_t        retval, *callback, *options;
-    njs_fs_calltype_t  calltype;
-    char               path_buf[NJS_MAX_PATH + 1];
-
-    static const njs_value_t  string_bigint = njs_string("bigint");
-    static const njs_value_t  string_throw = njs_string("throwIfNoEntry");
+    int                          fd, flags;
+    u_char                       *p, *end;
+    mode_t                       md;
+    ssize_t                      n;
+    njs_str_t                    content;
+    njs_int_t                    ret;
+    const char                   *path;
+    njs_value_t                  flag, mode, encode, retval, *data, *callback,
+                                 *options;
+    njs_typed_array_t            *array;
+    njs_fs_calltype_t            calltype;
+    njs_array_buffer_t           *buffer;
+    const njs_buffer_encoding_t  *encoding;
+    char                         path_buf[NJS_MAX_PATH + 1];
 
     path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path");
     if (njs_slow_path(path == NULL)) {
@@ -1082,77 +1756,146 @@ njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     callback = NULL;
     calltype = magic & 3;
-    options = njs_arg(args, nargs, 2);
+    options = njs_arg(args, nargs, 3);
 
-    if (njs_slow_path(calltype == NJS_FS_CALLBACK)) {
-        callback = njs_arg(args, nargs, njs_min(nargs - 1, 3));
+    if (calltype == NJS_FS_CALLBACK) {
+        callback = njs_arg(args, nargs, njs_min(nargs - 1, 4));
         if (!njs_is_function(callback)) {
             njs_type_error(vm, "\"callback\" must be a function");
             return NJS_ERROR;
         }
+
         if (options == callback) {
             options = njs_value_arg(&njs_value_undefined);
         }
     }
 
-    throw = 1;
+    njs_set_undefined(&flag);
+    njs_set_undefined(&mode);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
+    case NJS_STRING:
+        encode = *options;
+        break;
+
     case NJS_UNDEFINED:
         break;
 
     default:
         if (!njs_is_object(options)) {
             njs_type_error(vm, "Unknown options type: \"%s\" "
-                           "(an object required)",
+                           "(a string or object required)",
                            njs_type_string(options->type));
             return NJS_ERROR;
         }
 
-        ret = njs_value_property(vm, options, njs_value_arg(&string_bigint),
-                                 &retval);
+        ret = njs_value_property(vm, options, njs_value_arg(&string_flag),
+                                 &flag);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
 
-        if (njs_bool(&retval)) {
-            njs_type_error(vm, "\"bigint\" is not supported");
+        ret = njs_value_property(vm, options, njs_value_arg(&string_mode),
+                                 &mode);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
+                                 &encode);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+    }
+
+    data = njs_arg(args, nargs, 2);
+
+    switch (data->type) {
+    case NJS_TYPED_ARRAY:
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(data);
+        buffer = array->buffer;
+        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+            njs_type_error(vm, "detached buffer");
             return NJS_ERROR;
         }
 
-        if (calltype == NJS_FS_DIRECT) {
-            ret = njs_value_property(vm, options, njs_value_arg(&string_throw),
-                                     &retval);
-            if (njs_slow_path(ret == NJS_ERROR)) {
-                return ret;
-            }
+        content.start = &buffer->u.u8[array->offset];
+        content.length = array->byte_length;
+        break;
 
-            throw = njs_bool(&retval);
+    case NJS_STRING:
+    default:
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_value_to_string(vm, &retval, data);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_buffer_decode_string(vm, &retval, &retval, encoding);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
         }
+
+        njs_string_get(&retval, &content);
+        break;
     }
 
-    ret = ((magic >> 2) == NJS_FS_STAT) ? stat(path, &sb) : lstat(path, &sb);
-    if (njs_slow_path(ret != 0)) {
-        if (errno != ENOENT || throw) {
-            ret = njs_fs_error(vm,
-                               ((magic >> 2) == NJS_FS_STAT) ? "stat" : "lstat",
-                               strerror(errno), path, errno, &retval);
-            if (njs_slow_path(ret != NJS_OK)) {
-                return NJS_ERROR;
+    flags = njs_fs_flags(vm, &flag, O_CREAT | O_WRONLY);
+    if (njs_slow_path(flags == -1)) {
+        return NJS_ERROR;
+    }
+
+    flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC;
+
+    md = njs_fs_mode(vm, &mode, 0666);
+    if (njs_slow_path(md == (mode_t) -1)) {
+        return NJS_ERROR;
+    }
+
+    fd = open(path, flags, md);
+    if (njs_slow_path(fd < 0)) {
+        ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
+        goto done;
+    }
+
+    p = content.start;
+    end = p + content.length;
+
+    while (p < end) {
+        n = write(fd, p, end - p);
+        if (njs_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
             }
-        } else {
-            njs_set_undefined(&retval);
+
+            ret = njs_fs_error(vm, "write", strerror(errno), path, errno,
+                               &retval);
+            goto done;
         }
 
-        return njs_fs_result(vm, &retval, calltype, callback, 2);
+        p += n;
     }
 
-    ret = njs_fs_stats_create(vm, &sb, &retval);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
+    ret = NJS_OK;
+    njs_set_undefined(&retval);
+
+done:
+
+    if (fd != -1) {
+        (void) close(fd);
     }
 
-    return njs_fs_result(vm, &retval, calltype, callback, 2);
+    if (ret == NJS_OK) {
+        return njs_fs_result(vm, &retval, calltype, callback, 1);
+    }
+
+    return NJS_ERROR;
 }
 
 
@@ -1822,21 +2565,16 @@ static njs_int_t
 njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name, njs_value_t *type,
     njs_value_t *retval)
 {
-    njs_int_t     ret;
-    njs_object_t  *object;
+    njs_int_t  ret;
 
     static const njs_value_t  string_name = njs_string("name");
     static const njs_value_t  string_type = njs_string("type");
 
-    object = njs_object_alloc(vm);
-    if (njs_slow_path(object == NULL)) {
-        return NJS_ERROR;
+    ret = njs_vm_external_create(vm, retval, njs_fs_dirent_proto_id, NULL, 0);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
     }
 
-    object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FS_DIRENT].object;
-
-    njs_set_object(retval, object);
-
     ret = njs_value_property_set(vm, retval, njs_value_arg(&string_name),
                                  name);
     if (njs_slow_path(ret != NJS_OK)) {
@@ -1844,18 +2582,13 @@ njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name, njs_value_t *type,
     }
 
     /* TODO: use a private symbol as a key. */
-    ret = njs_value_property_set(vm, retval, njs_value_arg(&string_type),
-                                 type);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
-    }
-
-    return NJS_OK;
+    return njs_value_property_set(vm, retval, njs_value_arg(&string_type),
+                                  type);
 }
 
 
 static njs_int_t
-njs_dirent_constructor(njs_vm_t *vm, njs_value_t *args,
+njs_fs_dirent_constructor(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused)
 {
     if (njs_slow_path(!vm->top_frame->ctor)) {
@@ -1864,157 +2597,68 @@ njs_dirent_constructor(njs_vm_t *vm, njs_value_t *args,
     }
 
     return njs_fs_dirent_create(vm, njs_arg(args, nargs, 1),
-                                njs_arg(args, nargs, 2), &vm->retval);
-}
-
-
-static const njs_object_prop_t  njs_dirent_constructor_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("Dirent"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("length"),
-        .value = njs_value(NJS_NUMBER, 1, 2.0),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("prototype"),
-        .value = njs_prop_handler(njs_object_prototype_create),
-    },
-};
-
-
-const njs_object_init_t  njs_dirent_constructor_init = {
-    njs_dirent_constructor_properties,
-    njs_nitems(njs_dirent_constructor_properties),
-};
-
-
-static njs_int_t
-njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t testtype)
-{
-    njs_int_t    ret;
-    njs_value_t  type, *this;
-
-    static const njs_value_t  string_type = njs_string("type");
-
-    this = njs_argument(args, 0);
-
-    ret = njs_value_property(vm, this, njs_value_arg(&string_type), &type);
-    if (njs_slow_path(ret == NJS_ERROR)) {
-        return ret;
-    }
-
-    if (njs_slow_path(njs_is_number(&type)
-                      && (njs_number(&type) == NJS_DT_INVALID)))
-    {
-        njs_internal_error(vm, "dentry type is not supported on this platform");
-        return NJS_ERROR;
-    }
-
-    njs_set_boolean(&vm->retval,
-                    njs_is_number(&type) && testtype == njs_number(&type));
-
-    return NJS_OK;
-}
-
-
-static const njs_object_prop_t  njs_dirent_prototype_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
-        .value = njs_string("Dirent"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("constructor"),
-        .value = njs_prop_handler(njs_object_prototype_create_constructor),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isDirectory"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_DIR),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isFile"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_REG),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isBlockDevice"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_BLK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_long_string("isCharacterDevice"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_CHR),
-        .writable = 1,
-        .configurable = 1,
-    },
+                                njs_arg(args, nargs, 2), &vm->retval);
+}
 
+
+static const njs_object_prop_t  njs_dirent_constructor_properties[] =
+{
     {
         .type = NJS_PROPERTY,
-        .name = njs_string("isSymbolicLink"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_LNK),
-        .writable = 1,
+        .name = njs_string("name"),
+        .value = njs_string("Dirent"),
         .configurable = 1,
     },
 
     {
         .type = NJS_PROPERTY,
-        .name = njs_string("isFIFO"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_FIFO),
-        .writable = 1,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 2.0),
         .configurable = 1,
     },
 
     {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isSocket"),
-        .value = njs_native_function2(njs_fs_dirent_test, 0, DT_SOCK),
-        .writable = 1,
-        .configurable = 1,
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
     },
 };
 
 
-const njs_object_init_t  njs_dirent_prototype_init = {
-    njs_dirent_prototype_properties,
-    njs_nitems(njs_dirent_prototype_properties),
+const njs_object_init_t  njs_dirent_constructor_init = {
+    njs_dirent_constructor_properties,
+    njs_nitems(njs_dirent_constructor_properties),
 };
 
 
-const njs_object_type_init_t  njs_dirent_type_init = {
-    .constructor = njs_native_ctor(njs_dirent_constructor, 2, 0),
-    .prototype_props = &njs_dirent_prototype_init,
-    .constructor_props = &njs_dirent_constructor_init,
-    .prototype_value = { .object = { .type = NJS_OBJECT } },
-};
+static njs_int_t
+njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t testtype)
+{
+    njs_int_t    ret;
+    njs_value_t  type, *this;
+
+    static const njs_value_t  string_type = njs_string("type");
+
+    this = njs_argument(args, 0);
+
+    ret = njs_value_property(vm, this, njs_value_arg(&string_type), &type);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        return ret;
+    }
+
+    if (njs_slow_path(njs_is_number(&type)
+                      && (njs_number(&type) == NJS_DT_INVALID)))
+    {
+        njs_internal_error(vm, "dentry type is not supported on this platform");
+        return NJS_ERROR;
+    }
+
+    njs_set_boolean(&vm->retval,
+                    njs_is_number(&type) && testtype == njs_number(&type));
+
+    return NJS_OK;
+}
 
 
 static void
@@ -2077,38 +2721,18 @@ njs_fs_to_stat(njs_stat_t *dst, struct stat *st)
 static njs_int_t
 njs_fs_stats_create(njs_vm_t *vm, struct stat *st, njs_value_t *retval)
 {
-    njs_stat_t          *copy;
-    njs_object_value_t  *stat;
+    njs_stat_t  *stat;
 
-    stat = njs_object_value_alloc(vm, NJS_OBJ_TYPE_FS_STATS, 0, NULL);
+    stat = njs_mp_alloc(vm->mem_pool, sizeof(njs_stat_t));
     if (njs_slow_path(stat == NULL)) {
-        return NJS_ERROR;
-    }
-
-    stat->object.shared_hash =
-                      vm->prototypes[NJS_OBJ_TYPE_FS_STATS].object.shared_hash;
-
-    copy = njs_mp_alloc(vm->mem_pool, sizeof(njs_stat_t));
-    if (copy == NULL) {
         njs_memory_error(vm);
         return NJS_ERROR;
     }
 
-    njs_fs_to_stat(copy, st);
-
-    njs_set_data(&stat->value, copy, NJS_DATA_TAG_FS_STAT);
-    njs_set_object_value(retval, stat);
-
-    return NJS_OK;
-}
-
+    njs_fs_to_stat(stat, st);
 
-static njs_int_t
-njs_stats_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    njs_type_error(vm, "Stats is not a constructor");
-    return NJS_ERROR;
+    return njs_vm_external_create(vm, retval, njs_fs_stats_proto_id,
+                                  stat, 0);
 }
 
 
@@ -2116,18 +2740,14 @@ static njs_int_t
 njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t testtype)
 {
-    unsigned     mask;
-    njs_stat_t   *st;
-    njs_value_t  *this;
-
-    this = njs_argument(args, 0);
+    unsigned    mask;
+    njs_stat_t  *st;
 
-    if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_FS_STAT))) {
+    st = njs_vm_external(vm, njs_fs_stats_proto_id, njs_argument(args, 0));
+    if (njs_slow_path(st == NULL)) {
         return NJS_DECLINED;
     }
 
-    st = njs_object_data(this);
-
     switch (testtype) {
     case DT_DIR:
         mask = S_IFDIR;
@@ -2174,13 +2794,12 @@ njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
 
 #define njs_fs_time_ms(ts) ((ts)->tv_sec * 1000.0 + (ts)->tv_nsec / 1000000.0)
 
-    if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_FS_STAT))) {
+    st = njs_vm_external(vm, njs_fs_stats_proto_id, value);
+    if (njs_slow_path(st == NULL)) {
         return NJS_DECLINED;
     }
 
-    st = njs_object_data(value);
-
-    switch (prop->value.data.magic16) {
+    switch (prop->value.data.magic32 & 0xf) {
     case NJS_FS_STAT_DEV:
         v = st->st_dev;
         break;
@@ -2217,309 +2836,46 @@ njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
         v = st->st_blksize;
         break;
 
-    case NJS_FS_STAT_BLOCKS:
-        v = st->st_blocks;
-        break;
-
-    case NJS_FS_STAT_ATIME:
-        v = njs_fs_time_ms(&st->st_atim);
-        break;
-
-    case NJS_FS_STAT_BIRTHTIME:
-        v = njs_fs_time_ms(&st->st_birthtim);
-        break;
-
-    case NJS_FS_STAT_CTIME:
-        v = njs_fs_time_ms(&st->st_ctim);
-        break;
-
-    case NJS_FS_STAT_MTIME:
-    default:
-        v = njs_fs_time_ms(&st->st_mtim);
-        break;
-    }
-
-    switch (prop->value.data.magic32) {
-    case NJS_NUMBER:
-        njs_set_number(retval, v);
-        break;
-
-    case NJS_DATE:
-    default:
-        date = njs_date_alloc(vm, v);
-        if (njs_slow_path(date == NULL)) {
-            return NJS_ERROR;
-        }
-
-        njs_set_date(retval, date);
-        break;
-    }
-
-    return NJS_OK;
-}
-
-
-static const njs_object_prop_t  njs_stats_constructor_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("Stats"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("length"),
-        .value = njs_value(NJS_NUMBER, 1, 0),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("prototype"),
-        .value = njs_prop_handler(njs_object_prototype_create),
-    },
-};
-
-
-const njs_object_init_t  njs_stats_constructor_init = {
-    njs_stats_constructor_properties,
-    njs_nitems(njs_stats_constructor_properties),
-};
-
-
-static const njs_object_prop_t  njs_stats_prototype_properties[] =
-{
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
-        .value = njs_string("Stats"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("constructor"),
-        .value = njs_prop_handler(njs_object_prototype_create_constructor),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isBlockDevice"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_BLK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_long_string("isCharacterDevice"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_CHR),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isDirectory"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_DIR),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isFIFO"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_FIFO),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isFile"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_REG),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isSocket"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_SOCK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("isSymbolicLink"),
-        .value = njs_native_function2(njs_fs_stats_test, 0, DT_LNK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("dev"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_DEV,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("ino"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_INO,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("mode"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_MODE,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("nlink"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_NLINK,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("uid"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_UID,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("gid"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_GID,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("rdev"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_RDEV,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("size"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_SIZE,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("blksize"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BLKSIZE,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("blocks"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BLOCKS,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("atimeMs"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_ATIME,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("birthtimeMs"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BIRTHTIME,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("ctimeMs"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_CTIME,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("mtimeMs"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_MTIME,
-                                   NJS_NUMBER),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("atime"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_ATIME,
-                                   NJS_DATE),
-        .enumerable = 1,
-    },
+    case NJS_FS_STAT_BLOCKS:
+        v = st->st_blocks;
+        break;
 
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("birthtime"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_BIRTHTIME,
-                                   NJS_DATE),
-        .enumerable = 1,
-    },
+    case NJS_FS_STAT_ATIME:
+        v = njs_fs_time_ms(&st->st_atim);
+        break;
 
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("ctime"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_CTIME,
-                                   NJS_DATE),
-        .enumerable = 1,
-    },
+    case NJS_FS_STAT_BIRTHTIME:
+        v = njs_fs_time_ms(&st->st_birthtim);
+        break;
 
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("mtime"),
-        .value = njs_prop_handler2(njs_fs_stats_prop, NJS_FS_STAT_MTIME,
-                                   NJS_DATE),
-        .enumerable = 1,
-    },
-};
+    case NJS_FS_STAT_CTIME:
+        v = njs_fs_time_ms(&st->st_ctim);
+        break;
+
+    case NJS_FS_STAT_MTIME:
+    default:
+        v = njs_fs_time_ms(&st->st_mtim);
+        break;
+    }
 
+    switch (prop->value.data.magic32 >> 4) {
+    case NJS_NUMBER:
+        njs_set_number(retval, v);
+        break;
 
-const njs_object_init_t  njs_stats_prototype_init = {
-    njs_stats_prototype_properties,
-    njs_nitems(njs_stats_prototype_properties),
-};
+    case NJS_DATE:
+    default:
+        date = njs_date_alloc(vm, v);
+        if (njs_slow_path(date == NULL)) {
+            return NJS_ERROR;
+        }
 
+        njs_set_date(retval, date);
+        break;
+    }
 
-const njs_object_type_init_t  njs_stats_type_init = {
-    .constructor = njs_native_ctor(njs_stats_constructor, 0, 0),
-    .prototype_props = &njs_stats_prototype_init,
-    .constructor_props = &njs_stats_constructor_init,
-    .prototype_value = { .object = { .type = NJS_OBJECT } },
-};
+    return NJS_OK;
+}
 
 
 static const njs_object_prop_t  njs_fs_promises_properties[] =
@@ -2527,7 +2883,7 @@ static const njs_object_prop_t  njs_fs_promises_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("readFile"),
-        .value = njs_native_function2(njs_fs_read_file, 0, NJS_FS_PROMISE),
+        .value = njs_native_function2(njs_fs_read, 0, NJS_FS_PROMISE),
         .writable = 1,
         .configurable = 1,
     },
@@ -2535,7 +2891,7 @@ static const njs_object_prop_t  njs_fs_promises_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("appendFile"),
-        .value = njs_native_function2(njs_fs_write_file, 0,
+        .value = njs_native_function2(njs_fs_write, 0,
                                   njs_fs_magic(NJS_FS_PROMISE, NJS_FS_APPEND)),
         .writable = 1,
         .configurable = 1,
@@ -2544,8 +2900,8 @@ static const njs_object_prop_t  njs_fs_promises_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("writeFile"),
-        .value = njs_native_function2(njs_fs_write_file, 0,
-                                  njs_fs_magic(NJS_FS_PROMISE, NJS_FS_TRUNC)),
+        .value = njs_native_function2(njs_fs_write, 0,
+                                   njs_fs_magic(NJS_FS_PROMISE, NJS_FS_TRUNC)),
         .writable = 1,
         .configurable = 1,
     },
@@ -2692,255 +3048,47 @@ njs_fs_constants(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
 }
 
 
-static const njs_object_prop_t  njs_fs_object_properties[] =
+static njs_int_t
+njs_fs_init(njs_vm_t *vm)
 {
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("fs"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("constants"),
-        .value = njs_prop_handler(njs_fs_constants),
-        .enumerable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("promises"),
-        .value = njs_prop_handler(njs_fs_promises),
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("Dirent"),
-        .value = _njs_native_function(njs_dirent_constructor, 2, 1, 0),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("access"),
-        .value = njs_native_function2(njs_fs_access, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("accessSync"),
-        .value = njs_native_function2(njs_fs_access, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("readFile"),
-        .value = njs_native_function2(njs_fs_read_file, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("readFileSync"),
-        .value = njs_native_function2(njs_fs_read_file, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("appendFile"),
-        .value = njs_native_function2(njs_fs_write_file, 0,
-                                  njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_APPEND)),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("appendFileSync"),
-        .value = njs_native_function2(njs_fs_write_file, 0,
-                                   njs_fs_magic(NJS_FS_DIRECT, NJS_FS_APPEND)),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("writeFile"),
-        .value = njs_native_function2(njs_fs_write_file, 0,
-                                  njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_TRUNC)),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("writeFileSync"),
-        .value = njs_native_function2(njs_fs_write_file, 0,
-                                    njs_fs_magic(NJS_FS_DIRECT, NJS_FS_TRUNC)),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("rename"),
-        .value = njs_native_function2(njs_fs_rename, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("renameSync"),
-        .value = njs_native_function2(njs_fs_rename, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("symlink"),
-        .value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("symlinkSync"),
-        .value = njs_native_function2(njs_fs_symlink, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("unlink"),
-        .value = njs_native_function2(njs_fs_unlink, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("unlinkSync"),
-        .value = njs_native_function2(njs_fs_unlink, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("realpath"),
-        .value = njs_native_function2(njs_fs_realpath, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("realpathSync"),
-        .value = njs_native_function2(njs_fs_realpath, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("mkdir"),
-        .value = njs_native_function2(njs_fs_mkdir, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("mkdirSync"),
-        .value = njs_native_function2(njs_fs_mkdir, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("rmdir"),
-        .value = njs_native_function2(njs_fs_rmdir, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("rmdirSync"),
-        .value = njs_native_function2(njs_fs_rmdir, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
+    njs_int_t           ret, proto_id;
+    njs_mod_t           *module;
+    njs_opaque_value_t  value;
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("readdir"),
-        .value = njs_native_function2(njs_fs_readdir, 0, NJS_FS_CALLBACK),
-        .writable = 1,
-        .configurable = 1,
-    },
+    if (vm->options.sandbox) {
+        return NJS_OK;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("readdirSync"),
-        .value = njs_native_function2(njs_fs_readdir, 0, NJS_FS_DIRECT),
-        .writable = 1,
-        .configurable = 1,
-    },
+    njs_fs_stats_proto_id = njs_vm_external_prototype(vm, njs_ext_stats,
+                                                    njs_nitems(njs_ext_stats));
+    if (njs_slow_path(njs_fs_stats_proto_id < 0)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("lstat"),
-        .value = njs_native_function2(njs_fs_stat, 0,
-                                  njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_LSTAT)),
-        .writable = 1,
-        .configurable = 1,
-    },
+    njs_fs_dirent_proto_id = njs_vm_external_prototype(vm, njs_ext_dirent,
+                                                   njs_nitems(njs_ext_dirent));
+    if (njs_slow_path(njs_fs_dirent_proto_id < 0)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("lstatSync"),
-        .value = njs_native_function2(njs_fs_stat, 0,
-                                    njs_fs_magic(NJS_FS_DIRECT, NJS_FS_LSTAT)),
-        .writable = 1,
-        .configurable = 1,
-    },
+    proto_id = njs_vm_external_prototype(vm, njs_ext_fs,
+                                         njs_nitems(njs_ext_fs));
+    if (njs_slow_path(proto_id < 0)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("stat"),
-        .value = njs_native_function2(njs_fs_stat, 0,
-                                   njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_STAT)),
-        .writable = 1,
-        .configurable = 1,
-    },
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("statSync"),
-        .value = njs_native_function2(njs_fs_stat, 0,
-                                     njs_fs_magic(NJS_FS_DIRECT, NJS_FS_STAT)),
-        .writable = 1,
-        .configurable = 1,
-    },
-};
+    module = njs_module_add(vm, &njs_str_value("fs"), 1);
+    if (njs_slow_path(module == NULL)) {
+        return NJS_ERROR;
+    }
 
+    njs_value_assign(&module->value, &value);
+    module->function.native = 1;
 
-const njs_object_init_t  njs_fs_object_init = {
-    njs_fs_object_properties,
-    njs_nitems(njs_fs_object_properties),
-};
+    return NJS_OK;
+}
similarity index 90%
rename from src/njs_query_string.c
rename to external/njs_query_string.c
index 7f8d6b1a7ac1f645dc8a8cfbc09c2a0efe25d777..701449fc96fae5c62064cfbf1960eebd1dcbad2a 100644 (file)
@@ -18,11 +18,93 @@ static const njs_value_t  njs_decode_uri_str =
 static const njs_value_t  njs_max_keys_str = njs_string("maxKeys");
 
 
+static njs_int_t njs_query_string_parse(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
+static njs_int_t njs_query_string_stringify(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused);
 static njs_int_t njs_query_string_escape(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 static njs_int_t njs_query_string_unescape(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 
+static njs_int_t njs_query_string_init(njs_vm_t *vm);
+
+
+static njs_external_t  njs_ext_query_string[] = {
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("parse"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_query_string_parse,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("stringify"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_query_string_stringify,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("decode"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_query_string_parse,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("encode"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_query_string_stringify,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("escape"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_query_string_escape,
+            .magic8 = 0,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_METHOD,
+        .name.string = njs_str("unescape"),
+        .writable = 1,
+        .configurable = 1,
+        .u.method = {
+            .native = njs_query_string_unescape,
+            .magic8 = 0,
+        }
+    },
+};
+
+
+njs_module_t  njs_query_string_module = {
+    .name = njs_str("querystring"),
+    .init = njs_query_string_init,
+};
+
 
 static njs_object_t *
 njs_query_string_object_alloc(njs_vm_t *vm)
@@ -859,66 +941,31 @@ njs_query_string_unescape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
-static const njs_object_prop_t  njs_query_string_object_properties[] =
+static njs_int_t
+njs_query_string_init(njs_vm_t *vm)
 {
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("querystring"),
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("parse"),
-        .value = njs_native_function(njs_query_string_parse, 4),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("stringify"),
-        .value = njs_native_function(njs_query_string_stringify, 4),
-        .writable = 1,
-        .configurable = 1,
-    },
+    njs_int_t           ret, proto_id;
+    njs_mod_t           *module;
+    njs_opaque_value_t  value;
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("escape"),
-        .value = njs_native_function(njs_query_string_escape, 1),
-        .writable = 1,
-        .configurable = 1,
-    },
-
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("unescape"),
-        .value = njs_native_function(njs_query_string_unescape, 1),
-        .writable = 1,
-        .configurable = 1,
-    },
+    proto_id = njs_vm_external_prototype(vm, njs_ext_query_string,
+                                         njs_nitems(njs_ext_query_string));
+    if (njs_slow_path(proto_id < 0)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("decode"),
-        .value = njs_native_function(njs_query_string_parse, 4),
-        .writable = 1,
-        .configurable = 1,
-    },
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("encode"),
-        .value = njs_native_function(njs_query_string_stringify, 4),
-        .writable = 1,
-        .configurable = 1,
-    },
-};
+    module = njs_module_add(vm, &njs_str_value("querystring"), 1);
+    if (njs_slow_path(module == NULL)) {
+        return NJS_ERROR;
+    }
 
+    njs_value_assign(&module->value, &value);
+    module->function.native = 1;
 
-const njs_object_init_t  njs_query_string_object_init = {
-    njs_query_string_object_properties,
-    njs_nitems(njs_query_string_object_properties),
-};
+    return NJS_OK;
+}
index 7ac7ed754de2b819bbbd8a4a767016975a678548..450ebfd65fe6cdb63faf95f2910d09ec18c4e735 100644 (file)
@@ -6,7 +6,6 @@
 
 
 #include <njs_main.h>
-#include "njs_webcrypto.h"
 #include "njs_openssl.h"
 
 typedef enum {
@@ -138,6 +137,8 @@ static njs_int_t njs_webcrypto_result(njs_vm_t *vm, njs_value_t *result,
     njs_int_t rc);
 static void njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...);
 
+static njs_int_t njs_webcrypto_init(njs_vm_t *vm);
+
 static njs_webcrypto_entry_t njs_webcrypto_alg[] = {
 
 #define njs_webcrypto_algorithm(type, usage_mask, fmt_mask)                  \
@@ -487,6 +488,12 @@ static njs_external_t  njs_ext_webcrypto[] = {
 };
 
 
+njs_module_t  njs_webcrypto_module = {
+    .name = njs_str("webcrypto"),
+    .init = njs_webcrypto_init,
+};
+
+
 static njs_int_t    njs_webcrypto_crypto_key_proto_id;
 
 
@@ -2652,8 +2659,8 @@ njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...)
 }
 
 
-njs_int_t
-njs_external_webcrypto_init(njs_vm_t *vm)
+static njs_int_t
+njs_webcrypto_init(njs_vm_t *vm)
 {
     njs_int_t           ret, proto_id;
     njs_str_t           name;
diff --git a/external/njs_webcrypto.h b/external/njs_webcrypto.h
deleted file mode 100644 (file)
index 3331b57..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) NGINX, Inc.
- */
-
-
-#ifndef _NJS_EXTERNAL_WEBCRYPTO_H_INCLUDED_
-#define _NJS_EXTERNAL_WEBCRYPTO_H_INCLUDED_
-
-
-njs_int_t njs_external_webcrypto_init(njs_vm_t *vm);
-
-
-#endif /* _NJS_EXTERNAL_WEBCRYPTO_H_INCLUDED_ */
index 7ebdcfe9f0af2a1f6de623cd81a2f4a4d0bbe665..a0fbeeea691dd7e5682a7658ecf038f6456cf39b 100644 (file)
@@ -1,8 +1,7 @@
 ngx_addon_name="ngx_js_module"
 
 NJS_DEPS="$ngx_addon_dir/ngx_js.h \
-    $ngx_addon_dir/ngx_js_fetch.h \
-    $ngx_addon_dir/../external/njs_webcrypto.h"
+    $ngx_addon_dir/ngx_js_fetch.h"
 NJS_SRCS="$ngx_addon_dir/ngx_js.c \
     $ngx_addon_dir/ngx_js_fetch.c \
     $ngx_addon_dir/../external/njs_webcrypto.c"
index 3623216cec869a547374aeaa7d393e63c4f3233c..30f88d8149899e11d93f49a458b0da1d85c07d42 100644 (file)
@@ -3,7 +3,7 @@ cat << END                                            >> $NGX_MAKEFILE
 $ngx_addon_dir/../build/libnjs.a: $NGX_MAKEFILE
        cd $ngx_addon_dir/.. \\
        && if [ -f build/Makefile ]; then \$(MAKE) clean; fi \\
-       && CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-pcre2 \\
+       && CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-webcrypto --no-pcre2 \\
        && \$(MAKE)
 
 END
index 68592ad21ae1f8ddb3df963a462a9132a1dc100d..40ee8491f4aba16c4e8269c42042f3cd04bc8f6d 100644 (file)
@@ -3515,6 +3515,7 @@ ngx_http_js_init_main_conf(ngx_conf_t *cf, void *conf)
     options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW;
     options.ops = &ngx_http_js_ops;
     options.metas = &ngx_http_js_metas;
+    options.addons = njs_js_addon_modules;
     options.argv = ngx_argv;
     options.argc = ngx_argc;
 
index 012adb48edd4372675c141cf8f1247340b4eabae..6799b1820716ac1b546226a51dc8eff2cae9058d 100644 (file)
@@ -10,7 +10,9 @@
 #include <ngx_core.h>
 #include "ngx_js.h"
 #include "ngx_js_fetch.h"
-#include "../external/njs_webcrypto.h"
+
+
+extern njs_module_t  njs_webcrypto_module;
 
 
 static njs_external_t  ngx_js_ext_core[] = {
@@ -66,6 +68,12 @@ static njs_external_t  ngx_js_ext_core[] = {
 };
 
 
+njs_module_t *njs_js_addon_modules[] = {
+    &njs_webcrypto_module,
+    NULL,
+};
+
+
 ngx_int_t
 ngx_js_call(njs_vm_t *vm, ngx_str_t *fname, ngx_log_t *log,
     njs_opaque_value_t *args, njs_uint_t nargs)
@@ -177,12 +185,6 @@ ngx_js_core_init(njs_vm_t *vm, ngx_log_t *log)
         return NGX_ERROR;
     }
 
-    ret = njs_external_webcrypto_init(vm);
-    if (ret != NJS_OK) {
-        ngx_log_error(NGX_LOG_EMERG, log, 0, "failed to add webcrypto object");
-        return NGX_ERROR;
-    }
-
     proto_id = njs_vm_external_prototype(vm, ngx_js_ext_core,
                                          njs_nitems(ngx_js_ext_core));
     if (proto_id < 0) {
index 16891aeaf5d7f16bd36dbab7a021cde7509192da..2a9bf85629c0ea5b1226988f6c29510eea36046b 100644 (file)
@@ -72,4 +72,7 @@ ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str);
 ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n);
 
 
+extern njs_module_t *njs_js_addon_modules[];
+
+
 #endif /* _NGX_JS_H_INCLUDED_ */
index 44a7d38e537aecc928ef7bfb9e47585d032acbe3..b2b30907c702af63e7b0c723fb98eb3a66ae7585 100644 (file)
@@ -1537,6 +1537,7 @@ ngx_stream_js_init_main_conf(ngx_conf_t *cf, void *conf)
     options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW;
     options.ops = &ngx_stream_js_ops;
     options.metas = &ngx_stream_js_metas;
+    options.addons = njs_js_addon_modules;
     options.argv = ngx_argv;
     options.argc = ngx_argc;
 
index 907950a4b5616e69f51edc7247a1f4ffb73995b7..08a275942f347d3387f1f739509c848b8ece4593 100644 (file)
--- a/src/njs.h
+++ b/src/njs.h
@@ -197,11 +197,20 @@ typedef struct {
 } njs_vm_meta_t;
 
 
+typedef njs_int_t (*njs_addon_init_pt)(njs_vm_t *vm);
+
+typedef struct {
+    njs_str_t                       name;
+    njs_addon_init_pt               init;
+} njs_module_t;
+
+
 typedef struct {
     njs_external_ptr_t              external;
     njs_vm_shared_t                 *shared;
     njs_vm_ops_t                    *ops;
     njs_vm_meta_t                   *metas;
+    njs_module_t                    **addons;
     njs_str_t                       file;
 
     char                            **argv;
index 2c47657c4dc6319dd1b69f76f8e1d9d1de0ad799..778babdc325a8aa072917bac59b62335ff03aff9 100644 (file)
@@ -79,6 +79,56 @@ static njs_int_t njs_buffer_fill_typed_array(njs_vm_t *vm,
 static void njs_buffer_decode_destroy(njs_vm_t *vm, const njs_value_t *source,
     njs_value_t *target);
 
+static njs_int_t njs_buffer(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
+static njs_int_t njs_buffer_constants(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
+static njs_int_t njs_buffer_constant(njs_vm_t *vm,
+    njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval,
+    njs_value_t *retval);
+
+static njs_int_t njs_buffer_init(njs_vm_t *vm);
+
+
+static njs_external_t  njs_ext_buffer[] = {
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("Buffer"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_buffer,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("constants"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_buffer_constants,
+        }
+    },
+
+    {
+        .flags = NJS_EXTERN_PROPERTY,
+        .name.string = njs_str("kMaxLength"),
+        .enumerable = 1,
+        .u.property = {
+            .handler = njs_buffer_constant,
+            .magic32 = INT32_MAX,
+        }
+    },
+};
+
+
+njs_module_t  njs_buffer_module = {
+    .name = njs_str("buffer"),
+    .init = njs_buffer_init,
+};
+
 
 njs_int_t
 njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
@@ -2934,6 +2984,15 @@ static const njs_object_init_t  njs_buffer_constants_init = {
 };
 
 
+static njs_int_t
+njs_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+    njs_value_t *unused, njs_value_t *retval)
+{
+    return njs_object_prop_init(vm, &njs_buffer_constructor_init, prop, value,
+                                retval);
+}
+
+
 static njs_int_t
 njs_buffer_constants(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
     njs_value_t *unused, njs_value_t *retval)
@@ -2944,47 +3003,40 @@ njs_buffer_constants(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
 
 
 static njs_int_t
-njs_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
+njs_buffer_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value,
     njs_value_t *unused, njs_value_t *retval)
 {
-    return njs_object_prop_init(vm, &njs_buffer_constructor_init, prop, value,
-                                retval);
+    njs_value_number_set(retval, njs_vm_prop_magic32(prop));
+
+    return NJS_OK;
 }
 
 
-static const njs_object_prop_t  njs_buffer_object_properties[] =
+static njs_int_t
+njs_buffer_init(njs_vm_t *vm)
 {
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("name"),
-        .value = njs_string("buffer"),
-        .configurable = 1,
-    },
+    njs_int_t           ret, proto_id;
+    njs_mod_t           *module;
+    njs_opaque_value_t  value;
 
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("constants"),
-        .value = njs_prop_handler(njs_buffer_constants),
-        .enumerable = 1,
-    },
+    proto_id = njs_vm_external_prototype(vm, njs_ext_buffer,
+                                         njs_nitems(njs_ext_buffer));
+    if (njs_slow_path(proto_id < 0)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY_HANDLER,
-        .name = njs_string("Buffer"),
-        .value = njs_prop_handler(njs_buffer),
-        .enumerable = 1,
-    },
+    ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
 
-    {
-        .type = NJS_PROPERTY,
-        .name = njs_string("kMaxLength"),
-        .value = njs_value(NJS_NUMBER, 1, INT32_MAX),
-        .enumerable = 1,
-    },
-};
+    module = njs_module_add(vm, &njs_str_value("buffer"), 1);
+    if (njs_slow_path(module == NULL)) {
+        return NJS_ERROR;
+    }
 
+    njs_value_assign(&module->value, &value);
+    module->function.native = 1;
 
-const njs_object_init_t  njs_buffer_object_init = {
-    njs_buffer_object_properties,
-    njs_nitems(njs_buffer_object_properties),
-};
+    return NJS_OK;
+}
index 4633ebe1f434b1f1997e8627db008c31916b627a..ee42d94f2b96a73660005ede111f8dc1ea594c1d 100644 (file)
@@ -35,7 +35,6 @@ njs_int_t njs_buffer_decode_string(njs_vm_t *vm, const njs_value_t *value,
 
 
 extern const njs_object_type_init_t  njs_buffer_type_init;
-extern const njs_object_init_t  njs_buffer_object_init;
 
 
 #endif /* _NJS_BUFFER_H_INCLUDED_ */
index 5c117683edd9a19a366a99fb474fd33d3a3b3961..14ef6f23e4325b8b7da495cd3bbd7d4cf0bbb59b 100644 (file)
@@ -53,15 +53,6 @@ static const njs_object_init_t  *njs_object_init[] = {
 };
 
 
-static const njs_object_init_t  *njs_module_init[] = {
-    &njs_fs_object_init,
-    &njs_crypto_object_init,
-    &njs_query_string_object_init,
-    &njs_buffer_object_init,
-    NULL
-};
-
-
 static const njs_object_type_init_t *const
     njs_object_type_init[NJS_OBJ_TYPE_MAX] =
 {
@@ -88,10 +79,6 @@ static const njs_object_type_init_t *const
 
     &njs_iterator_type_init,
     &njs_array_iterator_type_init,
-    &njs_dirent_type_init,
-    &njs_stats_type_init,
-    &njs_hash_type_init,
-    &njs_hmac_type_init,
     &njs_typed_array_type_init,
 
     /* TypedArray types. */
@@ -135,20 +122,14 @@ njs_int_t
 njs_builtin_objects_create(njs_vm_t *vm)
 {
     njs_int_t                  ret;
-    njs_mod_t                  *module;
     njs_uint_t                 i;
     njs_object_t               *object, *string_object;
     njs_function_t             *constructor;
     njs_vm_shared_t            *shared;
-    njs_lvlhsh_query_t         lhq;
     njs_regexp_pattern_t       *pattern;
     njs_object_prototype_t     *prototype;
-    const njs_object_prop_t    *prop;
     const njs_object_init_t    *obj, **p;
 
-    static const njs_str_t  sandbox_key = njs_str("sandbox");
-    static const njs_str_t  name_key = njs_str("name");
-
     shared = njs_mp_zalloc(vm->mem_pool, sizeof(njs_vm_shared_t));
     if (njs_slow_path(shared == NULL)) {
         return NJS_ERROR;
@@ -229,62 +210,6 @@ njs_builtin_objects_create(njs_vm_t *vm)
         return NJS_ERROR;
     }
 
-    njs_lvlhsh_init(&shared->modules_hash);
-
-    lhq.replace = 0;
-    lhq.pool = vm->mem_pool;
-
-    for (p = njs_module_init; *p != NULL; p++) {
-        obj = *p;
-
-        module = njs_mp_zalloc(vm->mem_pool, sizeof(njs_mod_t));
-        if (njs_slow_path(module == NULL)) {
-            return NJS_ERROR;
-        }
-
-        module->function.native = 1;
-
-        ret = njs_object_hash_init(vm, &module->object.shared_hash, obj);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return NJS_ERROR;
-        }
-
-        if (vm->options.sandbox) {
-            lhq.key = sandbox_key;
-            lhq.key_hash = njs_djb_hash(sandbox_key.start, sandbox_key.length);
-            lhq.proto = &njs_object_hash_proto;
-
-            ret = njs_lvlhsh_find(&module->object.shared_hash, &lhq);
-            if (njs_fast_path(ret != NJS_OK)) {
-                continue;
-            }
-        }
-
-        lhq.key = name_key;
-        lhq.key_hash = njs_djb_hash(name_key.start, name_key.length);
-        lhq.proto = &njs_object_hash_proto;
-
-        ret = njs_lvlhsh_find(&module->object.shared_hash, &lhq);
-        if (njs_fast_path(ret != NJS_OK)) {
-            return NJS_ERROR;
-        }
-
-        prop = lhq.value;
-
-        njs_string_get(&prop->value, &module->name);
-        module->object.shared = 1;
-
-        lhq.key = module->name;
-        lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
-        lhq.proto = &njs_modules_hash_proto;
-        lhq.value = module;
-
-        ret = njs_lvlhsh_insert(&shared->modules_hash, &lhq);
-        if (njs_fast_path(ret != NJS_OK)) {
-            return NJS_ERROR;
-        }
-    }
-
     prototype = shared->prototypes;
 
     for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) {
@@ -338,6 +263,8 @@ njs_builtin_objects_create(njs_vm_t *vm)
     string_object->shared = 1;
     string_object->extensible = 0;
 
+    njs_lvlhsh_init(&shared->modules_hash);
+
     vm->shared = shared;
 
     return NJS_OK;
@@ -760,10 +687,13 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
 {
     uint8_t                 magic8;
     njs_int_t               ret;
+    njs_arr_t               **pprotos;
     njs_mod_t               *module;
     njs_uint_t              i, n;
-    njs_value_t             value;
+    njs_value_t             value, tag;
+    njs_object_t            object;
     njs_lvlhsh_each_t       lhe;
+    njs_exotic_slots_t      *slots;
     njs_function_name_t     *fn;
     njs_function_native_t   native;
     njs_builtin_traverse_t  ctx;
@@ -806,10 +736,10 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
         njs_set_object(&value, &vm->constructors[i].object);
 
         ret = njs_value_property(vm, &value, njs_value_arg(&njs_string_name),
-                                 &value);
+                                 &tag);
 
-        if (ret == NJS_OK && njs_is_string(&value)) {
-            njs_string_get(&value, &ctx.match);
+        if (ret == NJS_OK && njs_is_string(&tag)) {
+            njs_string_get(&tag, &ctx.match);
         }
 
         ret = njs_object_traverse(vm, &vm->constructors[i].object, &ctx,
@@ -833,7 +763,35 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
 
         ctx.match = module->name;
 
-        ret = njs_object_traverse(vm, &module->object, &ctx,
+        ret = njs_object_traverse(vm, njs_object(&module->value), &ctx,
+                                  njs_builtin_traverse);
+
+        if (ret == NJS_DONE) {
+            goto found;
+        }
+    }
+
+    /* External prototypes (not mapped to global object). */
+
+    ctx.match = njs_str_value("");
+
+    for (i = 0; i< vm->protos->items; i++) {
+        njs_memzero(&object, sizeof(njs_object_t));
+
+        pprotos = njs_arr_item(vm->protos, i);
+        slots = (*pprotos)->start;
+
+        object.shared_hash = slots->external_shared_hash;
+        object.slots = slots;
+
+        njs_set_object(&value, &object);
+
+        ret = njs_object_string_tag(vm, &value, &tag);
+        if (ret == NJS_OK && njs_is_string(&tag)) {
+            njs_string_get(&tag, &ctx.match);
+        }
+
+        ret = njs_object_traverse(vm, njs_object(&value), &ctx,
                                   njs_builtin_traverse);
 
         if (ret == NJS_DONE) {
diff --git a/src/njs_crypto.h b/src/njs_crypto.h
deleted file mode 100644 (file)
index 27472f1..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) NGINX, Inc.
- */
-
-#ifndef _NJS_CRYPTO_H_INCLUDED_
-#define _NJS_CRYPTO_H_INCLUDED_
-
-extern const njs_object_init_t  njs_crypto_object_init;
-
-extern const njs_object_type_init_t  njs_hash_type_init;
-extern const njs_object_type_init_t  njs_hmac_type_init;
-
-
-#endif /* _NJS_CRYPTO_H_INCLUDED_ */
diff --git a/src/njs_fs.h b/src/njs_fs.h
deleted file mode 100644 (file)
index 6d06c2a..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) NGINX, Inc.
- */
-
-#ifndef _NJS_FS_H_INCLUDED_
-#define _NJS_FS_H_INCLUDED_
-
-
-extern const njs_object_init_t  njs_fs_object_init;
-
-extern const njs_object_type_init_t  njs_dirent_type_init;
-extern const njs_object_type_init_t  njs_stats_type_init;
-
-#endif /* _NJS_FS_H_INCLUDED_ */
index 1f505304747711793382d79d743b3b60703ff1e9..aa084ac9383adea80d3abbd56b83ef1db87d7725 100644 (file)
 #include <njs_timer.h>
 #include <njs_module.h>
 
-#include <njs_fs.h>
-#include <njs_crypto.h>
-#include <njs_query_string.h>
-
 #include <njs_event.h>
 #include <njs_module.h>
 
index c02115656fc183ed0c5931959550adecd6c53745..842b2b565de5528edfb5bdd908711e61598070a9 100644 (file)
@@ -39,17 +39,17 @@ static njs_bool_t njs_module_realpath_equal(const njs_str_t *path1,
 static njs_int_t njs_module_read(njs_vm_t *vm, int fd, njs_str_t *body);
 static njs_mod_t *njs_module_find(njs_vm_t *vm, njs_str_t *name,
     njs_bool_t local);
-static njs_mod_t *njs_module_add(njs_vm_t *vm, njs_str_t *name);
 static njs_int_t njs_module_insert(njs_parser_t *parser, njs_mod_t *module);
 
 
 njs_int_t
 njs_module_load(njs_vm_t *vm)
 {
-    njs_int_t    ret;
-    njs_mod_t    **item, *module;
-    njs_uint_t   i;
-    njs_value_t  *value;
+    njs_int_t     ret;
+    njs_mod_t     **item, *module;
+    njs_uint_t    i;
+    njs_value_t   *value;
+    njs_object_t  *object;
 
     if (vm->modules == NULL) {
         return NJS_OK;
@@ -62,7 +62,12 @@ njs_module_load(njs_vm_t *vm)
 
         if (module->function.native) {
             value = njs_scope_valid_value(vm, module->index);
-            njs_set_object(value, &module->object);
+            njs_value_assign(value, &module->value);
+
+            object = njs_object_value_copy(vm, value);
+            if (njs_slow_path(object == NULL)) {
+                return NJS_ERROR;
+            }
 
         } else {
             ret = njs_vm_invoke(vm, &module->function, NULL, 0,
@@ -125,7 +130,7 @@ njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token,
 
     parser->node = NULL;
 
-    module = njs_module_find(parser->vm, &name, 0);
+    module = njs_module_find(parser->vm, &name, 1);
     if (module != NULL && module->function.native) {
         njs_lexer_consume_token(parser->lexer, 1);
 
@@ -153,7 +158,7 @@ njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token,
         goto fail;
     }
 
-    module = njs_module_find(parser->vm, &info.file, 0);
+    module = njs_module_find(parser->vm, &info.file, 1);
     if (module != NULL) {
         (void) close(info.fd);
         njs_lexer_consume_token(parser->lexer, 1);
@@ -233,7 +238,7 @@ njs_parser_module_lambda_after(njs_parser_t *parser, njs_lexer_token_t *token,
         return njs_parser_failed(parser);
     }
 
-    module = njs_module_add(parser->vm, &temp->info.file);
+    module = njs_module_add(parser->vm, &temp->info.file, 0);
     if (njs_slow_path(module == NULL)) {
         parser->lexer = temp->prev;
 
@@ -476,10 +481,10 @@ const njs_lvlhsh_proto_t  njs_modules_hash_proto
 
 
 static njs_mod_t *
-njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t local)
+njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared)
 {
     njs_int_t           ret;
-    njs_mod_t           *shared, *module;
+    njs_mod_t           *shrd, *module;
     njs_object_t        *object;
     njs_lvlhsh_query_t  lhq;
 
@@ -492,10 +497,10 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t local)
     }
 
     if (njs_lvlhsh_find(&vm->shared->modules_hash, &lhq) == NJS_OK) {
-        shared = lhq.value;
+        shrd = lhq.value;
 
-        if (!local) {
-            return shared;
+        if (shared) {
+            return shrd;
         }
 
         module = njs_mp_alloc(vm->mem_pool, sizeof(njs_mod_t));
@@ -504,15 +509,12 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t local)
             return NULL;
         }
 
-        memcpy(module, shared, sizeof(njs_mod_t));
-        object = &module->object;
+        memcpy(module, shrd, sizeof(njs_mod_t));
 
-        object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
-        object->slots = NULL;
-        object->shared = 0;
-        object->extensible = 1;
-        object->error_data = 0;
-        object->fast_array = 0;
+        object = njs_object_value_copy(vm, &module->value);
+        if (njs_slow_path(object == NULL)) {
+            return NULL;
+        }
 
         lhq.replace = 0;
         lhq.value = module;
@@ -528,11 +530,12 @@ njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t local)
 }
 
 
-static njs_mod_t *
-njs_module_add(njs_vm_t *vm, njs_str_t *name)
+njs_mod_t *
+njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared)
 {
     njs_int_t           ret;
     njs_mod_t           *module;
+    njs_lvlhsh_t        *hash;
     njs_lvlhsh_query_t  lhq;
 
     module = njs_mp_zalloc(vm->mem_pool, sizeof(njs_mod_t));
@@ -554,7 +557,9 @@ njs_module_add(njs_vm_t *vm, njs_str_t *name)
     lhq.pool = vm->mem_pool;
     lhq.proto = &njs_modules_hash_proto;
 
-    ret = njs_lvlhsh_insert(&vm->modules_hash, &lhq);
+    hash = shared ? &vm->shared->modules_hash : &vm->modules_hash;
+
+    ret = njs_lvlhsh_insert(hash, &lhq);
     if (njs_fast_path(ret == NJS_OK)) {
         return module;
     }
@@ -622,13 +627,14 @@ njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     njs_string_get(path, &name);
 
-    module = njs_module_find(vm, &name, 1);
+    module = njs_module_find(vm, &name, 0);
     if (njs_slow_path(module == NULL)) {
         njs_error(vm, "Cannot find module \"%V\"", &name);
 
         return NJS_ERROR;
     }
 
-    njs_set_object(&vm->retval, &module->object);
+    njs_value_assign(&vm->retval, &module->value);
+
     return NJS_OK;
 }
index 95c9bc0335cbe3f94d49cdf3631c2a767aa5f559..210f37cf1a979551ff68d765ffc6d69a2bfe9d9a 100644 (file)
@@ -10,7 +10,7 @@
 
 typedef struct {
     njs_str_t                   name;
-    njs_object_t                object;
+    njs_value_t                 value;
     njs_index_t                 index;
     njs_function_t              function;
 } njs_mod_t;
@@ -18,12 +18,14 @@ typedef struct {
 
 njs_int_t njs_module_load(njs_vm_t *vm);
 void njs_module_reset(njs_vm_t *vm);
+njs_mod_t *njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared);
 njs_int_t njs_parser_module(njs_parser_t *parser, njs_lexer_token_t *token,
     njs_queue_link_t *current);
 njs_int_t njs_module_require(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused);
 
 
+extern njs_module_t              *njs_modules[];
 extern const njs_lvlhsh_proto_t  njs_modules_hash_proto;
 
 
diff --git a/src/njs_query_string.h b/src/njs_query_string.h
deleted file mode 100644 (file)
index 1cdbcee..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-
-/*
- * Copyright (C) Alexander Borisov
- * Copyright (C) NGINX, Inc.
- */
-
-#ifndef _NJS_QUERY_STRING_H_INCLUDED_
-#define _NJS_QUERY_STRING_H_INCLUDED_
-
-extern const njs_object_init_t  njs_query_string_object_init;
-
-#endif /* _NJS_QUERY_STRING_H_INCLUDED_ */
index 89dcede74397a05745e7f0776f229248bb894d35..3fdd256abbe138a0e7af5e8cba78adbb2c2aa851 100644 (file)
 
 #endif
 
-#if (NJS_HAVE_OPENSSL)
-#include "../external/njs_webcrypto.h"
-#include "../external/njs_webcrypto.c"
-#endif
-
 
 typedef struct {
     uint8_t                 disassemble;
@@ -91,7 +86,7 @@ typedef struct {
 
 
 static njs_int_t njs_console_init(njs_vm_t *vm, njs_console_t *console);
-static njs_int_t njs_externals_init(njs_vm_t *vm, njs_console_t *console);
+static njs_int_t njs_externals_init(njs_vm_t *vm);
 static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options);
 static njs_int_t njs_process_script(njs_opts_t *opts,
     njs_console_t *console, const njs_str_t *script);
@@ -211,6 +206,18 @@ static njs_vm_ops_t njs_console_ops = {
 };
 
 
+njs_module_t  njs_console_module = {
+    .name = njs_str("console"),
+    .init = njs_externals_init,
+};
+
+
+static njs_module_t *njs_console_addon_modules[] = {
+    &njs_console_module,
+    NULL,
+};
+
+
 static njs_int_t      njs_console_proto_id;
 
 
@@ -279,6 +286,7 @@ main(int argc, char **argv)
     vm_options.module = opts.module;
 
     vm_options.ops = &njs_console_ops;
+    vm_options.addons = njs_console_addon_modules;
     vm_options.external = &njs_console;
     vm_options.argv = opts.argv;
     vm_options.argc = opts.argc;
@@ -691,15 +699,18 @@ njs_console_init(njs_vm_t *vm, njs_console_t *console)
 
 
 static njs_int_t
-njs_externals_init(njs_vm_t *vm, njs_console_t *console)
+njs_externals_init(njs_vm_t *vm)
 {
-    njs_int_t    ret;
-    njs_value_t  *value, method;
+    njs_int_t      ret;
+    njs_value_t    *value, method;
+    njs_console_t  *console;
 
     static const njs_str_t  console_name = njs_str("console");
     static const njs_str_t  print_name = njs_str("print");
     static const njs_str_t  console_log = njs_str("console.log");
 
+    console = vm->options.external;
+
     njs_console_proto_id = njs_vm_external_prototype(vm, njs_ext_console,
                                          njs_nitems(njs_ext_console));
     if (njs_slow_path(njs_console_proto_id < 0)) {
@@ -737,13 +748,6 @@ njs_externals_init(njs_vm_t *vm, njs_console_t *console)
         return NJS_ERROR;
     }
 
-#if (NJS_HAVE_OPENSSL)
-    ret = njs_external_webcrypto_init(vm);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-#endif
-
     return NJS_OK;
 }
 
@@ -763,11 +767,6 @@ njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options)
         return NULL;
     }
 
-    if (njs_externals_init(vm, vm_options->external) != NJS_OK) {
-        njs_stderror("failed to add external protos\n");
-        return NULL;
-    }
-
     for (i = 0; i < opts->n_paths; i++) {
         path.start = (u_char *) opts->paths[i];
         path.length = njs_strlen(opts->paths[i]);
index 96485d1c45027fada5723b32b9645f32f8c45165..62da8136e010e237602a0453e2cba4760295b849 100644 (file)
@@ -67,8 +67,6 @@ typedef enum {
 typedef enum {
     NJS_DATA_TAG_ANY = 0,
     NJS_DATA_TAG_EXTERNAL,
-    NJS_DATA_TAG_CRYPTO_HASH,
-    NJS_DATA_TAG_CRYPTO_HMAC,
     NJS_DATA_TAG_TEXT_ENCODER,
     NJS_DATA_TAG_TEXT_DECODER,
     NJS_DATA_TAG_ARRAY_ITERATOR,
index b7a4fef2353a77491300de9c0d88f4ee11603b76..b46f88c628bc07a21a875afcb6698dfb4b273bd4 100644 (file)
@@ -29,9 +29,11 @@ njs_vm_opt_init(njs_vm_opt_t *options)
 njs_vm_t *
 njs_vm_create(njs_vm_opt_t *options)
 {
-    njs_mp_t   *mp;
-    njs_vm_t   *vm;
-    njs_int_t  ret;
+    njs_mp_t      *mp;
+    njs_vm_t      *vm;
+    njs_int_t     ret;
+    njs_uint_t    i;
+    njs_module_t  **addons;
 
     mp = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16);
     if (njs_slow_path(mp == NULL)) {
@@ -79,6 +81,23 @@ njs_vm_create(njs_vm_opt_t *options)
         }
     }
 
+    for (i = 0; njs_modules[i] != NULL; i++) {
+        ret = njs_modules[i]->init(vm);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NULL;
+        }
+    }
+
+    if (options->addons != NULL) {
+        addons = options->addons;
+        for (i = 0; addons[i] != NULL; i++) {
+            ret = addons[i]->init(vm);
+            if (njs_slow_path(ret != NJS_OK)) {
+                return NULL;
+            }
+        }
+    }
+
     vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
 
     if (njs_scope_undefined_index(vm, 0) == NJS_INDEX_ERROR) {
index 66104e6f37f1f3790e168a002d924bde9cac4b57..b8f09b56490761d263c1c8bfdae575175f54a373 100644 (file)
@@ -56,10 +56,6 @@ typedef enum {
 #define NJS_OBJ_TYPE_HIDDEN_MIN    (NJS_OBJ_TYPE_ITERATOR)
     NJS_OBJ_TYPE_ITERATOR,
     NJS_OBJ_TYPE_ARRAY_ITERATOR,
-    NJS_OBJ_TYPE_FS_DIRENT,
-    NJS_OBJ_TYPE_FS_STATS,
-    NJS_OBJ_TYPE_CRYPTO_HASH,
-    NJS_OBJ_TYPE_CRYPTO_HMAC,
     NJS_OBJ_TYPE_TYPED_ARRAY,
 #define NJS_OBJ_TYPE_HIDDEN_MAX    (NJS_OBJ_TYPE_TYPED_ARRAY + 1)
 #define NJS_OBJ_TYPE_NORMAL_MAX    (NJS_OBJ_TYPE_HIDDEN_MAX)
index 74cede078680948946179cfc837589056df2ee7c..68ec80f08f88b63a5dd7f355bd0f52a35bee9f5e 100644 (file)
@@ -8,11 +8,6 @@
 
 #include "njs_externals_test.h"
 
-#if (NJS_HAVE_OPENSSL)
-#include "../external/njs_webcrypto.h"
-#include "../external/njs_webcrypto.c"
-#endif
-
 
 typedef struct {
     njs_lvlhsh_t          hash;
@@ -864,15 +859,6 @@ njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init,
 njs_int_t
 njs_externals_shared_init(njs_vm_t *vm)
 {
-#if (NJS_HAVE_OPENSSL)
-    njs_int_t  ret;
-
-    ret = njs_external_webcrypto_init(vm);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
-    }
-#endif
-
     return njs_externals_init_internal(vm, njs_test_requests, 1, 1);
 }
 
index 4f13f14c4e957bbee8af564c002864f228288d3c..c8440d12978d1e54b4162c4c45b69ee4cc3d7ad3 100644 (file)
@@ -19136,10 +19136,6 @@ static njs_unit_test_t  njs_crypto_module_test[] =
               "Hash('sha1').update('AB').digest('hex')"),
       njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
 
-    { njs_str("var h = require('crypto').createHash('sha1');"
-              "h.constructor.name"),
-      njs_str("Hash") },
-
     { njs_str("var hash = require('crypto').createHash.bind(undefined, 'md5');"
               "['hex', 'base64', 'base64url'].map(e => {"
               "   var h = hash().update('AB').digest().toString(e);"
@@ -19319,10 +19315,6 @@ static njs_unit_test_t  njs_crypto_module_test[] =
               "Hmac('sha1', '').digest('hex')"),
       njs_str("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") },
 
-    { njs_str("var h = require('crypto').createHmac('sha1', '');"
-              "h.constructor.name"),
-      njs_str("Hmac") },
-
     { njs_str("require('crypto').createHmac('sha1', '').digest() instanceof Buffer"),
       njs_str("true") },
 
@@ -21563,7 +21555,7 @@ static njs_unit_test_t  njs_backtraces_test[] =
     { njs_str("var h = require('crypto').createHash('sha1');"
               "h.update([])"),
       njs_str("TypeError: data argument \"array\" is not a string or Buffer-like object\n"
-              "    at Hash.prototype.update (native)\n"
+              "    at Hash.update (native)\n"
               "    at main (:1)\n") },
 
     { njs_str("require('crypto').createHmac('sha1', [])"),
@@ -21574,7 +21566,7 @@ static njs_unit_test_t  njs_backtraces_test[] =
     { njs_str("var h = require('crypto').createHmac('sha1', 'secret');"
               "h.update([])"),
       njs_str("TypeError: data argument \"array\" is not a string or Buffer-like object\n"
-              "    at Hmac.prototype.update (native)\n"
+              "    at Hmac.update (native)\n"
               "    at main (:1)\n") },
 
     { njs_str("function f(o) {function f_in(o) {return o.a.a};"