From 7e2ba38b52b122661ae72e373763455dfb3375a9 Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 12:43:04 +0200 Subject: [PATCH 1/7] Move moduleVUImpl to be part of the js.VU --- js/bundle.go | 12 +++++------- js/bundle_test.go | 18 +++++++++--------- js/initcontext.go | 15 ++++++++++++--- js/initcontext_test.go | 24 ++++++++++++------------ js/runner.go | 6 +++++- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/js/bundle.go b/js/bundle.go index 9c5e4e2b414..d30003bce0a 100644 --- a/js/bundle.go +++ b/js/bundle.go @@ -252,22 +252,20 @@ func (b *Bundle) getExports(logger logrus.FieldLogger, rt *goja.Runtime, options } // Instantiate creates a new runtime from this bundle. -func (b *Bundle) Instantiate(logger logrus.FieldLogger, vuID uint64) (bi *BundleInstance, instErr error) { - // TODO: actually use a real context here, so that the instantiation can be killed - // Placeholder for a real context. - ctxPtr := new(context.Context) - +func (b *Bundle) Instantiate( + logger logrus.FieldLogger, vuID uint64, vuImpl *moduleVUImpl, +) (bi *BundleInstance, instErr error) { // Instantiate the bundle into a new VM using a bound init context. This uses a context with a // runtime, but no state, to allow module-provided types to function within the init context. rt := goja.New() - init := newBoundInitContext(b.BaseInitContext, ctxPtr, rt) + init := newBoundInitContext(b.BaseInitContext, rt, vuImpl) if err := b.instantiate(logger, rt, init, vuID); err != nil { return nil, err } bi = &BundleInstance{ Runtime: rt, - Context: ctxPtr, + Context: vuImpl.ctxPtr, exports: make(map[string]goja.Callable), env: b.RuntimeOptions.Env, } diff --git a/js/bundle_test.go b/js/bundle_test.go index 56bb107bfd1..6f8f8b9875b 100644 --- a/js/bundle_test.go +++ b/js/bundle_test.go @@ -505,7 +505,7 @@ func TestNewBundleFromArchive(t *testing.T) { logger := testutils.NewLogger(t) checkBundle := func(t *testing.T, b *Bundle) { assert.Equal(t, lib.Options{VUs: null.IntFrom(12345)}, b.Options) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) val, err := bi.exports[consts.DefaultFn](goja.Undefined()) require.NoError(t, err) @@ -598,7 +598,7 @@ func TestNewBundleFromArchive(t *testing.T) { } b, err := NewBundleFromArchive(logger, arc, lib.RuntimeOptions{}, metrics.NewRegistry()) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) val, err := bi.exports[consts.DefaultFn](goja.Undefined()) require.NoError(t, err) @@ -742,7 +742,7 @@ func TestOpen(t *testing.T) { for source, b := range map[string]*Bundle{"source": sourceBundle, "archive": arcBundle} { b := b t.Run(source, func(t *testing.T) { - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) v, err := bi.exports[consts.DefaultFn](goja.Undefined()) require.NoError(t, err) @@ -778,7 +778,7 @@ func TestBundleInstantiate(t *testing.T) { require.NoError(t, err) logger := testutils.NewLogger(t) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) v, err := bi.exports[consts.DefaultFn](goja.Undefined()) if assert.NoError(t, err) { @@ -799,7 +799,7 @@ func TestBundleInstantiate(t *testing.T) { require.NoError(t, err) logger := testutils.NewLogger(t) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) bi.Runtime.Set("val", false) v, err := bi.exports[consts.DefaultFn](goja.Undefined()) @@ -821,7 +821,7 @@ func TestBundleInstantiate(t *testing.T) { require.NoError(t, err) logger := testutils.NewLogger(t) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) // Ensure `options` properties are correctly marshalled jsOptions := bi.Runtime.Get("options").ToObject(bi.Runtime) @@ -833,7 +833,7 @@ func TestBundleInstantiate(t *testing.T) { // Ensure options propagate correctly from outside to the script optOrig := b.Options.VUs b.Options.VUs = null.IntFrom(10) - bi2, err := b.Instantiate(logger, 0) + bi2, err := b.Instantiate(logger, 0, newModuleVUImpl()) assert.NoError(t, err) jsOptions = bi2.Runtime.Get("options").ToObject(bi2.Runtime) vus = jsOptions.Get("vus").Export() @@ -869,7 +869,7 @@ func TestBundleEnv(t *testing.T) { assert.Equal(t, "1", b.RuntimeOptions.Env["TEST_A"]) assert.Equal(t, "", b.RuntimeOptions.Env["TEST_B"]) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) if assert.NoError(t, err) { _, err := bi.exports[consts.DefaultFn](goja.Undefined()) assert.NoError(t, err) @@ -906,7 +906,7 @@ func TestBundleNotSharable(t *testing.T) { t.Run(name, func(t *testing.T) { t.Parallel() for i := 0; i < vus; i++ { - bi, err := b.Instantiate(logger, uint64(i)) + bi, err := b.Instantiate(logger, uint64(i), newModuleVUImpl()) require.NoError(t, err) for j := 0; j < iters; j++ { bi.Runtime.Set("__ITER", j) diff --git a/js/initcontext.go b/js/initcontext.go index 41182f170a6..9553c36bc8f 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -69,6 +69,7 @@ type InitContext struct { runtime *goja.Runtime compiler *compiler.Compiler + moduleVUImpl *moduleVUImpl // Pointer to a context that bridged modules are invoked with. ctxPtr *context.Context @@ -101,10 +102,11 @@ func NewInitContext( compatibilityMode: compatMode, logger: logger, modules: getJSModules(), + moduleVUImpl: &moduleVUImpl{ctxPtr: ctxPtr}, } } -func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Runtime) *InitContext { +func newBoundInitContext(base *InitContext, rt *goja.Runtime, vuImpl *moduleVUImpl) *InitContext { // we don't copy the exports as otherwise they will be shared and we don't want this. // this means that all the files will be executed again but once again only once per compilation // of the main file. @@ -117,7 +119,7 @@ func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Ru } return &InitContext{ runtime: rt, - ctxPtr: ctxPtr, + ctxPtr: vuImpl.ctxPtr, // remove this filesystems: base.filesystems, pwd: base.pwd, @@ -127,6 +129,7 @@ func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Ru compatibilityMode: base.compatibilityMode, logger: base.logger, modules: base.modules, + moduleVUImpl: vuImpl, } } @@ -158,6 +161,12 @@ type moduleVUImpl struct { // we can technically put lib.State here as well as anything else } +func newModuleVUImpl() *moduleVUImpl { + return &moduleVUImpl{ + ctxPtr: new(context.Context), + } +} + func (m *moduleVUImpl) Context() context.Context { return *m.ctxPtr } @@ -203,7 +212,7 @@ func (i *InitContext) requireModule(name string) (goja.Value, error) { return nil, fmt.Errorf("unknown module: %s", name) } if m, ok := mod.(modules.Module); ok { - instance := m.NewModuleInstance(&moduleVUImpl{ctxPtr: i.ctxPtr}) + instance := m.NewModuleInstance(i.moduleVUImpl) return i.runtime.ToValue(toESModuleExports(instance.Exports())), nil } if perInstance, ok := mod.(modules.HasModuleInstancePerVU); ok { diff --git a/js/initcontext_test.go b/js/initcontext_test.go index 1d05efcc2ff..488df696600 100644 --- a/js/initcontext_test.go +++ b/js/initcontext_test.go @@ -70,7 +70,7 @@ func TestInitContextRequire(t *testing.T) { return } - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) if !assert.NoError(t, err, "instance error") { return } @@ -100,7 +100,7 @@ func TestInitContextRequire(t *testing.T) { `) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) exports := bi.Runtime.Get("exports").ToObject(bi.Runtime) @@ -210,7 +210,7 @@ func TestInitContextRequire(t *testing.T) { assert.Contains(t, b.BaseInitContext.programs, "file://"+constPath) } - _, err = b.Instantiate(logger, 0) + _, err = b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) }) } @@ -234,7 +234,7 @@ func TestInitContextRequire(t *testing.T) { b, err := getSimpleBundle(t, "/script.js", data, fs) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) _, err = bi.exports[consts.DefaultFn](goja.Undefined()) assert.NoError(t, err) @@ -264,7 +264,7 @@ func createAndReadFile(t *testing.T, file string, content []byte, expectedLength return nil, err } - bi, err := b.Instantiate(testutils.NewLogger(t), 0) + bi, err := b.Instantiate(testutils.NewLogger(t), 0, newModuleVUImpl()) if !assert.NoError(t, err) { return nil, err } @@ -377,7 +377,7 @@ func TestRequestWithBinaryFile(t *testing.T) { `, srv.URL), fs) require.NoError(t, err) - bi, err := b.Instantiate(testutils.NewLogger(t), 0) + bi, err := b.Instantiate(testutils.NewLogger(t), 0, newModuleVUImpl()) assert.NoError(t, err) root, err := lib.NewGroup("", nil) @@ -526,7 +526,7 @@ func TestRequestWithMultipleBinaryFiles(t *testing.T) { `, srv.URL), fs) require.NoError(t, err) - bi, err := b.Instantiate(testutils.NewLogger(t), 0) + bi, err := b.Instantiate(testutils.NewLogger(t), 0, newModuleVUImpl()) assert.NoError(t, err) root, err := lib.NewGroup("", nil) @@ -579,7 +579,7 @@ func TestInitContextVU(t *testing.T) { export default function() { return vu; } `) require.NoError(t, err) - bi, err := b.Instantiate(testutils.NewLogger(t), 5) + bi, err := b.Instantiate(testutils.NewLogger(t), 5, newModuleVUImpl()) require.NoError(t, err) v, err := bi.exports[consts.DefaultFn](goja.Undefined()) require.NoError(t, err) @@ -612,7 +612,7 @@ export default function(){ b, err := getSimpleBundle(t, "/script.js", data, fs) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) _, err = bi.exports[consts.DefaultFn](goja.Undefined()) require.Error(t, err) @@ -643,7 +643,7 @@ export default function () { b, err := getSimpleBundle(t, "/script.js", data, fs) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) _, err = bi.exports[consts.DefaultFn](goja.Undefined()) require.Error(t, err) @@ -675,7 +675,7 @@ export default function () { b, err := getSimpleBundle(t, "/script.js", data, fs) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) _, err = bi.exports[consts.DefaultFn](goja.Undefined()) require.Error(t, err) @@ -706,7 +706,7 @@ export default function () { b, err := getSimpleBundle(t, "/script.js", data, fs) require.NoError(t, err) - bi, err := b.Instantiate(logger, 0) + bi, err := b.Instantiate(logger, 0, newModuleVUImpl()) require.NoError(t, err) _, err = bi.exports[consts.DefaultFn](goja.Undefined()) require.Error(t, err) diff --git a/js/runner.go b/js/runner.go index 65a1e66eb5b..05665fa2058 100644 --- a/js/runner.go +++ b/js/runner.go @@ -150,7 +150,8 @@ func (r *Runner) NewVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC // nolint:funlen func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleContainer) (*VU, error) { // Instantiate a new bundle, make a VU out of it. - bi, err := r.Bundle.Instantiate(r.Logger, idLocal) + moduleVUImpl := &moduleVUImpl{ctxPtr: new(context.Context)} + bi, err := r.Bundle.Instantiate(r.Logger, idLocal, moduleVUImpl) if err != nil { return nil, err } @@ -238,6 +239,7 @@ func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC BPool: bpool.NewBufferPool(100), Samples: samplesOut, scenarioIter: make(map[string]uint64), + moduleVUImpl: moduleVUImpl, } vu.state = &lib.State{ @@ -584,6 +586,8 @@ type VU struct { state *lib.State // count of iterations executed by this VU in each scenario scenarioIter map[string]uint64 + + moduleVUImpl *moduleVUImpl } // Verify that interfaces are implemented From b4764e8dd6c209570c948faccb1b12f2a475b3d0 Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 13:01:05 +0200 Subject: [PATCH 2/7] Don't use context to get vuModuleImpl fields --- js/bundle.go | 8 +++++--- js/initcontext.go | 23 +++++++++++++---------- js/initcontext_test.go | 14 ++++++++------ js/runner.go | 1 + 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/js/bundle.go b/js/bundle.go index d30003bce0a..b3410ed339e 100644 --- a/js/bundle.go +++ b/js/bundle.go @@ -257,12 +257,13 @@ func (b *Bundle) Instantiate( ) (bi *BundleInstance, instErr error) { // Instantiate the bundle into a new VM using a bound init context. This uses a context with a // runtime, but no state, to allow module-provided types to function within the init context. - rt := goja.New() - init := newBoundInitContext(b.BaseInitContext, rt, vuImpl) - if err := b.instantiate(logger, rt, init, vuID); err != nil { + vuImpl.runtime = goja.New() + init := newBoundInitContext(b.BaseInitContext, vuImpl) + if err := b.instantiate(logger, vuImpl.runtime, init, vuID); err != nil { return nil, err } + rt := vuImpl.runtime bi = &BundleInstance{ Runtime: rt, Context: vuImpl.ctxPtr, @@ -327,6 +328,7 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * CWD: init.pwd, Registry: b.registry, } + init.moduleVUImpl.initEnv = initenv ctx := common.WithInitEnv(context.Background(), initenv) *init.ctxPtr = common.WithRuntime(ctx, rt) unbindInit := common.BindToGlobal(rt, common.Bind(rt, init, init.ctxPtr)) diff --git a/js/initcontext.go b/js/initcontext.go index 9553c36bc8f..2a8174f3c57 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -102,11 +102,13 @@ func NewInitContext( compatibilityMode: compatMode, logger: logger, modules: getJSModules(), - moduleVUImpl: &moduleVUImpl{ctxPtr: ctxPtr}, + moduleVUImpl: &moduleVUImpl{ + ctxPtr: ctxPtr, runtime: rt, + }, } } -func newBoundInitContext(base *InitContext, rt *goja.Runtime, vuImpl *moduleVUImpl) *InitContext { +func newBoundInitContext(base *InitContext, vuImpl *moduleVUImpl) *InitContext { // we don't copy the exports as otherwise they will be shared and we don't want this. // this means that all the files will be executed again but once again only once per compilation // of the main file. @@ -118,8 +120,8 @@ func newBoundInitContext(base *InitContext, rt *goja.Runtime, vuImpl *moduleVUIm } } return &InitContext{ - runtime: rt, - ctxPtr: vuImpl.ctxPtr, // remove this + runtime: vuImpl.runtime, // remove this + ctxPtr: vuImpl.ctxPtr, // remove this filesystems: base.filesystems, pwd: base.pwd, @@ -155,10 +157,11 @@ func (i *InitContext) Require(arg string) goja.Value { } } -// TODO this likely should just be part of the initialized VU or at least to take stuff directly from it. type moduleVUImpl struct { - ctxPtr *context.Context - // we can technically put lib.State here as well as anything else + ctxPtr *context.Context + initEnv *common.InitEnvironment + state *lib.State + runtime *goja.Runtime } func newModuleVUImpl() *moduleVUImpl { @@ -172,15 +175,15 @@ func (m *moduleVUImpl) Context() context.Context { } func (m *moduleVUImpl) InitEnv() *common.InitEnvironment { - return common.GetInitEnv(*m.ctxPtr) // TODO thread it correctly instead + return m.initEnv } func (m *moduleVUImpl) State() *lib.State { - return lib.GetState(*m.ctxPtr) // TODO thread it correctly instead + return m.state } func (m *moduleVUImpl) Runtime() *goja.Runtime { - return common.GetRuntime(*m.ctxPtr) // TODO thread it correctly instead + return m.runtime } func toESModuleExports(exp modules.Exports) interface{} { diff --git a/js/initcontext_test.go b/js/initcontext_test.go index 488df696600..67d1189d988 100644 --- a/js/initcontext_test.go +++ b/js/initcontext_test.go @@ -377,7 +377,8 @@ func TestRequestWithBinaryFile(t *testing.T) { `, srv.URL), fs) require.NoError(t, err) - bi, err := b.Instantiate(testutils.NewLogger(t), 0, newModuleVUImpl()) + vuImpl := newModuleVUImpl() + bi, err := b.Instantiate(testutils.NewLogger(t), 0, vuImpl) assert.NoError(t, err) root, err := lib.NewGroup("", nil) @@ -389,7 +390,7 @@ func TestRequestWithBinaryFile(t *testing.T) { registry := metrics.NewRegistry() builtinMetrics := metrics.RegisterBuiltinMetrics(registry) - state := &lib.State{ + vuImpl.state = &lib.State{ Options: lib.Options{}, Logger: logger, Group: root, @@ -411,7 +412,7 @@ func TestRequestWithBinaryFile(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ctx = lib.WithState(ctx, state) + ctx = lib.WithState(ctx, vuImpl.state) ctx = common.WithRuntime(ctx, bi.Runtime) *bi.Context = ctx @@ -526,7 +527,8 @@ func TestRequestWithMultipleBinaryFiles(t *testing.T) { `, srv.URL), fs) require.NoError(t, err) - bi, err := b.Instantiate(testutils.NewLogger(t), 0, newModuleVUImpl()) + vuImpl := newModuleVUImpl() + bi, err := b.Instantiate(testutils.NewLogger(t), 0, vuImpl) assert.NoError(t, err) root, err := lib.NewGroup("", nil) @@ -538,7 +540,7 @@ func TestRequestWithMultipleBinaryFiles(t *testing.T) { registry := metrics.NewRegistry() builtinMetrics := metrics.RegisterBuiltinMetrics(registry) - state := &lib.State{ + vuImpl.state = &lib.State{ Options: lib.Options{}, Logger: logger, Group: root, @@ -560,7 +562,7 @@ func TestRequestWithMultipleBinaryFiles(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - ctx = lib.WithState(ctx, state) + ctx = lib.WithState(ctx, vuImpl.state) ctx = common.WithRuntime(ctx, bi.Runtime) *bi.Context = ctx diff --git a/js/runner.go b/js/runner.go index 05665fa2058..c76b2a7def4 100644 --- a/js/runner.go +++ b/js/runner.go @@ -258,6 +258,7 @@ func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC Group: r.defaultGroup, BuiltinMetrics: r.builtinMetrics, } + vu.moduleVUImpl.state = vu.state vu.Runtime.Set("console", common.Bind(vu.Runtime, vu.Console, vu.Context)) // This is here mostly so if someone tries they get a nice message From 54f17869fb031f031936abaae1fb14f68b8f645a Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 13:06:14 +0200 Subject: [PATCH 3/7] Drop runtime field in init context --- js/initcontext.go | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/js/initcontext.go b/js/initcontext.go index 2a8174f3c57..bb005ee6cdc 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -66,7 +66,6 @@ const openCantBeUsedOutsideInitContextMsg = `The "open()" function is only avail // TODO: refactor most/all of this state away, use common.InitEnvironment instead type InitContext struct { // Bound runtime; used to instantiate objects. - runtime *goja.Runtime compiler *compiler.Compiler moduleVUImpl *moduleVUImpl @@ -93,7 +92,6 @@ func NewInitContext( ctxPtr *context.Context, filesystems map[string]afero.Fs, pwd *url.URL, ) *InitContext { return &InitContext{ - runtime: rt, compiler: c, ctxPtr: ctxPtr, filesystems: filesystems, @@ -120,8 +118,7 @@ func newBoundInitContext(base *InitContext, vuImpl *moduleVUImpl) *InitContext { } } return &InitContext{ - runtime: vuImpl.runtime, // remove this - ctxPtr: vuImpl.ctxPtr, // remove this + ctxPtr: vuImpl.ctxPtr, // remove this filesystems: base.filesystems, pwd: base.pwd, @@ -144,14 +141,14 @@ func (i *InitContext) Require(arg string) goja.Value { // shadows attempts to name your own modules this. v, err := i.requireModule(arg) if err != nil { - common.Throw(i.runtime, err) + common.Throw(i.moduleVUImpl.runtime, err) } return v default: // Fall back to loading from the filesystem. v, err := i.requireFile(arg) if err != nil { - common.Throw(i.runtime, err) + common.Throw(i.moduleVUImpl.runtime, err) } return v } @@ -216,13 +213,13 @@ func (i *InitContext) requireModule(name string) (goja.Value, error) { } if m, ok := mod.(modules.Module); ok { instance := m.NewModuleInstance(i.moduleVUImpl) - return i.runtime.ToValue(toESModuleExports(instance.Exports())), nil + return i.moduleVUImpl.runtime.ToValue(toESModuleExports(instance.Exports())), nil } if perInstance, ok := mod.(modules.HasModuleInstancePerVU); ok { mod = perInstance.NewModuleInstancePerVU() } - return i.runtime.ToValue(common.Bind(i.runtime, mod, i.ctxPtr)), nil + return i.moduleVUImpl.runtime.ToValue(common.Bind(i.moduleVUImpl.runtime, mod, i.ctxPtr)), nil } func (i *InitContext) requireFile(name string) (goja.Value, error) { @@ -244,8 +241,8 @@ func (i *InitContext) requireFile(name string) (goja.Value, error) { } i.pwd = loader.Dir(fileURL) defer func() { i.pwd = pwd }() - exports := i.runtime.NewObject() - pgm.module = i.runtime.NewObject() + exports := i.moduleVUImpl.runtime.NewObject() + pgm.module = i.moduleVUImpl.runtime.NewObject() _ = pgm.module.Set("exports", exports) if pgm.pgm == nil { @@ -267,7 +264,7 @@ func (i *InitContext) requireFile(name string) (goja.Value, error) { i.programs[fileURL.String()] = pgm // Run the program. - f, err := i.runtime.RunProgram(pgm.pgm) + f, err := i.moduleVUImpl.runtime.RunProgram(pgm.pgm) if err != nil { delete(i.programs, fileURL.String()) return goja.Undefined(), err @@ -318,10 +315,10 @@ func (i *InitContext) Open(ctx context.Context, filename string, args ...string) } if len(args) > 0 && args[0] == "b" { - ab := i.runtime.NewArrayBuffer(data) - return i.runtime.ToValue(&ab), nil + ab := i.moduleVUImpl.runtime.NewArrayBuffer(data) + return i.moduleVUImpl.runtime.ToValue(&ab), nil } - return i.runtime.ToValue(string(data)), nil + return i.moduleVUImpl.runtime.ToValue(string(data)), nil } func readFile(fileSystem afero.Fs, filename string) (data []byte, err error) { From 73772051784666e473c04f983a578e400131f925 Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 13:12:41 +0200 Subject: [PATCH 4/7] Drop ctxPtr field in init context --- js/bundle.go | 8 ++++---- js/initcontext.go | 7 +------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/js/bundle.go b/js/bundle.go index b3410ed339e..bc3585d8295 100644 --- a/js/bundle.go +++ b/js/bundle.go @@ -314,7 +314,7 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * } rt.Set("__ENV", env) rt.Set("__VU", vuID) - rt.Set("console", common.Bind(rt, newConsole(logger), init.ctxPtr)) + rt.Set("console", common.Bind(rt, newConsole(logger), init.moduleVUImpl.ctxPtr)) if init.compatibilityMode == lib.CompatibilityModeExtended { rt.Set("global", rt.GlobalObject()) @@ -330,8 +330,8 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * } init.moduleVUImpl.initEnv = initenv ctx := common.WithInitEnv(context.Background(), initenv) - *init.ctxPtr = common.WithRuntime(ctx, rt) - unbindInit := common.BindToGlobal(rt, common.Bind(rt, init, init.ctxPtr)) + *init.moduleVUImpl.ctxPtr = common.WithRuntime(ctx, rt) + unbindInit := common.BindToGlobal(rt, common.Bind(rt, init, init.moduleVUImpl.ctxPtr)) if _, err := rt.RunProgram(b.Program); err != nil { var exception *goja.Exception if errors.As(err, &exception) { @@ -340,7 +340,7 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * return err } unbindInit() - *init.ctxPtr = nil + *init.moduleVUImpl.ctxPtr = nil // If we've already initialized the original VU init context, forbid // any subsequent VUs to open new files diff --git a/js/initcontext.go b/js/initcontext.go index bb005ee6cdc..3e9ab30fdf8 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -69,8 +69,6 @@ type InitContext struct { compiler *compiler.Compiler moduleVUImpl *moduleVUImpl - // Pointer to a context that bridged modules are invoked with. - ctxPtr *context.Context // Filesystem to load files and scripts from with the map key being the scheme filesystems map[string]afero.Fs @@ -93,7 +91,6 @@ func NewInitContext( ) *InitContext { return &InitContext{ compiler: c, - ctxPtr: ctxPtr, filesystems: filesystems, pwd: pwd, programs: make(map[string]programWithSource), @@ -118,8 +115,6 @@ func newBoundInitContext(base *InitContext, vuImpl *moduleVUImpl) *InitContext { } } return &InitContext{ - ctxPtr: vuImpl.ctxPtr, // remove this - filesystems: base.filesystems, pwd: base.pwd, compiler: base.compiler, @@ -219,7 +214,7 @@ func (i *InitContext) requireModule(name string) (goja.Value, error) { mod = perInstance.NewModuleInstancePerVU() } - return i.moduleVUImpl.runtime.ToValue(common.Bind(i.moduleVUImpl.runtime, mod, i.ctxPtr)), nil + return i.moduleVUImpl.runtime.ToValue(common.Bind(i.moduleVUImpl.runtime, mod, i.moduleVUImpl.ctxPtr)), nil } func (i *InitContext) requireFile(name string) (goja.Value, error) { From 0752d37c8eb5b4a5b0e46bb6f647dfc053671450 Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 13:26:19 +0200 Subject: [PATCH 5/7] Drop usage of common.Bind for initContext functions --- js/bundle.go | 5 ++++- js/initcontext.go | 4 ++-- js/initcontext_test.go | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/js/bundle.go b/js/bundle.go index bc3585d8295..2048c0b250a 100644 --- a/js/bundle.go +++ b/js/bundle.go @@ -331,7 +331,10 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * init.moduleVUImpl.initEnv = initenv ctx := common.WithInitEnv(context.Background(), initenv) *init.moduleVUImpl.ctxPtr = common.WithRuntime(ctx, rt) - unbindInit := common.BindToGlobal(rt, common.Bind(rt, init, init.moduleVUImpl.ctxPtr)) + unbindInit := common.BindToGlobal(rt, map[string]interface{}{ + "require": init.Require, + "open": init.Open, + }) if _, err := rt.RunProgram(b.Program); err != nil { var exception *goja.Exception if errors.As(err, &exception) { diff --git a/js/initcontext.go b/js/initcontext.go index 3e9ab30fdf8..3eab0d553e7 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -282,8 +282,8 @@ func (i *InitContext) compileImport(src, filename string) (*goja.Program, error) // Open implements open() in the init context and will read and return the // contents of a file. If the second argument is "b" it returns an ArrayBuffer // instance, otherwise a string representation. -func (i *InitContext) Open(ctx context.Context, filename string, args ...string) (goja.Value, error) { - if lib.GetState(ctx) != nil { +func (i *InitContext) Open(filename string, args ...string) (goja.Value, error) { + if i.moduleVUImpl.State() != nil { return nil, errors.New(openCantBeUsedOutsideInitContextMsg) } diff --git a/js/initcontext_test.go b/js/initcontext_test.go index 67d1189d988..b9cea76ffce 100644 --- a/js/initcontext_test.go +++ b/js/initcontext_test.go @@ -137,7 +137,8 @@ func TestInitContextRequire(t *testing.T) { fs := afero.NewMemMapFs() assert.NoError(t, afero.WriteFile(fs, "/file.js", []byte(`throw new Error("aaaa")`), 0o755)) _, err := getSimpleBundle(t, "/script.js", `import "/file.js"; export default function() {}`, fs) - assert.EqualError(t, err, "Error: aaaa\n\tat file:///file.js:2:7(3)\n\tat reflect.methodValueCall (native)\n\tat file:///script.js:1:0(14)\n") + assert.EqualError(t, err, + "Error: aaaa\n\tat file:///file.js:2:7(3)\n\tat go.k6.io/k6/js.(*InitContext).Require-fm (native)\n\tat file:///script.js:1:0(14)\n") }) imports := map[string]struct { From c87839116c71287f148b5fab61df1ce2e8038212 Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 13:33:27 +0200 Subject: [PATCH 6/7] Drop common.Bind for console through dropping the context there There really isn't a reason why it should use the context - if the context is done the whole runtime is interrupted so it won't even get to that code --- js/bundle.go | 2 +- js/console.go | 31 +++++++++++-------------------- js/console_test.go | 15 +-------------- js/runner.go | 2 +- 4 files changed, 14 insertions(+), 36 deletions(-) diff --git a/js/bundle.go b/js/bundle.go index 2048c0b250a..dc6bac545fb 100644 --- a/js/bundle.go +++ b/js/bundle.go @@ -314,7 +314,7 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init * } rt.Set("__ENV", env) rt.Set("__VU", vuID) - rt.Set("console", common.Bind(rt, newConsole(logger), init.moduleVUImpl.ctxPtr)) + _ = rt.Set("console", newConsole(logger)) if init.compatibilityMode == lib.CompatibilityModeExtended { rt.Set("global", rt.GlobalObject()) diff --git a/js/console.go b/js/console.go index 9937bb87ac5..997db6baf11 100644 --- a/js/console.go +++ b/js/console.go @@ -21,7 +21,6 @@ package js import ( - "context" "os" "strings" @@ -53,15 +52,7 @@ func newFileConsole(filepath string, formatter logrus.Formatter) (*console, erro return &console{l}, nil } -func (c console) log(ctx *context.Context, level logrus.Level, msgobj goja.Value, args ...goja.Value) { - if ctx != nil && *ctx != nil { - select { - case <-(*ctx).Done(): - return - default: - } - } - +func (c console) log(level logrus.Level, msgobj goja.Value, args ...goja.Value) { msg := msgobj.String() if len(args) > 0 { strs := make([]string, 1+len(args)) @@ -84,22 +75,22 @@ func (c console) log(ctx *context.Context, level logrus.Level, msgobj goja.Value } } -func (c console) Log(ctx *context.Context, msg goja.Value, args ...goja.Value) { - c.Info(ctx, msg, args...) +func (c console) Log(msg goja.Value, args ...goja.Value) { + c.Info(msg, args...) } -func (c console) Debug(ctx *context.Context, msg goja.Value, args ...goja.Value) { - c.log(ctx, logrus.DebugLevel, msg, args...) +func (c console) Debug(msg goja.Value, args ...goja.Value) { + c.log(logrus.DebugLevel, msg, args...) } -func (c console) Info(ctx *context.Context, msg goja.Value, args ...goja.Value) { - c.log(ctx, logrus.InfoLevel, msg, args...) +func (c console) Info(msg goja.Value, args ...goja.Value) { + c.log(logrus.InfoLevel, msg, args...) } -func (c console) Warn(ctx *context.Context, msg goja.Value, args ...goja.Value) { - c.log(ctx, logrus.WarnLevel, msg, args...) +func (c console) Warn(msg goja.Value, args ...goja.Value) { + c.log(logrus.WarnLevel, msg, args...) } -func (c console) Error(ctx *context.Context, msg goja.Value, args ...goja.Value) { - c.log(ctx, logrus.ErrorLevel, msg, args...) +func (c console) Error(msg goja.Value, args ...goja.Value) { + c.log(logrus.ErrorLevel, msg, args...) } diff --git a/js/console_test.go b/js/console_test.go index f816adb74d6..51827517022 100644 --- a/js/console_test.go +++ b/js/console_test.go @@ -48,12 +48,8 @@ func TestConsoleContext(t *testing.T) { rt := goja.New() rt.SetFieldNameMapper(common.FieldNameMapper{}) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - ctxPtr := &ctx - logger, hook := logtest.NewNullLogger() - rt.Set("console", common.Bind(rt, &console{logger}, ctxPtr)) + _ = rt.Set("console", &console{logger}) _, err := rt.RunString(`console.log("a")`) assert.NoError(t, err) @@ -61,20 +57,11 @@ func TestConsoleContext(t *testing.T) { assert.Equal(t, "a", entry.Message) } - ctx, cancel = context.WithCancel(context.Background()) - *ctxPtr = ctx _, err = rt.RunString(`console.log("b")`) assert.NoError(t, err) if entry := hook.LastEntry(); assert.NotNil(t, entry) { assert.Equal(t, "b", entry.Message) } - - cancel() - _, err = rt.RunString(`console.log("c")`) - assert.NoError(t, err) - if entry := hook.LastEntry(); assert.NotNil(t, entry) { - assert.Equal(t, "b", entry.Message) - } } func getSimpleRunner(tb testing.TB, filename, data string, opts ...interface{}) (*Runner, error) { diff --git a/js/runner.go b/js/runner.go index c76b2a7def4..e7c09324e08 100644 --- a/js/runner.go +++ b/js/runner.go @@ -259,7 +259,7 @@ func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC BuiltinMetrics: r.builtinMetrics, } vu.moduleVUImpl.state = vu.state - vu.Runtime.Set("console", common.Bind(vu.Runtime, vu.Console, vu.Context)) + _ = vu.Runtime.Set("console", vu.Console) // This is here mostly so if someone tries they get a nice message // instead of "Value is not an object: undefined ..." From 8e9ecd0bd58d6acf5b10214586d55520d54745cf Mon Sep 17 00:00:00 2001 From: Mihail Stoykov Date: Fri, 4 Feb 2022 13:37:20 +0200 Subject: [PATCH 7/7] Remove one unneeded usage of common.BindToGlobal --- js/runner.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/js/runner.go b/js/runner.go index e7c09324e08..6f509aa9911 100644 --- a/js/runner.go +++ b/js/runner.go @@ -263,11 +263,10 @@ func (r *Runner) newVU(idLocal, idGlobal uint64, samplesOut chan<- stats.SampleC // This is here mostly so if someone tries they get a nice message // instead of "Value is not an object: undefined ..." - common.BindToGlobal(vu.Runtime, map[string]interface{}{ - "open": func() { + _ = vu.Runtime.GlobalObject().Set("open", + func() { common.Throw(vu.Runtime, errors.New(openCantBeUsedOutsideInitContextMsg)) - }, - }) + }) return vu, nil }