Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spawn syscall #14

Merged
merged 7 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 14 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
7 changes: 0 additions & 7 deletions include/Makefile

This file was deleted.

File renamed without changes.
File renamed without changes.
213 changes: 213 additions & 0 deletions quickjs/ckb_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[32];
mohanson marked this conversation as resolved.
Show resolved Hide resolved
uint64_t length;
mohanson marked this conversation as resolved.
Show resolved Hide resolved
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)) {
Expand Down Expand Up @@ -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));
Expand Down
8 changes: 2 additions & 6 deletions quickjs/mocked.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
3 changes: 2 additions & 1 deletion tests/ckb_js_tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ all: \
syscall \
fs_bytecode\
simple_udt\
fs_mount
fs_mount \
module

cargo_test:
cargo test
Expand Down
20 changes: 12 additions & 8 deletions tests/ckb_js_tests/test_data/spawn_caller.c
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
33 changes: 33 additions & 0 deletions tests/ckb_js_tests/test_data/syscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);