diff --git a/.gitmodules b/.gitmodules index ceb7ba4..0a94cb8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,7 +4,6 @@ [submodule "deps/compiler-rt-builtins-riscv"] path = deps/compiler-rt-builtins-riscv url = https://github.com/nervosnetwork/compiler-rt-builtins-riscv - branch = update [submodule "deps/musl"] path = deps/musl url = https://github.com/xxuejie/musl diff --git a/Makefile b/Makefile index 419db0e..de57ad7 100644 --- a/Makefile +++ b/Makefile @@ -67,10 +67,6 @@ $(OBJDIR)/%.o: quickjs/%.c @echo build $< @$(CC) $(CFLAGS) -c -o $@ $< -$(OBJDIR)/%.o: include/%.c - @echo build $< - @$(CC) $(CFLAGS) -c -o $@ $< - test: make -f tests/examples/Makefile make -f tests/basic/Makefile @@ -86,6 +82,20 @@ clean: cd tests/ckb_js_tests && make clean make -C deps/compiler-rt-builtins-riscv clean +STYLE := "{BasedOnStyle: Google, TabWidth: 4, IndentWidth: 4, UseTab: Never, SortIncludes: false, ColumnLimit: 120}" + +fmt: + clang-format-18 -i -style=$(STYLE) \ + quickjs/ckb_cell_fs.c \ + quickjs/ckb_cell_fs.h \ + quickjs/ckb_module.c \ + quickjs/ckb_module.h \ + quickjs/mocked.c \ + quickjs/mocked.h \ + quickjs/qjs.c \ + quickjs/std_module.c \ + quickjs/std_module.h + install: wget 'https://github.com/nervosnetwork/ckb-standalone-debugger/releases/download/v0.118.0-rc2/ckb-debugger-linux-x64.tar.gz' tar zxvf ckb-debugger-linux-x64.tar.gz diff --git a/include/Makefile b/include/Makefile deleted file mode 100644 index da1adc4..0000000 --- a/include/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -STYLE := "{BasedOnStyle: Google, TabWidth: 4, IndentWidth: 4, UseTab: Never, SortIncludes: false, ColumnLimit: 120}" - -fmt: - clang-format-18 -i -style=$(STYLE) *.c *.h - clang-format-18 -i -style=$(STYLE) c-stdlib/*.h - clang-format-18 -i -style=$(STYLE) c-stdlib/src/*.c - clang-format-18 -i -style=$(STYLE) ../quickjs/qjs.c ../quickjs/std_module.c ../quickjs/std_module.h ../quickjs/ckb_module.c ../quickjs/ckb_module.h diff --git a/include/ckb_cell_fs.c b/quickjs/ckb_cell_fs.c similarity index 100% rename from include/ckb_cell_fs.c rename to quickjs/ckb_cell_fs.c diff --git a/include/ckb_cell_fs.h b/quickjs/ckb_cell_fs.h similarity index 100% rename from include/ckb_cell_fs.h rename to quickjs/ckb_cell_fs.h diff --git a/quickjs/ckb_module.c b/quickjs/ckb_module.c index 9ba6532..ec98c9f 100644 --- a/quickjs/ckb_module.c +++ b/quickjs/ckb_module.c @@ -365,6 +365,209 @@ 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, elem); + } + } + 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, elem); + } + } + 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); + 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[64]; + uint64_t length = 64; + err = ckb_inherited_fds(fds, &length); + CHECK(err); + JSValue obj = JS_NewArray(ctx); + 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; + 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; + uint8_t *buffer = (uint8_t *)malloc(length); + 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 pid = 0; + int8_t exit = 0; + err = JS_ToUint32(ctx, &pid, argv[0]); + CHECK(err); + err = ckb_wait((uint64_t)pid, &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 +619,16 @@ 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/quickjs/mocked.c b/quickjs/mocked.c index 028a6d0..71849e1 100644 --- a/quickjs/mocked.c +++ b/quickjs/mocked.c @@ -3,10 +3,6 @@ int __wrap_fesetround(int _round) { return 0; } -int __wrap_gettimeofday(struct timeval *restrict tv, struct timezone *restrict tz) { - return 0; -} +int __wrap_gettimeofday(struct timeval *restrict tv, struct timezone *restrict tz) { return 0; } -struct tm *__wrap_localtime_r(const time_t *a, struct tm *b) { - return 0; -} +struct tm *__wrap_localtime_r(const time_t *a, struct tm *b) { return 0; } diff --git a/tests/ckb_js_tests/Makefile b/tests/ckb_js_tests/Makefile index 1d5f4f9..452c56c 100644 --- a/tests/ckb_js_tests/Makefile +++ b/tests/ckb_js_tests/Makefile @@ -9,7 +9,8 @@ all: \ syscall \ fs_bytecode\ simple_udt\ - fs_mount + fs_mount \ + module cargo_test: cargo test diff --git a/tests/ckb_js_tests/test_data/spawn_caller.c b/tests/ckb_js_tests/test_data/spawn_caller.c index f7ef0ef..22a0813 100644 --- a/tests/ckb_js_tests/test_data/spawn_caller.c +++ b/tests/ckb_js_tests/test_data/spawn_caller.c @@ -1,19 +1,23 @@ #include "ckb_syscalls.h" int main() { + int err = 0; const char *argv[] = {"-f"}; - int8_t spawn_exit_code = -1; + uint64_t pid = 0; + uint64_t inherited_fds[1] = {0}; + int8_t exit_code = 0; spawn_args_t spgs = { - .memory_limit = 8, - .exit_code = &spawn_exit_code, - .content = NULL, - .content_length = NULL, + .argc = 1, + .argv = argv, + .process_id = &pid, + .inherited_fds = inherited_fds, }; - int success = ckb_spawn(1, 3, 0, 1, argv, &spgs); - if (success != 0) { + err = ckb_spawn(1, CKB_SOURCE_CELL_DEP, 0, 0, &spgs); + if (err != 0) { return 1; } - if (spawn_exit_code != 0) { + err = ckb_wait(pid, &exit_code); + if (err != 0) { return 1; } return 0; diff --git a/tests/ckb_js_tests/test_data/syscall.js b/tests/ckb_js_tests/test_data/syscall.js index 85343f8..5235329 100644 --- a/tests/ckb_js_tests/test_data/syscall.js +++ b/tests/ckb_js_tests/test_data/syscall.js @@ -109,6 +109,38 @@ 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(); + // Unlike the C version, we only need to pass in two parameters: argv and inherited_fds. + // * There is no need to use the argc parameter. + // * There is no need to add 0 to the end of inherited_fds as a terminator. + // * There is no need to pass in the pid address. + 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 +151,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);