Skip to content

Commit

Permalink
Spawn syscall
Browse files Browse the repository at this point in the history
  • Loading branch information
mohanson authored Sep 19, 2024
1 parent 151d989 commit 6da73b3
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 27 deletions.
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[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)) {
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);

0 comments on commit 6da73b3

Please sign in to comment.