Skip to content

Commit 7f985d4

Browse files
committed
Fix ES module deserialization
A module's imports are not serialized along with the module itself and that left the deserialized module with dangling references. Fix that by checking the module cache first, the module loader second. A glaring problem with cache checking is that the cached module doesn't have to be the module it was at the time of serialization. Why not call out to the module loader right away? Because then a module can get loaded twice and that's arguably even worse. The alternative of serializing modules transitively doesn't work for C modules and is also prone to loading the same module twice. Fixes: #913
1 parent 53c3a2d commit 7f985d4

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

api-test.c

+55
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#endif
44
#include <assert.h>
55
#include <stdlib.h>
6+
#include <string.h>
67
#include "quickjs.h"
78

89
#define MAX_TIME 10
@@ -180,12 +181,66 @@ static void is_array(void)
180181
JS_FreeRuntime(rt);
181182
}
182183

184+
static int loader_calls;
185+
186+
static JSModuleDef *loader(JSContext *ctx, const char *name, void *opaque)
187+
{
188+
loader_calls++;
189+
assert(!strcmp(name, "b"));
190+
static const char code[] = "export function f(x){}";
191+
JSValue ret = JS_Eval(ctx, code, strlen(code), "b",
192+
JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY);
193+
assert(!JS_IsException(ret));
194+
JSModuleDef *m = JS_VALUE_GET_PTR(ret);
195+
assert(m);
196+
JS_FreeValue(ctx, ret);
197+
return m;
198+
}
199+
200+
static void module_serde(void)
201+
{
202+
JSRuntime *rt = JS_NewRuntime();
203+
JS_SetDumpFlags(rt, JS_DUMP_MODULE_RESOLVE);
204+
JS_SetModuleLoaderFunc(rt, NULL, loader, NULL);
205+
JSContext *ctx = JS_NewContext(rt);
206+
static const char code[] = "import {f} from 'b'; f()";
207+
assert(loader_calls == 0);
208+
JSValue mod = JS_Eval(ctx, code, strlen(code), "a",
209+
JS_EVAL_TYPE_MODULE|JS_EVAL_FLAG_COMPILE_ONLY);
210+
assert(loader_calls == 1);
211+
assert(!JS_IsException(mod));
212+
assert(JS_IsModule(mod));
213+
size_t len = 0;
214+
uint8_t *buf = JS_WriteObject(ctx, &len, mod,
215+
JS_WRITE_OBJ_BYTECODE|JS_WRITE_OBJ_REFERENCE);
216+
assert(buf);
217+
assert(len > 0);
218+
JS_FreeValue(ctx, mod);
219+
assert(loader_calls == 1);
220+
mod = JS_ReadObject(ctx, buf, len, JS_READ_OBJ_BYTECODE);
221+
free(buf);
222+
assert(loader_calls == 1); // 'b' is returned from cache
223+
assert(!JS_IsException(mod));
224+
JSValue ret = JS_EvalFunction(ctx, mod);
225+
assert(!JS_IsException(ret));
226+
assert(JS_IsPromise(ret));
227+
JSValue result = JS_PromiseResult(ctx, ret);
228+
assert(!JS_IsException(result));
229+
assert(JS_IsUndefined(result));
230+
JS_FreeValue(ctx, result);
231+
JS_FreeValue(ctx, ret);
232+
JS_FreeValue(ctx, mod);
233+
JS_FreeContext(ctx);
234+
JS_FreeRuntime(rt);
235+
}
236+
183237
int main(void)
184238
{
185239
sync_call();
186240
async_call();
187241
async_call_stack_overflow();
188242
raw_context_global_var();
189243
is_array();
244+
module_serde();
190245
return 0;
191246
}

quickjs.c

+5
Original file line numberDiff line numberDiff line change
@@ -35150,8 +35150,13 @@ static JSValue JS_ReadModule(BCReaderState *s)
3515035150
goto fail;
3515135151
for(i = 0; i < m->req_module_entries_count; i++) {
3515235152
JSReqModuleEntry *rme = &m->req_module_entries[i];
35153+
JSModuleDef **pm = &rme->module;
3515335154
if (bc_get_atom(s, &rme->module_name))
3515435155
goto fail;
35156+
*pm = js_host_resolve_imported_module_atom(s->ctx, m->module_name,
35157+
rme->module_name);
35158+
if (!*pm)
35159+
goto fail;
3515535160
}
3515635161
}
3515735162

quickjs.h

+5
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,11 @@ static inline bool JS_IsObject(JSValue v)
662662
return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT;
663663
}
664664

665+
static inline bool JS_IsModule(JSValue v)
666+
{
667+
return JS_VALUE_GET_TAG(v) == JS_TAG_MODULE;
668+
}
669+
665670
JS_EXTERN JSValue JS_Throw(JSContext *ctx, JSValue obj);
666671
JS_EXTERN JSValue JS_GetException(JSContext *ctx);
667672
JS_EXTERN bool JS_HasException(JSContext *ctx);

0 commit comments

Comments
 (0)