Skip to content

Add parserless build mode #1021

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

Merged
merged 1 commit into from
Apr 10, 2025
Merged
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
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,19 @@ jobs:
run: |
make jscheck

parserless:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: build
run: |
make QJS_DISABLE_PARSER=ON QJS_BUILD_EXAMPLES=ON
- name: test
run: |
./build/hello
./build/hello_module


meson:
runs-on: ${{ matrix.platform }}
name: meson on ${{ matrix.platform }} (${{ matrix.mode.name }} ${{ matrix.flavor }}, ${{ matrix.features.name }})
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ xoption(QJS_BUILD_EXAMPLES "Build examples" OFF)
xoption(QJS_BUILD_CLI_STATIC "Build a static qjs executable" OFF)
xoption(QJS_BUILD_CLI_WITH_MIMALLOC "Build the qjs executable with mimalloc" OFF)
xoption(QJS_BUILD_CLI_WITH_STATIC_MIMALLOC "Build the qjs executable with mimalloc (statically linked)" OFF)
xoption(QJS_DISABLE_PARSER "Disable JS source code parser" OFF)
xoption(QJS_ENABLE_ASAN "Enable AddressSanitizer (ASan)" OFF)
xoption(QJS_ENABLE_MSAN "Enable MemorySanitizer (MSan)" OFF)
xoption(QJS_ENABLE_TSAN "Enable ThreadSanitizer (TSan)" OFF)
Expand Down
56 changes: 54 additions & 2 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3455,6 +3455,8 @@ const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
return cstr;
}

#ifndef QJS_DISABLE_PARSER

/* return a string atom containing name concatenated with str1 */
/* `str1` may be pure ASCII or UTF-8 encoded */
// TODO(chqrlie): use string concatenation instead of UTF-8 conversion
Expand Down Expand Up @@ -3497,6 +3499,8 @@ static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
return js_atom_concat_str(ctx, name, buf);
}

#endif // QJS_DISABLE_PARSER

static inline bool JS_IsEmptyString(JSValueConst v)
{
return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
Expand Down Expand Up @@ -20372,8 +20376,6 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
opcode_info[(op) >= OP_TEMP_START ? \
(op) + (OP_TEMP_END - OP_TEMP_START) : (op)]

static __exception int next_token(JSParseState *s);

static void free_token(JSParseState *s, JSToken *token)
{
switch(token->val) {
Expand Down Expand Up @@ -20480,6 +20482,10 @@ int JS_PRINTF_FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, JS_PRINTF_FORMAT
return -1;
}

#ifndef QJS_DISABLE_PARSER

static __exception int next_token(JSParseState *s);

static int js_parse_expect(JSParseState *s, int tok)
{
char buf[ATOM_GET_STR_BUF_SIZE];
Expand Down Expand Up @@ -20824,6 +20830,8 @@ static __exception int js_parse_regexp(JSParseState *s)
return -1;
}

#endif // QJS_DISABLE_PARSER

static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
char *static_buf)
{
Expand Down Expand Up @@ -20851,6 +20859,8 @@ static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
return 0;
}

#ifndef QJS_DISABLE_PARSER

/* convert a TOK_IDENT to a keyword when needed */
static void update_token_ident(JSParseState *s)
{
Expand Down Expand Up @@ -21425,6 +21435,8 @@ static __exception int next_token(JSParseState *s)
return -1;
}

#endif // QJS_DISABLE_PARSER

static int json_parse_error(JSParseState *s, const uint8_t *curp, const char *msg)
{
const uint8_t *p, *line_start;
Expand Down Expand Up @@ -21725,6 +21737,8 @@ static __exception int json_next_token(JSParseState *s)
return -1;
}

#ifndef QJS_DISABLE_PARSER

/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
only set if TOK_IMPORT is returned */
/* XXX: handle all unicode cases */
Expand Down Expand Up @@ -27641,6 +27655,8 @@ static __exception int js_parse_statement_or_decl(JSParseState *s,
return -1;
}

#endif // QJS_DISABLE_PARSER

/* 'name' is freed */
static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
{
Expand Down Expand Up @@ -27726,6 +27742,8 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
js_free(ctx, m);
}

#ifndef QJS_DISABLE_PARSER

static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
JSAtom module_name)
{
Expand All @@ -27750,6 +27768,8 @@ static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
return i;
}

#endif // QJS_DISABLE_PARSER

static JSExportEntry *find_export_entry(JSContext *ctx, const JSModuleDef *m,
JSAtom export_name)
{
Expand Down Expand Up @@ -27794,6 +27814,8 @@ static JSExportEntry *add_export_entry2(JSContext *ctx,
return me;
}

#ifndef QJS_DISABLE_PARSER

static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
JSAtom local_name, JSAtom export_name,
JSExportTypeEnum export_type)
Expand All @@ -27817,6 +27839,8 @@ static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
return 0;
}

#endif // QJS_DISABLE_PARSER

/* create a C module */
/* `name_str` may be pure ASCII or UTF-8 encoded */
JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
Expand Down Expand Up @@ -29465,6 +29489,8 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
return js_dup(m->promise);
}

#ifndef QJS_DISABLE_PARSER

static __exception JSAtom js_parse_from_clause(JSParseState *s)
{
JSAtom module_name;
Expand Down Expand Up @@ -29904,6 +29930,8 @@ static JSFunctionDef *js_new_function_def(JSContext *ctx,
return fd;
}

#endif // QJS_DISABLE_PARSER

static void free_bytecode_atoms(JSRuntime *rt,
const uint8_t *bc_buf, int bc_len,
bool use_short_opcodes)
Expand Down Expand Up @@ -29937,6 +29965,8 @@ static void free_bytecode_atoms(JSRuntime *rt,
}
}

#ifndef QJS_DISABLE_PARSER

static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
{
int i;
Expand Down Expand Up @@ -29999,6 +30029,8 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
js_free(ctx, fd);
}

#endif // QJS_DISABLE_PARSER

#ifdef ENABLE_DUMPS // JS_DUMP_BYTECODE_*
static const char *skip_lines(const char *p, int n) {
while (p && n-- > 0 && *p) {
Expand Down Expand Up @@ -30455,6 +30487,8 @@ static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionB
}
#endif

#ifndef QJS_DISABLE_PARSER

static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
bool is_local, bool is_arg,
int var_idx, JSAtom var_name,
Expand Down Expand Up @@ -33762,8 +33796,10 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
are used to compile the eval and they must be ordered by scope,
so it is necessary to create the closure variables before any
other variable lookup is done. */
#ifndef QJS_DISABLE_PARSER
if (fd->has_eval_call)
add_eval_variables(ctx, fd);
#endif // QJS_DISABLE_PARSER

/* add the module global variables in the closure */
if (fd->module) {
Expand Down Expand Up @@ -33922,6 +33958,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
return JS_EXCEPTION;
}

#endif // QJS_DISABLE_PARSER

static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
{
int i;
Expand Down Expand Up @@ -33956,6 +33994,8 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
}
}

#ifndef QJS_DISABLE_PARSER

static __exception int js_parse_directives(JSParseState *s)
{
char str[20];
Expand Down Expand Up @@ -34767,6 +34807,8 @@ static __exception int js_parse_program(JSParseState *s)
return 0;
}

#endif // QJS_DISABLE_PARSER

static void js_parse_init(JSContext *ctx, JSParseState *s,
const char *input, size_t input_len,
const char *filename, int line)
Expand Down Expand Up @@ -34822,6 +34864,8 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
}

#ifndef QJS_DISABLE_PARSER

/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
/* `export_name` and `input` may be pure ASCII or UTF-8 encoded */
static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
Expand Down Expand Up @@ -34938,6 +34982,8 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
return JS_EXCEPTION;
}

#endif // QJS_DISABLE_PARSER

/* the indirection is needed to make 'eval' optional */
static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
const char *input, size_t input_len,
Expand Down Expand Up @@ -52859,7 +52905,9 @@ void JS_AddIntrinsicDate(JSContext *ctx)

void JS_AddIntrinsicEval(JSContext *ctx)
{
#ifndef QJS_DISABLE_PARSER
ctx->eval_internal = __JS_EvalInternal;
#endif // QJS_DISABLE_PARSER
}

/* BigInt */
Expand Down Expand Up @@ -57526,6 +57574,7 @@ static void _JS_AddIntrinsicCallSite(JSContext *ctx)

bool JS_DetectModule(const char *input, size_t input_len)
{
#ifndef QJS_DISABLE_PARSER
JSRuntime *rt;
JSContext *ctx;
JSValue val;
Expand Down Expand Up @@ -57554,6 +57603,9 @@ bool JS_DetectModule(const char *input, size_t input_len)
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return is_module;
#else
return false;
#endif // QJS_DISABLE_PARSER
}

uintptr_t js_std_cmd(int cmd, ...) {
Expand Down
1 change: 1 addition & 0 deletions quickjs.h
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,7 @@ JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
* wholly infallible: non-strict classic scripts may _parse_ okay as a module
* but not _execute_ as one (different runtime semantics.) Use with caution.
* |input| can be either ASCII or UTF-8 encoded source code.
* Returns false if QuickJS was built with -DQJS_DISABLE_PARSER.
*/
JS_EXTERN bool JS_DetectModule(const char *input, size_t input_len);
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
Expand Down
Loading