Skip to content

Commit 8cd59bf

Browse files
authored
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: #606
1 parent bed51fa commit 8cd59bf

File tree

10 files changed

+114
-97
lines changed

10 files changed

+114
-97
lines changed

gen/function_source.c

Lines changed: 44 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,52 @@
22

33
#include "quickjs-libc.h"
44

5-
const uint32_t qjsc_function_source_size = 390;
5+
const uint32_t qjsc_function_source_size = 338;
66

7-
const uint8_t qjsc_function_source[390] = {
8-
0x10, 0x06, 0x01, 0x0c, 0x61, 0x63, 0x74, 0x75,
9-
0x61, 0x6c, 0x01, 0x02, 0x66, 0x01, 0x30, 0x74,
10-
0x65, 0x73, 0x74, 0x73, 0x2f, 0x66, 0x75, 0x6e,
11-
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x6f,
12-
0x75, 0x72, 0x63, 0x65, 0x2e, 0x6a, 0x73, 0x01,
7+
const uint8_t qjsc_function_source[338] = {
8+
0x10, 0x05, 0x01, 0x30, 0x74, 0x65, 0x73, 0x74,
9+
0x73, 0x2f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69,
10+
0x6f, 0x6e, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63,
11+
0x65, 0x2e, 0x6a, 0x73, 0x01, 0x0c, 0x61, 0x63,
12+
0x74, 0x75, 0x61, 0x6c, 0x01, 0x02, 0x66, 0x01,
1313
0x0c, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x01,
14-
0x14, 0x75, 0x73, 0x65, 0x20, 0x73, 0x74, 0x72,
15-
0x69, 0x63, 0x74, 0x01, 0x34, 0x66, 0x75, 0x6e,
16-
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x28,
17-
0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75,
18-
0x72, 0x6e, 0x20, 0x34, 0x32, 0x20, 0x7d, 0x0c,
19-
0x00, 0xfa, 0x01, 0xa2, 0x01, 0x00, 0x06, 0x00,
20-
0x03, 0x00, 0x01, 0xa0, 0x01, 0x06, 0xa4, 0x01,
21-
0x00, 0x00, 0x00, 0xba, 0x03, 0x02, 0x00, 0x30,
22-
0xbc, 0x03, 0x04, 0x00, 0x70, 0xba, 0x03, 0x04,
23-
0x03, 0x70, 0x10, 0x00, 0x01, 0x00, 0xe4, 0x01,
24-
0x00, 0x01, 0x00, 0x0c, 0x43, 0xfa, 0x01, 0xbc,
25-
0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03,
26-
0x00, 0xbc, 0x2a, 0x28, 0xbe, 0x03, 0x03, 0x01,
27-
0x04, 0x02, 0x1e, 0x0c, 0x0e, 0x1a, 0x66, 0x75,
28-
0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66,
29-
0x28, 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x74,
30-
0x75, 0x72, 0x6e, 0x20, 0x34, 0x32, 0x20, 0x7d,
31-
0x0c, 0x03, 0xc2, 0x05, 0x08, 0xc2, 0x04, 0x3f,
32-
0xe0, 0x00, 0x00, 0x00, 0x80, 0x3f, 0xde, 0x00,
33-
0x00, 0x00, 0x40, 0x3e, 0xe0, 0x00, 0x00, 0x00,
34-
0x80, 0xbf, 0x00, 0x40, 0xde, 0x00, 0x00, 0x00,
35-
0x00, 0x04, 0xe1, 0x00, 0x00, 0x00, 0xc9, 0x04,
36-
0xe2, 0x00, 0x00, 0x00, 0x3a, 0xe0, 0x00, 0x00,
37-
0x00, 0x61, 0x01, 0x00, 0x38, 0xde, 0x00, 0x00,
38-
0x00, 0x42, 0x38, 0x00, 0x00, 0x00, 0x24, 0x00,
39-
0x00, 0xca, 0x06, 0xc9, 0x62, 0x01, 0x00, 0x38,
40-
0xe0, 0x00, 0x00, 0x00, 0xaf, 0xea, 0x0b, 0x38,
41-
0x94, 0x00, 0x00, 0x00, 0x62, 0x01, 0x00, 0xef,
42-
0x2f, 0x61, 0x03, 0x00, 0x61, 0x02, 0x00, 0x38,
43-
0x3b, 0x00, 0x00, 0x00, 0x38, 0xe0, 0x00, 0x00,
44-
0x00, 0x04, 0xde, 0x00, 0x00, 0x00, 0x9d, 0x31,
45-
0x01, 0x00, 0x04, 0x00, 0xcb, 0x62, 0x02, 0x00,
46-
0x42, 0x38, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00,
47-
0xcc, 0x06, 0xc9, 0x62, 0x03, 0x00, 0x38, 0xe0,
48-
0x00, 0x00, 0x00, 0xaf, 0xea, 0x0b, 0x38, 0x94,
49-
0x00, 0x00, 0x00, 0x62, 0x03, 0x00, 0xef, 0x2f,
50-
0x68, 0x03, 0x00, 0x68, 0x02, 0x00, 0xc5, 0x28,
51-
0xbe, 0x03, 0x01, 0x01, 0x28, 0x60, 0x01, 0x49,
52-
0x02, 0x21, 0x1a, 0x1b, 0x04, 0x1e, 0x1d, 0x12,
53-
0x26, 0x49, 0x1d, 0x0c, 0x06, 0x11, 0x18, 0x2a,
54-
0x1c, 0x37, 0x41, 0x21, 0x1c, 0x34, 0x18, 0x1b,
55-
0x04, 0x26, 0x11, 0x3f, 0x1d, 0x0c, 0x06, 0x11,
56-
0x18, 0x2a, 0x1c, 0x53, 0x41, 0x00,
14+
0x34, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
15+
0x6e, 0x20, 0x66, 0x28, 0x29, 0x20, 0x7b, 0x20,
16+
0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x34,
17+
0x32, 0x20, 0x7d, 0x0d, 0xba, 0x03, 0x00, 0x00,
18+
0x00, 0x00, 0x00, 0x0c, 0x20, 0xfa, 0x01, 0xa2,
19+
0x01, 0x00, 0x05, 0x00, 0x03, 0x02, 0x01, 0x74,
20+
0x05, 0xbc, 0x03, 0x02, 0x00, 0x30, 0xbe, 0x03,
21+
0x04, 0x00, 0x70, 0xbc, 0x03, 0x04, 0x02, 0x70,
22+
0x10, 0x00, 0x01, 0x00, 0xe4, 0x01, 0x00, 0x01,
23+
0x00, 0xc0, 0x03, 0x00, 0x0d, 0xbe, 0x03, 0x01,
24+
0x01, 0x0c, 0x43, 0xfa, 0x01, 0xbe, 0x03, 0x00,
25+
0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0xbc,
26+
0x2a, 0x28, 0xba, 0x03, 0x03, 0x01, 0x04, 0x02,
27+
0x1e, 0x0c, 0x0e, 0x1a, 0x66, 0x75, 0x6e, 0x63,
28+
0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x28, 0x29,
29+
0x20, 0x7b, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72,
30+
0x6e, 0x20, 0x34, 0x32, 0x20, 0x7d, 0x0c, 0x03,
31+
0xc2, 0x04, 0x08, 0xcc, 0x08, 0xea, 0x05, 0xbf,
32+
0x00, 0xe2, 0x29, 0x04, 0xe1, 0x00, 0x00, 0x00,
33+
0xe1, 0x61, 0x00, 0x00, 0xde, 0x42, 0x38, 0x00,
34+
0x00, 0x00, 0x24, 0x00, 0x00, 0xc9, 0x62, 0x00,
35+
0x00, 0x65, 0x00, 0x00, 0xaf, 0xea, 0x0b, 0x38,
36+
0x94, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0xef,
37+
0x2f, 0x61, 0x02, 0x00, 0x61, 0x01, 0x00, 0x38,
38+
0x3b, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x04,
39+
0xdf, 0x00, 0x00, 0x00, 0x9d, 0x31, 0x01, 0x00,
40+
0x03, 0x00, 0xca, 0x62, 0x01, 0x00, 0x42, 0x38,
41+
0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0xcb, 0x62,
42+
0x02, 0x00, 0x65, 0x00, 0x00, 0xaf, 0xea, 0x0b,
43+
0x38, 0x94, 0x00, 0x00, 0x00, 0x62, 0x02, 0x00,
44+
0xef, 0x2f, 0x68, 0x02, 0x00, 0x68, 0x01, 0x00,
45+
0x06, 0x2e, 0xba, 0x03, 0x01, 0x01, 0x22, 0x1f,
46+
0x01, 0x27, 0x1c, 0x1b, 0x04, 0x0a, 0x1d, 0x12,
47+
0x26, 0x35, 0x17, 0x11, 0x18, 0x20, 0x1c, 0x37,
48+
0x41, 0x21, 0x1c, 0x2a, 0x18, 0x1b, 0x04, 0x26,
49+
0x11, 0x3f, 0x17, 0x11, 0x18, 0x20, 0x1c, 0x53,
50+
0x41, 0x00,
5751
};
5852

5953
static JSContext *JS_NewCustomContext(JSRuntime *rt)

gen/hello.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,23 @@
22

33
#include "quickjs-libc.h"
44

5-
const uint32_t qjsc_hello_size = 93;
5+
const uint32_t qjsc_hello_size = 105;
66

7-
const uint8_t qjsc_hello[93] = {
8-
0x10, 0x04, 0x01, 0x0e, 0x63, 0x6f, 0x6e, 0x73,
9-
0x6f, 0x6c, 0x65, 0x01, 0x06, 0x6c, 0x6f, 0x67,
10-
0x01, 0x16, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20,
11-
0x57, 0x6f, 0x72, 0x6c, 0x64, 0x01, 0x22, 0x65,
12-
0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f,
13-
0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x6a, 0x73,
14-
0x0c, 0x00, 0xfa, 0x00, 0xa2, 0x01, 0x00, 0x01,
15-
0x00, 0x03, 0x00, 0x00, 0x14, 0x01, 0xa4, 0x01,
16-
0x00, 0x00, 0x00, 0x38, 0xdd, 0x00, 0x00, 0x00,
17-
0x42, 0xde, 0x00, 0x00, 0x00, 0x04, 0xdf, 0x00,
18-
0x00, 0x00, 0x24, 0x01, 0x00, 0xcd, 0x28, 0xc0,
19-
0x03, 0x01, 0x01, 0x00, 0x00,
7+
const uint8_t qjsc_hello[105] = {
8+
0x10, 0x04, 0x01, 0x22, 0x65, 0x78, 0x61, 0x6d,
9+
0x70, 0x6c, 0x65, 0x73, 0x2f, 0x68, 0x65, 0x6c,
10+
0x6c, 0x6f, 0x2e, 0x6a, 0x73, 0x01, 0x0e, 0x63,
11+
0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x01, 0x06,
12+
0x6c, 0x6f, 0x67, 0x01, 0x16, 0x48, 0x65, 0x6c,
13+
0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
14+
0x0d, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
15+
0x0c, 0x20, 0xfa, 0x01, 0xa2, 0x01, 0x00, 0x00,
16+
0x00, 0x03, 0x00, 0x00, 0x19, 0x00, 0x08, 0xea,
17+
0x02, 0x29, 0x38, 0xde, 0x00, 0x00, 0x00, 0x42,
18+
0xdf, 0x00, 0x00, 0x00, 0x04, 0xe0, 0x00, 0x00,
19+
0x00, 0x24, 0x01, 0x00, 0x0e, 0x06, 0x2e, 0xba,
20+
0x03, 0x01, 0x01, 0x04, 0x01, 0x01, 0x17, 0x02,
21+
0x00,
2022
};
2123

2224
static JSContext *JS_NewCustomContext(JSRuntime *rt)

quickjs.c

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20250,34 +20250,6 @@ static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end)
2025020250
}
2025120251
}
2025220252

20253-
/* return true if 'input' contains the source of a module
20254-
(heuristic). 'input' must be a zero terminated.
20255-
20256-
Heuristic:
20257-
- Skip comments
20258-
- Expect 'import' keyword not followed by '(' or '.'
20259-
- Expect 'export' keyword
20260-
- Expect 'await' keyword
20261-
*/
20262-
/* input is pure ASCII or UTF-8 encoded source code */
20263-
BOOL JS_DetectModule(const char *input, size_t input_len)
20264-
{
20265-
const uint8_t *p = (const uint8_t *)input;
20266-
int tok;
20267-
20268-
skip_shebang(&p, p + input_len);
20269-
switch(simple_next_token(&p, FALSE)) {
20270-
case TOK_IMPORT:
20271-
tok = simple_next_token(&p, FALSE);
20272-
return (tok != '.' && tok != '(');
20273-
case TOK_AWAIT:
20274-
case TOK_EXPORT:
20275-
return TRUE;
20276-
default:
20277-
return FALSE;
20278-
}
20279-
}
20280-
2028120253
static inline int get_prev_opcode(JSFunctionDef *fd) {
2028220254
if (fd->last_opcode_pos < 0)
2028320255
return OP_invalid;
@@ -26380,6 +26352,7 @@ static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
2638026352
/* load the module */
2638126353
if (!rt->module_loader_func) {
2638226354
/* XXX: use a syntax error ? */
26355+
// XXX: update JS_DetectModule when you change this
2638326356
JS_ThrowReferenceError(ctx, "could not load module '%s'",
2638426357
cname);
2638526358
js_free(ctx, cname);
@@ -54702,6 +54675,38 @@ static void _JS_AddIntrinsicCallSite(JSContext *ctx)
5470254675
countof(js_callsite_proto_funcs));
5470354676
}
5470454677

54678+
BOOL JS_DetectModule(const char *input, size_t input_len)
54679+
{
54680+
JSRuntime *rt;
54681+
JSContext *ctx;
54682+
JSValue val;
54683+
BOOL is_module;
54684+
54685+
is_module = TRUE;
54686+
rt = JS_NewRuntime();
54687+
if (!rt)
54688+
return FALSE;
54689+
ctx = JS_NewContextRaw(rt);
54690+
if (!ctx) {
54691+
JS_FreeRuntime(rt);
54692+
return FALSE;
54693+
}
54694+
JS_AddIntrinsicRegExp(ctx); // otherwise regexp literals don't parse
54695+
val = __JS_EvalInternal(ctx, JS_UNDEFINED, input, input_len, "<unnamed>",
54696+
JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY, -1);
54697+
if (JS_IsException(val)) {
54698+
const char *msg = JS_ToCString(ctx, rt->current_exception);
54699+
// gruesome hack to recognize exceptions from import statements;
54700+
// necessary because we don't pass in a module loader
54701+
is_module = !!strstr(msg, "ReferenceError: could not load module");
54702+
JS_FreeCString(ctx, msg);
54703+
}
54704+
JS_FreeValue(ctx, val);
54705+
JS_FreeContext(ctx);
54706+
JS_FreeRuntime(rt);
54707+
return is_module;
54708+
}
54709+
5470554710
#undef malloc
5470654711
#undef free
5470754712
#undef realloc

quickjs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,12 @@ JS_EXTERN JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj,
693693
JS_EXTERN JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj,
694694
JSValue new_target,
695695
int argc, JSValue *argv);
696+
/* Try to detect if the input is a module. Returns TRUE if parsing the input
697+
* as a module produces no syntax errors. It's a naive approach that is not
698+
* wholly infallible: non-strict classic scripts may _parse_ okay as a module
699+
* but not _execute_ as one (different runtime semantics.) Use with caution.
700+
* |input| can be either ASCII or UTF-8 encoded source code.
701+
*/
696702
JS_EXTERN JS_BOOL JS_DetectModule(const char *input, size_t input_len);
697703
/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
698704
JS_EXTERN JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,

tests/detect_module/0.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
await undefined

tests/detect_module/1.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
const p = Promise.resolve(42)
2+
await p

tests/detect_module/2.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
await = 42 // parsed as classic script

tests/detect_module/3.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*---
2+
negative:
3+
phase: parse
4+
type: SyntaxError
5+
---*/
6+
// the import statement makes it a module but `await = 42` is a SyntaxError
7+
import * as _ from "dummy"
8+
await = 42

tests/detect_module/4.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// imports should classify it as a module, even when not at the top
2+
os.now()
3+
import * as os from "os"

tests/test_module_detect.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)