Skip to content

Commit b88aa63

Browse files
committed
test(module): pin parse-time cache publication
1 parent 040f94c commit b88aa63

3 files changed

Lines changed: 265 additions & 0 deletions

File tree

dev-support/module-c-oracles.tsv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ dynamic-compiled-reentry tests/fixtures/module_dynamic_compiled_reentry.c eed5dc
44
import-attributes tests/fixtures/module_import_attributes_loader2.c d243a74b07d59d60f4d811c66447087547a43c5541baf60eb48b7d4c337e2863 tests/fixtures/module_import_attributes_loader2.quickjs-2026-06-04.txt 31121aad6e62055f7389392c83c09efaa225fa126b27bb9c08db6d8daca41e28 loader2 import-attributes contract
55
import-meta tests/fixtures/module_import_meta_host.c e695e55164c6c07440e5f05135f14fe959367836e5380f7fc245d409130b503d tests/fixtures/module_import_meta_host.quickjs-2026-06-04.txt 928013ebd2080271e69a56516d5dd024e76cdd94fdb9bdc8fc27c08ece0efc8f canonical host import.meta contract
66
json tests/fixtures/module_json_loader2.c 325f1d3e2495da5af5ccb893176110c263cfd9355523275703328b2162fe02ae tests/fixtures/module_json_loader2.quickjs-2026-06-04.txt 3f3dbef40a32f908c14dc29318a4c4ac68cbb2bc328db50200a8d2ea4a95cbb9 loader2 JSON-module contract
7+
parse-cache-publication tests/fixtures/module_parse_cache_publication.c 15bdac6b059a043af460cf38f6af9ce7766b3828847562401bb03497547c450c tests/fixtures/module_parse_cache_publication.quickjs-2026-06-04.txt 68559deb9caf9a812a9b2f407a3674ee1d42010c5bffe837bfe543103c771200 parse-time module cache publication and failure cleanup
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/*
2+
* QuickJS 2026-06-04 oracle for parse-time module cache publication.
3+
*
4+
* This is test-only C and links only against the pinned external oracle. The
5+
* product engine remains Rust-only.
6+
*/
7+
8+
#include "quickjs.h"
9+
10+
#include <stdio.h>
11+
#include <string.h>
12+
13+
typedef enum ProbeMode {
14+
PROBE_SUCCESS,
15+
PROBE_FAILURE,
16+
} ProbeMode;
17+
18+
typedef struct ProbeState {
19+
ProbeMode mode;
20+
unsigned int check_count;
21+
unsigned int load_count;
22+
JSValue nested_module;
23+
} ProbeState;
24+
25+
static const char *mode_name(ProbeMode mode)
26+
{
27+
return mode == PROBE_SUCCESS ? "success" : "failure";
28+
}
29+
30+
static const char *boolean_name(int value)
31+
{
32+
return value ? "true" : "false";
33+
}
34+
35+
static const char *promise_state_name(JSPromiseStateEnum state)
36+
{
37+
switch (state) {
38+
case JS_PROMISE_PENDING:
39+
return "pending";
40+
case JS_PROMISE_FULFILLED:
41+
return "fulfilled";
42+
case JS_PROMISE_REJECTED:
43+
return "rejected";
44+
}
45+
return "unknown";
46+
}
47+
48+
static int check_attributes(JSContext *ctx, void *opaque,
49+
JSValueConst attributes)
50+
{
51+
ProbeState *state = opaque;
52+
const char *label = mode_name(state->mode);
53+
unsigned int check_index = state->check_count++;
54+
const char *source = state->mode == PROBE_SUCCESS
55+
? "export const marker = 99;"
56+
: "export const broken = ;";
57+
58+
printf("%s check[%u] attrs=%s has-exception=%s\n", label, check_index,
59+
JS_IsObject(attributes) ? "object" : "other",
60+
boolean_name(JS_HasException(ctx)));
61+
if (check_index != 0) {
62+
JS_ThrowInternalError(ctx, "unexpected recursive attribute checker");
63+
return -1;
64+
}
65+
66+
state->nested_module =
67+
JS_Eval(ctx, source, strlen(source), "same.js",
68+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
69+
printf("%s reentry result=%s has-exception=%s\n", label,
70+
JS_IsException(state->nested_module) ? "exception" : "module",
71+
boolean_name(JS_HasException(ctx)));
72+
return JS_IsException(state->nested_module) ? -1 : 0;
73+
}
74+
75+
static JSModuleDef *load_module(JSContext *ctx, const char *module_name,
76+
void *opaque, JSValueConst attributes)
77+
{
78+
ProbeState *state = opaque;
79+
80+
(void)attributes;
81+
printf("%s load[%u] name=%s\n", mode_name(state->mode),
82+
state->load_count++, module_name);
83+
JS_ThrowReferenceError(ctx, "unexpected loader call for '%s'",
84+
module_name);
85+
return NULL;
86+
}
87+
88+
static int run_success_scenario(void)
89+
{
90+
static const char source[] =
91+
"import { marker as cachedMarker } from './same.js' with { type: "
92+
"'probe' };"
93+
"export const marker = 41;"
94+
"globalThis.__cachePublicationResult = cachedMarker + 1;";
95+
JSRuntime *runtime = JS_NewRuntime();
96+
JSContext *context;
97+
ProbeState state = {
98+
.mode = PROBE_SUCCESS,
99+
.nested_module = JS_UNDEFINED,
100+
};
101+
JSValue outer_module = JS_UNDEFINED;
102+
JSValue evaluation = JS_UNDEFINED;
103+
JSValue global = JS_UNDEFINED;
104+
JSValue result = JS_UNDEFINED;
105+
int32_t result_number = 0;
106+
int modules_are_distinct;
107+
int status = 0;
108+
109+
if (!runtime)
110+
return 2;
111+
context = JS_NewContext(runtime);
112+
if (!context) {
113+
JS_FreeRuntime(runtime);
114+
return 2;
115+
}
116+
JS_SetModuleLoaderFunc2(runtime, NULL, load_module, check_attributes,
117+
&state);
118+
119+
outer_module = JS_Eval(
120+
context, source, sizeof(source) - 1, "same.js",
121+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
122+
modules_are_distinct =
123+
!JS_IsException(outer_module) &&
124+
!JS_IsException(state.nested_module) &&
125+
JS_VALUE_GET_PTR(outer_module) != JS_VALUE_GET_PTR(state.nested_module);
126+
printf("success outer result=%s distinct=%s checks=%u loads=%u "
127+
"has-exception=%s\n",
128+
JS_IsException(outer_module) ? "exception" : "module",
129+
boolean_name(modules_are_distinct), state.check_count,
130+
state.load_count, boolean_name(JS_HasException(context)));
131+
if (JS_IsException(outer_module) || !modules_are_distinct ||
132+
state.check_count != 1 || state.load_count != 0 ||
133+
JS_HasException(context)) {
134+
status = 1;
135+
goto done;
136+
}
137+
138+
evaluation = JS_EvalFunction(context, outer_module);
139+
outer_module = JS_UNDEFINED;
140+
if (JS_IsException(evaluation)) {
141+
status = 1;
142+
goto done;
143+
}
144+
global = JS_GetGlobalObject(context);
145+
result = JS_GetPropertyStr(context, global, "__cachePublicationResult");
146+
if (JS_IsException(result) ||
147+
JS_ToInt32(context, &result_number, result) < 0) {
148+
status = 1;
149+
goto done;
150+
}
151+
printf("success evaluate promise=%s result=%d has-exception=%s\n",
152+
promise_state_name(JS_PromiseState(context, evaluation)),
153+
result_number, boolean_name(JS_HasException(context)));
154+
if (JS_PromiseState(context, evaluation) != JS_PROMISE_FULFILLED ||
155+
result_number != 42 || JS_HasException(context)) {
156+
status = 1;
157+
}
158+
159+
done:
160+
JS_FreeValue(context, result);
161+
JS_FreeValue(context, global);
162+
JS_FreeValue(context, evaluation);
163+
JS_FreeValue(context, outer_module);
164+
JS_FreeValue(context, state.nested_module);
165+
JS_FreeContext(context);
166+
JS_FreeRuntime(runtime);
167+
return status;
168+
}
169+
170+
static int run_failure_scenario(void)
171+
{
172+
static const char source[] =
173+
"import './same.js' with { type: 'probe' };"
174+
"export const marker = 41;";
175+
static const char retry_source[] = "export const marker = 42;";
176+
JSRuntime *runtime = JS_NewRuntime();
177+
JSContext *context;
178+
ProbeState state = {
179+
.mode = PROBE_FAILURE,
180+
.nested_module = JS_UNDEFINED,
181+
};
182+
JSValue outer_module;
183+
JSValue exception;
184+
JSValue exception_name;
185+
JSValue retry_module;
186+
const char *exception_name_string;
187+
int status = 0;
188+
189+
if (!runtime)
190+
return 2;
191+
context = JS_NewContext(runtime);
192+
if (!context) {
193+
JS_FreeRuntime(runtime);
194+
return 2;
195+
}
196+
JS_SetModuleLoaderFunc2(runtime, NULL, load_module, check_attributes,
197+
&state);
198+
199+
outer_module = JS_Eval(
200+
context, source, sizeof(source) - 1, "same.js",
201+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
202+
printf("failure outer result=%s checks=%u loads=%u has-exception=%s\n",
203+
JS_IsException(outer_module) ? "exception" : "module",
204+
state.check_count, state.load_count,
205+
boolean_name(JS_HasException(context)));
206+
if (!JS_IsException(outer_module) || state.check_count != 1 ||
207+
state.load_count != 0 || !JS_HasException(context)) {
208+
JS_FreeValue(context, outer_module);
209+
status = 1;
210+
goto done;
211+
}
212+
213+
exception = JS_GetException(context);
214+
exception_name = JS_GetPropertyStr(context, exception, "name");
215+
exception_name_string = JS_ToCString(context, exception_name);
216+
printf("failure exception=%s has-exception-after-get=%s\n",
217+
exception_name_string ? exception_name_string : "<non-string>",
218+
boolean_name(JS_HasException(context)));
219+
if (!exception_name_string || strcmp(exception_name_string, "SyntaxError") ||
220+
JS_HasException(context)) {
221+
status = 1;
222+
}
223+
if (exception_name_string)
224+
JS_FreeCString(context, exception_name_string);
225+
JS_FreeValue(context, exception_name);
226+
JS_FreeValue(context, exception);
227+
228+
retry_module = JS_Eval(
229+
context, retry_source, sizeof(retry_source) - 1, "same.js",
230+
JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
231+
printf("failure retry result=%s checks=%u loads=%u has-exception=%s\n",
232+
JS_IsException(retry_module) ? "exception" : "module",
233+
state.check_count, state.load_count,
234+
boolean_name(JS_HasException(context)));
235+
if (JS_IsException(retry_module) || state.check_count != 1 ||
236+
state.load_count != 0 || JS_HasException(context)) {
237+
status = 1;
238+
}
239+
JS_FreeValue(context, retry_module);
240+
241+
done:
242+
JS_FreeValue(context, state.nested_module);
243+
JS_FreeContext(context);
244+
JS_FreeRuntime(runtime);
245+
return status;
246+
}
247+
248+
int main(void)
249+
{
250+
int status = run_success_scenario();
251+
252+
if (status)
253+
return status;
254+
return run_failure_scenario();
255+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
success check[0] attrs=object has-exception=false
2+
success reentry result=module has-exception=false
3+
success outer result=module distinct=true checks=1 loads=0 has-exception=false
4+
success evaluate promise=fulfilled result=42 has-exception=false
5+
failure check[0] attrs=object has-exception=false
6+
failure reentry result=exception has-exception=true
7+
failure outer result=exception checks=1 loads=0 has-exception=true
8+
failure exception=SyntaxError has-exception-after-get=false
9+
failure retry result=module checks=1 loads=0 has-exception=false

0 commit comments

Comments
 (0)