mirror of
https://github.com/DoneJS-Runtime/quickjs-done-nextgen.git
synced 2025-01-09 17:43:15 +00:00
Improve JS_DetectModule (#610)
It's still not infallible (I don't think it can ever be, the whole premise is wrong) but hopefully it's a little less fallible now. Fixes: https://github.com/quickjs-ng/quickjs/issues/606
This commit is contained in:
parent
bed51fab0a
commit
8cd59bf7c4
10 changed files with 54 additions and 33 deletions
Binary file not shown.
BIN
gen/hello.c
BIN
gen/hello.c
Binary file not shown.
61
quickjs.c
61
quickjs.c
|
@ -20250,34 +20250,6 @@ static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end)
|
|||
}
|
||||
}
|
||||
|
||||
/* return true if 'input' contains the source of a module
|
||||
(heuristic). 'input' must be a zero terminated.
|
||||
|
||||
Heuristic:
|
||||
- Skip comments
|
||||
- Expect 'import' keyword not followed by '(' or '.'
|
||||
- Expect 'export' keyword
|
||||
- Expect 'await' keyword
|
||||
*/
|
||||
/* input is pure ASCII or UTF-8 encoded source code */
|
||||
BOOL JS_DetectModule(const char *input, size_t input_len)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)input;
|
||||
int tok;
|
||||
|
||||
skip_shebang(&p, p + input_len);
|
||||
switch(simple_next_token(&p, FALSE)) {
|
||||
case TOK_IMPORT:
|
||||
tok = simple_next_token(&p, FALSE);
|
||||
return (tok != '.' && tok != '(');
|
||||
case TOK_AWAIT:
|
||||
case TOK_EXPORT:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int get_prev_opcode(JSFunctionDef *fd) {
|
||||
if (fd->last_opcode_pos < 0)
|
||||
return OP_invalid;
|
||||
|
@ -26380,6 +26352,7 @@ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
|
|||
/* load the module */
|
||||
if (!rt->module_loader_func) {
|
||||
/* XXX: use a syntax error ? */
|
||||
// XXX: update JS_DetectModule when you change this
|
||||
JS_ThrowReferenceError(ctx, "could not load module '%s'",
|
||||
cname);
|
||||
js_free(ctx, cname);
|
||||
|
@ -54702,6 +54675,38 @@ static void _JS_AddIntrinsicCallSite(JSContext *ctx)
|
|||
countof(js_callsite_proto_funcs));
|
||||
}
|
||||
|
||||
BOOL JS_DetectModule(const char *input, size_t input_len)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSContext *ctx;
|
||||
JSValue val;
|
||||
BOOL is_module;
|
||||
|
||||
is_module = TRUE;
|
||||
rt = JS_NewRuntime();
|
||||
if (!rt)
|
||||
return FALSE;
|
||||
ctx = JS_NewContextRaw(rt);
|
||||
if (!ctx) {
|
||||
JS_FreeRuntime(rt);
|
||||
return FALSE;
|
||||
}
|
||||
JS_AddIntrinsicRegExp(ctx); // otherwise regexp literals don't parse
|
||||
val = __JS_EvalInternal(ctx, JS_UNDEFINED, input, input_len, "<unnamed>",
|
||||
JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY, -1);
|
||||
if (JS_IsException(val)) {
|
||||
const char *msg = JS_ToCString(ctx, rt->current_exception);
|
||||
// gruesome hack to recognize exceptions from import statements;
|
||||
// necessary because we don't pass in a module loader
|
||||
is_module = !!strstr(msg, "ReferenceError: could not load module");
|
||||
JS_FreeCString(ctx, msg);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
return is_module;
|
||||
}
|
||||
|
||||
#undef malloc
|
||||
#undef free
|
||||
#undef realloc
|
||||
|
|
|
@ -693,6 +693,12 @@ JS_EXTERN JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj,
|
|||
JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj,
|
||||
JSValue new_target,
|
||||
int argc, JSValue *argv);
|
||||
/* Try to detect if the input is a module. Returns TRUE if parsing the input
|
||||
* as a module produces no syntax errors. It's a naive approach that is not
|
||||
* 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.
|
||||
*/
|
||||
JS_EXTERN JS_BOOL JS_DetectModule(const char *input, size_t input_len);
|
||||
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
|
||||
JS_EXTERN JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
|
||||
|
|
1
tests/detect_module/0.js
Normal file
1
tests/detect_module/0.js
Normal file
|
@ -0,0 +1 @@
|
|||
await undefined
|
2
tests/detect_module/1.js
Normal file
2
tests/detect_module/1.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
const p = Promise.resolve(42)
|
||||
await p
|
1
tests/detect_module/2.js
Normal file
1
tests/detect_module/2.js
Normal file
|
@ -0,0 +1 @@
|
|||
await = 42 // parsed as classic script
|
8
tests/detect_module/3.js
Normal file
8
tests/detect_module/3.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*---
|
||||
negative:
|
||||
phase: parse
|
||||
type: SyntaxError
|
||||
---*/
|
||||
// the import statement makes it a module but `await = 42` is a SyntaxError
|
||||
import * as _ from "dummy"
|
||||
await = 42
|
3
tests/detect_module/4.js
Normal file
3
tests/detect_module/4.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
// imports should classify it as a module, even when not at the top
|
||||
os.now()
|
||||
import * as os from "os"
|
|
@ -1,5 +0,0 @@
|
|||
// This needs to be parsed as a module or will throw SyntaxError.
|
||||
//
|
||||
|
||||
await 0;
|
||||
|
Loading…
Reference in a new issue