Made File.readFile async.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3667 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
2021-10-06 01:25:33 +00:00
parent 24a91219c1
commit 470814f147
5 changed files with 196 additions and 144 deletions

View File

@ -45,41 +45,84 @@ void tf_file_init(JSContext* context) {
JS_FreeValue(context, global);
}
static void _free_array_buffer_data(JSRuntime* runtime, void* opaque, void* ptr) {
free(ptr);
static const int k_file_read_max = 4 * 1024 * 1024;
static void _file_read_close_callback(uv_fs_t* req)
{
uv_fs_req_cleanup(req);
free(req);
}
static void _file_read_read_callback(uv_fs_t* req)
{
uv_fs_req_cleanup(req);
tf_task_t* task = req->loop->data;
JSContext* context = tf_task_get_context(task);
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
if (req->result >= 0)
{
JSValue arrayBuffer = JS_NewArrayBufferCopy(context, (uint8_t*)(req + 1), req->result);
JSValue global = JS_GetGlobalObject(context);
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
JSValue typedArray = JS_CallConstructor(context, constructor, 1, &arrayBuffer);
JS_FreeValue(context, constructor);
JS_FreeValue(context, global);
JS_FreeValue(context, arrayBuffer);
tf_task_resolve_promise(task, promise, typedArray);
JS_FreeValue(context, typedArray);
}
else
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
}
int result = uv_fs_close(req->loop, req, req->file, _file_read_close_callback);
if (result < 0)
{
free(req);
}
}
static void _file_read_open_callback(uv_fs_t* req)
{
uv_fs_req_cleanup(req);
tf_task_t* task = req->loop->data;
JSContext* context = tf_task_get_context(task);
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
if (req->result >= 0)
{
uv_buf_t buf = { .base = (char*)(req + 1), .len = k_file_read_max };
uv_file file = req->result;
int result = uv_fs_read(req->loop, req, file, &buf, 1, 0, _file_read_read_callback);
if (result < 0)
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
result = uv_fs_close(req->loop, req, file, _file_read_close_callback);
}
}
else
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
free(req);
}
}
static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
void* task = JS_GetContextOpaque(context);
const char* file_name = JS_ToCString(context, argv[0]);
FILE* file = fopen(file_name, "rb");
JS_FreeCString(context, file_name);
if (!file) {
return JS_NULL;
}
long size = 0;
if (fseek(file, 0, SEEK_END) == 0) {
size = ftell(file);
promiseid_t promise = tf_task_allocate_promise(task);
uv_fs_t* req = malloc(sizeof(uv_fs_t) + k_file_read_max);
*req = (uv_fs_t)
{
.data = (void*)(intptr_t)promise,
};
int result = uv_fs_open(tf_task_get_loop(task), req, file_name, UV_FS_O_RDONLY, 0, _file_read_open_callback);
if (result < 0)
{
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
}
if (size >= 0 &&
size < 4 * 1024 * 1024 &&
fseek(file, 0, SEEK_SET) == 0) {
uint8_t* data = malloc(size);
if (data &&
fread(data, 1, size, file) == (size_t)size) {
JSValue arrayBuffer = JS_NewArrayBuffer(context, data, size, _free_array_buffer_data, NULL, false);
JSValue global = JS_GetGlobalObject(context);
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
JSValue typedArray = JS_CallConstructor(context, constructor, 1, &arrayBuffer);
JS_FreeValue(context, constructor);
JS_FreeValue(context, global);
JS_FreeValue(context, arrayBuffer);
return typedArray;
} else if (data) {
free(data);
}
}
return JS_NULL;
JS_FreeCString(context, file_name);
return tf_task_get_promise(task, promise);
}
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {

View File

@ -1142,6 +1142,7 @@ tf_task_t* tf_task_create() {
tf_task_t* task = malloc(sizeof(tf_task_t));
*task = (tf_task_t) { 0 };
task->_loop = uv_loop_new();
task->_loop->data = task;
++_count;
task->_runtime = JS_NewRuntime();
task->_context = JS_NewContext(task->_runtime);

View File

@ -480,6 +480,36 @@ static void _test_socket(const tf_test_options_t* options)
unlink("out/test.js");
}
static void _test_file(const tf_test_options_t* options)
{
FILE* file = fopen("out/test.js", "w");
fprintf(file,
"'use strict';\n"
"File.readFile('out/test.js').then(function(data) {\n"
" print('READ', utf8Decode(data));\n"
"}).catch(function(error) {\n"
" print('ERROR', error);\n"
" exit(1);\n"
"});\n"
"File.readFile('out/missing.txt').then(function(data) {\n"
" print('READ', utf8Decode(data));\n"
" exit(1);\n"
"}).catch(function(error) {\n"
" print('expected error', error);\n"
"});\n");
fclose(file);
char command[256];
snprintf(command, sizeof(command), "%s run --ssb-port=0 -s out/test.js", options->exe_path);
printf("%s\n", command);
int result = system(command);
printf("returned %d\n", WEXITSTATUS(result));
assert(WIFEXITED(result));
assert(WEXITSTATUS(result) == 0);
unlink("out/test.js");
}
static void _tf_test_run(const tf_test_options_t* options, const char* name, void (*test)(const tf_test_options_t* options))
{
bool specified = false;
@ -521,5 +551,6 @@ void tf_tests(const tf_test_options_t* options)
_tf_test_run(options, "icu", _test_icu);
_tf_test_run(options, "uint8array", _test_uint8array);
_tf_test_run(options, "socket", _test_socket);
_tf_test_run(options, "file", _test_file);
printf("Tests completed.\n");
}