From: Artem S. Povalyukhin Date: Sat, 25 Jan 2020 18:55:50 +0000 (+0300) Subject: Introduced fs.promises API. X-Git-Tag: 0.3.9~24 X-Git-Url: http://git.kaiwu.me/sitemap.xml?a=commitdiff_plain;h=096f5aaee1897bdfc8551f92e56df8e871cd37ff;p=njs.git Introduced fs.promises API. --- diff --git a/src/njs_fs.c b/src/njs_fs.c index d20568fd..2e3b15b3 100644 --- a/src/njs_fs.c +++ b/src/njs_fs.c @@ -25,39 +25,33 @@ 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; } njs_fs_entry_t; -typedef struct { - int errn; - const char *desc; - const char *syscall; -} njs_fs_ioerror_t; - +static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data); -static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, - 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 magic); -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, 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_add_event(njs_vm_t *vm, const njs_value_t *callback, - const njs_value_t *err, const njs_value_t *result); + const char *desc, njs_value_t *path, int errn, njs_value_t *retval); +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 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 const njs_value_t njs_fs_errno_string = njs_string("errno"); -static const njs_value_t njs_fs_path_string = njs_string("path"); -static const njs_value_t njs_fs_syscall_string = njs_string("syscall"); +static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback, + const njs_value_t *args, njs_uint_t nargs); static njs_fs_entry_t njs_flags_table[] = { @@ -101,130 +95,106 @@ njs_fs_path_arg(njs_vm_t *vm, const char **dst, const njs_value_t* src, } -njs_inline void -njs_fs_set_ioerr(njs_fs_ioerror_t *ioerror, int errn, const char *desc, - const char *syscall) -{ - ioerror->errn = errn; - ioerror->desc = desc; - ioerror->syscall = syscall; -} - - 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 flag, encoding, data; - njs_int_t ret; - const char *path; - struct stat sb; - 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")); + 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"); + + path = njs_arg(args, nargs, 1); + ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path")); if (njs_slow_path(ret != NJS_OK)) { return ret; } + callback = NULL; + options = njs_arg(args, nargs, 2); - if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { + if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_is_function(callback)) { njs_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } + if (options == callback) { - options = &njs_value_undefined; + options = njs_value_arg(&njs_value_undefined); } - - } else { - /* GCC complains about uninitialized callback. */ - callback = NULL; } - flag.start = NULL; - encoding.length = 0; - encoding.start = NULL; - - if (njs_slow_path(!njs_is_undefined(options))) { - 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); - } + njs_set_undefined(&flag); + njs_set_undefined(&encoding); - lhq.key_hash = NJS_ENCODING_HASH; - lhq.key = njs_str_value("encoding"); - lhq.proto = &njs_object_hash_proto; + switch (options->type) { + case NJS_STRING: + encoding = *options; + break; - ret = njs_lvlhsh_find(njs_object_hash(&args[2]), &lhq); - if (ret == NJS_OK) { - prop = lhq.value; - njs_string_get(&prop->value, &encoding); - } + case NJS_UNDEFINED: + break; - } else { - njs_type_error(vm, "Unknown options type " - "(a string or object required)"); + default: + if (!njs_is_object(options)) { + njs_type_error(vm, "Unknown options type: \"%s\" " + "(a string or object required)", + njs_type_string(options->type)); return NJS_ERROR; } - } - if (flag.start == NULL) { - flag = njs_str_value("r"); + ret = njs_value_property(vm, options, njs_value_arg(&string_flag), + &flag); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } + + ret = njs_value_property(vm, options, njs_value_arg(&string_encoding), + &encoding); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } } - flags = njs_fs_flags(&flag); + flags = njs_fs_flags(vm, &flag, O_RDONLY); 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); + enc = njs_fs_encoding(vm, &encoding); + if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) { return NJS_ERROR; } - njs_fs_set_ioerr(&ioerror, 0, NULL, NULL); - - fd = open(path, flags); + fd = open(file_path, flags); if (njs_slow_path(fd < 0)) { - njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "open"); + ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval); goto done; } ret = fstat(fd, &sb); if (njs_slow_path(ret == -1)) { - njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "fstat"); + ret = njs_fs_error(vm, "stat", strerror(errno), path, errno, &retval); goto done; } if (njs_slow_path(!S_ISREG(sb.st_mode))) { - njs_fs_set_ioerr(&ioerror, 0, "File is not regular", "stat"); + ret = njs_fs_error(vm, "stat", "File is not regular", path, 0, &retval); goto done; } - if (encoding.length != 0) { + if (enc == NJS_FS_ENC_UTF8) { length = sb.st_size; if (length > NJS_STRING_MAP_STRIDE) { @@ -244,21 +214,23 @@ njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, size = sb.st_size; if (njs_fast_path(size != 0)) { - start = njs_string_alloc(vm, &vm->retval, size, length); + start = njs_string_alloc(vm, &retval, size, length); if (njs_slow_path(start == NULL)) { - goto fail; + ret = NJS_ERROR; + goto done; } data.start = start; data.length = size; - ret = njs_fs_fd_read(vm, fd, &data, &ioerror); - if (ret != NJS_OK) { - if (ioerror.desc != NULL) { - goto done; + 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 fail; + goto done; } start = data.start; @@ -268,34 +240,34 @@ njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, data.length = 0; - ret = njs_fs_fd_read(vm, fd, &data, &ioerror); - if (ret != NJS_OK) { - if (ioerror.desc != NULL) { - goto done; + 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 fail; + goto done; } size = data.length; start = data.start; - ret = njs_string_new(vm, &vm->retval, start, size, length); + ret = njs_string_new(vm, &retval, start, size, length); if (njs_slow_path(ret != NJS_OK)) { - goto fail; + goto done; } } - if (encoding.length != 0) { + if (enc == NJS_FS_ENC_UTF8) { length = njs_utf8_length(start, size); if (length >= 0) { - njs_string_length_set(&vm->retval, length); + njs_string_length_set(&retval, length); } else { - njs_fs_set_ioerr(&ioerror, 0, - "Non-UTF8 file, convertion is not implemented", - NULL); + ret = njs_fs_error(vm, NULL, "Non-UTF8 file, convertion " + "is not implemented", path, 0, &retval); goto done; } } @@ -306,51 +278,8 @@ done: (void) close(fd); } - 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, "promise callback is not implemented"); - return NJS_ERROR; - } - - 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_add_event(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: - - if (fd != -1) { - (void) close(fd); + if (ret == NJS_OK) { + return njs_fs_result(vm, &retval, calltype, callback, 2); } return NJS_ERROR; @@ -361,27 +290,30 @@ 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 data, flag, encoding; - njs_int_t ret; - const char *path; - njs_value_t *mode, err; - njs_fs_calltype_t calltype; - const njs_value_t *callback, *options; - njs_fs_ioerror_t ioerror; - 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")); + 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"); + + path = njs_arg(args, nargs, 1); + ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path")); if (njs_slow_path(ret != NJS_OK)) { return ret; } - if (njs_slow_path(!njs_is_string(njs_arg(args, nargs, 2)))) { + 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; } @@ -390,104 +322,84 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, calltype = magic & 3; options = njs_arg(args, nargs, 3); - if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { + if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 4)); if (!njs_is_function(callback)) { njs_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } + if (options == callback) { - options = &njs_value_undefined; + options = njs_value_arg(&njs_value_undefined); } } - mode = NULL; - /* GCC complains about uninitialized flag.length. */ - flag.length = 0; - flag.start = NULL; - encoding.length = 0; - encoding.start = NULL; - - if (njs_slow_path(!njs_is_undefined(options))) { - 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); - } + njs_set_undefined(&flag); + njs_set_undefined(&mode); + njs_set_undefined(&encoding); - lhq.key_hash = NJS_ENCODING_HASH; - lhq.key = njs_str_value("encoding"); - lhq.proto = &njs_object_hash_proto; + switch (options->type) { + case NJS_STRING: + encoding = *options; + break; - ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq); - if (ret == NJS_OK) { - prop = lhq.value; - njs_string_get(&prop->value, &encoding); - } + case NJS_UNDEFINED: + break; - lhq.key_hash = NJS_MODE_HASH; - lhq.key = njs_str_value("mode"); - lhq.proto = &njs_object_hash_proto; + default: + if (!njs_is_object(options)) { + njs_type_error(vm, "Unknown options type: \"%s\" " + "(a string or object required)", + njs_type_string(options->type)); + return NJS_ERROR; + } - ret = njs_lvlhsh_find(njs_object_hash(&args[3]), &lhq); - if (ret == NJS_OK) { - prop = lhq.value; - mode = &prop->value; - } + ret = njs_value_property(vm, options, njs_value_arg(&string_flag), + &flag); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; + } - } else { - njs_type_error(vm, "Unknown options type " - "(a string or object required)"); - return NJS_ERROR; + ret = njs_value_property(vm, options, njs_value_arg(&string_mode), + &mode); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } - } - 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; + ret = njs_value_property(vm, options, njs_value_arg(&string_encoding), + &encoding); + if (njs_slow_path(ret == NJS_ERROR)) { + return ret; } + } - } else { - flags = O_CREAT | O_WRONLY; - flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC; + flags = njs_fs_flags(vm, &flag, O_CREAT | O_WRONLY); + if (njs_slow_path(flags == -1)) { + return NJS_ERROR; } - if (mode != NULL) { - md = njs_fs_mode(mode); + flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC; - } else { - md = 0666; + md = njs_fs_mode(vm, &mode, 0666); + if (njs_slow_path(md == (mode_t) -1)) { + return NJS_ERROR; } - if (encoding.length != 0 - && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0)) - { - njs_type_error(vm, "Unknown encoding: \"%V\"", &encoding); + enc = njs_fs_encoding(vm, &encoding); + if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) { return NJS_ERROR; } - njs_fs_set_ioerr(&ioerror, 0, NULL, NULL); - - fd = open(path, flags, md); + fd = open(file_path, flags, md); if (njs_slow_path(fd < 0)) { - njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "open"); + ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval); goto done; } - njs_string_get(&args[2], &data); + njs_string_get(data, &content); - p = data.start; - end = p + data.length; + p = content.start; + end = p + content.length; while (p < end) { n = write(fd, p, end - p); @@ -496,57 +408,27 @@ njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, continue; } - njs_fs_set_ioerr(&ioerror, errno, strerror(errno), "write"); + ret = njs_fs_error(vm, "write", strerror(errno), path, errno, + &retval); goto done; } p += n; } + ret = NJS_OK; + njs_set_undefined(&retval); + done: if (fd != -1) { (void) close(fd); } - 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; - } - - njs_set_undefined(&vm->retval); - return NJS_OK; - } - - if (calltype == NJS_FS_PROMISE) { - njs_internal_error(vm, "not implemented"); - return NJS_ERROR; + if (ret == NJS_OK) { + return njs_fs_result(vm, &retval, calltype, callback, 1); } - 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_add_event(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; } @@ -584,8 +466,7 @@ njs_fs_rename_sync(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, static njs_int_t -njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data, - njs_fs_ioerror_t *ioerror) +njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data) { u_char *p, *end, *start; size_t size; @@ -610,7 +491,6 @@ njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data, n = read(fd, p, end - p); if (njs_slow_path(n < 0)) { - njs_fs_set_ioerr(ioerror, errno, strerror(errno), "read"); return NJS_DECLINED; } @@ -645,152 +525,224 @@ njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data, } +static int +njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags) +{ + njs_str_t flags; + njs_int_t ret; + njs_fs_entry_t *fl; + + if (njs_is_undefined(value)) { + return default_flags; + } + + ret = njs_value_to_string(vm, value, value); + if (njs_slow_path(ret != NJS_OK)) { + return -1; + } + + njs_string_get(value, &flags); + + for (fl = &njs_flags_table[0]; fl->name.length != 0; fl++) { + if (njs_strstr_eq(&flags, &fl->name)) { + return fl->value; + } + } + + njs_type_error(vm, "Unknown file open flags: \"%V\"", &flags); + + return -1; +} + + +static mode_t +njs_fs_mode(njs_vm_t *vm, njs_value_t *value, mode_t default_mode) +{ + uint32_t u32; + njs_int_t ret; + + if (njs_is_undefined(value)) { + return default_mode; + } + + ret = njs_value_to_uint32(vm, value, &u32); + if (njs_slow_path(ret != NJS_OK)) { + return (mode_t) -1; + } + + return (mode_t) u32; +} + + +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) { - size_t size; - njs_int_t ret; - njs_value_t string, value; - njs_object_t *error; - njs_object_prop_t *prop; - njs_lvlhsh_query_t lhq; + size_t size; + njs_int_t ret; + njs_value_t value; + njs_object_t *error; + + static const njs_value_t string_errno = njs_string("errno"); + static const njs_value_t string_path = njs_string("path"); + static const njs_value_t string_syscall = njs_string("syscall"); size = description != NULL ? njs_strlen(description) : 0; - ret = njs_string_new(vm, &string, (u_char *) description, size, size); + ret = njs_string_new(vm, &value, (u_char *) description, size, size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - error = njs_error_alloc(vm, NJS_OBJ_TYPE_ERROR, NULL, &string); + error = njs_error_alloc(vm, NJS_OBJ_TYPE_ERROR, NULL, &value); if (njs_slow_path(error == NULL)) { return NJS_ERROR; } - lhq.replace = 0; - lhq.pool = vm->mem_pool; + njs_set_object(retval, error); if (errn != 0) { - lhq.key = njs_str_value("errno"); - lhq.key_hash = NJS_ERRNO_HASH; - lhq.proto = &njs_object_hash_proto; - njs_set_number(&value, errn); - - prop = njs_object_prop_alloc(vm, &njs_fs_errno_string, &value, 1); - if (njs_slow_path(prop == NULL)) { - return NJS_ERROR; - } - - lhq.value = prop; - - ret = njs_lvlhsh_insert(&error->hash, &lhq); + ret = njs_value_property_set(vm, retval, njs_value_arg(&string_errno), + &value); if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } } if (path != NULL) { - lhq.key = njs_str_value("path"); - lhq.key_hash = NJS_PATH_HASH; - lhq.proto = &njs_object_hash_proto; - - prop = njs_object_prop_alloc(vm, &njs_fs_path_string, path, 1); - if (njs_slow_path(prop == NULL)) { - return NJS_ERROR; - } - - lhq.value = prop; - - ret = njs_lvlhsh_insert(&error->hash, &lhq); + ret = njs_value_property_set(vm, retval, njs_value_arg(&string_path), + path); if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } } if (syscall != NULL) { size = njs_strlen(syscall); - ret = njs_string_new(vm, &string, (u_char *) syscall, size, size); + ret = njs_string_new(vm, &value, (u_char *) syscall, size, size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } - lhq.key = njs_str_value("sycall"); - lhq.key_hash = NJS_SYSCALL_HASH; - lhq.proto = &njs_object_hash_proto; - - prop = njs_object_prop_alloc(vm, &njs_fs_syscall_string, &string, 1); - if (njs_slow_path(prop == NULL)) { - return NJS_ERROR; - } - - lhq.value = prop; - - ret = njs_lvlhsh_insert(&error->hash, &lhq); + ret = njs_value_property_set(vm, retval, njs_value_arg(&string_syscall), + &value); if (njs_slow_path(ret != NJS_OK)) { - njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } } - njs_set_object(retval, error); - return NJS_OK; } -static int -njs_fs_flags(njs_str_t *value) +static njs_int_t +ngx_fs_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, + njs_index_t unused) { - njs_fs_entry_t *fl; - - for (fl = &njs_flags_table[0]; fl->name.length != 0; fl++) { - if (njs_strstr_eq(value, &fl->name)) { - return fl->value; - } - } + njs_value_t value; - return -1; + return njs_function_call(vm, njs_function(&args[1]), &njs_value_undefined, + &args[2], 1, &value); } -static mode_t -njs_fs_mode(njs_value_t *value) +static const njs_value_t promise_trampoline = + njs_native_function(ngx_fs_promise_trampoline, 2); + + +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) { - switch (value->type) { - case NJS_OBJECT_NUMBER: - value = njs_object_value(value); - /* Fall through. */ + njs_int_t ret; + njs_value_t promise, callbacks[2], arguments[2]; - case NJS_NUMBER: - return (mode_t) njs_number(value); + switch (calltype) { + case NJS_FS_DIRECT: + vm->retval = *result; + return njs_is_error(result) ? NJS_ERROR : NJS_OK; - case NJS_OBJECT_STRING: - value = njs_object_value(value); - /* Fall through. */ + case NJS_FS_PROMISE: + ret = njs_vm_promise_create(vm, &promise, &callbacks[0]); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } - case NJS_STRING: - return (mode_t) njs_string_to_number(value, 0); + arguments[0] = njs_is_error(result) ? callbacks[1] : callbacks[0]; + arguments[1] = *result; + + ret = njs_fs_add_event(vm, njs_value_arg(&promise_trampoline), + njs_value_arg(&arguments), 2); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + vm->retval = promise; + + return NJS_OK; + + case NJS_FS_CALLBACK: + if (njs_is_error(result)) { + arguments[0] = *result; + njs_set_undefined(&arguments[1]); + + } else { + njs_set_undefined(&arguments[0]); + arguments[1] = *result; + } + + ret = njs_fs_add_event(vm, callback, njs_value_arg(&arguments), + nargs); + if (njs_slow_path(ret != NJS_OK)) { + return ret; + } + + njs_set_undefined(&vm->retval); + + return NJS_OK; default: - return (mode_t) 0; + njs_internal_error(vm, "invalid calltype"); + + return NJS_ERROR; } } static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback, - const njs_value_t *err, const njs_value_t *result) + const njs_value_t *args, njs_uint_t nargs) { - 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"); @@ -813,12 +765,7 @@ njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback, goto memory_error; } - /* GC: retain */ - event->args[0] = *err; - - if (nargs == 2) { - event->args[1] = *result; - } + memcpy(event->args, args, sizeof(njs_value_t) * nargs); event->host_event = ops->set_timer(vm->external, 0, event); if (njs_slow_path(event->host_event == NULL)) { @@ -836,6 +783,50 @@ memory_error: } +static const njs_object_prop_t njs_fs_promises_properties[] = +{ + { + .type = NJS_PROPERTY, + .name = njs_string("readFile"), + .value = njs_native_function2(njs_fs_read_file, 0, NJS_FS_PROMISE), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("appendFile"), + .value = njs_native_function2(njs_fs_write_file, 0, + njs_fs_magic(NJS_FS_PROMISE, NJS_FS_APPEND)), + .writable = 1, + .configurable = 1, + }, + + { + .type = NJS_PROPERTY, + .name = njs_string("writeFile"), + .value = njs_native_function2(njs_fs_write_file, 0, + njs_fs_magic(NJS_FS_PROMISE, NJS_FS_TRUNC)), + .writable = 1, + .configurable = 1, + }, +}; + + +static const njs_object_init_t njs_fs_promises_init = { + njs_fs_promises_properties, + njs_nitems(njs_fs_promises_properties), +}; + + +static njs_int_t +njs_fs_promises(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, + njs_value_t *unused, njs_value_t *retval) +{ + return njs_object_prop_init(vm, &njs_fs_promises_init, prop, value, retval); +} + + static const njs_object_prop_t njs_fs_object_properties[] = { { @@ -845,6 +836,12 @@ static const njs_object_prop_t njs_fs_object_properties[] = .configurable = 1, }, + { + .type = NJS_PROPERTY_HANDLER, + .name = njs_string("promises"), + .value = njs_prop_handler(njs_fs_promises), + }, + { .type = NJS_PROPERTY, .name = njs_string("readFile"), diff --git a/src/njs_object.h b/src/njs_object.h index 73333c98..3f5cda99 100644 --- a/src/njs_object.h +++ b/src/njs_object.h @@ -81,6 +81,8 @@ njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest, njs_value_t *value, njs_value_t *setval); const char *njs_prop_type_string(njs_object_prop_type_t type); +njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init, + const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval); njs_inline njs_bool_t diff --git a/src/njs_object_prop.c b/src/njs_object_prop.c index 47e6e035..5e229132 100644 --- a/src/njs_object_prop.c +++ b/src/njs_object_prop.c @@ -881,3 +881,54 @@ njs_prop_type_string(njs_object_prop_type_t type) return "unknown"; } } + + +njs_int_t +njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init, + const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval) +{ + njs_int_t ret; + njs_object_t *object; + njs_object_prop_t *prop; + njs_lvlhsh_query_t lhq; + + object = njs_object_alloc(vm); + if (object == NULL) { + return NJS_ERROR; + } + + ret = njs_object_hash_create(vm, &object->hash, init->properties, + init->items); + if (njs_slow_path(ret != NJS_OK)) { + return NJS_ERROR; + } + + prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), + sizeof(njs_object_prop_t)); + if (njs_slow_path(prop == NULL)) { + njs_memory_error(vm); + return NJS_ERROR; + } + + *prop = *base; + + prop->type = NJS_PROPERTY; + njs_set_object(&prop->value, object); + + lhq.proto = &njs_object_hash_proto; + njs_string_get(&prop->name, &lhq.key); + lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); + lhq.value = prop; + lhq.replace = 1; + lhq.pool = vm->mem_pool; + + ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq); + if (njs_fast_path(ret == NJS_OK)) { + *retval = prop->value; + return NJS_OK; + } + + njs_internal_error(vm, "lvlhsh insert failed"); + + return NJS_ERROR; +} diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index 6c64385a..5ce8f49a 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -15892,7 +15892,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var fs = require('fs');" "fs.readFileSync('/njs_unknown_path', true)"), - njs_str("TypeError: Unknown options type (a string or object required)") }, + njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") }, /* require('fs').writeFile() */ @@ -15935,7 +15935,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', true, function () {})"), - njs_str("TypeError: Unknown options type (a string or object required)") }, + njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") }, /* require('fs').writeFileSync() */ @@ -15965,7 +15965,7 @@ static njs_unit_test_t njs_test[] = { njs_str("var fs = require('fs');" "fs.writeFileSync('/njs_unknown_path', '', true)"), - njs_str("TypeError: Unknown options type (a string or object required)") }, + njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") }, /* require('fs').renameSync() */ @@ -16009,6 +16009,22 @@ static njs_unit_test_t njs_test[] = "func.map(test).every((x) => x)"), njs_str("true")}, + /* require('fs').promises */ + + { njs_str("var fs = require('fs');" + "typeof fs.promises"), + njs_str("object") }, + + { njs_str("var " + "fs = require('fs').promises," + "func = [" + "'readFile'," + "'writeFile'," + "'appendFile'," + "];" + "func.every((x) => typeof fs[x] == 'function')"), + njs_str("true")}, + /* require('crypto').createHash() */ { njs_str("var h = require('crypto').createHash('sha1');" @@ -16484,6 +16500,9 @@ static njs_unit_test_t njs_shared_test[] = { njs_str("var fs = require('fs'); fs.a = 1; fs.a"), njs_str("1") }, + { njs_str("require('fs').promises === require('fs').promises"), + njs_str("true") }, + { njs_str("isFinite()"), njs_str("false") }, diff --git a/test/js/fs_promises_001.js b/test/js/fs_promises_001.js new file mode 100644 index 00000000..74f3a492 --- /dev/null +++ b/test/js/fs_promises_001.js @@ -0,0 +1,60 @@ +var fs = require('fs').promises; +var fname = './build/test/fs_promises_001'; + +Promise.resolve() +.then(() => { + return fs.writeFile(fname, fname); +}) +.then((data) => { + console.log('init ok', data === undefined); +}) +.catch((e) => { + console.log('init failed', e); +}) +.then(() => { + return fs.readFile(fname).then(fs.readFile); +}) +.then((data) => { + console.log('short citcut ok', data == fname); +}) +.catch((e) => { + console.log('short citcut failed', e); +}) +.then(() => { + var read = fs.readFile.bind(fs, fname, 'utf8'); + var write = fs.writeFile.bind(fs, fname); + var append = fs.appendFile.bind(fs, fname); + + return write(fname).then(read).then(append).then(read); +}) +.then((data) => { + console.log('chain ok', data == (fname + fname)); +}) +.catch((e) => { + console.log('chain failed', e); +}) +.then(() => { + // nodejs incompatible + try { + return fs.readFile(); + } catch (e) { + console.log('error 1 ok', e instanceof TypeError) + } + try { + return fs.writeFile(); + } 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'); +}) +.catch((e) => { + console.log('errors failed - reject on bad args'); +}) +; diff --git a/test/njs_expect_test.exp b/test/njs_expect_test.exp index de48e114..e5583994 100644 --- a/test/njs_expect_test.exp +++ b/test/njs_expect_test.exp @@ -1061,3 +1061,12 @@ PatchedPromise.constructor PatchedPromise.constructor PatchedPromise.constructor PatchedPromise async done" + +njs_run {"./test/js/fs_promises_001.js"} \ +"init ok true +short citcut ok true +chain ok true +error 1 ok true +error 2 ok true +error 3 ok true +errors ok"