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
35 changes: 30 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ 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

QJS_OBJS=$(OBJDIR)/qjs.o $(OBJDIR)/repl.o $(QJS_LIB_OBJS)
QJS_BC_OBJS=$(OBJDIR)/quickjs-bytecode.o

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

HOST_LIBS=-lm -ldl -lpthread
LIBS=-lm -lpthread
Expand All @@ -269,7 +271,7 @@ qjs$(EXE): $(QJS_OBJS)
qjs-debug$(EXE): $(patsubst %.o, %.debug.o, $(QJS_OBJS))
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_LIB_OBJS)
qjsc$(EXE): $(OBJDIR)/qjsc.o $(QJS_BC_OBJS) $(QJS_LIB_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

fuzz_eval: $(OBJDIR)/fuzz_eval.o $(OBJDIR)/fuzz_common.o libquickjs.fuzz.a
Expand All @@ -286,7 +288,7 @@ libfuzzer: fuzz_eval fuzz_compile fuzz_regexp
ifneq ($(CROSS_PREFIX),)

$(QJSC): $(OBJDIR)/qjsc.host.o \
$(patsubst %.o, %.host.o, $(QJS_LIB_OBJS))
$(patsubst %.o, %.host.o, $(QJS_BC_OBJS) $(QJS_LIB_OBJS))
$(HOST_CC) $(LDFLAGS) -o $@ $^ $(HOST_LIBS)

endif #CROSS_PREFIX
Expand Down Expand Up @@ -369,7 +371,7 @@ clean:
rm -f repl.c out.c
rm -f *.a *.o *.d *~ unicode_gen regexp_test fuzz_eval fuzz_compile fuzz_regexp $(PROGS)
rm -f hello.c test_fib.c
rm -f examples/*.so tests/*.so
rm -f examples/*.so tests/*.so tests/*.qbc examples/*.qbc
rm -rf $(OBJDIR)/ *.dSYM/ qjs-debug$(EXE)
rm -rf run-test262-debug$(EXE)
rm -f run_octane run_sunspider_like
Expand Down Expand Up @@ -452,21 +454,44 @@ ifdef CONFIG_SHARED_LIBS
test: tests/bjson.so examples/point.so
endif

test: qjs$(EXE)
test: qjs$(EXE) qjsc$(EXE)
$(WINE) ./qjs$(EXE) tests/test_closure.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_closure.qbc tests/test_closure.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_closure.qbc
$(WINE) ./qjs$(EXE) tests/test_language.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_language.qbc tests/test_language.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_language.qbc
$(WINE) ./qjs$(EXE) --std tests/test_builtin.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_builtin.qbc tests/test_builtin.js
$(WINE) ./qjs$(EXE) --std --bytecode tests/test_builtin.qbc
$(WINE) ./qjs$(EXE) tests/test_loop.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_loop.qbc tests/test_loop.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_loop.qbc
$(WINE) ./qjs$(EXE) tests/test_bigint.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_bigint.qbc tests/test_bigint.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_bigint.qbc
$(WINE) ./qjs$(EXE) tests/test_cyclic_import.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_cyclic_import.qbc tests/test_cyclic_import.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_cyclic_import.qbc
$(WINE) ./qjs$(EXE) tests/test_worker.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_worker.qbc tests/test_worker.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_worker.qbc
ifndef CONFIG_WIN32
$(WINE) ./qjs$(EXE) tests/test_std.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_std.qbc tests/test_std.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_std.qbc
$(WINE) ./qjs$(EXE) tests/test_rw_handler.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_rw_handler.qbc tests/test_rw_handler.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_rw_handler.qbc
./tests/test_bytecode_file.sh
endif
ifdef CONFIG_SHARED_LIBS
$(WINE) ./qjs$(EXE) tests/test_bjson.js
$(WINE) ./qjsc$(EXE) --bytecode -o tests/test_bjson.qbc tests/test_bjson.js
$(WINE) ./qjs$(EXE) --bytecode tests/test_bjson.qbc
$(WINE) ./qjs$(EXE) examples/test_point.js
$(WINE) ./qjsc$(EXE) --bytecode -o examples/test_point.qbc examples/test_point.js
$(WINE) ./qjs$(EXE) --bytecode examples/test_point.qbc
endif

stats: qjs$(EXE)
Expand Down
58 changes: 57 additions & 1 deletion qjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

#include "cutils.h"
#include "quickjs-libc.h"
#include "quickjs-bytecode.h"

extern const uint8_t qjsc_repl[];
extern const uint32_t qjsc_repl_size;
Expand Down Expand Up @@ -103,6 +104,37 @@ static int eval_file(JSContext *ctx, const char *filename, int module, int stric
return ret;
}

static int eval_bytecode_file(JSContext *ctx, const char *filename)
{
JSValue obj, val;
int ret = -1;

obj = qjs_bytecode_read_file(ctx, filename);
if (JS_IsException(obj))
goto exception;

if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
if (JS_ResolveModule(ctx, obj) < 0) {
JS_FreeValue(ctx, obj);
goto exception;
}
js_module_set_import_meta(ctx, obj, FALSE, TRUE);
val = JS_EvalFunction(ctx, obj);
val = js_std_await(ctx, val);
} else {
val = JS_EvalFunction(ctx, obj);
}
if (JS_IsException(val))
goto exception;
JS_FreeValue(ctx, val);
ret = 0;
return ret;

exception:
js_std_dump_error(ctx);
return ret;
}

/* also used to initialize the worker context */
static JSContext *JS_NewCustomContext(JSRuntime *rt)
{
Expand Down Expand Up @@ -297,6 +329,7 @@ void help(void)
"-i --interactive go to interactive mode\n"
"-m --module load as ES6 module (default=autodetect)\n"
" --script load as ES6 script (default=autodetect)\n"
" --bytecode file load a checked bytecode file\n"
" --strict force strict mode\n"
"-I --include file include an additional file\n"
" --std make 'std' and 'os' available to the loaded script\n"
Expand Down Expand Up @@ -324,6 +357,8 @@ int main(int argc, char **argv)
int empty_run = 0;
int module = -1;
int strict = 0;
char *bytecode_file = NULL;
int bytecode_arg_index = -1;
int load_std = 0;
int dump_unhandled_promise_rejection = 1;
size_t memory_limit = 0;
Expand Down Expand Up @@ -393,6 +428,15 @@ int main(int argc, char **argv)
module = 0;
continue;
}
if (!strcmp(longopt, "bytecode")) {
if (optind >= argc) {
fprintf(stderr, "qjs: expecting bytecode filename\n");
exit(1);
}
bytecode_arg_index = optind;
bytecode_file = argv[optind++];
goto done_options;
}
if (!strcmp(longopt, "strict")) {
strict = 1;
continue;
Expand Down Expand Up @@ -449,6 +493,12 @@ int main(int argc, char **argv)
help();
}
}
done_options:

if (bytecode_file && expr) {
fprintf(stderr, "qjs: --bytecode cannot be combined with -e\n");
exit(1);
}

if (trace_memory) {
js_trace_malloc_init(&trace_data);
Expand Down Expand Up @@ -482,7 +532,9 @@ int main(int argc, char **argv)
}

if (!empty_run) {
js_std_add_helpers(ctx, argc - optind, argv + optind);
int script_arg_index;
script_arg_index = bytecode_file ? bytecode_arg_index : optind;
js_std_add_helpers(ctx, argc - script_arg_index, argv + script_arg_index);

/* make 'std' and 'os' visible to non module code */
if (load_std) {
Expand Down Expand Up @@ -510,6 +562,10 @@ int main(int argc, char **argv)
if (eval_buf(ctx, expr, strlen(expr), "<cmdline>", eval_flags))
goto fail;
} else
if (bytecode_file) {
if (eval_bytecode_file(ctx, bytecode_file))
goto fail;
} else
if (optind >= argc) {
/* interactive mode */
interactive = 1;
Expand Down
87 changes: 68 additions & 19 deletions qjsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

#include "cutils.h"
#include "quickjs-libc.h"
#include "quickjs-bytecode.h"

typedef struct {
char *name;
Expand Down Expand Up @@ -296,13 +297,15 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
find_unique_cname(cname, sizeof(cname));
}

/* output the module name */
fprintf(outfile, "static const uint8_t %s_module_name[] = {\n",
cname);
dump_hex(outfile, (const uint8_t *)module_name, strlen(module_name) + 1);
fprintf(outfile, "};\n\n");
if (outfile) {
/* output the module name */
fprintf(outfile, "static const uint8_t %s_module_name[] = {\n",
cname);
dump_hex(outfile, (const uint8_t *)module_name, strlen(module_name) + 1);
fprintf(outfile, "};\n\n");

output_object_code(ctx, outfile, val, cname, CNAME_TYPE_JSON_MODULE);
output_object_code(ctx, outfile, val, cname, CNAME_TYPE_JSON_MODULE);
}
JS_FreeValue(ctx, val);
} else {
JSValue func_val;
Expand All @@ -317,7 +320,8 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
if (namelist_find(&cname_list, cname)) {
find_unique_cname(cname, sizeof(cname));
}
output_object_code(ctx, outfile, func_val, cname, CNAME_TYPE_MODULE);
if (outfile)
output_object_code(ctx, outfile, func_val, cname, CNAME_TYPE_MODULE);

/* the module is already referenced, so we must free it */
m = JS_VALUE_GET_PTR(func_val);
Expand All @@ -327,13 +331,10 @@ JSModuleDef *jsc_module_loader(JSContext *ctx,
return m;
}

static void compile_file(JSContext *ctx, FILE *fo,
const char *filename,
const char *c_name1,
int module)
static JSValue compile_file_to_object(JSContext *ctx, const char *filename,
int module)
{
uint8_t *buf;
char c_name[1024];
int eval_flags;
JSValue obj;
size_t buf_len;
Expand All @@ -358,6 +359,18 @@ static void compile_file(JSContext *ctx, FILE *fo,
exit(1);
}
js_free(ctx, buf);
return obj;
}

static void compile_file(JSContext *ctx, FILE *fo,
const char *filename,
const char *c_name1,
int module)
{
char c_name[1024];
JSValue obj;

obj = compile_file_to_object(ctx, filename, module);
if (c_name1) {
pstrcpy(c_name, sizeof(c_name), c_name1);
} else {
Expand Down Expand Up @@ -398,6 +411,7 @@ void help(void)
"options are:\n"
"-c only output bytecode to a C file\n"
"-e output main() and bytecode to a C file (default = executable output)\n"
"-b --bytecode output checked bytecode to a file\n"
"-o output set the output filename\n"
"-N cname set the C name of the generated data\n"
"-m compile as Javascript module (default=autodetect)\n"
Expand Down Expand Up @@ -555,6 +569,7 @@ static size_t get_suffixed_size(const char *str)
typedef enum {
OUTPUT_C,
OUTPUT_C_MAIN,
OUTPUT_BYTECODE,
OUTPUT_EXECUTABLE,
} OutputTypeEnum;

Expand Down Expand Up @@ -639,6 +654,10 @@ int main(int argc, char **argv)
output_type = OUTPUT_C_MAIN;
continue;
}
if (opt == 'b' || !strcmp(longopt, "bytecode")) {
output_type = OUTPUT_BYTECODE;
continue;
}
if (opt == 'N') {
cname = get_short_optarg(&optind, opt, arg, argc, argv);
break;
Expand Down Expand Up @@ -732,11 +751,23 @@ int main(int argc, char **argv)
if (!out_filename) {
if (output_type == OUTPUT_EXECUTABLE) {
out_filename = "a.out";
} else if (output_type == OUTPUT_BYTECODE) {
out_filename = "out.qbc";
} else {
out_filename = "out.c";
}
}

if (output_type == OUTPUT_BYTECODE && argc - optind != 1) {
fprintf(stderr, "qjsc: bytecode output expects exactly one input file\n");
exit(1);
}

if (output_type == OUTPUT_BYTECODE && byte_swap) {
fprintf(stderr, "qjsc: -x is not supported with --bytecode\n");
exit(1);
}

if (output_type == OUTPUT_EXECUTABLE) {
#if defined(_WIN32) || defined(__ANDROID__)
/* XXX: find a /tmp directory ? */
Expand All @@ -748,13 +779,6 @@ int main(int argc, char **argv)
pstrcpy(cfilename, sizeof(cfilename), out_filename);
}

fo = fopen(cfilename, "w");
if (!fo) {
perror(cfilename);
exit(1);
}
outfile = fo;

rt = JS_NewRuntime();
ctx = JS_NewContext(rt);

Expand All @@ -763,6 +787,31 @@ int main(int argc, char **argv)
/* loader for ES6 modules */
JS_SetModuleLoaderFunc2(rt, NULL, jsc_module_loader, NULL, NULL);

if (output_type == OUTPUT_BYTECODE) {
JSValue obj;
int ret;

obj = compile_file_to_object(ctx, argv[optind], module);
ret = qjs_bytecode_write_file(ctx, out_filename, obj, 0);
JS_FreeValue(ctx, obj);
if (ret) {
js_std_dump_error(ctx);
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
namelist_free(&cname_list);
namelist_free(&cmodule_list);
namelist_free(&init_module_list);
return ret ? 1 : 0;
}

fo = fopen(cfilename, "w");
if (!fo) {
perror(cfilename);
exit(1);
}
outfile = fo;

fprintf(fo, "/* File generated automatically by the QuickJS compiler. */\n"
"\n"
);
Expand Down
Loading