]> git.kaiwu.me - njs.git/commitdiff
Nodejs style file methods.
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 17 Nov 2017 15:55:07 +0000 (18:55 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Fri, 17 Nov 2017 15:55:07 +0000 (18:55 +0300)
19 files changed:
Makefile
njs/njs_builtin.c
njs/njs_builtin.h
njs/njs_fs.c [new file with mode: 0644]
njs/njs_fs.h [new file with mode: 0644]
njs/njs_generator.c
njs/njs_lexer_keyword.c
njs/njs_module.c [new file with mode: 0644]
njs/njs_module.h [new file with mode: 0644]
njs/njs_object_hash.h
njs/njs_parser.c
njs/njs_parser.h
njs/njs_string.c
njs/njs_string.h
njs/njs_vm.h
njs/njscript.c
njs/test/njs_expect_test.exp
njs/test/njs_interactive_test.c
njs/test/njs_unit_test.c

index f73607de45e9652dc029743788aaa4c209e6d31f..ee524cfb4a81685312544622622c208fabbf2c7e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -22,6 +22,8 @@ $(NXT_BUILDDIR)/libnjs.a: \
        $(NXT_BUILDDIR)/njs_date.o \
        $(NXT_BUILDDIR)/njs_error.o \
        $(NXT_BUILDDIR)/njs_math.o \
+       $(NXT_BUILDDIR)/njs_module.o \
+       $(NXT_BUILDDIR)/njs_fs.o \
        $(NXT_BUILDDIR)/njs_extern.o \
        $(NXT_BUILDDIR)/njs_variable.o \
        $(NXT_BUILDDIR)/njs_builtin.o \
@@ -56,6 +58,8 @@ $(NXT_BUILDDIR)/libnjs.a: \
                $(NXT_BUILDDIR)/njs_date.o \
                $(NXT_BUILDDIR)/njs_error.o \
                $(NXT_BUILDDIR)/njs_math.o \
+               $(NXT_BUILDDIR)/njs_module.o \
+               $(NXT_BUILDDIR)/njs_fs.o \
                $(NXT_BUILDDIR)/njs_extern.o \
                $(NXT_BUILDDIR)/njs_variable.o \
                $(NXT_BUILDDIR)/njs_builtin.o \
@@ -299,6 +303,28 @@ $(NXT_BUILDDIR)/njs_math.o: \
                -I$(NXT_LIB) -Injs \
                njs/njs_math.c
 
+$(NXT_BUILDDIR)/njs_module.o: \
+       $(NXT_BUILDDIR)/libnxt.a \
+       njs/njscript.h \
+       njs/njs_vm.h \
+       njs/njs_module.h \
+       njs/njs_module.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_module.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) -Injs \
+               njs/njs_module.c
+
+$(NXT_BUILDDIR)/njs_fs.o: \
+       $(NXT_BUILDDIR)/libnxt.a \
+       njs/njscript.h \
+       njs/njs_vm.h \
+       njs/njs_fs.h \
+       njs/njs_fs.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_fs.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) -Injs \
+               njs/njs_fs.c
+
 $(NXT_BUILDDIR)/njs_extern.o: \
        $(NXT_BUILDDIR)/libnxt.a \
        njs/njscript.h \
@@ -332,6 +358,7 @@ $(NXT_BUILDDIR)/njs_builtin.o: \
        njs/njs_string.h \
        njs/njs_object.h \
        njs/njs_array.h \
+       njs/njs_module.h \
        njs/njs_function.h \
        njs/njs_regexp.h \
        njs/njs_parser.h \
index de7da438cf8da8dcd4ca90728aa9bcc9054dadb4..fa9378a2c37dbaf0fc69033a291b8f61df159720 100644 (file)
@@ -30,6 +30,8 @@
 #include <njs_date.h>
 #include <njs_error.h>
 #include <njs_math.h>
+#include <njs_module.h>
+#include <njs_fs.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -54,6 +56,11 @@ const njs_object_init_t    *njs_object_init[] = {
 };
 
 
+const njs_object_init_t    *njs_module_init[] = {
+    &njs_fs_object_init          /* fs                 */
+};
+
+
 const njs_object_init_t  *njs_prototype_init[] = {
     &njs_object_prototype_init,
     &njs_array_prototype_init,
@@ -111,8 +118,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
 {
     nxt_int_t               ret;
     nxt_uint_t              i;
+    njs_module_t            *module;
     njs_object_t            *objects;
     njs_function_t          *functions, *constructors;
+    nxt_lvlhsh_query_t      lhq;
     njs_object_prototype_t  *prototypes;
 
     static const njs_object_prototype_t  prototype_values[] = {
@@ -193,6 +202,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         NULL,                         /* encodeURIComponent */
         NULL,                         /* decodeURI          */
         NULL,                         /* decodeURIComponent */
+        NULL,                         /* require */
     };
 
     static const njs_function_init_t  native_functions[] = {
@@ -208,6 +218,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { njs_string_encode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } },
         { njs_string_decode_uri,           { NJS_SKIP_ARG, NJS_STRING_ARG } },
         { njs_string_decode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } },
+        { njs_module_require,              { NJS_SKIP_ARG, NJS_STRING_ARG } },
     };
 
     static const njs_object_prop_t    null_proto_property = {
@@ -249,6 +260,37 @@ njs_builtin_objects_create(njs_vm_t *vm)
         objects[i].shared = 1;
     }
 
+    lhq.replace = 0;
+    lhq.proto = &njs_modules_hash_proto;
+    lhq.pool = vm->mem_cache_pool;
+
+    for (i = NJS_MODULE_FS; i < NJS_MODULE_MAX; i++) {
+        module = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_module_t));
+        if (nxt_slow_path(module == NULL)) {
+            return NJS_ERROR;
+        }
+
+        module->name = njs_module_init[i]->name;
+
+        ret = njs_object_hash_create(vm, &module->object.shared_hash,
+                                     njs_module_init[i]->properties,
+                                     njs_module_init[i]->items);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+
+        module->object.shared = 1;
+
+        lhq.key = module->name;
+        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+        lhq.value = module;
+
+        ret = nxt_lvlhsh_insert(&vm->modules_hash, &lhq);
+        if (nxt_fast_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+    }
+
     functions = vm->shared->functions;
 
     for (i = NJS_FUNCTION_EVAL; i < NJS_FUNCTION_MAX; i++) {
@@ -857,10 +899,11 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
     size_t                  len;
     nxt_str_t               string;
     nxt_uint_t              i;
+    njs_module_t            *module;
     njs_object_t            *objects;
     njs_function_t          *constructors;
     njs_object_prop_t       *prop;
-    nxt_lvlhsh_each_t       lhe;
+    nxt_lvlhsh_each_t       lhe, lhe_prop;
     njs_object_prototype_t  *prototypes;
 
     objects = vm->shared->objects;
@@ -978,5 +1021,44 @@ njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function,
         }
     }
 
+    nxt_lvlhsh_each_init(&lhe, &njs_modules_hash_proto);
+
+    for ( ;; ) {
+        module = nxt_lvlhsh_each(&vm->modules_hash, &lhe);
+        if (module == NULL) {
+            break;
+        }
+
+        nxt_lvlhsh_each_init(&lhe_prop, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&module->object.shared_hash, &lhe_prop);
+            if (prop == NULL) {
+                break;
+            }
+
+            if (!njs_is_function(&prop->value)) {
+                continue;
+            }
+
+            if (function == prop->value.data.u.function) {
+                njs_string_get(&prop->name, &string);
+                len = module->name.length + string.length + sizeof(".");
+
+                buf = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (buf == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(buf, len, "%s.%s", module->name.start, string.start);
+
+                name->length = len;
+                name->start = (u_char *) buf;
+
+                return NXT_OK;
+            }
+        }
+    }
+
     return NXT_DECLINED;
 }
index bac1a8a52b06cc75eb3c8832a9790247c5c1ea71..a5169b3dd065cf57d10a5535587a8fa776904f99 100644 (file)
@@ -9,6 +9,7 @@
 
 
 extern const njs_object_init_t  *njs_object_init[];
+extern const njs_object_init_t  *njs_module_init[];
 extern const njs_object_init_t  *njs_prototype_init[];
 extern const njs_object_init_t  *njs_constructor_init[];
 
diff --git a/njs/njs_fs.c b/njs/njs_fs.c
new file mode 100644 (file)
index 0000000..ea71ed8
--- /dev/null
@@ -0,0 +1,1095 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_alignment.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_function.h>
+#include <njs_error.h>
+#include <njs_fs.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+
+
+typedef struct {
+    union {
+        njs_continuation_t  cont;
+        u_char              padding[NJS_CONTINUATION_SIZE];
+    } u;
+
+    nxt_bool_t              done;
+} njs_fs_cont_t;
+
+
+typedef struct {
+    nxt_str_t               name;
+    int                     value;
+} njs_fs_entry_t;
+
+
+static njs_ret_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_append_file(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_write_file(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags);
+static njs_ret_t njs_fs_write_file_sync_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags);
+static njs_ret_t njs_fs_done(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+
+static njs_ret_t njs_fs_error(njs_vm_t *vm, const char *syscall,
+    const char *description, njs_value_t *path, int errn, njs_value_t *retval);
+static int njs_fs_flags(nxt_str_t *value);
+static mode_t njs_fs_mode(njs_value_t *value);
+
+
+static const njs_value_t  njs_fs_errno_string = njs_string("errno");
+static const njs_value_t  njs_fs_path_string = njs_string("path");
+static const njs_value_t  njs_fs_syscall_string = njs_string("syscall");
+
+
+static njs_fs_entry_t njs_flags_table[] = {
+    { nxt_string("r"),   O_RDONLY },
+    { nxt_string("r+"),  O_RDWR },
+    { nxt_string("w"),   O_TRUNC  | O_CREAT  | O_WRONLY },
+    { nxt_string("w+"),  O_TRUNC  | O_CREAT  | O_RDWR },
+    { nxt_string("a"),   O_APPEND | O_CREAT  | O_WRONLY },
+    { nxt_string("a+"),  O_APPEND | O_CREAT  | O_RDWR },
+    { nxt_string("rs"),  O_SYNC   | O_RDONLY },
+    { nxt_string("sr"),  O_SYNC   | O_RDONLY },
+    { nxt_string("wx"),  O_TRUNC  | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("xw"),  O_TRUNC  | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("ax"),  O_APPEND | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("xa"),  O_APPEND | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("rs+"), O_SYNC   | O_RDWR },
+    { nxt_string("sr+"), O_SYNC   | O_RDWR },
+    { nxt_string("wx+"), O_TRUNC  | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_string("xw+"), O_TRUNC  | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_string("ax+"), O_APPEND | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_string("xa+"), O_APPEND | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_null_string, 0 }
+};
+
+
+static njs_ret_t
+njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *start, *end;
+    ssize_t             n, length;
+    nxt_str_t           flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    struct stat         sb;
+    njs_value_t         *callback, arguments[3];
+    njs_fs_cont_t       *cont;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 3)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (!njs_is_function(&args[2])) {
+        if (njs_is_string(&args[2])) {
+            njs_string_get(&args[2], &encoding);
+
+        } else if (njs_is_object(&args[2])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+
+        if (nxt_slow_path(nargs < 4 || !njs_is_function(&args[3]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[3];
+
+    } else {
+        if (nxt_slow_path(!njs_is_function(&args[2]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[2];
+    }
+
+    if (flag.start == NULL) {
+        flag = nxt_string_value("r");
+    }
+
+    flags = njs_fs_flags(&flag);
+    if (nxt_slow_path(flags == -1)) {
+        njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                 (int) flag.length, flag.start);
+        return NJS_ERROR;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    ret = fstat(fd, &sb);
+    if (nxt_slow_path(ret == -1)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "stat";
+        goto done;
+    }
+
+    if (nxt_slow_path(!S_ISREG(sb.st_mode))) {
+        errn = 0;
+        description = "File is not regular";
+        syscall = "stat";
+        goto done;
+    }
+
+    if (encoding.length != 0) {
+        length = sb.st_size;
+
+    } else {
+        length = 0;
+    }
+
+    start = njs_string_alloc(vm, &arguments[2], sb.st_size, length);
+    if (nxt_slow_path(start == NULL)) {
+        goto memory_error;
+    }
+
+    p = start;
+    end = p + sb.st_size;
+
+    while (p < end) {
+        n = read(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "read";
+            goto done;
+        }
+
+        p += n;
+    }
+
+    if (encoding.length != 0) {
+        length = nxt_utf8_length(start, sb.st_size);
+
+        if (length >= 0) {
+            njs_string_offset_map_init(start, sb.st_size);
+            njs_string_length_set(&arguments[2], length);
+
+        } else {
+            errn = 0;
+            description = "Non-UTF8 file, convertion is not implemented";
+            syscall = NULL;
+            goto done;
+        }
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        ret = njs_fs_error(vm, syscall, description, &args[1], errn,
+                           &arguments[1]);
+
+        if (nxt_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        arguments[2] = njs_value_void;
+
+    } else {
+        arguments[1] = njs_value_void;
+    }
+
+    arguments[0] = njs_value_void;
+
+    cont = njs_vm_continuation(vm);
+    cont->u.cont.function = njs_fs_done;
+
+    return njs_function_apply(vm, callback->data.u.function,
+                              arguments, 3, (njs_index_t) &vm->retval);
+
+memory_error:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    njs_exception_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_ret_t
+njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *start, *end;
+    ssize_t             n, length;
+    nxt_str_t           flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    struct stat         sb;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 2)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (nargs == 3) {
+        if (njs_is_string(&args[2])) {
+            njs_string_get(&args[2], &encoding);
+
+        } else if (njs_is_object(&args[2])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+    }
+
+    if (flag.start == NULL) {
+        flag = nxt_string_value("r");
+    }
+
+    flags = njs_fs_flags(&flag);
+    if (nxt_slow_path(flags == -1)) {
+        njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                 (int) flag.length, flag.start);
+        return NJS_ERROR;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    ret = fstat(fd, &sb);
+    if (nxt_slow_path(ret == -1)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "stat";
+        goto done;
+    }
+
+    if (nxt_slow_path(!S_ISREG(sb.st_mode))) {
+        errn = 0;
+        description = "File is not regular";
+        syscall = "stat";
+        goto done;
+    }
+
+    if (encoding.length != 0) {
+        length = sb.st_size;
+
+    } else {
+        length = 0;
+    }
+
+    start = njs_string_alloc(vm, &vm->retval, sb.st_size, length);
+    if (nxt_slow_path(start == NULL)) {
+        goto memory_error;
+    }
+
+    p = start;
+    end = p + sb.st_size;
+
+    while (p < end) {
+        n = read(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "read";
+            goto done;
+        }
+
+        p += n;
+    }
+
+    if (encoding.length != 0) {
+        length = nxt_utf8_length(start, sb.st_size);
+
+        if (length >= 0) {
+            njs_string_offset_map_init(start, sb.st_size);
+            njs_string_length_set(&vm->retval, length);
+
+        } else {
+            errn = 0;
+            description = "Non-UTF8 file, convertion is not implemented";
+            syscall = NULL;
+            goto done;
+        }
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        (void) njs_fs_error(vm, syscall, description, &args[1], errn,
+                            &vm->retval);
+
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+
+memory_error:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    njs_exception_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_ret_t
+njs_fs_append_file(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    return njs_fs_write_file_internal(vm, args, nargs,
+                                      O_APPEND | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t
+njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    return njs_fs_write_file_internal(vm, args, nargs,
+                                      O_TRUNC | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_fs_write_file_sync_internal(vm, args, nargs,
+                                           O_APPEND | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_fs_write_file_sync_internal(vm, args, nargs,
+                                           O_TRUNC | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *end;
+    mode_t              md;
+    ssize_t             n;
+    nxt_str_t           data, flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    njs_value_t         *callback, *mode, arguments[2];
+    njs_fs_cont_t       *cont;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 4)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[2]))) {
+        njs_exception_type_error(vm, "data must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    mode = NULL;
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (!njs_is_function(&args[3])) {
+        if (njs_is_string(&args[3])) {
+            njs_string_get(&args[3], &encoding);
+
+        } else if (njs_is_object(&args[3])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+            lhq.key_hash = NJS_MODE_HASH;
+            lhq.key = nxt_string_value("mode");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                mode = &prop->value;
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+
+        if (nxt_slow_path(nargs < 5 || !njs_is_function(&args[4]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[4];
+
+    } else {
+        if (nxt_slow_path(!njs_is_function(&args[3]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[3];
+    }
+
+    if (flag.start != NULL) {
+        flags = njs_fs_flags(&flag);
+        if (nxt_slow_path(flags == -1)) {
+            njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                     (int) flag.length, flag.start);
+            return NJS_ERROR;
+        }
+
+    } else {
+        flags = default_flags;
+    }
+
+    if (mode != NULL) {
+        md = njs_fs_mode(mode);
+
+    } else {
+        md = 0666;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags, md);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    njs_string_get(&args[2], &data);
+
+    p = data.start;
+    end = p + data.length;
+
+    while (p < end) {
+        n = write(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "write";
+            goto done;
+        }
+
+        p += n;
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        ret = njs_fs_error(vm, syscall, description, &args[1], errn,
+                           &arguments[1]);
+
+        if (nxt_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+    } else {
+        arguments[1] = njs_value_void;
+    }
+
+    arguments[0] = njs_value_void;
+
+    cont = njs_vm_continuation(vm);
+    cont->u.cont.function = njs_fs_done;
+
+    return njs_function_apply(vm, callback->data.u.function,
+                              arguments, 2, (njs_index_t) &vm->retval);
+}
+
+
+static njs_ret_t
+njs_fs_write_file_sync_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *end;
+    mode_t              md;
+    ssize_t             n;
+    nxt_str_t           data, flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    njs_value_t         *mode;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 3)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[2]))) {
+        njs_exception_type_error(vm, "data must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    mode = NULL;
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (nargs == 4) {
+        if (njs_is_string(&args[3])) {
+            njs_string_get(&args[3], &encoding);
+
+        } else if (njs_is_object(&args[3])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+            lhq.key_hash = NJS_MODE_HASH;
+            lhq.key = nxt_string_value("mode");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                mode = &prop->value;
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+    }
+
+    if (flag.start != NULL) {
+        flags = njs_fs_flags(&flag);
+        if (nxt_slow_path(flags == -1)) {
+            njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                     (int) flag.length, flag.start);
+            return NJS_ERROR;
+        }
+
+    } else {
+        flags = default_flags;
+    }
+
+    if (mode != NULL) {
+        md = njs_fs_mode(mode);
+
+    } else {
+        md = 0666;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags, md);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    njs_string_get(&args[2], &data);
+
+    p = data.start;
+    end = p + data.length;
+
+    while (p < end) {
+        n = write(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "write";
+            goto done;
+        }
+
+        p += n;
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        ret = njs_fs_error(vm, syscall, description, &args[1], errn,
+                           &vm->retval);
+
+        if (nxt_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+    } else {
+        vm->retval = njs_value_void;
+    }
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t njs_fs_done(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    vm->retval = njs_value_void;
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t njs_fs_error(njs_vm_t *vm, const char *syscall,
+    const char *description, njs_value_t *path, int errn, njs_value_t *retval)
+{
+    size_t              size;
+    nxt_int_t           ret;
+    njs_value_t         string, value;
+    njs_object_t        *error;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    size = description != NULL ? strlen(description) : 0;
+
+    ret = njs_string_new(vm, &string, (u_char *) description, size, size);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        goto memory_error;
+    }
+
+    error = njs_error_alloc(vm, NJS_OBJECT_ERROR, NULL, &string);
+    if (nxt_slow_path(error == NULL)) {
+        goto memory_error;
+    }
+
+    lhq.replace = 0;
+    lhq.pool = vm->mem_cache_pool;
+
+    if (errn != 0) {
+        lhq.key = nxt_string_value("errno");
+        lhq.key_hash = NJS_ERRNO_HASH;
+        lhq.proto = &njs_object_hash_proto;
+
+        value.data.type = NJS_NUMBER;
+        value.data.truth = 1;
+        value.data.u.number = errn;
+
+        prop = njs_object_prop_alloc(vm, &njs_fs_errno_string, &value, 1);
+        if (nxt_slow_path(prop == NULL)) {
+            goto memory_error;
+        }
+
+        lhq.value = prop;
+
+        ret = nxt_lvlhsh_insert(&error->hash, &lhq);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_exception_internal_error(vm, NULL, NULL);
+            return NJS_ERROR;
+        }
+    }
+
+    if (path != NULL) {
+        lhq.key = nxt_string_value("path");
+        lhq.key_hash = NJS_PATH_HASH;
+        lhq.proto = &njs_object_hash_proto;
+
+        prop = njs_object_prop_alloc(vm, &njs_fs_path_string, path, 1);
+        if (nxt_slow_path(prop == NULL)) {
+            goto memory_error;
+        }
+
+        lhq.value = prop;
+
+        ret = nxt_lvlhsh_insert(&error->hash, &lhq);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_exception_internal_error(vm, NULL, NULL);
+            return NJS_ERROR;
+        }
+    }
+
+    if (syscall != NULL) {
+        size = strlen(syscall);
+        ret = njs_string_new(vm, &string, (u_char *) syscall, size, size);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            goto memory_error;
+        }
+
+        lhq.key = nxt_string_value("sycall");
+        lhq.key_hash = NJS_SYSCALL_HASH;
+        lhq.proto = &njs_object_hash_proto;
+
+        prop = njs_object_prop_alloc(vm, &njs_fs_syscall_string, &string, 1);
+        if (nxt_slow_path(prop == NULL)) {
+            goto memory_error;
+        }
+
+        lhq.value = prop;
+
+        ret = nxt_lvlhsh_insert(&error->hash, &lhq);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            njs_exception_internal_error(vm, NULL, NULL);
+            return NJS_ERROR;
+        }
+    }
+
+    retval->data.u.object = error;
+    retval->type = NJS_OBJECT_ERROR;
+    retval->data.truth = 1;
+
+    return NJS_OK;
+
+memory_error:
+
+    njs_exception_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static int
+njs_fs_flags(nxt_str_t *value)
+{
+    njs_fs_entry_t  *fl;
+
+    for (fl = &njs_flags_table[0]; fl->name.length != 0; fl++) {
+        if (nxt_strstr_eq(value, &fl->name)) {
+            return fl->value;
+        }
+    }
+
+    return -1;
+}
+
+
+static mode_t
+njs_fs_mode(njs_value_t *value)
+{
+    switch (value->type) {
+    case NJS_OBJECT_NUMBER:
+        value = &value->data.u.object_value->value;
+        /* Fall through. */
+
+    case NJS_NUMBER:
+        return (mode_t) value->data.u.number;
+
+    case NJS_OBJECT_STRING:
+    value = &value->data.u.object_value->value;
+        /* Fall through. */
+
+    case NJS_STRING:
+        return (mode_t) njs_string_to_number(value, 0);
+
+    default:
+        return (mode_t) 0;
+    }
+}
+
+
+static const njs_object_prop_t  njs_fs_object_properties[] =
+{
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("readFile"),
+        .value = njs_native_function(njs_fs_read_file,
+                                     njs_continuation_size(njs_fs_cont_t), 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("readFileSync"),
+        .value = njs_native_function(njs_fs_read_file_sync, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("appendFile"),
+        .value = njs_native_function(njs_fs_append_file,
+                                     njs_continuation_size(njs_fs_cont_t), 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("appendFileSync"),
+        .value = njs_native_function(njs_fs_append_file_sync,
+                                     njs_continuation_size(njs_fs_cont_t), 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("writeFile"),
+        .value = njs_native_function(njs_fs_write_file,
+                                     njs_continuation_size(njs_fs_cont_t), 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("writeFileSync"),
+        .value = njs_native_function(njs_fs_write_file_sync,
+                                     njs_continuation_size(njs_fs_cont_t), 0),
+    },
+
+};
+
+
+const njs_object_init_t  njs_fs_object_init = {
+    nxt_string("fs"),
+    njs_fs_object_properties,
+    nxt_nitems(njs_fs_object_properties),
+};
diff --git a/njs/njs_fs.h b/njs/njs_fs.h
new file mode 100644 (file)
index 0000000..89e7fa6
--- /dev/null
@@ -0,0 +1,13 @@
+
+/*
+ * 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;
+
+
+#endif /* _NJS_FS_H_INCLUDED_ */
index 885fc95899d3ee3f69af289f09a0dd2fc07c9800..670580b372bd38253b7019cfbd52da38009bb3fd 100644 (file)
@@ -319,6 +319,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_ENCODE_URI_COMPONENT:
     case NJS_TOKEN_DECODE_URI:
     case NJS_TOKEN_DECODE_URI_COMPONENT:
+    case NJS_TOKEN_REQUIRE:
         return njs_generate_builtin_object(vm, parser, node);
 
     case NJS_TOKEN_FUNCTION:
index 3cd847630cbb91ea5bd36fc60b6fc0435799f097..88bb0f307c41ec029c347d7904096c3cd7a54dbf 100644 (file)
@@ -101,6 +101,7 @@ static const njs_keyword_t  njs_keywords[] = {
     { nxt_string("encodeURIComponent"),  NJS_TOKEN_ENCODE_URI_COMPONENT, 0 },
     { nxt_string("decodeURI"),     NJS_TOKEN_DECODE_URI, 0 },
     { nxt_string("decodeURIComponent"),  NJS_TOKEN_DECODE_URI_COMPONENT, 0 },
+    { nxt_string("require"),      NJS_TOKEN_REQUIRE, 0 },
 
     /* Reserved words. */
 
diff --git a/njs/njs_module.c b/njs/njs_module.c
new file mode 100644 (file)
index 0000000..b44c743
--- /dev/null
@@ -0,0 +1,85 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_number.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_function.h>
+#include <njs_error.h>
+#include <njs_module.h>
+#include <string.h>
+#include <stdio.h>
+
+
+static nxt_int_t
+njs_module_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+    njs_module_t  *module;
+
+    module = data;
+
+    if (nxt_strstr_eq(&lhq->key, &module->name)) {
+        return NXT_OK;
+    }
+
+    return NXT_DECLINED;
+}
+
+
+const nxt_lvlhsh_proto_t  njs_modules_hash_proto
+    nxt_aligned(64) =
+{
+    NXT_LVLHSH_DEFAULT,
+    0,
+    njs_module_hash_test,
+    njs_lvlhsh_alloc,
+    njs_lvlhsh_free,
+};
+
+
+njs_ret_t njs_module_require(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    njs_module_t        *module;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nargs < 2) {
+        njs_exception_type_error(vm, "missing path", NULL);
+        return NJS_ERROR;
+    }
+
+    njs_string_get(&args[1], &lhq.key);
+    lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+    lhq.proto = &njs_modules_hash_proto;
+
+    if (nxt_lvlhsh_find(&vm->modules_hash, &lhq) == NXT_OK) {
+        module = lhq.value;
+        module->object.__proto__ = &vm->prototypes[NJS_PROTOTYPE_OBJECT].object;
+
+        vm->retval.data.u.object = &module->object;
+        vm->retval.type = NJS_OBJECT;
+        vm->retval.data.truth = 1;
+
+        return NXT_OK;
+    }
+
+    njs_exception_error(vm, "Cannot find module '%.*s'", (int) lhq.key.length,
+                        lhq.key.start);
+
+    return NJS_ERROR;
+}
diff --git a/njs/njs_module.h b/njs/njs_module.h
new file mode 100644 (file)
index 0000000..441e2a7
--- /dev/null
@@ -0,0 +1,22 @@
+
+/*
+ * Copyright (C) Dmitry Volynsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_MODULE_H_INCLUDED_
+#define _NJS_MODULE_H_INCLUDED_
+
+typedef struct {
+    nxt_str_t                   name;
+    njs_object_t                object;
+} njs_module_t;
+
+
+njs_ret_t njs_module_require(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+
+extern const nxt_lvlhsh_proto_t  njs_modules_hash_proto;
+
+
+#endif /* _NJS_MODULE_H_INCLUDED_ */
index eb8ec08357d1f396f0d7dfb87b4003aaf9c27ebc..8b7ce3ac600be3b7bbcd9d4dc3c5f07fada97553 100644 (file)
         'e'), 'n'), 'u'), 'm'), 'e'), 'r'), 'a'), 'b'), 'l'), 'e')
 
 
+#define NJS_ERRNO_HASH                                                        \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        'e'), 'r'), 'r'), 'n'), 'o')
+
+
+#define NJS_ENCODING_HASH                                                     \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        'e'), 'n'), 'c'), 'o'), 'd'), 'i'), 'n'), 'g')
+
+
+#define NJS_FLAG_HASH                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        'f'), 'l'), 'a'), 'g')
+
+
 #define NJS_INDEX_HASH                                                        \
     nxt_djb_hash_add(                                                         \
     nxt_djb_hash_add(                                                         \
         'm'), 'e'), 's'), 's'), 'a'), 'g'), 'e')
 
 
+#define NJS_MODE_HASH                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        'm'), 'o'), 'd'), 'e')
+
+
+#define NJS_SYSCALL_HASH                                                      \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        's'), 'y'), 's'), 'c'), 'a'), 'l'), 'l')
+
+
+#define NJS_PATH_HASH                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(                                                         \
+    nxt_djb_hash_add(NXT_DJB_HASH_INIT,                                       \
+        'p'), 'a'), 't'), 'h')
+
+
 #define NJS_PROTOTYPE_HASH                                                    \
     nxt_djb_hash_add(                                                         \
     nxt_djb_hash_add(                                                         \
index d97e0827f7f6d92786cd7c4d6d428b8f65ad35ea..1cdc6445a8a0aae211f012ab188fb469e5727ba2 100644 (file)
@@ -2071,6 +2071,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token)
     case NJS_TOKEN_ENCODE_URI_COMPONENT:
     case NJS_TOKEN_DECODE_URI:
     case NJS_TOKEN_DECODE_URI_COMPONENT:
+    case NJS_TOKEN_REQUIRE:
         return njs_parser_builtin_function(vm, parser, node);
 
     default:
index f2fd25c494d94014144d8c13eaf6ae8102d56de5..149ce2834fb2dcf38b6dcd167b3585c052972c8e 100644 (file)
@@ -198,6 +198,7 @@ typedef enum {
     NJS_TOKEN_ENCODE_URI_COMPONENT,
     NJS_TOKEN_DECODE_URI,
     NJS_TOKEN_DECODE_URI_COMPONENT,
+    NJS_TOKEN_REQUIRE,
 
     NJS_TOKEN_RESERVED,
 } njs_token_t;
index 915737386b3388db2fff5b93aae490d95cab6022..cfd278d751b448ba8bf2b97b396bba72067dfa14 100644 (file)
@@ -17,6 +17,7 @@
 #include <nxt_random.h>
 #include <nxt_pcre.h>
 #include <nxt_malloc.h>
+#include <nxt_string.h>
 #include <nxt_mem_cache_pool.h>
 #include <njscript.h>
 #include <njs_vm.h>
@@ -3062,6 +3063,51 @@ njs_string_to_index(njs_value_t *value)
 }
 
 
+/*
+ * If string value is null-terminated the corresponding C string
+ * is returned as is, otherwise the new copy is allocated with
+ * the terminating zero byte.
+ */
+u_char *
+njs_string_to_c_string(njs_vm_t *vm, njs_value_t *value)
+{
+    u_char  *p, *data, *start;
+    size_t  size;
+
+    if (value->short_string.size != NJS_STRING_LONG) {
+        start = value->short_string.start;
+        size = value->short_string.size;
+
+        if (start[size] == '\0') {
+            return start;
+
+        } else if (size < NJS_STRING_SHORT) {
+            start[size] = '\0';
+            return start;
+        }
+
+    } else {
+        start = value->data.u.string->start;
+        size = value->data.string_size;
+
+        if (start[size] == '\0') {
+            return start;
+        }
+    }
+
+    data = nxt_mem_cache_alloc(vm->mem_cache_pool, size + 1);
+    if (nxt_slow_path(data == NULL)) {
+        njs_exception_memory_error(vm);
+        return NULL;
+    }
+
+    p = nxt_cpymem(data, start, size);
+    *p++ = '\0';
+
+    return data;
+}
+
+
 static const njs_object_prop_t  njs_string_prototype_properties[] =
 {
     {
index 0f605c3c204c3a0f86cc6cdba5f9cba4a3a1823a..4b282ff399f5991c09fbb857b7751ecb17c3b29f 100644 (file)
@@ -144,6 +144,7 @@ njs_ret_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
     const njs_value_t *src);
 double njs_string_to_index(njs_value_t *value);
 double njs_string_to_number(njs_value_t *value, nxt_bool_t parse_float);
+u_char *njs_string_to_c_string(njs_vm_t *vm, njs_value_t *value);
 njs_ret_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
 njs_ret_t njs_string_encode_uri_component(njs_vm_t *vm, njs_value_t *args,
index 00c96a1f82197eed73eb55f6a5d2481452c3dfd0..76abbf0792c38da50eede1161b6263071995ea4f 100644 (file)
@@ -449,6 +449,16 @@ typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
     } while (0)
 
 
+#define njs_string_length_set(value, _length)                                 \
+    do {                                                                      \
+        if ((value)->short_string.size != NJS_STRING_LONG) {                  \
+            (value)->short_string.length = length;                            \
+                                                                              \
+        } else {                                                              \
+            (value)->data.u.string->length = length;                          \
+        }                                                                     \
+    } while (0)
+
 #define njs_is_primitive(value)                                               \
     ((value)->type <= NJS_STRING)
 
@@ -836,6 +846,12 @@ enum njs_object_e {
 };
 
 
+enum njs_module_e {
+    NJS_MODULE_FS = 0,
+#define NJS_MODULE_MAX         (NJS_MODULE_FS + 1)
+};
+
+
 enum njs_function_e {
     NJS_FUNCTION_EVAL = 0,
     NJS_FUNCTION_TO_STRING,
@@ -847,7 +863,8 @@ enum njs_function_e {
     NJS_FUNCTION_STRING_ENCODE_URI_COMPONENT,
     NJS_FUNCTION_STRING_DECODE_URI,
     NJS_FUNCTION_STRING_DECODE_URI_COMPONENT,
-#define NJS_FUNCTION_MAX       (NJS_FUNCTION_STRING_DECODE_URI_COMPONENT + 1)
+    NJS_FUNCTION_REQUIRE,
+#define NJS_FUNCTION_MAX       (NJS_FUNCTION_REQUIRE + 1)
 };
 
 
@@ -934,6 +951,7 @@ struct njs_vm_s {
     nxt_lvlhsh_t             externals_hash;
     nxt_lvlhsh_t             variables_hash;
     nxt_lvlhsh_t             values_hash;
+    nxt_lvlhsh_t             modules_hash;
 
     /*
      * The prototypes and constructors arrays must be together because
index 6c6d23a7e41fadb005f6ed9bf7154076a10f4495..ccafbaf7d45eea61c227dbdc67445769afeed69f 100644 (file)
@@ -159,6 +159,8 @@ njs_vm_create(njs_vm_opt_t *options)
 
             vm->shared->empty_regexp_pattern = pattern;
 
+            nxt_lvlhsh_init(&vm->modules_hash);
+
             ret = njs_builtin_objects_create(vm);
             if (nxt_slow_path(ret != NXT_OK)) {
                 return NULL;
@@ -324,6 +326,7 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache_pool_t *mcp, void **external)
 
         nvm->variables_hash = vm->variables_hash;
         nvm->values_hash = vm->values_hash;
+        nvm->modules_hash = vm->modules_hash;
         nvm->externals_hash = vm->externals_hash;
 
         nvm->current = vm->current;
index 0a4fae3f25fea5a7e584ff0f2930f491c9b5a8f3..a80c08920698764c8b1a30f6d3f15e78228853b6 100644 (file)
@@ -182,3 +182,224 @@ njs_test {
     {"JSON.parse(Error()\r\n"
      "JSON.parse(Error()\r\nSyntaxError: Unexpected token \"\" in 1"}
 }
+
+# require('fs')
+
+set file [open /tmp/njs_test_file w]
+puts -nonewline $file "αβZγ"
+flush $file
+
+exec /bin/echo -ne {\x80\x80} > /tmp/njs_test_file_non_utf8
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.read\t"
+     "fs.read\aFile"}
+}
+
+# require('fs').readFile()
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFile('/tmp/njs_test_file', 'utf8', function (e, data) {console.log(data[2]+data.length)})\r\n"
+     "Z4\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFile('/tmp/njs_test_file', function (e, data) {console.log(data[4]+data.length)})\r\n"
+     "Z7\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFile('/tmp/njs_test_file', {encoding:'utf8',flag:'r+'}, function (e, data) {console.log(data)})\r\n"
+     "αβZγ\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs'); \r\n"
+     "undefined\r\n>> "}
+    {"fs.readFile('/tmp/njs_unknown_path', 'utf8', function (e) {console.log(JSON.stringify(e))})\r\n"
+     "{\"errno\":2,\"path\":\"/tmp/njs_unknown_path\",\"syscall\":\"open\"}\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs'); \r\n"
+     "undefined\r\n>> "}
+    {"fs.readFile('/tmp/njs_unknown_path', {encoding:'utf8', flag:'r+'}, function (e) {console.log(e)})\r\n"
+     "Error: No such file or directory\r\nundefined\r\n>> "}
+}
+
+# require('fs').readFileSync()
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file', 'utf8')[2]\r\n"
+     "Z\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file')[4]\r\n"
+     "Z\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file', {encoding:'utf8',flag:'r+'})\r\n"
+     "αβZγ\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs'); \r\n"
+     "undefined\r\n>> "}
+    {"try { fs.readFileSync('/tmp/njs_unknown_path')} catch (e) {console.log(JSON.stringify(e))}\r\n"
+     "{\"errno\":2,\"path\":\"/tmp/njs_unknown_path\",\"syscall\":\"open\"}\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file_non_utf8').charCodeAt(1)\r\n"
+     "128"}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file_non_utf8', 'utf8')\r\n"
+     "Error: Non-UTF8 file, convertion is not implemented"}
+}
+
+
+# require('fs').writeFile()
+
+exec rm -fr /tmp/njs_test_file2
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"function h1(e) {if (e) {throw e}; console.log(fs.readFileSync('/tmp/njs_test_file2'))}\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFile('/tmp/njs_test_file2', 'ABC', h1)\r\n"
+     "ABC\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFile('/tmp/njs_test_file2', 'ABC', 'utf8', function (e) { if (e) {throw e}; console.log(fs.readFileSync('/tmp/njs_test_file2'))})\r\n"
+     "ABC\r\nundefined\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFile('/tmp/njs_test_file2', 'ABC', {encoding:'utf8', mode:0o666}, function (e) { if (e) {throw e}; console.log(fs.readFileSync('/tmp/njs_test_file2'))})\r\n"
+     "ABC\r\nundefined\r\n>> "}
+}
+
+exec rm -fr /tmp/njs_wo_file
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFile('/tmp/njs_wo_file', 'ABC', {mode:0o222}, function (e) {console.log(fs.readFileSync('/tmp/njs_wo_file'))})\r\n"
+     "Error: Permission denied"}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFile('/invalid_path', 'ABC', function (e) { console.log(JSON.stringify(e))})\r\n"
+     "{\"errno\":13,\"path\":\"/invalid_path\",\"syscall\":\"open\"}\r\nundefined\r\n>> "}
+}
+
+# require('fs').writeFileSync()
+
+exec rm -fr /tmp/njs_test_file2
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFileSync('/tmp/njs_test_file2', 'ABC')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file2')\r\n"
+     "ABC\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFileSync('/tmp/njs_test_file2', 'ABC', 'utf8')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file2')\r\n"
+     "ABC\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFileSync('/tmp/njs_test_file2', 'ABC')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFileSync('/tmp/njs_test_file2', 'ABC')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file2')\r\n"
+     "ABC\r\n>> "}
+}
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFileSync('/tmp/njs_test_file2', 'ABC', {encoding:'utf8', mode:0o666})\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file2')\r\n"
+     "ABC\r\n>> "}
+}
+
+exec rm -fr /tmp/njs_wo_file
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.writeFileSync('/tmp/njs_wo_file', 'ABC', {mode:0o222}); fs.readFileSync('/tmp/njs_wo_file')\r\n"
+     "Error: Permission denied"}
+}
+
+# require('fs').appendFile()
+
+exec rm -fr /tmp/njs_test_file2
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"function h1(e) {console.log(fs.readFileSync('/tmp/njs_test_file2'))}\r\n"
+     "undefined\r\n>> "}
+    {"function h2(e) {fs.appendFile('/tmp/njs_test_file2', 'ABC', h1)}\r\n"
+     "undefined\r\n>> "}
+    {"fs.appendFile('/tmp/njs_test_file2', 'ABC', h2)\r\n"
+     "ABCABC\r\nundefined\r\n>> "}
+}
+
+# require('fs').appendFileSync()
+
+exec rm -fr /tmp/njs_test_file2
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.appendFileSync('/tmp/njs_test_file2', 'ABC')\r\n"
+     "undefined\r\n>> "}
+    {"fs.appendFileSync('/tmp/njs_test_file2', 'ABC')\r\n"
+     "undefined\r\n>> "}
+    {"fs.readFileSync('/tmp/njs_test_file2')\r\n"
+     "ABCABC\r\n>> "}
+}
index 25030f4bb79c5926b6821f58b4e1ec85483f49b7..8a1ad75db8690190c797391c4b9debf6feaca7de 100644 (file)
@@ -178,6 +178,11 @@ static njs_interactive_test_t  njs_test[] =
                  "    at f (:1)\n"
                  "    at main (native)\n") },
 
+    { nxt_string("var fs = require('fs'); fs.readFile()" ENTER),
+      nxt_string("TypeError: too few arguments\n"
+                 "    at fs.readFile (native)\n"
+                 "    at main (native)\n") },
+
     /* Exception in njs_vm_retval() */
 
     { nxt_string("var o = { toString: function() { return [1] } }" ENTER
index da64050954d177ae73184e9816abb1e32f799f09..c2fb830f4de03b05811c7374ac8a7161af3955aa 100644 (file)
@@ -8750,6 +8750,142 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = {}; a.a = a; JSON.stringify(a)"),
       nxt_string("TypeError: Nested too deep or a cyclic structure") },
 
+    /* require(). */
+
+    { nxt_string("require('unknown_module')"),
+      nxt_string("Error: Cannot find module 'unknown_module'") },
+
+    { nxt_string("require()"),
+      nxt_string("TypeError: missing path") },
+
+    { nxt_string("var fs = require('fs'); typeof fs"),
+      nxt_string("object") },
+
+    /* require('fs').readFile() */
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile()"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile('/njs_unknown_path')"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile('/njs_unknown_path', {flag:'xx'})"),
+      nxt_string("TypeError: callback must be a function") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"),
+      nxt_string("TypeError: callback must be a function") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"),
+      nxt_string("TypeError: Unknown file open flags: 'xx'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile('/njs_unknown_path', {encoding:'ascii'}, function () {})"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFile('/njs_unknown_path', 'ascii', function () {})"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    /* require('fs').readFileSync() */
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFileSync()"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFileSync({})"),
+      nxt_string("TypeError: path must be a string") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFileSync('/njs_unknown_path', {flag:'xx'})"),
+      nxt_string("TypeError: Unknown file open flags: 'xx'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFileSync('/njs_unknown_path', {encoding:'ascii'})"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFileSync('/njs_unknown_path', 'ascii')"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.readFileSync('/njs_unknown_path', true)"),
+      nxt_string("TypeError: Unknown options type (a string or object required)") },
+
+
+    /* require('fs').writeFile() */
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile()"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path')"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path', '')"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile({}, '', function () {})"),
+      nxt_string("TypeError: path must be a string") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path', '', 'utf8')"),
+      nxt_string("TypeError: callback must be a function") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"),
+      nxt_string("TypeError: Unknown file open flags: 'xx'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path', '', {encoding:'ascii'}, function () {})"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path', '', 'ascii', function () {})"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFile('/njs_unknown_path', '', true, function () {})"),
+      nxt_string("TypeError: Unknown options type (a string or object required)") },
+
+    /* require('fs').writeFileSync() */
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync()"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync('/njs_unknown_path')"),
+      nxt_string("TypeError: too few arguments") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync({}, '')"),
+      nxt_string("TypeError: path must be a string") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync('/njs_unknown_path', '', {flag:'xx'})"),
+      nxt_string("TypeError: Unknown file open flags: 'xx'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync('/njs_unknown_path', '', {encoding:'ascii'})"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync('/njs_unknown_path', '', 'ascii')"),
+      nxt_string("TypeError: Unknown encoding: 'ascii'") },
+
+    { nxt_string("var fs = require('fs');"
+                 "fs.writeFileSync('/njs_unknown_path', '', true)"),
+      nxt_string("TypeError: Unknown options type (a string or object required)") },
+
     /* Trick: number to boolean. */
 
     { nxt_string("var a = 0; !!a"),