From 32d89e84f33fa7ec64f1db55811aaa8a45f66c2f Mon Sep 17 00:00:00 2001 From: mohanson Date: Mon, 9 Sep 2024 16:23:48 +0800 Subject: [PATCH] spawn --- quickjs/ckb_module.c | 213 ++++++++++++++++++++++++ tests/ckb_js_tests/test_data/syscall.js | 29 ++++ 2 files changed, 242 insertions(+) diff --git a/quickjs/ckb_module.c b/quickjs/ckb_module.c index 9ba6532..2c634e4 100644 --- a/quickjs/ckb_module.c +++ b/quickjs/ckb_module.c @@ -365,6 +365,210 @@ static int get_property(JSContext *ctx, JSValueConst *obj, const char *prop, int return err; } +static JSValue syscall_spawn_cell(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + size_t code_hash_len = 0; + uint8_t code_hash[32]; + uint32_t hash_type = 0; + uint32_t offset = 0; + uint32_t length = 0; + uint32_t spgs_argc = 0; + const char* spgs_argv[32] = {}; + uint64_t spgs_pid = 0; + uint64_t spgs_fds[32] = {0}; + + JSValue buffer = JS_GetTypedArrayBuffer(ctx, argv[0], NULL, NULL, NULL); + CHECK2(!JS_IsException(buffer), SyscallErrorArgument); + uint8_t *p = JS_GetArrayBuffer(ctx, &code_hash_len, buffer); + CHECK2(code_hash_len == 32 && p != NULL, -1); + memcpy(code_hash, p, 32); + + err = JS_ToUint32(ctx, &hash_type, argv[1]); + CHECK(err); + err = JS_ToUint32(ctx, &offset, argv[2]); + CHECK(err); + err = JS_ToUint32(ctx, &length, argv[3]); + CHECK(err); + + JSValue val; + val = JS_GetPropertyStr(ctx, argv[4], "argv"); + CHECK2(!JS_IsException(val), SyscallErrorArgument); + if (!JS_IsUndefined(val)) { + for (int i = 0; i < 32; i++) { + const JSValue elem = JS_GetPropertyUint32(ctx, val, i); + if (JS_IsUndefined(elem)) { + break; + } + const char *str = JS_ToCString(ctx, elem); + spgs_argc += 1; + spgs_argv[i] = str; + } + } + JS_FreeValue(ctx, val); + + val = JS_GetPropertyStr(ctx, argv[4], "inherited_fds"); + CHECK2(!JS_IsException(val), SyscallErrorArgument); + if (!JS_IsUndefined(val)) { + uint32_t temp; + for (int i = 0; i < 32; i++) { + const JSValue elem = JS_GetPropertyUint32(ctx, val, i); + if (JS_IsUndefined(elem)) { + break; + } + err = JS_ToUint32(ctx, &temp, elem); + CHECK(err); + spgs_fds[i] = temp; + } + } + JS_FreeValue(ctx, val); + + spawn_args_t spgs = { + .argc = spgs_argc, + .argv = spgs_argv, + .process_id = &spgs_pid, + .inherited_fds = &spgs_fds[0], + }; + + err = ckb_spawn_cell(code_hash, hash_type, offset, length, &spgs); + CHECK(err); +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return JS_NewInt64(ctx, spgs_pid); + } +} + +static JSValue syscall_pipe(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + uint64_t fds[2]; + err = ckb_pipe(fds); + CHECK(err); + JSValue obj = JS_NewArray(ctx); + CHECK2(!JS_IsException(obj), SyscallErrorArgument); + JS_SetPropertyUint32(ctx, obj, 0, JS_NewUint32(ctx, fds[0])); + JS_SetPropertyUint32(ctx, obj, 1, JS_NewUint32(ctx, fds[1])); +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return obj; + } +} + +static JSValue syscall_inherited_fds(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + uint64_t fds[2]; + uint64_t length; + err = ckb_inherited_fds(fds, &length); + CHECK(err); + JSValue obj = JS_NewArray(ctx); + CHECK2(!JS_IsException(obj), SyscallErrorArgument); + for (int i = 0; i < length; i++) { + JS_SetPropertyUint32(ctx, obj, i, JS_NewUint32(ctx, (uint32_t)fds[i])); + } +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return obj; + } +} + +static JSValue syscall_read(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + uint64_t fd = 0; + void* buffer = {}; + size_t length = 0; + uint32_t u32 = 0; + err = JS_ToUint32(ctx, &u32, argv[0]); + CHECK(err); + fd = u32; + err = JS_ToUint32(ctx, &u32, argv[1]); + CHECK(err); + length = u32; + CHECK2(length <= 1024, SyscallErrorArgument); + err = ckb_read(fd, buffer, &length); + CHECK(err); +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return JS_NewArrayBuffer(ctx, buffer, length, my_free, buffer, false); + } +} + +static JSValue syscall_write(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + uint64_t fd = 0; + uint32_t u32 = 0; + err = JS_ToUint32(ctx, &u32, argv[0]); + CHECK(err); + fd = (uint64_t)u32; + size_t length = 0; + JSValue buffer = JS_GetTypedArrayBuffer(ctx, argv[1], NULL, NULL, NULL); + CHECK2(!JS_IsException(buffer), SyscallErrorArgument); + uint8_t *content = JS_GetArrayBuffer(ctx, &length, buffer); + CHECK2(content != NULL, SyscallErrorUnknown); + err = ckb_write(fd, content, &length); + CHECK(err); +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return JS_UNDEFINED; + } +} + +static JSValue syscall_close(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + uint32_t fd = 0; + err = JS_ToUint32(ctx, &fd, argv[0]); + CHECK(err); + err = ckb_close((uint64_t)fd); + CHECK(err); +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return JS_UNDEFINED; + } +} + +static JSValue syscall_wait(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + int err = 0; + uint32_t fd = 0; + int8_t exit = 0; + err = JS_ToUint32(ctx, &fd, argv[0]); + CHECK(err); + err = ckb_wait((uint64_t)fd, &exit); + CHECK(err); +exit: + if (err != 0) { + return JS_EXCEPTION; + } else { + return JS_NewInt32(ctx, exit); + } +} + +static JSValue syscall_process_id(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { + uint64_t pid = ckb_process_id(); + return JS_NewUint32(ctx, (uint32_t)pid); +} + +static int _load_block_extension(void *addr, uint64_t *len, LoadData *data) { + return ckb_load_block_extension(addr, len, data->offset, data->index, data->source); +} + +static JSValue syscall_load_block_extension(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { + LoadData data = {0}; + JSValue ret = parse_args(ctx, &data, false, argc, argv, _load_block_extension); + if (JS_IsException(ret)) { + return ret; + } + return syscall_load(ctx, &data); +} + static JSValue mount(JSContext *ctx, JSValueConst this_value, int argc, JSValueConst *argv) { JSValue buf = syscall_load_cell_data(ctx, this_value, argc, argv); if (JS_IsException(buf)) { @@ -416,6 +620,15 @@ int js_init_module_ckb(JSContext *ctx) { JS_SetPropertyStr(ctx, ckb, "vm_version", JS_NewCFunction(ctx, syscall_vm_version, "vm_version", 0)); JS_SetPropertyStr(ctx, ckb, "current_cycles", JS_NewCFunction(ctx, syscall_current_cycles, "current_cycles", 0)); JS_SetPropertyStr(ctx, ckb, "exec_cell", JS_NewCFunction(ctx, syscall_exec_cell, "exec_cell", 4)); + JS_SetPropertyStr(ctx, ckb, "spawn_cell", JS_NewCFunction(ctx, syscall_spawn_cell, "spawn_cell", 5)); + JS_SetPropertyStr(ctx, ckb, "pipe", JS_NewCFunction(ctx, syscall_pipe, "pipe", 0)); + JS_SetPropertyStr(ctx, ckb, "inherited_fds", JS_NewCFunction(ctx, syscall_inherited_fds, "inherited_fds", 0)); + JS_SetPropertyStr(ctx, ckb, "read", JS_NewCFunction(ctx, syscall_read, "read", 2)); + JS_SetPropertyStr(ctx, ckb, "write", JS_NewCFunction(ctx, syscall_write, "write", 2)); + JS_SetPropertyStr(ctx, ckb, "close", JS_NewCFunction(ctx, syscall_close, "close", 1)); + JS_SetPropertyStr(ctx, ckb, "wait", JS_NewCFunction(ctx, syscall_wait, "wait", 1)); + JS_SetPropertyStr(ctx, ckb, "process_id", JS_NewCFunction(ctx, syscall_process_id, "process_id", 0)); + JS_SetPropertyStr(ctx, ckb, "load_block_extension", JS_NewCFunction(ctx, syscall_load_block_extension, "load_block_extension", 3)); JS_SetPropertyStr(ctx, ckb, "mount", JS_NewCFunction(ctx, mount, "mount", 2)); JS_SetPropertyStr(ctx, ckb, "SOURCE_INPUT", JS_NewInt64(ctx, CKB_SOURCE_INPUT)); JS_SetPropertyStr(ctx, ckb, "SOURCE_OUTPUT", JS_NewInt64(ctx, CKB_SOURCE_OUTPUT)); diff --git a/tests/ckb_js_tests/test_data/syscall.js b/tests/ckb_js_tests/test_data/syscall.js index 85343f8..6a09804 100644 --- a/tests/ckb_js_tests/test_data/syscall.js +++ b/tests/ckb_js_tests/test_data/syscall.js @@ -109,6 +109,34 @@ function test_misc() { console.log('test_misc done'); } +function test_spawn() { + console.log('test_spawn ...'); + const js_code = ` + let fds = ckb.inherited_fds(); + ckb.write(fds[0], new Uint8Array([0, 1, 2, 3])); + ckb.close(fds[0]); + ckb.exit(42); + `; + let code_hash = new Uint8Array([ + 0xdf, 0x97, 0x77, 0x78, 0x08, 0x9b, 0xf3, 0x3f, 0xc5, 0x1f, 0x22, 0x45, 0xfa, 0x6d, 0xb7, 0xfa, + 0x18, 0x19, 0xd5, 0x03, 0x11, 0x31, 0xa8, 0x3d, 0x4e, 0xcb, 0xcb, 0x6c, 0xba, 0x07, 0xce, 0x91 + ]); + let fds = ckb.pipe(); + let spawn_args = { + argv: ['-e', js_code], + inherited_fds: [fds[1]], + }; + let pid = ckb.spawn_cell(code_hash, ckb.SCRIPT_HASH_TYPE_TYPE, 0, 0, spawn_args); + let txt = new Uint8Array(ckb.read(fds[0], 4)); + console.assert(txt[0] == 0); + console.assert(txt[1] == 1); + console.assert(txt[2] == 2); + console.assert(txt[3] == 3); + let ret = ckb.wait(pid); + console.assert(ret == 42); + console.log('test_spawn done'); +} + test_misc(); test_partial_loading(ckb.load_witness); test_partial_loading(ckb.load_cell_data); @@ -119,5 +147,6 @@ test_partial_loading_without_comparing(ckb.load_script); test_partial_loading_without_comparing(ckb.load_cell); test_partial_loading_field_without_comparing(ckb.load_cell_by_field, ckb.CELL_FIELD_CAPACITY); test_partial_loading_field_without_comparing(ckb.load_input_by_field, ckb.INPUT_FIELD_OUT_POINT); +test_spawn() ckb.exit(0);