Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Binary file added .obj-rt/cutils.bytecode.lto.o
Binary file not shown.
1 change: 1 addition & 0 deletions .obj-rt/cutils.bytecode.lto.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.obj-rt/cutils.bytecode.lto.o: cutils.c cutils.h
Binary file added .obj-rt/cutils.bytecode.o
Binary file not shown.
1 change: 1 addition & 0 deletions .obj-rt/cutils.bytecode.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.obj-rt/cutils.bytecode.o: cutils.c cutils.h
Binary file added .obj-rt/dtoa.bytecode.lto.o
Binary file not shown.
1 change: 1 addition & 0 deletions .obj-rt/dtoa.bytecode.lto.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.obj-rt/dtoa.bytecode.lto.o: dtoa.c cutils.h dtoa.h
Binary file added .obj-rt/dtoa.bytecode.o
Binary file not shown.
1 change: 1 addition & 0 deletions .obj-rt/dtoa.bytecode.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.obj-rt/dtoa.bytecode.o: dtoa.c cutils.h dtoa.h
Binary file added .obj-rt/libregexp.bytecode.lto.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/libregexp.bytecode.lto.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/libregexp.bytecode.lto.o: libregexp.c cutils.h libregexp.h \
libunicode.h libregexp-opcode.h
Binary file added .obj-rt/libregexp.bytecode.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/libregexp.bytecode.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/libregexp.bytecode.o: libregexp.c cutils.h libregexp.h \
libunicode.h libregexp-opcode.h
Binary file added .obj-rt/libunicode.bytecode.lto.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/libunicode.bytecode.lto.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/libunicode.bytecode.lto.o: libunicode.c cutils.h libunicode.h \
libunicode-table.h
Binary file added .obj-rt/libunicode.bytecode.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/libunicode.bytecode.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/libunicode.bytecode.o: libunicode.c cutils.h libunicode.h \
libunicode-table.h
Binary file added .obj-rt/quickjs-libc.bytecode.lto.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/quickjs-libc.bytecode.lto.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/quickjs-libc.bytecode.lto.o: quickjs-libc.c cutils.h list.h \
quickjs-libc.h quickjs.h
Binary file added .obj-rt/quickjs-libc.bytecode.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/quickjs-libc.bytecode.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/quickjs-libc.bytecode.o: quickjs-libc.c cutils.h list.h \
quickjs-libc.h quickjs.h
Binary file added .obj-rt/quickjs.bytecode.lto.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/quickjs.bytecode.lto.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/quickjs.bytecode.lto.o: quickjs.c cutils.h list.h quickjs.h \
libregexp.h libunicode.h dtoa.h quickjs-atom.h quickjs-opcode.h
Binary file added .obj-rt/quickjs.bytecode.o
Binary file not shown.
2 changes: 2 additions & 0 deletions .obj-rt/quickjs.bytecode.o.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.obj-rt/quickjs.bytecode.o: quickjs.c cutils.h list.h quickjs.h \
libregexp.h libunicode.h dtoa.h quickjs-atom.h quickjs-opcode.h
28 changes: 28 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ all: $(OBJDIR) $(OBJDIR)/quickjs.check.o $(OBJDIR)/qjs.check.o $(PROGS)

QJS_LIB_OBJS=$(OBJDIR)/quickjs.o $(OBJDIR)/dtoa.o $(OBJDIR)/libregexp.o $(OBJDIR)/libunicode.o $(OBJDIR)/cutils.o $(OBJDIR)/quickjs-libc.o

OBJDIR_RT=.obj-rt
QJS_BYTECODE_OBJS=$(patsubst $(OBJDIR)/%.o, $(OBJDIR_RT)/%.bytecode.o, $(QJS_LIB_OBJS))
QJS_BYTECODE_LTO_OBJS=$(patsubst $(OBJDIR)/%.o, $(OBJDIR_RT)/%.bytecode.lto.o, $(QJS_LIB_OBJS))

QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)

HOST_LIBS=-lm -ldl -lpthread
Expand All @@ -257,6 +261,9 @@ LIBS+=$(EXTRA_LIBS)
$(OBJDIR):
mkdir -p $(OBJDIR) $(OBJDIR)/examples $(OBJDIR)/tests

$(OBJDIR_RT):
mkdir -p $(OBJDIR_RT)

qjs$(EXE): $(QJS_OBJS)
$(CC) $(LDFLAGS) $(LDEXPORT) -o $@ $^ $(LIBS)

Expand Down Expand Up @@ -308,6 +315,12 @@ libquickjs.a: $(patsubst %.o, %.nolto.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^
endif # CONFIG_LTO

libquickjs-bytecode.a: $(QJS_BYTECODE_OBJS)
$(AR) rcs $@ $^

libquickjs-bytecode.lto.a: $(QJS_BYTECODE_LTO_OBJS)
$(AR) rcs $@ $^

libquickjs.fuzz.a: $(patsubst %.o, %.fuzz.o, $(QJS_LIB_OBJS))
$(AR) rcs $@ $^

Expand Down Expand Up @@ -353,6 +366,12 @@ $(OBJDIR)/%.fuzz.o: %.c | $(OBJDIR)
$(OBJDIR)/%.check.o: %.c | $(OBJDIR)
$(CC) $(CFLAGS) -DCONFIG_CHECK_JSVALUE -c -o $@ $<

$(OBJDIR_RT)/%.bytecode.o: %.c | $(OBJDIR_RT)
$(CC) $(CFLAGS_NOLTO) -MF $(OBJDIR_RT)/$(@F).d -DCONFIG_BYTECODE_ONLY_RUNTIME -c -o $@ $<

$(OBJDIR_RT)/%.bytecode.lto.o: %.c | $(OBJDIR_RT)
$(CC) $(CFLAGS_OPT) -MF $(OBJDIR_RT)/$(@F).d -DCONFIG_BYTECODE_ONLY_RUNTIME -c -o $@ $<

regexp_test: libregexp.c libunicode.c cutils.c
$(CC) $(LDFLAGS) $(CFLAGS) -DTEST -o $@ libregexp.c libunicode.c cutils.c $(LIBS)

Expand All @@ -362,6 +381,7 @@ unicode_gen: $(OBJDIR)/unicode_gen.host.o $(OBJDIR)/cutils.host.o libunicode.c u
clean:
rm -f repl.c out.c
rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS)
rm -rf $(OBJDIR_RT)
rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug$(EXE)
Expand Down Expand Up @@ -446,6 +466,14 @@ ifdef CONFIG_SHARED_LIBS
test: tests/bjson.so examples/point.so
endif

test-bytecode-runtime: libquickjs-bytecode.a libquickjs-bytecode.lto.a qjsc$(EXE)
$(QJSC) -flto -fno-eval -fno-regexp -fno-json -fno-module-loader \
-o tests/test-bytecode-rt tests/test_bytecode_runtime.js
@nm tests/test-bytecode-rt | grep -E ' [Tt] (__JS_EvalInternal|js_parse_|js_compile_|js_evalScript|js_loadScript|js_std_parseExtJSON|js_worker_ctor)' \
&& (echo "FAIL: parser symbols found in bytecode-only binary" && exit 1) \
|| echo "PASS: no parser symbols"
@tests/test-bytecode-rt

test: qjs$(EXE)
$(WINE) ./qjs$(EXE) tests/test_closure.js
$(WINE) ./qjs$(EXE) tests/test_language.js
Expand Down
19 changes: 16 additions & 3 deletions qjsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
#include "cutils.h"
#include "quickjs-libc.h"

#ifdef CONFIG_BYTECODE_ONLY_RUNTIME
#error "qjsc must be built with the full QuickJS engine"
#endif

typedef struct {
char *name;
char *short_name;
Expand Down Expand Up @@ -79,6 +83,15 @@ static const FeatureEntry feature_list[] = {
{ "weakref", "WeakRef" },
};

#define FE_MASK(i) ((uint64_t)1 << (i))
#define BYTECODE_ONLY_TRIGGER_MASK \
(FE_MASK(1) | FE_MASK(3) | FE_MASK(4) | FE_MASK(FE_MODULE_LOADER))

static BOOL runtime_needs_parser(void)
{
return (feature_bitmap & BYTECODE_ONLY_TRIGGER_MASK) != 0;
}

void namelist_add(namelist_t *lp, const char *name, const char *short_name,
int flags)
{
Expand Down Expand Up @@ -450,7 +463,7 @@ static int output_executable(const char *out_filename, const char *cfilename,
BOOL use_lto, BOOL verbose, const char *exename)
{
const char *argv[64];
const char **arg, *bn_suffix, *lto_suffix;
const char **arg, *lib_suffix, *lto_suffix;
char libjsname[1024];
char exe_dir[1024], inc_dir[1024], lib_dir[1024], buf[1024], *p;
int ret;
Expand All @@ -476,7 +489,7 @@ static int output_executable(const char *out_filename, const char *cfilename,
}

lto_suffix = "";
bn_suffix = "";
lib_suffix = runtime_needs_parser() ? "" : "-bytecode";

arg = argv;
*arg++ = CONFIG_CC;
Expand All @@ -499,7 +512,7 @@ static int output_executable(const char *out_filename, const char *cfilename,
*arg++ = "-rdynamic";
*arg++ = cfilename;
snprintf(libjsname, sizeof(libjsname), "%s/libquickjs%s%s.a",
lib_dir, bn_suffix, lto_suffix);
lib_dir, lib_suffix, lto_suffix);
*arg++ = libjsname;
*arg++ = "-lm";
*arg++ = "-ldl";
Expand Down
30 changes: 29 additions & 1 deletion quickjs-libc.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@ JSModuleDef *js_module_loader(JSContext *ctx,
return NULL;
}
res = js_module_test_json(ctx, attributes);
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
if (has_suffix(module_name, ".json") || res > 0) {
/* compile as JSON or JSON5 depending on "type" */
JSValue val;
Expand Down Expand Up @@ -723,6 +724,12 @@ JSModuleDef *js_module_loader(JSContext *ctx,
m = JS_VALUE_GET_PTR(func_val);
JS_FreeValue(ctx, func_val);
}
#else
js_free(ctx, buf);
JS_ThrowReferenceError(ctx, "could not load module filename '%s'",
module_name);
return NULL;
#endif
}
return m;
}
Expand Down Expand Up @@ -870,6 +877,7 @@ static int get_bool_option(JSContext *ctx, BOOL *pbool,
return 0;
}

#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
Expand Down Expand Up @@ -918,6 +926,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValueConst this_val,
}
return ret;
}
#endif

static JSClassID js_std_file_class_id;

Expand Down Expand Up @@ -957,6 +966,7 @@ static JSValue js_std_strerror(JSContext *ctx, JSValueConst this_val,
return JS_NewString(ctx, strerror(err));
}

#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
Expand All @@ -971,6 +981,7 @@ static JSValue js_std_parseExtJSON(JSContext *ctx, JSValueConst this_val,
JS_FreeCString(ctx, str);
return obj;
}
#endif

static JSValue js_new_std_file(JSContext *ctx, FILE *f,
BOOL close_in_finalizer,
Expand Down Expand Up @@ -1640,16 +1651,20 @@ static const JSCFunctionListEntry js_std_error_props[] = {
static const JSCFunctionListEntry js_std_funcs[] = {
JS_CFUNC_DEF("exit", 1, js_std_exit ),
JS_CFUNC_DEF("gc", 0, js_std_gc ),
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
JS_CFUNC_DEF("evalScript", 1, js_evalScript ),
JS_CFUNC_DEF("loadScript", 1, js_loadScript ),
#endif
JS_CFUNC_DEF("getenv", 1, js_std_getenv ),
JS_CFUNC_DEF("setenv", 1, js_std_setenv ),
JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ),
JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ),
JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ),
JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ),
JS_CFUNC_DEF("strerror", 1, js_std_strerror ),
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
JS_CFUNC_DEF("parseExtJSON", 1, js_std_parseExtJSON ),
#endif

/* FILE I/O */
JS_CFUNC_DEF("open", 2, js_std_open ),
Expand Down Expand Up @@ -3580,6 +3595,7 @@ static JSClassDef js_worker_class = {

static void *worker_func(void *opaque)
{
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
WorkerFuncArgs *args = opaque;
JSRuntime *rt;
JSThreadState *ts;
Expand Down Expand Up @@ -3626,6 +3642,7 @@ static void *worker_func(void *opaque)
JS_FreeContext(ctx);
js_std_free_handlers(rt);
JS_FreeRuntime(rt);
#endif
return NULL;
}

Expand Down Expand Up @@ -3664,6 +3681,9 @@ static JSValue js_worker_ctor_internal(JSContext *ctx, JSValueConst new_target,
static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv)
{
#ifdef CONFIG_BYTECODE_ONLY_RUNTIME
return JS_ThrowTypeError(ctx, "Worker is not supported");
#else
JSRuntime *rt = JS_GetRuntime(ctx);
WorkerFuncArgs *args = NULL;
pthread_t tid;
Expand Down Expand Up @@ -3742,6 +3762,7 @@ static JSValue js_worker_ctor(JSContext *ctx, JSValueConst new_target,
}
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
#endif
}

static JSValue js_worker_postMessage(JSContext *ctx, JSValueConst this_val,
Expand Down Expand Up @@ -3987,8 +4008,12 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs));

#ifdef CONFIG_BYTECODE_ONLY_RUNTIME
obj = JS_UNDEFINED;
#else
obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1,
JS_CFUNC_constructor, 0);
#endif
JS_SetConstructor(ctx, obj, proto);

JS_SetClassProto(ctx, js_worker_class_id, proto);
Expand All @@ -4000,7 +4025,8 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
JS_PROP_C_W_E);
}

JS_SetModuleExport(ctx, m, "Worker", obj);
if (!JS_IsUndefined(obj))
JS_SetModuleExport(ctx, m, "Worker", obj);
}
#endif /* USE_WORKER */

Expand Down Expand Up @@ -4087,8 +4113,10 @@ void js_std_add_helpers(JSContext *ctx, int argc, char **argv)

JS_SetPropertyStr(ctx, global_obj, "print",
JS_NewCFunction(ctx, js_print, "print", 1));
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
JS_SetPropertyStr(ctx, global_obj, "__loadScript",
JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1));
#endif

JS_FreeValue(ctx, global_obj);
}
Expand Down
8 changes: 8 additions & 0 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -35439,6 +35439,7 @@ static int add_global_variables(JSContext *ctx, JSFunctionDef *fd)
/* create a function object from a function definition. The function
definition is freed. All the child functions are also created. It
must be done this way to resolve all the variables. */
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
{
JSValue func_obj;
Expand Down Expand Up @@ -35697,6 +35698,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
js_free_function_def(ctx, fd);
return JS_EXCEPTION;
}
#endif /* !CONFIG_BYTECODE_ONLY_RUNTIME */

static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
{
Expand Down Expand Up @@ -36493,6 +36495,7 @@ static __exception int js_parse_function_decl(JSParseState *s,
JS_PARSE_EXPORT_NONE, NULL);
}

#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
static __exception int js_parse_program(JSParseState *s)
{
JSFunctionDef *fd = s->cur_func;
Expand Down Expand Up @@ -36544,6 +36547,7 @@ static __exception int js_parse_program(JSParseState *s)

return 0;
}
#endif /* !CONFIG_BYTECODE_ONLY_RUNTIME */

static void js_parse_init(JSContext *ctx, JSParseState *s,
const char *input, size_t input_len,
Expand Down Expand Up @@ -36603,6 +36607,7 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
}

/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
const char *input, size_t input_len,
const char *filename, int flags, int scope_idx)
Expand Down Expand Up @@ -36717,6 +36722,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
JS_FreeValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
return JS_EXCEPTION;
}
#endif /* !CONFIG_BYTECODE_ONLY_RUNTIME */

/* the indirection is needed to make 'eval' optional */
static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
Expand Down Expand Up @@ -55479,7 +55485,9 @@ int JS_AddIntrinsicDate(JSContext *ctx)

int JS_AddIntrinsicEval(JSContext *ctx)
{
#ifndef CONFIG_BYTECODE_ONLY_RUNTIME
ctx->eval_internal = __JS_EvalInternal;
#endif
return 0;
}

Expand Down
Binary file added tests/test-bytecode-rt
Binary file not shown.
18 changes: 18 additions & 0 deletions tests/test_bytecode_runtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function test_closure() {
let x = 10;
return function(y) {
return x + y;
};
}

let f = test_closure();
if (f(5) !== 15) throw new Error("Closure failed");

let arr = [1, 2, 3].map(x => x * 2);
if (arr[0] !== 2 || arr[1] !== 4 || arr[2] !== 6) throw new Error("Array map failed");

let p = Promise.resolve(42);
p.then(v => {
if (v !== 42) throw new Error("Promise failed");
console.log("Bytecode runtime test passed");
});