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
27 changes: 20 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,23 @@ ifdef CONFIG_ARM32
MQJS_BUILD_FLAGS=-m32
endif

PROGS=mqjs$(EXE) example$(EXE)
PROGS=mqjs$(EXE) example$(EXE) mqjsc$(EXE)
TEST_PROGS=dtoa_test libm_test
LIB=libmquickjs.a

all: $(PROGS)
all: $(LIB) $(PROGS)

MQJS_OBJS=mqjs.o readline_tty.o readline.o mquickjs.o dtoa.o libm.o cutils.o
LIB_OBJS=mquickjs.o dtoa.o libm.o cutils.o mqjs_runtime.o readline.o readline_tty.o
MQJS_OBJS=mqjs.repl.o
LIBS=-lm

mqjs$(EXE): $(MQJS_OBJS)
$(LIB): $(LIB_OBJS)
$(AR) rcs $@ $^

mqjs$(EXE): $(MQJS_OBJS) $(LIB)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

mqjsc$(EXE): mqjsc.o $(LIB)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

mquickjs.o: mquickjs_atom.h
Expand All @@ -97,12 +105,16 @@ mquickjs_atom.h: mqjs_stdlib
mqjs_stdlib.h: mqjs_stdlib
./mqjs_stdlib $(MQJS_BUILD_FLAGS) > $@

mqjs.o: mqjs_stdlib.h
mqjs.repl.o: mqjs.c mqjs_stdlib.h
$(CC) $(CFLAGS) -DCONFIG_MQJS_REPL -c -o $@ mqjs.c

mqjs_runtime.o: mqjs.c
$(CC) $(CFLAGS) -c -o $@ mqjs.c

# C API example
example.o: example_stdlib.h

example$(EXE): example.o mquickjs.o dtoa.o libm.o cutils.o
example$(EXE): example.o $(LIB)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)

example_stdlib: example_stdlib.host.o mquickjs_build.host.o
Expand All @@ -117,7 +129,7 @@ example_stdlib.h: example_stdlib
%.host.o: %.c
$(HOST_CC) $(HOST_CFLAGS) -c -o $@ $<

test: mqjs example
test: mqjs example mqjsc
./mqjs tests/test_closure.js
./mqjs tests/test_language.js
./mqjs tests/test_loop.js
Expand All @@ -127,6 +139,7 @@ test: mqjs example
# @sha256sum -c test_builtin.sha256
./mqjs -b test_builtin.bin
./example tests/test_rect.js
./tests/test_mqjsc.sh

microbench: mqjs
./mqjs tests/microbench.js
Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@ system.
Use the option `--no-column` to remove the column number debug info
(only line numbers are remaining) if you want to save some storage.

## Compiler

The compiler is `mqjsc`. It can produce a standalone C program binary
executable that embeds the MicroQuickJS engine along with the
compiled bytecode. Usage:

```
usage: mqjsc [options] [file]
-h --help list options
-o FILE set the output filename (default = a.out)
-c only output C source file
-m32 force 32 bit bytecode output
```

Example:

```sh
./mqjsc -o my_prog examples/mqjsc_example.js
./my_prog arg1 arg2
```

## Stricter mode

MQuickJS only supports a subset of JavaScript (mostly ES5). It is
Expand Down
22 changes: 22 additions & 0 deletions examples/mqjsc_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* Example for mqjsc */

print("Welcome to MicroQuickJS Standalone!");

function greet(name) {
print("Hello, " + name + "!");
}

if (scriptArgs && scriptArgs.length > 0) {
for (var arg of scriptArgs) {
greet(arg);
}
} else {
greet("World");
}

print("Memory limit: 16 MB (default in generated C wrapper)");
print("Current time: " + Date.now());

setTimeout(function() {
print("This message is printed after 500ms");
}, 500);
123 changes: 55 additions & 68 deletions mqjs.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Micro QuickJS REPL
* Micro QuickJS runtime functions
*
* Copyright (c) 2017-2025 Fabrice Bellard
* Copyright (c) 2017-2025 Charlie Gordon
Expand Down Expand Up @@ -38,15 +38,44 @@
#include "cutils.h"
#include "readline_tty.h"
#include "mquickjs.h"
#include "mqjs.h"

static uint8_t *load_file(const char *filename, int *plen);
static void dump_error(JSContext *ctx);
uint8_t *load_file(const char *filename, int *plen)
{
FILE *f;
uint8_t *buf;
int buf_len;

f = fopen(filename, "rb");
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
buf_len = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(buf_len + 1);
if (!buf) {
fclose(f);
return NULL;
}
if (fread(buf, 1, buf_len, f) != buf_len) {
free(buf);
fclose(f);
return NULL;
}
buf[buf_len] = '\0';
fclose(f);
if (plen)
*plen = buf_len;
return buf;
}

static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
int i;
JSValue v;

for(i = 0; i < argc; i++) {
if (i != 0)
putchar(' ');
Expand All @@ -65,49 +94,49 @@ static JSValue js_print(JSContext *ctx, JSValue *this_val, int argc, JSValue *ar
return JS_UNDEFINED;
}

static JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
JS_GC(ctx);
return JS_UNDEFINED;
}

#if defined(__linux__) || defined(__APPLE__)
static int64_t get_time_ms(void)
int64_t get_time_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000 + (ts.tv_nsec / 1000000);
}
#else
static int64_t get_time_ms(void)
int64_t get_time_ms(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
}
#endif

static JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return JS_NewInt64(ctx, (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000));
}

static JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
return JS_NewInt64(ctx, get_time_ms());
}

/* load a script */
static JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
const char *filename;
JSCStringBuf buf_str;
uint8_t *buf;
int buf_len;
JSValue ret;

filename = JS_ToCString(ctx, argv[0], &buf_str);
if (!filename)
return JS_EXCEPTION;
Expand All @@ -118,23 +147,14 @@ static JSValue js_load(JSContext *ctx, JSValue *this_val, int argc, JSValue *arg
return ret;
}

/* timers */
typedef struct {
BOOL allocated;
JSGCRef func;
int64_t timeout; /* in ms */
} JSTimer;

#define MAX_TIMERS 16

static JSTimer js_timer_list[MAX_TIMERS];

static JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
JSTimer *th;
int delay, i;
JSValue *pfunc;

if (!JS_IsFunction(ctx, argv[0]))
return JS_ThrowTypeError(ctx, "not a function");
if (JS_ToInt32(ctx, &delay, argv[1]))
Expand All @@ -152,7 +172,7 @@ static JSValue js_setTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValu
return JS_ThrowInternalError(ctx, "too many timers");
}

static JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv)
{
int timer_id;
JSTimer *th;
Expand All @@ -169,7 +189,7 @@ static JSValue js_clearTimeout(JSContext *ctx, JSValue *this_val, int argc, JSVa
return JS_UNDEFINED;
}

static void run_timers(JSContext *ctx)
void run_timers(JSContext *ctx)
{
int64_t min_delay, delay, cur_time;
BOOL has_timer;
Expand All @@ -193,10 +213,10 @@ static void run_timers(JSContext *ctx)
goto fail;
JS_PushArg(ctx, th->func.val); /* func name */
JS_PushArg(ctx, JS_NULL); /* this */

JS_DeleteGCRef(ctx, &th->func);
th->allocated = FALSE;

ret = JS_Call(ctx, 0);
if (JS_IsException(ret)) {
fail:
Expand All @@ -220,52 +240,14 @@ static void run_timers(JSContext *ctx)
}
}

#include "mqjs_stdlib.h"

#define STYLE_DEFAULT COLOR_BRIGHT_GREEN
#define STYLE_COMMENT COLOR_WHITE
#define STYLE_STRING COLOR_BRIGHT_CYAN
#define STYLE_REGEX COLOR_CYAN
#define STYLE_NUMBER COLOR_GREEN
#define STYLE_KEYWORD COLOR_BRIGHT_WHITE
#define STYLE_FUNCTION COLOR_BRIGHT_YELLOW
#define STYLE_TYPE COLOR_BRIGHT_MAGENTA
#define STYLE_IDENTIFIER COLOR_BRIGHT_GREEN
#define STYLE_ERROR COLOR_RED
#define STYLE_RESULT COLOR_BRIGHT_WHITE
#define STYLE_ERROR_MSG COLOR_BRIGHT_RED

static uint8_t *load_file(const char *filename, int *plen)
{
FILE *f;
uint8_t *buf;
int buf_len;

f = fopen(filename, "rb");
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
buf_len = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(buf_len + 1);
fread(buf, 1, buf_len, f);
buf[buf_len] = '\0';
fclose(f);
if (plen)
*plen = buf_len;
return buf;
}

static int js_log_err_flag;

static void js_log_func(void *opaque, const void *buf, size_t buf_len)
void js_log_func(void *opaque, const void *buf, size_t buf_len)
{
fwrite(buf, 1, buf_len, js_log_err_flag ? stderr : stdout);
}

static void dump_error(JSContext *ctx)
void dump_error(JSContext *ctx)
{
JSValue obj;
obj = JS_GetException(ctx);
Expand All @@ -276,6 +258,10 @@ static void dump_error(JSContext *ctx)
fprintf(stderr, "%s\n", term_colors[COLOR_NONE]);
}

#ifdef CONFIG_MQJS_REPL

#include "mqjs_stdlib.h"

static int eval_buf(JSContext *ctx, const char *eval_str, const char *filename, BOOL is_repl, int parse_flags)
{
JSValue val;
Expand Down Expand Up @@ -654,7 +640,7 @@ int main(int argc, const char **argv)
case 'm':
count *= 1024;
/* fall thru */
case 'k':
case 'k':
count *= 1024;
/* fall thru */
default:
Expand Down Expand Up @@ -772,3 +758,4 @@ int main(int argc, const char **argv)
free(mem_buf);
return 1;
}
#endif
Loading