]> git.kaiwu.me - njs.git/commitdiff
Adding support for Buffer objects in "fs" methods.
authorDmitry Volyntsev <xeioex@nginx.com>
Mon, 28 Sep 2020 17:45:44 +0000 (17:45 +0000)
committerDmitry Volyntsev <xeioex@nginx.com>
Mon, 28 Sep 2020 17:45:44 +0000 (17:45 +0000)
fs.writeFile(), fs.appendFile() and friends may accept an instance of
Buffer as an argument.

Also, fs.readFile() and friends now return an instance of Buffer instead of
Byte-string when encoding is not provided.

Added Buffer encoding for fs.readdir(), fs.realpath() and friends.

src/njs_fs.c
src/test/njs_unit_test.c
test/js/fs_appendFile.js [new file with mode: 0644]
test/js/fs_appendFileSync.js [new file with mode: 0644]
test/js/fs_promises_001.js
test/js/fs_promises_007.js
test/js/fs_readFile.js [new file with mode: 0644]
test/js/fs_readFileSync.js [new file with mode: 0644]
test/js/fs_writeFile.js [new file with mode: 0644]
test/js/fs_writeFileSync.js [new file with mode: 0644]
test/njs_expect_test.exp

index 158f0923436faaa0e99ca50963dd479bca7bdb7b..1a20743c2a89aee5e4b757d426a71ec0f1751da0 100644 (file)
@@ -50,13 +50,6 @@ typedef enum {
 } njs_fs_writemode_t;
 
 
-typedef enum {
-    NJS_FS_ENC_INVALID,
-    NJS_FS_ENC_NONE,
-    NJS_FS_ENC_UTF8,
-} njs_fs_encoding_t;
-
-
 typedef struct {
     njs_str_t   name;
     int         value;
@@ -92,19 +85,19 @@ static njs_int_t njs_fs_error(njs_vm_t *vm, const char *syscall,
 static njs_int_t njs_fs_result(njs_vm_t *vm, njs_value_t *result,
     njs_index_t calltype, const njs_value_t* callback, njs_uint_t nargs);
 
-static njs_int_t
-njs_file_tree_walk(const char *path, njs_file_tree_walk_cb_t cb, int fd_limit,
-    njs_ftw_flags_t flags);
+static njs_int_t njs_file_tree_walk(const char *path,
+    njs_file_tree_walk_cb_t cb, int fd_limit, njs_ftw_flags_t flags);
 
 static njs_int_t njs_fs_make_path(njs_vm_t *vm, const char *path, mode_t md,
     njs_bool_t recursive, njs_value_t *retval);
 static njs_int_t njs_fs_rmtree(njs_vm_t *vm, const char *path,
     njs_bool_t recursive, njs_value_t *retval);
 
+static njs_int_t njs_fs_path(njs_vm_t *vm, const char **dst,
+    const njs_value_t* src, const njs_str_t *prop_name);
 static int njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags);
 static mode_t njs_fs_mode(njs_vm_t *vm, njs_value_t *value,
     mode_t default_mode);
-static njs_fs_encoding_t njs_fs_encoding(njs_vm_t *vm, njs_value_t *value);
 
 static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
     const njs_value_t *args, njs_uint_t nargs);
@@ -135,44 +128,28 @@ static njs_fs_entry_t njs_flags_table[] = {
 };
 
 
-njs_inline njs_int_t
-njs_fs_path_arg(njs_vm_t *vm, const char **dst, const njs_value_t* src,
-    const njs_str_t *prop_name)
-{
-    if (njs_slow_path(!njs_is_string(src))) {
-        njs_type_error(vm, "\"%V\" must be a string", prop_name);
-        return NJS_ERROR;
-    }
-
-    *dst = njs_string_to_c_string(vm, njs_value_arg(src));
-    if (njs_slow_path(*dst == NULL)) {
-        return NJS_ERROR;
-    }
-
-    return NJS_OK;
-}
+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_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;
-    u_char             *start;
-    size_t             size;
-    ssize_t            length;
-    njs_str_t          data;
-    njs_int_t          ret;
-    const char         *file_path;
-    njs_value_t        flag, encoding, retval, *callback, *options, *path;
-    struct stat        sb;
-    njs_fs_encoding_t  enc;
-
-    static const njs_value_t  string_flag = njs_string("flag");
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    int                          fd, flags;
+    njs_str_t                    data;
+    njs_int_t                    ret;
+    const char                   *file_path;
+    njs_value_t                  flag, encode, retval, *callback, *options,
+                                 *path;
+    struct stat                  sb;
+    const njs_buffer_encoding_t  *encoding;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -194,11 +171,11 @@ njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     njs_set_undefined(&flag);
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -219,7 +196,7 @@ njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -230,9 +207,12 @@ njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        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(file_path, flags);
@@ -252,88 +232,25 @@ njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         goto done;
     }
 
-    if (enc == NJS_FS_ENC_UTF8) {
-        length = sb.st_size;
+    data.start = NULL;
+    data.length = sb.st_size;
 
-        if (length > NJS_STRING_MAP_STRIDE) {
-            /*
-             * At this point length is not known, in order to set it to
-             * the correct value after file is read, we need to ensure that
-             * offset_map is allocated by njs_string_alloc(). This can be
-             * achieved by making length != size.
-             */
-            length += 1;
+    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);
         }
 
-    } else {
-        length = 0;
+        goto done;
     }
 
-    size = sb.st_size;
-
-    if (njs_fast_path(size != 0)) {
-        start = njs_string_alloc(vm, &retval, size, length);
-        if (njs_slow_path(start == NULL)) {
-            ret = NJS_ERROR;
-            goto done;
-        }
-
-        data.start = start;
-        data.length = size;
-
-        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);
-            }
-
-            goto done;
-        }
-
-        if (njs_slow_path(data.length < size)) {
-            /* Pseudo-files may return less data than declared by st_size. */
-            njs_string_truncate(&retval, data.length, length);
-        }
-
-        size = data.length;
-        start = data.start;
+    if (encoding == NULL) {
+        ret = njs_buffer_set(vm, &retval, data.start, data.length);
 
     } else {
-        /* size of the file is not known in advance. */
-
-        data.length = 0;
-
-        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);
-            }
-
-            goto done;
-        }
-
-        size = data.length;
-        start = data.start;
-
-        ret = njs_string_new(vm, &retval, start, size, length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto done;
-        }
-    }
-
-    if (enc == NJS_FS_ENC_UTF8) {
-        length = njs_utf8_length(start, size);
-
-        if (length >= 0) {
-            njs_string_length_set(&retval, length);
-
-        } else {
-            ret = njs_fs_error(vm, NULL, "Non-UTF8 file, convertion "
-                               "is not implemented", path, 0, &retval);
-            goto done;
-        }
+        ret = encoding->encode(vm, &retval, &data);
+        njs_mp_free(vm->mem_pool, data.start);
     }
 
 done:
@@ -354,34 +271,26 @@ 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         *file_path;
-    njs_value_t        flag, mode, encoding, retval,
-                       *path, *data, *callback, *options;
-    njs_fs_encoding_t  enc;
-    njs_fs_calltype_t  calltype;
-
-    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_encoding = njs_string("encoding");
+    int                          fd, flags;
+    u_char                       *p, *end;
+    mode_t                       md;
+    ssize_t                      n;
+    njs_str_t                    content;
+    njs_int_t                    ret;
+    const char                   *file_path;
+    njs_value_t                  flag, mode, encode, retval, *path, *data,
+                                 *callback, *options;
+    njs_typed_array_t            *array;
+    njs_fs_calltype_t            calltype;
+    njs_array_buffer_t           *buffer;
+    const njs_buffer_encoding_t  *encoding;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    data = njs_arg(args, nargs, 2);
-    if (njs_slow_path(!njs_is_string(data))) {
-        njs_type_error(vm, "\"data\" must be a string");
-        return NJS_ERROR;
-    }
-
     callback = NULL;
     calltype = magic & 3;
     options = njs_arg(args, nargs, 3);
@@ -400,11 +309,11 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     njs_set_undefined(&flag);
     njs_set_undefined(&mode);
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -431,12 +340,49 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &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;
+        }
+
+        content.start = &buffer->u.u8[array->offset];
+        content.length = array->byte_length;
+        break;
+
+    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;
+    }
+
     flags = njs_fs_flags(vm, &flag, O_CREAT | O_WRONLY);
     if (njs_slow_path(flags == -1)) {
         return NJS_ERROR;
@@ -449,19 +395,12 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
-    }
-
     fd = open(file_path, flags, md);
     if (njs_slow_path(fd < 0)) {
         ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
         goto done;
     }
 
-    njs_string_get(data, &content);
-
     p = content.start;
     end = p + content.length;
 
@@ -515,13 +454,13 @@ njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    ret = njs_fs_path_arg(vm, &old_path, njs_arg(args, nargs, 1),
+    ret = njs_fs_path(vm, &old_path, njs_arg(args, nargs, 1),
                           &njs_str_value("oldPath"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    ret = njs_fs_path_arg(vm, &new_path, njs_arg(args, nargs, 2),
+    ret = njs_fs_path(vm, &new_path, njs_arg(args, nargs, 2),
                           &njs_str_value("newPath"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
@@ -552,7 +491,7 @@ njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_value_t  retval, *path, *callback, *mode;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -610,13 +549,13 @@ njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_value_t  retval, *target, *path, *callback, *type;
 
     target = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &target_path, target, &njs_str_value("target"));
+    ret = njs_fs_path(vm, &target_path, target, &njs_str_value("target"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
     path = njs_arg(args, nargs, 2);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -666,7 +605,7 @@ njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_value_t  retval, *path, *callback;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -700,19 +639,15 @@ static njs_int_t
 njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t calltype)
 {
-    u_char             *resolved_path;
-    size_t             size;
-    ssize_t            length;
-    njs_int_t          ret;
-    const char         *file_path;
-    njs_value_t        encoding, retval, *path, *callback, *options;
-    njs_fs_encoding_t  enc;
-    char               path_buf[MAXPATHLEN];
-
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    njs_int_t                    ret;
+    njs_str_t                    s;
+    const char                   *file_path;
+    njs_value_t                  encode, retval, *path, *callback, *options;
+    const njs_buffer_encoding_t  *encoding;
+    char                         path_buf[MAXPATHLEN];
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -732,11 +667,11 @@ njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -751,33 +686,34 @@ njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
+    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;
+        }
     }
 
-    resolved_path = (u_char *) realpath(file_path, path_buf);
-    if (njs_slow_path(resolved_path == NULL)) {
+    s.start = (u_char *) realpath(file_path, path_buf);
+    if (njs_slow_path(s.start == NULL)) {
         ret = njs_fs_error(vm, "realpath", strerror(errno), path, errno,
                            &retval);
         goto done;
     }
 
-    size = njs_strlen(resolved_path);
-    length = njs_utf8_length(resolved_path, size);
-    if (njs_slow_path(length < 0)) {
-        length = 0;
-    }
+    s.length = njs_strlen(s.start);
 
-    ret = njs_string_new(vm, &retval, resolved_path, size, length);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
+    if (encoding == NULL) {
+        ret = njs_buffer_new(vm, &retval, s.start, s.length);
+
+    } else {
+        ret = encoding->encode(vm, &retval, &s);
     }
 
 done:
@@ -799,11 +735,8 @@ njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     const char   *file_path;
     njs_value_t  mode, recursive, retval, *path, *callback, *options;
 
-    static const njs_value_t  string_mode = njs_string("mode");
-    static const njs_value_t  string_recursive = njs_string("recursive");
-
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -878,10 +811,8 @@ njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     const char   *file_path;
     njs_value_t  recursive, retval, *path, *callback, *options;
 
-    static const njs_value_t  string_recursive = njs_string("recursive");
-
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -935,23 +866,20 @@ static njs_int_t
 njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t calltype)
 {
-    DIR                *dir;
-    u_char             *d_name;
-    size_t             size;
-    ssize_t            length;
-    njs_int_t          ret;
-    const char         *dir_path;
-    njs_value_t        encoding, types, ename, etype, retval, *path, *callback,
-                       *options, *value;
-    njs_array_t        *results;
-    struct dirent      *entry;
-    njs_fs_encoding_t  enc;
-
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    DIR                          *dir;
+    njs_str_t                    s;
+    njs_int_t                    ret;
+    const char                   *dir_path;
+    njs_value_t                  encode, types, ename, etype, retval, *path,
+                                 *callback, *options, *value;
+    njs_array_t                  *results;
+    struct dirent                *entry;
+    const njs_buffer_encoding_t  *encoding;
+
     static const njs_value_t  string_types = njs_string("withFileTypes");
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &dir_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &dir_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -971,11 +899,11 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     njs_set_false(&types);
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -990,7 +918,7 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -1002,9 +930,12 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
+    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;
+        }
     }
 
     results = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
@@ -1033,41 +964,38 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             goto done;
         }
 
-        d_name = (u_char *) entry->d_name;
-
-        size = njs_strlen(d_name);
-        length = njs_utf8_length(d_name, size);
-        if (njs_slow_path(length < 0)) {
-            length = 0;
-        }
+        s.start = (u_char *) entry->d_name;
+        s.length = njs_strlen(s.start);
 
-        if ((length == 1 && d_name[0] == '.')
-            || (length == 2 && (d_name[0] == '.' && d_name[1] == '.')))
+        if ((s.length == 1 && s.start[0] == '.')
+            || (s.length == 2 && (s.start[0] == '.' && s.start[1] == '.')))
         {
             continue;
         }
 
-        if (njs_fast_path(!njs_is_true(&types))) {
-            ret = njs_array_string_add(vm, results, d_name, size, length);
-            if (njs_slow_path(ret != NJS_OK)) {
-                goto done;
-            }
+        value = njs_array_push(vm, results);
+        if (njs_slow_path(value == NULL)) {
+            goto done;
+        }
 
-            continue;
+        if (encoding == NULL) {
+            ret = njs_buffer_set(vm, &ename, s.start, s.length);
+
+        } else {
+            ret = encoding->encode(vm, &ename, &s);
         }
 
-        ret = njs_string_new(vm, &ename, d_name, size, length);
         if (njs_slow_path(ret != NJS_OK)) {
             goto done;
         }
 
-        njs_set_number(&etype, njs_dentry_type(entry));
-
-        value = njs_array_push(vm, results);
-        if (njs_slow_path(value == NULL)) {
-            goto done;
+        if (njs_fast_path(!njs_is_true(&types))) {
+            *value = ename;
+            continue;
         }
 
+        njs_set_number(&etype, njs_dentry_type(entry));
+
         ret = njs_fs_dirent_create(vm, &ename, &etype, value);
         if (njs_slow_path(ret != NJS_OK)) {
             goto done;
@@ -1099,12 +1027,12 @@ njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data)
 
     if (size == 0) {
         size = 4096;
+    }
 
-        data->start = njs_mp_alloc(vm->mem_pool, size);
-        if (data->start == NULL) {
-            njs_memory_error(vm);
-            return NJS_ERROR;
-        }
+    data->start = njs_mp_alloc(vm->mem_pool, size);
+    if (data->start == NULL) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
     }
 
     p = data->start;
@@ -1474,6 +1402,61 @@ njs_fs_rmtree(njs_vm_t *vm, const char *path, njs_bool_t recursive,
 }
 
 
+static njs_int_t
+njs_fs_path(njs_vm_t *vm, const char **dst, const njs_value_t* src,
+    const njs_str_t *prop_name)
+{
+    u_char              *data, *p, *start;
+    njs_typed_array_t   *array;
+    njs_array_buffer_t  *buffer;
+
+    switch (src->type) {
+    case NJS_STRING:
+        *dst = njs_string_to_c_string(vm, njs_value_arg(src));
+        if (njs_slow_path(*dst == NULL)) {
+            return NJS_ERROR;
+        }
+
+        break;
+
+    case NJS_TYPED_ARRAY:
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(src);
+        buffer = array->buffer;
+        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+            njs_type_error(vm, "detached buffer");
+            return NJS_ERROR;
+        }
+
+        start = &buffer->u.u8[array->offset];
+
+        if (njs_slow_path(memchr(start, '\0', array->byte_length) != 0)) {
+            njs_type_error(vm, "\"%V\" must be a Buffer without null bytes",
+                           prop_name);
+            return NJS_ERROR;
+        }
+
+        data = njs_mp_alloc(vm->mem_pool, array->byte_length + 1);
+        if (njs_slow_path(data == NULL)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        p = njs_cpymem(data, start, array->byte_length);
+        *p++ = '\0';
+
+        *dst = (char *) data;
+        break;
+
+    default:
+        njs_type_error(vm, "\"%V\" must be a string or Buffer", prop_name);
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
 static int
 njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags)
 {
@@ -1526,32 +1509,6 @@ njs_fs_mode(njs_vm_t *vm, njs_value_t *value, mode_t default_mode)
 }
 
 
-static njs_fs_encoding_t
-njs_fs_encoding(njs_vm_t *vm, njs_value_t *value)
-{
-    njs_str_t  enc;
-    njs_int_t  ret;
-
-    if (njs_is_undefined(value)) {
-        return NJS_FS_ENC_NONE;
-    }
-
-    ret = njs_value_to_string(vm, value, value);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_FS_ENC_INVALID;
-    }
-
-    njs_string_get(value, &enc);
-
-    if (enc.length != 4 || memcmp(enc.start, "utf8", 4) != 0) {
-        njs_type_error(vm, "Unknown encoding: \"%V\"", &enc);
-        return NJS_FS_ENC_INVALID;
-    }
-
-    return NJS_FS_ENC_UTF8;
-}
-
-
 static njs_int_t
 njs_fs_error(njs_vm_t *vm, const char *syscall, const char *description,
     njs_value_t *path, int errn, njs_value_t *retval)
index c683814610244d89908c703a19d50d2796a1fad7..661bf6a67fbef7d1b45b4f220e69c70ce28a6df9 100644 (file)
@@ -17013,145 +17013,146 @@ static njs_unit_test_t  njs_test[] =
     /* require('fs').readFile() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile()"),
-      njs_str("TypeError: \"path\" must be a string") },
+              "fs.readFile()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path')"),
+              "var path = Buffer.from('/broken'); path[3] = 0;"
+              "fs.readFile(path)"),
+      njs_str("TypeError: \"path\" must be a Buffer without null bytes") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFile('/njs_unknown_path')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', 'utf8')"),
+              "fs.readFile('/njs_unknown_path', 'utf8')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {flag:'xx'})"),
+              "fs.readFile('/njs_unknown_path', {flag:'xx'})"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"),
+              "fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"),
+              "fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {encoding:'ascii'}, function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.readFile('/njs_unknown_path', {encoding:'ascii'}, function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', 'ascii', function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.readFile('/njs_unknown_path', 'ascii', function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     /* require('fs').readFileSync() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync()"),
-      njs_str("TypeError: \"path\" must be a string") },
+              "fs.readFileSync()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync({})"),
-      njs_str("TypeError: \"path\" must be a string") },
+              "fs.readFileSync({})"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', {flag:'xx'})"),
+              "fs.readFileSync('/njs_unknown_path', {flag:'xx'})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', {encoding:'ascii'})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.readFileSync(Buffer.from('/njs_unknown_path'), {encoding:'ascii'})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', 'ascii')"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.readFileSync('/njs_unknown_path', 'ascii')"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', true)"),
+              "fs.readFileSync('/njs_unknown_path', true)"),
       njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") },
 
 
     /* require('fs').writeFile() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile()"),
-      njs_str("TypeError: \"path\" must be a string") },
+              "fs.writeFile()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile({}, '', function () {})"),
-      njs_str("TypeError: \"path\" must be a string") },
+              "fs.writeFile({}, '', function () {})"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path')"),
-      njs_str("TypeError: \"data\" must be a string") },
+              "fs.writeFile('/njs_unknown_path')"),
+      njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '')"),
+              "fs.writeFile('/njs_unknown_path', '')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', undefined)"),
+              "fs.writeFile('/njs_unknown_path', '', undefined)"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', 'utf8')"),
+              "fs.writeFile('/njs_unknown_path', '', 'utf8')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"),
+              "fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', {encoding:'ascii'}, function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.writeFile('/njs_unknown_path', '', {encoding:'ascii'}, function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', 'ascii', function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.writeFile('/njs_unknown_path', '', 'ascii', function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', true, function () {})"),
+              "fs.writeFile('/njs_unknown_path', '', true, function () {})"),
       njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") },
 
     /* require('fs').writeFileSync() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync()"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path')"),
-      njs_str("TypeError: \"data\" must be a string") },
+              "fs.writeFileSync()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync({}, '')"),
-      njs_str("TypeError: \"path\" must be a string") },
+              "fs.writeFileSync({}, '')"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path', '', {flag:'xx'})"),
+              "fs.writeFileSync('/njs_unknown_path', '', {flag:'xx'})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path', '', {encoding:'ascii'})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.writeFileSync('/njs_unknown_path', '', {encoding:'ascii'})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path', '', 'ascii')"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.writeFileSync('/njs_unknown_path', '', 'ascii')"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path', '', true)"),
+              "fs.writeFileSync('/njs_unknown_path', '', true)"),
       njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") },
 
     /* require('fs').renameSync() */
 
     { njs_str("var fs = require('fs');"
               "fs.renameSync()"),
-      njs_str("TypeError: \"oldPath\" must be a string") },
+      njs_str("TypeError: \"oldPath\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
               "fs.renameSync('/njs_unknown_path')"),
-      njs_str("TypeError: \"newPath\" must be a string") },
+      njs_str("TypeError: \"newPath\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
               "[undefined, null, false, NaN, Symbol(), {}, Object('/njs_unknown_path')]"
@@ -17171,7 +17172,7 @@ static njs_unit_test_t  njs_test[] =
 
     { njs_str("var fs = require('fs');"
               "fs.access()"),
-      njs_str("TypeError: \"path\" must be a string") },
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
               "fs.access('/njs_unknown_path')"),
@@ -17189,7 +17190,7 @@ static njs_unit_test_t  njs_test[] =
 
     { njs_str("var fs = require('fs');"
               "fs.accessSync()"),
-      njs_str("TypeError: \"path\" must be a string") },
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
 
     { njs_str("var fs = require('fs');"
               "fs.accessSync('/njs_unknown_path', 'fail')"),
diff --git a/test/js/fs_appendFile.js b/test/js/fs_appendFile.js
new file mode 100644 (file)
index 0000000..7a6fa39
--- /dev/null
@@ -0,0 +1,61 @@
+var fs = require('fs');
+var fname = './build/test/fs_appendFile';
+
+var argv = process.argv.slice(2);
+
+var data = (() => {
+    var value = argv[0];
+    var type = argv[1];
+    var offset = argv[2] ? parseInt(argv[2]) : 0;
+
+    switch (type) {
+    case 'Buffer':
+        return Buffer.from(Buffer.from(value).buffer, offset);
+    case 'DataView':
+        return new DataView(Buffer.from(value).buffer, offset);
+    case 'Object':
+        return {toString(){return value}};
+    case 'String':
+        return String(value);
+    case 'Symbol':
+        return Symbol(value);
+    case 'Uint8Array':
+        return new Uint8Array(Buffer.from(value).buffer, offset);
+    default:
+        throw new Error(`Unknown data type:${type}`);
+    }
+})();
+
+var options = (() => {
+    var encoding = argv[2];
+    var mode = argv[3] ? parseInt(argv[3].slice(2), 8) : 0;
+
+    if (encoding && mode) {
+        return {encoding, mode};
+
+    } else if (encoding) {
+        return encoding;
+    }
+
+    return undefined;
+})();
+
+try { fs.unlinkSync(fname); } catch (e) {}
+
+function done(e) {
+    if (e) {throw e};
+    var data = fs.readFileSync(fname);
+    console.log(String(data));
+}
+
+function append(cb) {
+    if (options) {
+        var path = Buffer.from(`@${fname}`).slice(1);
+        fs.appendFile(path, data, options, cb);
+
+    } else {
+        fs.appendFile(fname, data, cb);
+    }
+}
+
+append((e) => {if (e) {throw e}; append(done);})
diff --git a/test/js/fs_appendFileSync.js b/test/js/fs_appendFileSync.js
new file mode 100644 (file)
index 0000000..b6372b8
--- /dev/null
@@ -0,0 +1,59 @@
+var fs = require('fs');
+var fname = './build/test/fs_appendFileSync';
+
+var argv = process.argv.slice(2);
+
+var data = (() => {
+    var value = argv[0];
+    var type = argv[1];
+    var offset = argv[2] ? parseInt(argv[2]) : 0;
+
+    switch (type) {
+    case 'Buffer':
+        return Buffer.from(Buffer.from(value).buffer, offset);
+    case 'DataView':
+        return new DataView(Buffer.from(value).buffer, offset);
+    case 'Object':
+        return {toString(){return value}};
+    case 'String':
+        return String(value);
+    case 'Symbol':
+        return Symbol(value);
+    case 'Uint8Array':
+        return new Uint8Array(Buffer.from(value).buffer, offset);
+    default:
+        throw new Error(`Unknown data type:${type}`);
+    }
+})();
+
+var options = (() => {
+    var encoding = argv[2];
+    var mode = argv[3] ? parseInt(argv[3].slice(2), 8) : 0;
+
+    if (encoding && mode) {
+        return {encoding, mode};
+
+    } else if (encoding) {
+        return encoding;
+    }
+
+    return undefined;
+})();
+
+function append() {
+    if (options) {
+        var path = Buffer.from(`@${fname}`).slice(1);
+        fs.appendFileSync(path, data, options);
+
+    } else {
+        fs.appendFileSync(fname, data);
+    }
+}
+
+try { fs.unlinkSync(fname); } catch (e) {}
+
+append();
+append();
+
+var ret = fs.readFileSync(fname);
+console.log(String(ret));
index 477b2549b759f5169ed2c2e2d8055d1a3a93a60e..22a1efc5177984a73d52ce1c1f80b53e20f4938a 100644 (file)
@@ -45,11 +45,6 @@ Promise.resolve()
     } catch (e) {
         console.log('error 2 ok', e instanceof TypeError)
     }
-    try {
-        return fs.writeFile(fname);
-    } catch (e) {
-        console.log('error 3 ok', e instanceof TypeError)
-    }
 })
 .then((data) => {
     console.log('errors ok');
index 3dccf275435171fb02a2f59ac837f1925a90e6b6..e02796cbbe49a67aad4289df7f8322e3dd459999 100644 (file)
@@ -96,6 +96,16 @@ var testSync = () => new Promise((resolve, reject) => {
             throw new Error('fs.readdirSync - error 6');
         }
 
+        var dir_buffer = fs.readdirSync(dname, {encoding:'buffer'});
+        if (dir_buffer.length != 3 || !(dir_buffer[0] instanceof Buffer)) {
+            throw new Error('fs.readdirSync - error 7');
+        }
+
+        var dir_buffer_types = fs.readdirSync(dname, {encoding:'buffer', withFileTypes: true});
+        if (dir_buffer_types.length != 3 || !(dir_buffer_types[0].name instanceof Buffer)) {
+            throw new Error('fs.readdirSync - error 8');
+        }
+
         resolve();
 
     } catch (e) {
@@ -171,7 +181,7 @@ Promise.resolve()
     console.log('test fs.readdirSync');
 })
 .catch((e) => {
-    console.log('test fs.readdirSync failed', JSON.stringify(e));
+    console.log('test fs.readdirSync failed', e, JSON.stringify(e));
 })
 
 .then(testCallback)
@@ -179,7 +189,7 @@ Promise.resolve()
     console.log('test fs.readdir');
 })
 .catch((e) => {
-    console.log('test fs.readdir failed', JSON.stringify(e));
+    console.log('test fs.readdir failed', e, JSON.stringify(e));
 })
 
 .then(() => {
@@ -227,5 +237,5 @@ Promise.resolve()
     console.log('test fsp.readdir');
 })
 .catch((e) => {
-    console.log('test fsp.readdir failed', JSON.stringify(e));
+    console.log('test fsp.readdir failed', e, JSON.stringify(e));
 });
diff --git a/test/js/fs_readFile.js b/test/js/fs_readFile.js
new file mode 100644 (file)
index 0000000..c7a97ea
--- /dev/null
@@ -0,0 +1,40 @@
+var fs = require('fs');
+
+var argv = process.argv.slice(2);
+var fname = argv[0];
+
+var options = (() => {
+    var encoding = argv[1];
+    var flags = argv[2];
+
+    if (encoding && flags) {
+        return {encoding, flags};
+
+    } else if (encoding) {
+        return encoding;
+    }
+
+    return undefined;
+})();
+
+function type(v) {
+    if (v instanceof Buffer) {
+        return 'Buffer';
+    }
+
+    return typeof v;
+}
+
+function done(e, data) {
+    if (e) {console.log(JSON.stringify(e))};
+       console.log(String(data), type(data), data.length);
+}
+
+if (options) {
+       var path = Buffer.from(`@${fname}`).slice(1);
+       fs.readFile(path, options, done);
+
+} else {
+       fs.readFile(fname, done);
+}
+
diff --git a/test/js/fs_readFileSync.js b/test/js/fs_readFileSync.js
new file mode 100644 (file)
index 0000000..2e4d811
--- /dev/null
@@ -0,0 +1,43 @@
+var fs = require('fs');
+
+var argv = process.argv.slice(2);
+var fname = argv[0];
+
+var options = (() => {
+    var encoding = argv[1];
+    var flags = argv[2];
+
+    if (encoding && flags) {
+        return {encoding, flags};
+
+    } else if (encoding) {
+        return encoding;
+    }
+
+    return undefined;
+})();
+
+function type(v) {
+    if (v instanceof Buffer) {
+        return 'Buffer';
+    }
+
+    return typeof v;
+}
+
+var data;
+
+try {
+    if (options) {
+        var path = Buffer.from(`@${fname}`).slice(1);
+        data = fs.readFileSync(path, options);
+
+    } else {
+        data = fs.readFileSync(fname);
+    }
+
+} catch (e) {
+    console.log(JSON.stringify(e));
+}
+
+console.log(String(data), type(data), data.length);
diff --git a/test/js/fs_writeFile.js b/test/js/fs_writeFile.js
new file mode 100644 (file)
index 0000000..daa752b
--- /dev/null
@@ -0,0 +1,57 @@
+var fs = require('fs');
+var fname = './build/test/fs_writeFile';
+
+var argv = process.argv.slice(2);
+
+var data = (() => {
+    var value = argv[0];
+    var type = argv[1];
+    var offset = argv[2] ? parseInt(argv[2]) : 0;
+
+    switch (type) {
+    case 'Buffer':
+        return Buffer.from(Buffer.from(value).buffer, offset);
+    case 'DataView':
+        return new DataView(Buffer.from(value).buffer, offset);
+    case 'Object':
+        return {toString(){return value}};
+    case 'String':
+        return String(value);
+    case 'Symbol':
+        return Symbol(value);
+    case 'Uint8Array':
+        return new Uint8Array(Buffer.from(value).buffer, offset);
+    default:
+        throw new Error(`Unknown data type:${type}`);
+    }
+})();
+
+var options = (() => {
+    var encoding = argv[2];
+    var mode = argv[3] ? parseInt(argv[3].slice(2), 8) : 0;
+
+    if (encoding && mode) {
+        return {encoding, mode};
+
+    } else if (encoding) {
+        return encoding;
+    }
+
+    return undefined;
+})();
+
+try { fs.unlinkSync(fname); } catch (e) {}
+
+function cb(e) {
+    if (e) {throw e};
+    var data = fs.readFileSync(fname);
+    console.log(String(data));
+}
+
+if (options) {
+    var path = Buffer.from(`@${fname}`).slice(1);
+    fs.writeFile(path, data, options, cb);
+
+} else {
+    fs.writeFile(fname, data, cb);
+}
diff --git a/test/js/fs_writeFileSync.js b/test/js/fs_writeFileSync.js
new file mode 100644 (file)
index 0000000..29621f2
--- /dev/null
@@ -0,0 +1,54 @@
+var fs = require('fs');
+var fname = './build/test/fs_writeFileSync';
+
+var argv = process.argv.slice(2);
+
+var data = (() => {
+    var value = argv[0];
+    var type = argv[1];
+    var offset = argv[2] ? parseInt(argv[2]) : 0;
+
+    switch (type) {
+    case 'Buffer':
+        return Buffer.from(Buffer.from(value).buffer, offset);
+    case 'DataView':
+        return new DataView(Buffer.from(value).buffer, offset);
+    case 'Object':
+        return {toString(){return value}};
+    case 'String':
+        return String(value);
+    case 'Symbol':
+        return Symbol(value);
+    case 'Uint8Array':
+        return new Uint8Array(Buffer.from(value).buffer, offset);
+    default:
+        throw new Error(`Unknown data type:${type}`);
+    }
+})();
+
+var options = (() => {
+    var encoding = argv[2];
+    var mode = argv[3] ? parseInt(argv[3].slice(2), 8) : 0;
+
+    if (encoding && mode) {
+        return {encoding, mode};
+
+    } else if (encoding) {
+        return encoding;
+    }
+
+    return undefined;
+})();
+
+try { fs.unlinkSync(fname); } catch (e) {}
+
+if (options) {
+    var path = Buffer.from(`@${fname}`).slice(1);
+    fs.writeFileSync(path, data, options);
+
+} else {
+    fs.writeFileSync(fname, data);
+}
+
+var ret = fs.readFileSync(fname);
+console.log(String(ret));
index e891dcfe4e0fff7a277556e7d895d43735a28bbe..f5b1ea6e2c8dfee64041d72a1bd9f7d49ca1865c 100644 (file)
@@ -461,55 +461,22 @@ njs_test {
 
 # require('fs').readFile()
 
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFile('test/fs/utf8', 'utf8', (...args) => void console.log(args.length))\r\n"
-     "undefined\r\n2\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFile('test/fs/utf8', 'utf8', function (e, data) {console.log(data[2]+data.length)})\r\n"
-     "undefined\r\nZ4\r\n>> "}
-}
+njs_run {"./test/js/fs_readFile.js" "test/fs/utf8"} "αβZγ Buffer 7"
+njs_run {"./test/js/fs_readFile.js" "test/fs/utf8" "utf8"} "αβZγ string 4"
+njs_run {"./test/js/fs_readFile.js" "test/fs/utf8" "utf8" "r+"} "αβZγ string 4"
+njs_run {"./test/js/fs_readFile.js" "test/fs/nonexistent"} \
+    "{\"errno\":2,\"code\":\"ENOENT\",\"path\":\"test/fs/nonexistent\",\"syscall\":\"open\"}"
+njs_run {"./test/js/fs_readFile.js" "test/fs/non_utf8" "utf8"} "�� string 2"
+njs_run {"./test/js/fs_readFile.js" "test/fs/non_utf8" "hex"} "8080 string 4"
+njs_run {"./test/js/fs_readFile.js" "test/fs/non_utf8" "base64"} "gIA= string 4"
 
 njs_test {
     {"var fs = require('fs')\r\n"
      "undefined\r\n>> "}
-    {"fs.readFile('test/fs/utf8', function (e, data) {console.log(data[4]+data.length)})\r\n"
-     "undefined\r\nZ7\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFile('test/fs/utf8', {encoding:'utf8',flag:'r+'}, function (e, data) {console.log(data)})\r\n"
-     "undefined\r\nαβZγ\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFile('test/fs/ascii', function (e, data) {console.log(data[599])})\r\n"
-     "undefined\r\nx\r\n>> "}
-    {"fs.readFile('test/fs/ascii', {encoding:'utf8',flag:'r+'}, function (e, data) {console.log(data[599])})\r\n"
-     "undefined\r\nx\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'); \r\n"
-     "undefined\r\n>> "}
-    {"fs.readFile('test/fs/nonexistent', 'utf8', function (e) {console.log(JSON.stringify(e))})\r\n"
-     "undefined\r\n{\"errno\":2,\"code\":\"ENOENT\",\"path\":\"test/fs/nonexistent\",\"syscall\":\"open\"}\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'); \r\n"
-     "undefined\r\n>> "}
-    {"fs.readFile('test/fs/nonexistent', {encoding:'utf8', flag:'r+'}, function (e) {console.log(e)})\r\n"
-     "undefined\r\nError: No such file or directory\r\n>> "}
+    {"fs.readFile('test/fs/ascii', 'utf8', function (e, data) {console.log(data[599], data[600])})\r\n"
+     "undefined\r\nx undefined\r\n>> "}
+    {"fs.readFile('test/fs/ascii', {encoding:'utf8',flag:'r+'}, function (e, data) {console.log(data[599], data[600])})\r\n"
+     "undefined\r\nx undefined\r\n>> "}
 }
 
 njs_test {
@@ -523,60 +490,20 @@ njs_test {
 
 # require('fs').readFileSync()
 
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync('test/fs/utf8').toString('base64')\r\n"
-     "'zrHOslrOsw=='\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync('test/fs/utf8', 'utf8')[2]\r\n"
-     "'Z'\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync('test/fs/utf8')[4]\r\n"
-     "'Z'\r\n>> "}
-}
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/utf8"} "αβZγ Buffer 7"
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/utf8" "utf8"} "αβZγ string 4"
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/utf8" "utf8" "r+"} "αβZγ string 4"
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/nonexistent"} \
+    "{\"errno\":2,\"code\":\"ENOENT\",\"path\":\"test/fs/nonexistent\",\"syscall\":\"open\"}"
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/non_utf8" "utf8"} "�� string 2"
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/non_utf8" "hex"} "8080 string 4"
+njs_run {"./test/js/fs_readFileSync.js" "test/fs/non_utf8" "base64"} "gIA= string 4"
 
 njs_test {
     {"var fs = require('fs')\r\n"
      "undefined\r\n>> "}
-    {"fs.readFileSync('test/fs/utf8', {encoding:'utf8',flag:'r+'})\r\n"
-     "'αβZγ'\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'), fn = 'test/fs/ascii'\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync(fn)[599] + fs.readFileSync(fn, 'utf8')[599]\r\n"
-     "'xx'\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'); \r\n"
-     "undefined\r\n>> "}
-    {"try { fs.readFileSync('test/fs/nonexistent')} catch (e) {console.log(JSON.stringify(e))}\r\n"
-     "{\"errno\":2,\"code\":\"ENOENT\",\"path\":\"test/fs/nonexistent\",\"syscall\":\"open\"}\r\nundefined\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync('test/fs/non_utf8').charCodeAt(1)\r\n"
-     "128"}
-}
-
-njs_test {
-    {"var fs = require('fs')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync('test/fs/non_utf8', 'utf8')\r\n"
-     "Error: Non-UTF8 file, convertion is not implemented"}
+    {"fs.readFileSync('test/fs/non_utf8', 'utf8').charCodeAt(1)\r\n"
+     "65533"}
 }
 
 njs_test {
@@ -594,46 +521,19 @@ njs_test {
 
 # require('fs').writeFile()
 
-exec rm -fr ./build/test/file2
+njs_run {"./test/js/fs_writeFile.js" "ABCD" "Buffer" "1"} "BCD"
+njs_run {"./test/js/fs_writeFile.js" "ABC" "DataView"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "414243" "Object" "hex"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "ABC" "String"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "ABC" "Symbol"} "TypeError: Cannot convert a Symbol value to a string*"
+njs_run {"./test/js/fs_writeFile.js" "ABC" "Uint8Array"} "ABC"
 
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"function h1(e) {if (e) {throw e}; console.log(fs.readFileSync(fn))}\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFile(fn, 'ABC', h1)\r\n"
-     "undefined\r\nABC\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFile(fn, 'ABC', (...args) => void console.log(args.length))\r\n"
-     "undefined\r\n1\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFile(fn, 'ABC', 'utf8', function (e) { if (e) {throw e}; console.log(fs.readFileSync(fn))})\r\n"
-     "undefined\r\nABC\r\n>> "}
-}
-
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFile(fn, 'ABC', {encoding:'utf8', mode:0o666}, function (e) { if (e) {throw e}; console.log(fs.readFileSync(fn))})\r\n"
-     "undefined\r\nABC\r\n>> "}
-}
-
-exec rm -fr ./build/test/wo_file
-
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/wo_file';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFile(fn, 'ABC', {mode:0o222}, function (e) {console.log(fs.readFileSync(fn))})\r\n"
-     "Error: Permission denied"}
-}
+njs_run {"./test/js/fs_writeFile.js" "ABC" "String" "utf8"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "ABC" "String" "utf8" "0o666"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "ABC" "String" "utf8" "0o222"} "Error: Permission denied*"
+njs_run {"./test/js/fs_writeFile.js" "414243" "String" "hex"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "QUJD" "String" "base64"} "ABC"
+njs_run {"./test/js/fs_writeFile.js" "QUJD" "String" "base64url"} "ABC"
 
 njs_test {
     {"var fs = require('fs')\r\n"
@@ -644,25 +544,19 @@ njs_test {
 
 # require('fs').writeFileSync()
 
-exec rm -fr ./build/test/file2
-
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFileSync(fn, 'ABC')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync(fn)\r\n"
-     "'ABC'\r\n>> "}
-}
+njs_run {"./test/js/fs_writeFileSync.js" "ABCD" "Buffer" "1"} "BCD"
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "DataView"} "ABC"
+njs_run {"./test/js/fs_writeFileSync.js" "414243" "Object" "hex"} "ABC"
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "String"} "ABC"
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "Symbol"} "TypeError: Cannot convert a Symbol value to a string*"
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "Uint8Array"} "ABC"
 
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFileSync(fn, 'ABC', 'utf8')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync(fn)\r\n"
-     "'ABC'\r\n>> "}
-}
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "String" "utf8"} "ABC"
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "String" "utf8" "0o666"} "ABC"
+njs_run {"./test/js/fs_writeFileSync.js" "ABC" "String" "utf8" "0o222"} "Error: Permission denied*"
+njs_run {"./test/js/fs_writeFileSync.js" "78797a" "String" "hex"} "xyz"
+njs_run {"./test/js/fs_writeFileSync.js" "eHl6" "String" "base64"} "xyz"
+njs_run {"./test/js/fs_writeFileSync.js" "eHl6" "String" "base64url"} "xyz"
 
 njs_test {
     {"var fs = require('fs'), fn = './build/test/file2';\r\n"
@@ -671,57 +565,41 @@ njs_test {
      "undefined\r\n>> "}
     {"fs.writeFileSync(fn, 'ABC')\r\n"
      "undefined\r\n>> "}
-    {"fs.readFileSync(fn)\r\n"
+    {"fs.readFileSync(fn, 'utf8')\r\n"
      "'ABC'\r\n>> "}
 }
 
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFileSync(fn, 'ABC', {encoding:'utf8', mode:0o666})\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync(fn)\r\n"
-     "'ABC'\r\n>> "}
-}
-
-exec rm -fr ./build/test/wo_file
-
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/wo_file';\r\n"
-     "undefined\r\n>> "}
-    {"fs.writeFileSync(fn, 'ABC', {mode:0o222}); fs.readFileSync(fn)\r\n"
-     "Error: Permission denied"}
-}
-
 # require('fs').appendFile()
 
-exec rm -fr ./build/test/file2
+njs_run {"./test/js/fs_appendFile.js" "ABCD" "Buffer" "1"} "BCDBCD"
+njs_run {"./test/js/fs_appendFile.js" "ABC" "DataView"} "ABCABC"
+njs_run {"./test/js/fs_appendFile.js" "414243" "Object" "hex"} "ABCABC"
+njs_run {"./test/js/fs_appendFile.js" "ABC" "String"} "ABCABC"
+njs_run {"./test/js/fs_appendFile.js" "ABC" "Symbol"} "TypeError: Cannot convert a Symbol value to a string*"
+njs_run {"./test/js/fs_appendFile.js" "ABC" "Uint8Array"} "ABCABC"
 
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"function h1(e) {console.log(fs.readFileSync(fn))}\r\n"
-     "undefined\r\n>> "}
-    {"function h2(e) {fs.appendFile(fn, 'ABC', h1)}\r\n"
-     "undefined\r\n>> "}
-    {"fs.appendFile(fn, 'ABC', h2)\r\n"
-     "undefined\r\nABCABC\r\n>> "}
-}
+njs_run {"./test/js/fs_appendFile.js" "ABC" "String" "utf8"} "ABC"
+njs_run {"./test/js/fs_appendFile.js" "ABC" "String" "utf8" "0o666"} "ABC"
+njs_run {"./test/js/fs_appendFile.js" "ABC" "String" "utf8" "0o222"} "Error: Permission denied*"
+njs_run {"./test/js/fs_appendFile.js" "414243" "String" "hex"} "ABC"
+njs_run {"./test/js/fs_appendFile.js" "QUJD" "String" "base64"} "ABC"
+njs_run {"./test/js/fs_appendFile.js" "QUJD" "String" "base64url"} "ABC"
 
 # require('fs').appendFileSync()
 
-exec rm -fr ./build/test/file2
+njs_run {"./test/js/fs_appendFileSync.js" "ABCD" "Buffer" "1"} "BCDBCD"
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "DataView"} "ABCABC"
+njs_run {"./test/js/fs_appendFileSync.js" "414243" "Object" "hex"} "ABCABC"
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "String"} "ABCABC"
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "Symbol"} "TypeError: Cannot convert a Symbol value to a string*"
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "Uint8Array"} "ABCABC"
 
-njs_test {
-    {"var fs = require('fs'), fn = './build/test/file2';\r\n"
-     "undefined\r\n>> "}
-    {"fs.appendFileSync(fn, 'ABC')\r\n"
-     "undefined\r\n>> "}
-    {"fs.appendFileSync(fn, 'ABC')\r\n"
-     "undefined\r\n>> "}
-    {"fs.readFileSync(fn)\r\n"
-     "'ABCABC'\r\n>> "}
-}
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "String" "utf8"} "ABC"
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "String" "utf8" "0o666"} "ABC"
+njs_run {"./test/js/fs_appendFileSync.js" "ABC" "String" "utf8" "0o222"} "Error: Permission denied*"
+njs_run {"./test/js/fs_appendFileSync.js" "414243" "String" "hex"} "ABC"
+njs_run {"./test/js/fs_appendFileSync.js" "QUJD" "String" "base64"} "ABC"
+njs_run {"./test/js/fs_appendFileSync.js" "QUJD" "String" "base64url"} "ABC"
 
 # require('fs').renameSync()
 
@@ -734,17 +612,30 @@ njs_test {
      "undefined\r\n>> "}
     {"fs.renameSync(fn1, fn2)\r\n"
      "undefined\r\n>> "}
-    {"fs.readFileSync(fn2)\r\n"
+    {"String(fs.readFileSync(fn2))\r\n"
      "'ABC'\r\n>> "}
 }
 
 njs_test {
-    {"var fs = require('fs')\r\n"
+    {"var fs = require('fs'), fn = './build/test/file2'\r\n"
      "undefined\r\n>> "}
-    {"fs.renameSync('build/test/file2', 'test/fs/')\r\n"
+    {"fs.writeFileSync(fn, 'ABC')\r\n"
+     "undefined\r\n>> "}
+    {"fs.renameSync(fn, 'test/fs/')\r\n"
      "Error: Not a directory*"}
 }
 
+# require('fs').realpathSync()
+
+njs_test {
+    {"var fs = require('fs')\r\n"
+     "undefined\r\n>> "}
+    {"fs.realpathSync('./build/test/..').endsWith('build')\r\n"
+     "true\r\n>> "}
+    {"fs.realpathSync('./build/test/..', {encoding:'buffer'}) instanceof Buffer\r\n"
+     "true\r\n>> "}
+}
+
 njs_run {"-c" "setTimeout(() => {console.log('A'.repeat(1024))}, 0); ref"} \
 "^Thrown:
 ReferenceError: \"ref\" is not defined
@@ -1110,7 +1001,6 @@ short circut ok true
 chain ok true
 error 1 ok true
 error 2 ok true
-error 3 ok true
 errors ok"
 
 njs_run {"./test/js/fs_promises_002.js"} \