diff --git a/README.md b/README.md index d4d2b25..3449d42 100644 --- a/README.md +++ b/README.md @@ -1686,7 +1686,7 @@ import ( ) func main() { - // enable module import + // install the default file-based module loader rt := quickjs.NewRuntime(quickjs.WithModuleImport(true)) defer rt.Close() diff --git a/README_zh-cn.md b/README_zh-cn.md index 296c332..8ae991e 100644 --- a/README_zh-cn.md +++ b/README_zh-cn.md @@ -1681,7 +1681,7 @@ import ( ) func main() { - // 启用模块导入 + // 安装默认的基于文件系统的模块 loader rt := quickjs.NewRuntime(quickjs.WithModuleImport(true)) defer rt.Close() diff --git a/runtime.go b/runtime.go index 67488f8..215b980 100644 --- a/runtime.go +++ b/runtime.go @@ -476,12 +476,17 @@ func WithCanBlock(canBlock bool) Option { } } +// WithModuleImport installs or clears the default quickjs-libc file/module loader. +// When enabled, import/export resolution can load modules via quickjs-ng's default loader. +// When disabled, module resolution fails closed unless another loader is installed. func WithModuleImport(moduleImport bool) Option { return func(o *Options) { o.moduleImport = moduleImport } } +// WithStripInfo is retained for backward compatibility only. +// Deprecated: quickjs-ng does not expose a runtime-level strip-info API, so this option is a no-op. func WithStripInfo(strip int) Option { return func(o *Options) { o.strip = strip @@ -512,7 +517,7 @@ func NewRuntime(opts ...Option) *Runtime { maxStackSize: 0, canBlock: true, moduleImport: false, - strip: 1, + strip: 0, ownerGoroutineCheck: true, strictThreadAffinity: false, } @@ -894,7 +899,8 @@ func (r *Runtime) SetExecuteTimeout(timeout uint64) { r.interruptHandlerState.Store(nil) } -// SetStripInfo sets the strip info for the runtime. +// SetStripInfo is retained for backward compatibility only. +// Deprecated: quickjs-ng does not expose a runtime-level strip-info API, so this method is a no-op. func (r *Runtime) SetStripInfo(strip int) { if r == nil { return @@ -911,7 +917,8 @@ func (r *Runtime) SetStripInfo(strip int) { _ = strip } -// SetModuleImport sets whether the runtime supports module import. +// SetModuleImport installs or clears the default quickjs-libc file/module loader. +// When disabled, import resolution fails closed unless another loader is installed. func (r *Runtime) SetModuleImport(moduleImport bool) { if r == nil { return @@ -919,12 +926,16 @@ func (r *Runtime) SetModuleImport(moduleImport bool) { if !r.ensureOwnerAccess() { return } - r.mu.RLock() - defer r.mu.RUnlock() + r.mu.Lock() + defer r.mu.Unlock() if r.closed.Load() || r.ref == nil { return } - C.JS_SetModuleLoaderFunc2(r.ref, (*C.JSModuleNormalizeFunc)(unsafe.Pointer(nil)), (*C.JSModuleLoaderFunc2)(C.js_module_loader), (*C.JSModuleCheckSupportedImportAttributes)(C.js_module_check_attributes), unsafe.Pointer(nil)) + if moduleImport { + C.JS_SetModuleLoaderFunc2(r.ref, (*C.JSModuleNormalizeFunc)(unsafe.Pointer(nil)), (*C.JSModuleLoaderFunc2)(C.js_module_loader), (*C.JSModuleCheckSupportedImportAttributes)(C.js_module_check_attributes), unsafe.Pointer(nil)) + return + } + C.JS_SetModuleLoaderFunc2(r.ref, (*C.JSModuleNormalizeFunc)(unsafe.Pointer(nil)), nil, nil, unsafe.Pointer(nil)) } // SetInterruptHandler sets a user interrupt handler using simplified approach. diff --git a/runtime_test.go b/runtime_test.go index 29027e3..f91b255 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -908,6 +908,76 @@ func TestRuntimeAdvancedOptions(t *testing.T) { require.Equal(t, "GC test", result4.ToString()) } +func TestRuntimeWithModuleImportOptionInstallsDefaultLoader(t *testing.T) { + rt := NewRuntime(WithModuleImport(true)) + defer rt.Close() + + ctx := rt.NewContext() + require.NotNil(t, ctx) + defer ctx.Close() + + result := ctx.Eval(` + (async function() { + const mod = await import("./test/fib_module.js"); + return mod.fib(6); + })() + `, EvalAwait(true)) + defer result.Free() + + require.False(t, result.IsException()) + require.EqualValues(t, 8, result.ToInt32()) +} + +func TestRuntimeSetModuleImportToggle(t *testing.T) { + evalFibImport := func(ctx *Context) *Value { + return ctx.Eval(` + (async function() { + const mod = await import("./test/fib_module.js"); + return mod.fib(6); + })() + `, EvalAwait(true)) + } + + requireImportFailure := func(ctx *Context) { + result := evalFibImport(ctx) + require.NotNil(t, result) + defer result.Free() + require.True(t, result.IsException()) + + err := ctx.Exception() + require.Error(t, err) + require.Contains(t, strings.ToLower(err.Error()), "could not load module") + } + + requireImportSuccess := func(ctx *Context) { + result := evalFibImport(ctx) + require.NotNil(t, result) + defer result.Free() + require.False(t, result.IsException()) + require.EqualValues(t, 8, result.ToInt32()) + } + + rt := NewRuntime() + defer rt.Close() + + ctx := rt.NewContext() + require.NotNil(t, ctx) + requireImportFailure(ctx) + ctx.Close() + + rt.SetModuleImport(true) + ctx = rt.NewContext() + require.NotNil(t, ctx) + requireImportSuccess(ctx) + ctx.Close() + + rt.SetModuleImport(false) + ctx = rt.NewContext() + require.NotNil(t, ctx) + defer ctx.Close() + requireImportFailure(ctx) +} + func TestRuntimeTimeoutOpaqueLifecycle(t *testing.T) { base := timeoutOpaqueCount()