diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2c285266..d4d74163d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 }}) diff --git a/CMakeLists.txt b/CMakeLists.txt index 748660bbe..88e98bb0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/quickjs.c b/quickjs.c index 70dd5aff0..12e46294b 100644 --- a/quickjs.c +++ b/quickjs.c @@ -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 @@ -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; @@ -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) { @@ -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]; @@ -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) { @@ -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) { @@ -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; @@ -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 */ @@ -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) { @@ -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) { @@ -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) { @@ -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) @@ -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, @@ -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; @@ -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) @@ -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; @@ -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) { @@ -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, @@ -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) { @@ -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; @@ -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]; @@ -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) @@ -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, @@ -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, @@ -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 */ @@ -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; @@ -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, ...) { diff --git a/quickjs.h b/quickjs.h index b2cae31ac..e39947312 100644 --- a/quickjs.h +++ b/quickjs.h @@ -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'. */