Last active
January 21, 2020 10:27
-
-
Save drsm/3f4567cacbc0f088862b04a0b5d5d7f2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User Artem S. Povalyukhin <artem.povaluhin@gmail.com> | |
# Date 1579515849 -10800 | |
# Mon Jan 20 13:24:09 2020 +0300 | |
# Node ID 596273ff490913494cf60b27ba7e8066fb8a3126 | |
# Parent c735708203e8093eb63abaeef2f7b79fb11b6ebe | |
Fixed callback invocations in "fs" module. | |
diff -r c735708203e8 -r 596273ff4909 src/njs_fs.c | |
--- a/src/njs_fs.c Fri Jan 17 10:04:28 2020 +0300 | |
+++ b/src/njs_fs.c Mon Jan 20 13:24:09 2020 +0300 | |
@@ -14,31 +14,39 @@ typedef struct { | |
} njs_fs_entry_t; | |
+typedef struct { | |
+ int errn; | |
+ const char *desc; | |
+ const char *syscall; | |
+} njs_fs_ioerror_t; | |
+ | |
+ | |
+njs_inline | |
+njs_fs_ioerror_t njs_fs_ioerror(int errn, const char *desc, const char *syscall) | |
+{ | |
+ return (njs_fs_ioerror_t){errn, desc, syscall}; | |
+} | |
+ | |
+ | |
static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused); | |
-static njs_int_t njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused); | |
+ njs_uint_t nargs, njs_index_t calltype); | |
static njs_int_t njs_fs_append_file(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused); | |
+ njs_uint_t nargs, njs_index_t calltype); | |
static njs_int_t njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused); | |
-static njs_int_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused); | |
-static njs_int_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused); | |
+ njs_uint_t nargs, njs_index_t calltype); | |
static njs_int_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, int default_flags); | |
-static njs_int_t njs_fs_write_file_sync_internal(njs_vm_t *vm, | |
- njs_value_t *args, njs_uint_t nargs, int default_flags); | |
+ njs_uint_t nargs, njs_index_t calltype, int default_flags); | |
static njs_int_t njs_fs_rename_sync(njs_vm_t *vm, njs_value_t *args, | |
njs_uint_t nargs, njs_index_t unused); | |
-static njs_int_t njs_fs_fd_read(njs_vm_t *vm, njs_value_t *path, int fd, | |
- njs_str_t *data); | |
+static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data, | |
+ njs_fs_ioerror_t *ioerror); | |
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); | |
static int njs_fs_flags(njs_str_t *value); | |
static mode_t njs_fs_mode(njs_value_t *value); | |
+static njs_int_t njs_fs_invoke_callback(njs_vm_t *vm, const njs_value_t *callback, | |
+ const njs_value_t *err, const njs_value_t *result); | |
static const njs_value_t njs_fs_errno_string = njs_string("errno"); | |
@@ -87,21 +95,30 @@ njs_fs_path_arg(njs_vm_t *vm, const char | |
} | |
+typedef enum { | |
+ NJS_FS_DIRECT = 0, | |
+ NJS_FS_PROMISE = 1, /* TODO */ | |
+ NJS_FS_CALLBACK = 2, | |
+} njs_fs_calltype_t; | |
+ | |
+ | |
static njs_int_t | |
njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
- njs_index_t unused) | |
+ njs_index_t calltype) | |
{ | |
- int fd, errn, flags; | |
+ int fd, flags; | |
u_char *start; | |
size_t size; | |
ssize_t length; | |
njs_str_t flag, encoding, data; | |
njs_int_t ret; | |
- const char *path, *syscall, *description; | |
+ const char *path; | |
struct stat sb; | |
- njs_value_t *callback, arguments[3]; | |
+ const njs_value_t *callback, *options; | |
+ njs_value_t err; | |
njs_object_prop_t *prop; | |
njs_lvlhsh_query_t lhq; | |
+ njs_fs_ioerror_t ioerror; | |
ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1), | |
&njs_str_value("path")); | |
@@ -109,247 +126,28 @@ njs_fs_read_file(njs_vm_t *vm, njs_value | |
return ret; | |
} | |
- if (njs_slow_path(nargs < 3)) { | |
- njs_type_error(vm, "too few arguments"); | |
- return NJS_ERROR; | |
+ options = njs_arg(args, nargs, 2); | |
+ | |
+ if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { | |
+ callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); | |
+ if (!njs_is_function(callback)) { | |
+ njs_type_error(vm, "\"callback\" must be a function"); | |
+ return NJS_ERROR; | |
+ } | |
+ if (options == callback) { | |
+ options = &njs_value_undefined; | |
+ } | |
+ | |
+ } else { | |
+ /* GCC complains about uninitialized callback. */ | |
+ callback = NULL; | |
} | |
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 = njs_str_value("flag"); | |
- lhq.proto = &njs_object_hash_proto; | |
- | |
- ret = njs_lvlhsh_find(njs_object_hash(&args[2]), &lhq); | |
- if (ret == NJS_OK) { | |
- prop = lhq.value; | |
- njs_string_get(&prop->value, &flag); | |
- } | |
- | |
- lhq.key_hash = NJS_ENCODING_HASH; | |
- lhq.key = njs_str_value("encoding"); | |
- lhq.proto = &njs_object_hash_proto; | |
- | |
- ret = njs_lvlhsh_find(njs_object_hash(&args[2]), &lhq); | |
- if (ret == NJS_OK) { | |
- prop = lhq.value; | |
- njs_string_get(&prop->value, &encoding); | |
- } | |
- | |
- } else { | |
- njs_type_error(vm, "Unknown options type " | |
- "(a string or object required)"); | |
- return NJS_ERROR; | |
- } | |
- | |
- if (njs_slow_path(nargs < 4 || !njs_is_function(&args[3]))) { | |
- njs_type_error(vm, "callback must be a function"); | |
- return NJS_ERROR; | |
- } | |
- | |
- callback = &args[3]; | |
- | |
- } else { | |
- if (njs_slow_path(!njs_is_function(&args[2]))) { | |
- njs_type_error(vm, "callback must be a function"); | |
- return NJS_ERROR; | |
- } | |
- | |
- callback = &args[2]; | |
- } | |
- | |
- if (flag.start == NULL) { | |
- flag = njs_str_value("r"); | |
- } | |
- | |
- flags = njs_fs_flags(&flag); | |
- if (njs_slow_path(flags == -1)) { | |
- njs_type_error(vm, "Unknown file open flags: \"%V\"", &flag); | |
- return NJS_ERROR; | |
- } | |
- | |
- if (encoding.length != 0 | |
- && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) | |
- { | |
- njs_type_error(vm, "Unknown encoding: \"%V\"", &encoding); | |
- return NJS_ERROR; | |
- } | |
- | |
- description = NULL; | |
- | |
- /* GCC 4 complains about uninitialized errn and syscall. */ | |
- errn = 0; | |
- syscall = NULL; | |
- | |
- fd = open(path, flags); | |
- if (njs_slow_path(fd < 0)) { | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "open"; | |
- goto done; | |
- } | |
- | |
- ret = fstat(fd, &sb); | |
- if (njs_slow_path(ret == -1)) { | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "stat"; | |
- goto done; | |
- } | |
- | |
- if (njs_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; | |
- | |
- 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; | |
- } | |
- | |
- } else { | |
- length = 0; | |
- } | |
- | |
- size = sb.st_size; | |
- | |
- if (njs_fast_path(size != 0)) { | |
- | |
- start = njs_string_alloc(vm, &arguments[2], size, length); | |
- if (njs_slow_path(start == NULL)) { | |
- goto fail; | |
- } | |
- | |
- data.start = start; | |
- data.length = size; | |
- | |
- ret = njs_fs_fd_read(vm, &args[1], fd, &data); | |
- if (ret != NJS_OK) { | |
- goto fail; | |
- } | |
- | |
- start = data.start; | |
- | |
- } else { | |
- /* size of the file is not known in advance. */ | |
- | |
- data.length = 0; | |
- | |
- ret = njs_fs_fd_read(vm, &args[1], fd, &data); | |
- if (ret != NJS_OK) { | |
- goto fail; | |
- } | |
- | |
- size = data.length; | |
- start = data.start; | |
- | |
- ret = njs_string_new(vm, &arguments[2], start, size, length); | |
- if (njs_slow_path(ret != NJS_OK)) { | |
- goto fail; | |
- } | |
- } | |
- | |
- if (encoding.length != 0) { | |
- length = njs_utf8_length(start, size); | |
- | |
- if (length >= 0) { | |
- 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 != -1) { | |
- (void) close(fd); | |
- } | |
- | |
- if (description != 0) { | |
- (void) njs_fs_error(vm, syscall, description, &args[1], errn, | |
- &arguments[1]); | |
- | |
- njs_set_undefined(&arguments[2]); | |
- | |
- } else { | |
- njs_set_undefined(&arguments[1]); | |
- } | |
- | |
- njs_set_undefined(&arguments[0]); | |
- | |
- ret = njs_function_apply(vm, njs_function(callback), arguments, 3, | |
- &vm->retval); | |
- | |
- if (njs_slow_path(ret != NJS_OK)) { | |
- return ret; | |
- } | |
- | |
- njs_set_undefined(&vm->retval); | |
- | |
- return NJS_OK; | |
- | |
-fail: | |
- | |
- if (fd != -1) { | |
- (void) close(fd); | |
- } | |
- | |
- return NJS_ERROR; | |
-} | |
- | |
- | |
-static njs_int_t | |
-njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
- njs_index_t unused) | |
-{ | |
- int fd, errn, flags; | |
- u_char *start; | |
- size_t size; | |
- ssize_t length; | |
- njs_str_t flag, encoding, data; | |
- njs_int_t ret; | |
- const char *path, *syscall, *description; | |
- struct stat sb; | |
- njs_object_prop_t *prop; | |
- njs_lvlhsh_query_t lhq; | |
- | |
- ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1), | |
- &njs_str_value("path")); | |
- if (njs_slow_path(ret != NJS_OK)) { | |
- return ret; | |
- } | |
- | |
- if (njs_slow_path(nargs < 2)) { | |
- njs_type_error(vm, "too few arguments"); | |
- return NJS_ERROR; | |
- } | |
- | |
- flag.start = NULL; | |
- encoding.length = 0; | |
- encoding.start = NULL; | |
- | |
- if (nargs == 3) { | |
+ if (njs_slow_path(!njs_is_undefined(options))) { | |
if (njs_is_string(&args[2])) { | |
njs_string_get(&args[2], &encoding); | |
@@ -391,11 +189,6 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ | |
return NJS_ERROR; | |
} | |
- path = njs_string_to_c_string(vm, &args[1]); | |
- if (njs_slow_path(path == NULL)) { | |
- return NJS_ERROR; | |
- } | |
- | |
if (encoding.length != 0 | |
&& (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) | |
{ | |
@@ -403,32 +196,22 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ | |
return NJS_ERROR; | |
} | |
- description = NULL; | |
- | |
- /* GCC 4 complains about uninitialized errn and syscall. */ | |
- errn = 0; | |
- syscall = NULL; | |
+ ioerror = njs_fs_ioerror(0, NULL, NULL); | |
fd = open(path, flags); | |
if (njs_slow_path(fd < 0)) { | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "open"; | |
+ ioerror = njs_fs_ioerror(errno, strerror(errno), "open"); | |
goto done; | |
} | |
ret = fstat(fd, &sb); | |
if (njs_slow_path(ret == -1)) { | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "stat"; | |
+ ioerror = njs_fs_ioerror(errno, strerror(errno), "stat"); | |
goto done; | |
} | |
if (njs_slow_path(!S_ISREG(sb.st_mode))) { | |
- errn = 0; | |
- description = "File is not regular"; | |
- syscall = "stat"; | |
+ ioerror = njs_fs_ioerror(0, "File is not regular", "stat"); | |
goto done; | |
} | |
@@ -452,7 +235,6 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ | |
size = sb.st_size; | |
if (njs_fast_path(size != 0)) { | |
- | |
start = njs_string_alloc(vm, &vm->retval, size, length); | |
if (njs_slow_path(start == NULL)) { | |
goto fail; | |
@@ -461,10 +243,13 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ | |
data.start = start; | |
data.length = size; | |
- ret = njs_fs_fd_read(vm, &args[1], fd, &data); | |
- if (ret != NJS_OK) { | |
+ ret = njs_fs_fd_read(vm, fd, &data, &ioerror); | |
+ if (ret == NJS_ERROR) { | |
goto fail; | |
} | |
+ if (ioerror.desc != NULL) { | |
+ goto done; | |
+ } | |
start = data.start; | |
@@ -473,10 +258,13 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ | |
data.length = 0; | |
- ret = njs_fs_fd_read(vm, &args[1], fd, &data); | |
- if (ret != NJS_OK) { | |
+ ret = njs_fs_fd_read(vm, fd, &data, &ioerror); | |
+ if (ret == NJS_ERROR) { | |
goto fail; | |
} | |
+ if (ioerror.desc != NULL) { | |
+ goto done; | |
+ } | |
size = data.length; | |
start = data.start; | |
@@ -494,9 +282,8 @@ njs_fs_read_file_sync(njs_vm_t *vm, njs_ | |
njs_string_length_set(&vm->retval, length); | |
} else { | |
- errn = 0; | |
- description = "Non-UTF8 file, convertion is not implemented"; | |
- syscall = NULL; | |
+ ioerror = njs_fs_ioerror(0, | |
+ "Non-UTF8 file, convertion is not implemented", NULL); | |
goto done; | |
} | |
} | |
@@ -507,12 +294,46 @@ done: | |
(void) close(fd); | |
} | |
- if (description != 0) { | |
- return njs_fs_error(vm, syscall, description, &args[1], errn, | |
- &vm->retval); | |
+ if (njs_fast_path(calltype == NJS_FS_DIRECT)) { | |
+ if (njs_slow_path(ioerror.desc != NULL)) { | |
+ (void) njs_fs_error(vm, ioerror.syscall, ioerror.desc, &args[1], ioerror.errn, | |
+ &vm->retval); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ return NJS_OK; | |
+ } | |
+ | |
+ if (calltype == NJS_FS_PROMISE) { | |
+ njs_internal_error(vm, "not implemented"); | |
+ return NJS_ERROR; | |
} | |
- return NJS_OK; | |
+ if (calltype == NJS_FS_CALLBACK) { | |
+ if (njs_slow_path(ioerror.desc)) { | |
+ ret = njs_fs_error(vm, ioerror.syscall, ioerror.desc, &args[1], ioerror.errn, | |
+ &err); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ njs_set_undefined(&vm->retval); | |
+ | |
+ } else { | |
+ njs_set_undefined(&err); | |
+ } | |
+ | |
+ ret = njs_fs_invoke_callback(vm, callback, &err, &vm->retval); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ njs_set_undefined(&vm->retval); | |
+ return NJS_OK; | |
+ } | |
+ | |
+ njs_internal_error(vm, "invalid calltype"); | |
+ return NJS_ERROR; | |
fail: | |
@@ -526,51 +347,37 @@ fail: | |
static njs_int_t | |
njs_fs_append_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
- njs_index_t unused) | |
+ njs_index_t calltype) | |
{ | |
return njs_fs_write_file_internal(vm, args, nargs, | |
- O_APPEND | O_CREAT | O_WRONLY); | |
+ calltype, O_APPEND | O_CREAT | O_WRONLY); | |
} | |
static njs_int_t | |
njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, | |
- njs_index_t unused) | |
+ njs_index_t calltype) | |
{ | |
return njs_fs_write_file_internal(vm, args, nargs, | |
- O_TRUNC | O_CREAT | O_WRONLY); | |
-} | |
- | |
- | |
-static njs_int_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args, | |
- njs_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_int_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, njs_index_t unused) | |
-{ | |
- return njs_fs_write_file_sync_internal(vm, args, nargs, | |
- O_TRUNC | O_CREAT | O_WRONLY); | |
+ calltype, O_TRUNC | O_CREAT | O_WRONLY); | |
} | |
static njs_int_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, int default_flags) | |
+ njs_uint_t nargs, njs_index_t calltype, int default_flags) | |
{ | |
- int fd, errn, flags; | |
+ int fd, flags; | |
u_char *p, *end; | |
mode_t md; | |
ssize_t n; | |
njs_str_t data, flag, encoding; | |
njs_int_t ret; | |
- const char *path, *syscall, *description; | |
- njs_value_t *callback, *mode, arguments[2]; | |
+ const char *path; | |
+ const njs_value_t *callback, *options; | |
+ njs_value_t *mode, err; | |
njs_object_prop_t *prop; | |
njs_lvlhsh_query_t lhq; | |
+ njs_fs_ioerror_t ioerror; | |
ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1), | |
&njs_str_value("path")); | |
@@ -578,14 +385,22 @@ static njs_int_t njs_fs_write_file_inter | |
return ret; | |
} | |
- if (njs_slow_path(nargs < 4)) { | |
- njs_type_error(vm, "too few arguments"); | |
+ if (njs_slow_path(!njs_is_string(njs_arg(args, nargs, 2)))) { | |
+ njs_type_error(vm, "\"data\" must be a string"); | |
return NJS_ERROR; | |
} | |
- if (njs_slow_path(!njs_is_string(&args[2]))) { | |
- njs_type_error(vm, "data must be a string"); | |
- return NJS_ERROR; | |
+ options = njs_arg(args, nargs, 3); | |
+ | |
+ if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { | |
+ callback = njs_arg(args, nargs, njs_min(nargs - 1, 4)); | |
+ if (!njs_is_function(callback)) { | |
+ njs_type_error(vm, "\"callback\" must be a function"); | |
+ return NJS_ERROR; | |
+ } | |
+ if (options == callback) { | |
+ options = &njs_value_undefined; | |
+ } | |
} | |
mode = NULL; | |
@@ -595,191 +410,7 @@ static njs_int_t njs_fs_write_file_inter | |
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 = njs_str_value("flag"); | |
- lhq.proto = &njs_object_hash_proto; | |
- | |
- ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq); | |
- if (ret == NJS_OK) { | |
- prop = lhq.value; | |
- njs_string_get(&prop->value, &flag); | |
- } | |
- | |
- lhq.key_hash = NJS_ENCODING_HASH; | |
- lhq.key = njs_str_value("encoding"); | |
- lhq.proto = &njs_object_hash_proto; | |
- | |
- ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq); | |
- if (ret == NJS_OK) { | |
- prop = lhq.value; | |
- njs_string_get(&prop->value, &encoding); | |
- } | |
- | |
- lhq.key_hash = NJS_MODE_HASH; | |
- lhq.key = njs_str_value("mode"); | |
- lhq.proto = &njs_object_hash_proto; | |
- | |
- ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq); | |
- if (ret == NJS_OK) { | |
- prop = lhq.value; | |
- mode = &prop->value; | |
- } | |
- | |
- } else { | |
- njs_type_error(vm, "Unknown options type " | |
- "(a string or object required)"); | |
- return NJS_ERROR; | |
- } | |
- | |
- if (njs_slow_path(nargs < 5 || !njs_is_function(&args[4]))) { | |
- njs_type_error(vm, "callback must be a function"); | |
- return NJS_ERROR; | |
- } | |
- | |
- callback = &args[4]; | |
- | |
- } else { | |
- if (njs_slow_path(!njs_is_function(&args[3]))) { | |
- njs_type_error(vm, "callback must be a function"); | |
- return NJS_ERROR; | |
- } | |
- | |
- callback = &args[3]; | |
- } | |
- | |
- if (flag.start != NULL) { | |
- flags = njs_fs_flags(&flag); | |
- if (njs_slow_path(flags == -1)) { | |
- njs_type_error(vm, "Unknown file open flags: \"%V\"", &flag); | |
- return NJS_ERROR; | |
- } | |
- | |
- } else { | |
- flags = default_flags; | |
- } | |
- | |
- if (mode != NULL) { | |
- md = njs_fs_mode(mode); | |
- | |
- } else { | |
- md = 0666; | |
- } | |
- | |
- if (encoding.length != 0 | |
- && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) | |
- { | |
- njs_type_error(vm, "Unknown encoding: \"%V\"", &encoding); | |
- return NJS_ERROR; | |
- } | |
- | |
- description = NULL; | |
- | |
- /* GCC 4 complains about uninitialized errn and syscall. */ | |
- errn = 0; | |
- syscall = NULL; | |
- | |
- fd = open(path, flags, md); | |
- if (njs_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 (njs_slow_path(n == -1)) { | |
- if (errno == EINTR) { | |
- continue; | |
- } | |
- | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "write"; | |
- goto done; | |
- } | |
- | |
- p += n; | |
- } | |
- | |
-done: | |
- | |
- if (fd != -1) { | |
- (void) close(fd); | |
- } | |
- | |
- if (description != 0) { | |
- (void) njs_fs_error(vm, syscall, description, &args[1], errn, | |
- &arguments[1]); | |
- | |
- } else { | |
- njs_set_undefined(&arguments[1]); | |
- } | |
- | |
- njs_set_undefined(&arguments[0]); | |
- | |
- ret = njs_function_apply(vm, njs_function(callback), arguments, 2, | |
- &vm->retval); | |
- | |
- if (njs_slow_path(ret != NJS_OK)) { | |
- return ret; | |
- } | |
- | |
- njs_set_undefined(&vm->retval); | |
- | |
- return NJS_OK; | |
-} | |
- | |
- | |
-static njs_int_t | |
-njs_fs_write_file_sync_internal(njs_vm_t *vm, njs_value_t *args, | |
- njs_uint_t nargs, int default_flags) | |
-{ | |
- int fd, errn, flags; | |
- u_char *p, *end; | |
- mode_t md; | |
- ssize_t n; | |
- njs_str_t data, flag, encoding; | |
- njs_int_t ret; | |
- const char *path, *syscall, *description; | |
- njs_value_t *mode; | |
- njs_object_prop_t *prop; | |
- njs_lvlhsh_query_t lhq; | |
- | |
- ret = njs_fs_path_arg(vm, &path, njs_arg(args, nargs, 1), | |
- &njs_str_value("path")); | |
- if (njs_slow_path(ret != NJS_OK)) { | |
- return ret; | |
- } | |
- | |
- if (njs_slow_path(nargs < 3)) { | |
- njs_type_error(vm, "too few arguments"); | |
- return NJS_ERROR; | |
- } | |
- | |
- if (njs_slow_path(!njs_is_string(&args[2]))) { | |
- njs_type_error(vm, "data must be a string"); | |
- return NJS_ERROR; | |
- } | |
- | |
- mode = NULL; | |
- /* GCC complains about uninitialized flag.length. */ | |
- flag.length = 0; | |
- flag.start = NULL; | |
- encoding.length = 0; | |
- encoding.start = NULL; | |
- | |
- if (nargs == 4) { | |
+ if (njs_slow_path(!njs_is_undefined(options))) { | |
if (njs_is_string(&args[3])) { | |
njs_string_get(&args[3], &encoding); | |
@@ -846,17 +477,11 @@ njs_fs_write_file_sync_internal(njs_vm_t | |
return NJS_ERROR; | |
} | |
- description = NULL; | |
- | |
- /* GCC 4 complains about uninitialized errn and syscall. */ | |
- errn = 0; | |
- syscall = NULL; | |
+ ioerror = njs_fs_ioerror(0, NULL, NULL); | |
fd = open(path, flags, md); | |
if (njs_slow_path(fd < 0)) { | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "open"; | |
+ ioerror = njs_fs_ioerror(errno, strerror(errno), "open"); | |
goto done; | |
} | |
@@ -872,9 +497,7 @@ njs_fs_write_file_sync_internal(njs_vm_t | |
continue; | |
} | |
- errn = errno; | |
- description = strerror(errno); | |
- syscall = "write"; | |
+ ioerror = njs_fs_ioerror(errno, strerror(errno), "write"); | |
goto done; | |
} | |
@@ -887,15 +510,45 @@ done: | |
(void) close(fd); | |
} | |
- if (description != 0) { | |
- return njs_fs_error(vm, syscall, description, &args[1], errn, | |
- &vm->retval); | |
+ if (njs_fast_path(calltype == NJS_FS_DIRECT)) { | |
+ if (njs_slow_path(ioerror.desc != NULL)) { | |
+ (void) njs_fs_error(vm, ioerror.syscall, ioerror.desc, &args[1], | |
+ ioerror.errn, &vm->retval); | |
+ return NJS_ERROR; | |
+ } | |
- } else { | |
njs_set_undefined(&vm->retval); | |
+ return NJS_OK; | |
+ } | |
+ | |
+ if (calltype == NJS_FS_PROMISE) { | |
+ njs_internal_error(vm, "not implemented"); | |
+ return NJS_ERROR; | |
} | |
- return NJS_OK; | |
+ if (calltype == NJS_FS_CALLBACK) { | |
+ if (ioerror.desc != NULL) { | |
+ ret = njs_fs_error(vm, ioerror.syscall, ioerror.desc, &args[1], | |
+ ioerror.errn, &err); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ } else { | |
+ njs_set_undefined(&err); | |
+ } | |
+ | |
+ ret = njs_fs_invoke_callback(vm, callback, &err, NULL); | |
+ if (njs_slow_path(ret != NJS_OK)) { | |
+ return ret; | |
+ } | |
+ | |
+ njs_set_undefined(&vm->retval); | |
+ return NJS_OK; | |
+ } | |
+ | |
+ njs_internal_error(vm, "invalid calltype"); | |
+ return NJS_ERROR; | |
} | |
@@ -920,8 +573,9 @@ njs_fs_rename_sync(njs_vm_t *vm, njs_val | |
ret = rename(old_path, new_path); | |
if (njs_slow_path(ret != 0)) { | |
- return njs_fs_error(vm, "rename", strerror(errno), NULL, errno, | |
+ (void) njs_fs_error(vm, "rename", strerror(errno), NULL, errno, | |
&vm->retval); | |
+ return NJS_ERROR; | |
} | |
njs_set_undefined(&vm->retval); | |
@@ -931,7 +585,8 @@ njs_fs_rename_sync(njs_vm_t *vm, njs_val | |
static njs_int_t | |
-njs_fs_fd_read(njs_vm_t *vm, njs_value_t *path, int fd, njs_str_t *data) | |
+njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data, | |
+ njs_fs_ioerror_t *ioerror) | |
{ | |
u_char *p, *end, *start; | |
size_t size; | |
@@ -956,8 +611,8 @@ njs_fs_fd_read(njs_vm_t *vm, njs_value_t | |
n = read(fd, p, end - p); | |
if (njs_slow_path(n < 0)) { | |
- return njs_fs_error(vm, "read", strerror(errno), path, errno, | |
- &vm->retval); | |
+ *ioerror = njs_fs_ioerror(errno, strerror(errno), "read"); | |
+ return NJS_DECLINED; | |
} | |
p += n; | |
@@ -1084,7 +739,7 @@ njs_fs_error(njs_vm_t *vm, const char *s | |
njs_set_object(retval, error); | |
- return NJS_ERROR; | |
+ return NJS_OK; | |
} | |
@@ -1127,6 +782,58 @@ njs_fs_mode(njs_value_t *value) | |
} | |
+static njs_int_t | |
+njs_fs_invoke_callback(njs_vm_t *vm, const njs_value_t *callback, const njs_value_t *err, | |
+ const njs_value_t *result) | |
+{ | |
+ njs_int_t nargs; | |
+ njs_event_t *event; | |
+ njs_vm_ops_t *ops; | |
+ | |
+ nargs = (result == NULL) ? 1 : 2; | |
+ | |
+ ops = vm->options.ops; | |
+ if (njs_slow_path(ops == NULL)) { | |
+ njs_internal_error(vm, "not supported by host environment"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ event = njs_mp_alloc(vm->mem_pool, sizeof(njs_event_t)); | |
+ if (njs_slow_path(event == NULL)) { | |
+ goto memory_error; | |
+ } | |
+ event->destructor = ops->clear_timer; | |
+ event->function = njs_function(callback); | |
+ event->nargs = nargs; | |
+ event->once = 1; | |
+ event->posted = 0; | |
+ | |
+ event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs); | |
+ if (njs_slow_path(event->args == NULL)) { | |
+ goto memory_error; | |
+ } | |
+ /* GC: retain */ | |
+ event->args[0] = *err; | |
+ if (nargs == 2) { | |
+ event->args[1] = *result; | |
+ } | |
+ | |
+ event->host_event = ops->set_timer(vm->external, 0, event); | |
+ if (njs_slow_path(event->host_event == NULL)) { | |
+ njs_internal_error(vm, "set_timer() failed"); | |
+ return NJS_ERROR; | |
+ } | |
+ | |
+ return njs_add_event(vm, event); | |
+ | |
+memory_error: | |
+ | |
+ njs_memory_error(vm); | |
+ | |
+ return NJS_ERROR; | |
+} | |
+ | |
+ | |
static const njs_object_prop_t njs_fs_object_properties[] = | |
{ | |
{ | |
@@ -1139,7 +846,7 @@ static const njs_object_prop_t njs_fs_o | |
{ | |
.type = NJS_PROPERTY, | |
.name = njs_string("readFile"), | |
- .value = njs_native_function(njs_fs_read_file, 0), | |
+ .value = njs_native_function2(njs_fs_read_file, 0, NJS_FS_CALLBACK), | |
.writable = 1, | |
.configurable = 1, | |
}, | |
@@ -1147,7 +854,7 @@ static const njs_object_prop_t njs_fs_o | |
{ | |
.type = NJS_PROPERTY, | |
.name = njs_string("readFileSync"), | |
- .value = njs_native_function(njs_fs_read_file_sync, 0), | |
+ .value = njs_native_function2(njs_fs_read_file, 0, NJS_FS_DIRECT), | |
.writable = 1, | |
.configurable = 1, | |
}, | |
@@ -1155,7 +862,7 @@ static const njs_object_prop_t njs_fs_o | |
{ | |
.type = NJS_PROPERTY, | |
.name = njs_string("appendFile"), | |
- .value = njs_native_function(njs_fs_append_file, 0), | |
+ .value = njs_native_function2(njs_fs_append_file, 0, NJS_FS_CALLBACK), | |
.writable = 1, | |
.configurable = 1, | |
}, | |
@@ -1163,7 +870,7 @@ static const njs_object_prop_t njs_fs_o | |
{ | |
.type = NJS_PROPERTY, | |
.name = njs_string("appendFileSync"), | |
- .value = njs_native_function(njs_fs_append_file_sync, 0), | |
+ .value = njs_native_function2(njs_fs_append_file, 0, NJS_FS_DIRECT), | |
.writable = 1, | |
.configurable = 1, | |
}, | |
@@ -1171,7 +878,7 @@ static const njs_object_prop_t njs_fs_o | |
{ | |
.type = NJS_PROPERTY, | |
.name = njs_string("writeFile"), | |
- .value = njs_native_function(njs_fs_write_file, 0), | |
+ .value = njs_native_function2(njs_fs_write_file, 0, NJS_FS_CALLBACK), | |
.writable = 1, | |
.configurable = 1, | |
}, | |
@@ -1179,7 +886,7 @@ static const njs_object_prop_t njs_fs_o | |
{ | |
.type = NJS_PROPERTY, | |
.name = njs_string("writeFileSync"), | |
- .value = njs_native_function(njs_fs_write_file_sync, 0), | |
+ .value = njs_native_function2(njs_fs_write_file, 0, NJS_FS_DIRECT), | |
.writable = 1, | |
.configurable = 1, | |
}, | |
diff -r c735708203e8 -r 596273ff4909 src/test/njs_unit_test.c | |
--- a/src/test/njs_unit_test.c Fri Jan 17 10:04:28 2020 +0300 | |
+++ b/src/test/njs_unit_test.c Mon Jan 20 13:24:09 2020 +0300 | |
@@ -15524,15 +15524,19 @@ static njs_unit_test_t njs_test[] = | |
{ njs_str("var fs = require('fs');" | |
"fs.readFile('/njs_unknown_path')"), | |
- njs_str("TypeError: too few arguments") }, | |
+ njs_str("TypeError: \"callback\" must be a function") }, | |
+ | |
+ { njs_str("var fs = require('fs');" | |
+ "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'})"), | |
- njs_str("TypeError: callback must be a function") }, | |
+ njs_str("TypeError: \"callback\" must be a function") }, | |
{ njs_str("var fs = require('fs');" | |
"fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"), | |
- njs_str("TypeError: callback must be a function") }, | |
+ njs_str("TypeError: \"callback\" must be a function") }, | |
{ njs_str("var fs = require('fs');" | |
"fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"), | |
@@ -15580,20 +15584,24 @@ static njs_unit_test_t njs_test[] = | |
njs_str("TypeError: \"path\" must be a string") }, | |
{ njs_str("var fs = require('fs');" | |
- "fs.writeFile('/njs_unknown_path')"), | |
- njs_str("TypeError: too few arguments") }, | |
- | |
- { njs_str("var fs = require('fs');" | |
- "fs.writeFile('/njs_unknown_path', '')"), | |
- njs_str("TypeError: too few arguments") }, | |
- | |
- { njs_str("var fs = require('fs');" | |
"fs.writeFile({}, '', function () {})"), | |
njs_str("TypeError: \"path\" must be a string") }, | |
{ njs_str("var fs = require('fs');" | |
+ "fs.writeFile('/njs_unknown_path')"), | |
+ njs_str("TypeError: \"data\" must be a string") }, | |
+ | |
+ { njs_str("var fs = require('fs');" | |
+ "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)"), | |
+ njs_str("TypeError: \"callback\" must be a function") }, | |
+ | |
+ { njs_str("var fs = require('fs');" | |
"fs.writeFile('/njs_unknown_path', '', 'utf8')"), | |
- njs_str("TypeError: callback must be a function") }, | |
+ njs_str("TypeError: \"callback\" must be a function") }, | |
{ njs_str("var fs = require('fs');" | |
"fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"), | |
@@ -15619,7 +15627,7 @@ static njs_unit_test_t njs_test[] = | |
{ njs_str("var fs = require('fs');" | |
"fs.writeFileSync('/njs_unknown_path')"), | |
- njs_str("TypeError: too few arguments") }, | |
+ njs_str("TypeError: \"data\" must be a string") }, | |
{ njs_str("var fs = require('fs');" | |
"fs.writeFileSync({}, '')"), | |
diff -r c735708203e8 -r 596273ff4909 test/njs_expect_test.exp | |
--- a/test/njs_expect_test.exp Fri Jan 17 10:04:28 2020 +0300 | |
+++ b/test/njs_expect_test.exp Mon Jan 20 13:24:09 2020 +0300 | |
@@ -432,54 +432,61 @@ njs_test { | |
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" | |
- "Z4\r\nundefined\r\n>> "} | |
+ "undefined\r\nZ4\r\n>> "} | |
} | |
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" | |
- "Z7\r\nundefined\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" | |
- "αβZγ\r\nundefined\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" | |
- "x\r\nundefined\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" | |
- "x\r\nundefined\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" | |
- "{\"errno\":2,\"path\":\"test/fs/nonexistent\",\"syscall\":\"open\"}\r\nundefined\r\n>> "} | |
+ "undefined\r\n{\"errno\":2,\"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" | |
- "Error: No such file or directory\r\nundefined\r\n>> "} | |
+ "undefined\r\nError: No such file or directory\r\n>> "} | |
} | |
njs_test { | |
{"var fs = require('fs')\r\n" | |
"undefined\r\n>> "} | |
{"fs.readFile('/proc/version', (e, data) => {console.log(e || data.slice(0,5) == 'Linux')})\r\n" | |
- "true\r\nundefined\r\n>> "} | |
+ "undefined\r\ntrue\r\n>> "} | |
{"fs.readFile('/proc/cpuinfo', (e, data) => {console.log(e || data.slice(0,9) == 'processor')})\r\n" | |
- "true\r\nundefined\r\n>> "} | |
+ "undefined\r\ntrue\r\n>> "} | |
} | |
# require('fs').readFileSync() | |
@@ -563,21 +570,28 @@ njs_test { | |
{"function h1(e) {if (e) {throw e}; console.log(fs.readFileSync(fn))}\r\n" | |
"undefined\r\n>> "} | |
{"fs.writeFile(fn, 'ABC', h1)\r\n" | |
- "ABC\r\nundefined\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" | |
- "ABC\r\nundefined\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" | |
- "ABC\r\nundefined\r\n>> "} | |
+ "undefined\r\nABC\r\n>> "} | |
} | |
exec rm -fr ./build/test/wo_file | |
@@ -593,7 +607,7 @@ 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>> "} | |
+ "undefined\r\n{\"errno\":13,\"path\":\"/invalid_path\",\"syscall\":\"open\"}\r\n>> "} | |
} | |
# require('fs').writeFileSync() | |
@@ -659,7 +673,7 @@ njs_test { | |
{"function h2(e) {fs.appendFile(fn, 'ABC', h1)}\r\n" | |
"undefined\r\n>> "} | |
{"fs.appendFile(fn, 'ABC', h2)\r\n" | |
- "ABCABC\r\nundefined\r\n>> "} | |
+ "undefined\r\nABCABC\r\n>> "} | |
} | |
# require('fs').appendFileSync() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment