Skip to content

Commit

Permalink
Drop legacy require support
Browse files Browse the repository at this point in the history
  • Loading branch information
mstoykov committed Apr 19, 2024
1 parent 0a37d2d commit 05d8abf
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 212 deletions.
48 changes: 37 additions & 11 deletions js/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,10 @@ func (b *Bundle) instantiate(vuImpl *moduleVUImpl, vuID uint64) (*goja.Object, e
FileSystems: b.filesystems,
CWD: b.pwd,
}
vuImpl.initEnv = initenv

modSys := modules.NewModuleSystem(b.ModuleResolver, vuImpl)
b.setInitGlobals(rt, vuImpl, modSys)
vuImpl.initEnv = initenv
defer func() {
vuImpl.initEnv = nil
}()
Expand All @@ -303,7 +303,6 @@ func (b *Bundle) instantiate(vuImpl *moduleVUImpl, vuID uint64) (*goja.Object, e
var exportsV goja.Value
err = common.RunWithPanicCatching(b.preInitState.Logger, rt, func() error {
return vuImpl.eventLoop.Start(func() error {
//nolint:govet // here we shadow err on purpose
var err error
exportsV, err = modSys.RunSourceData(b.sourceData)
return err
Expand Down Expand Up @@ -369,17 +368,40 @@ func (b *Bundle) setupJSRuntime(rt *goja.Runtime, vuID int64, logger logrus.Fiel
return nil
}

// this exists only to make the check in the init context.
// this exists to support `open` current behaviour and to check init context conformity
type requireImpl struct {
inInitContext func() bool
internal *modules.LegacyRequireImpl
vu *moduleVUImpl
modSys *modules.ModuleSystem
}

func (r *requireImpl) require(specifier string) (*goja.Object, error) {
if !r.inInitContext() {
if r.vu.state != nil {
return nil, fmt.Errorf(cantBeUsedOutsideInitContextMsg, "require")
}
return r.internal.Require(specifier)
return r.modSys.Require(specifier)
}

// getPreviousRequiringFile is a helper that is currently need for the implemnetation of `open`.
// it depends on the `require` method above
func (r *requireImpl) getPreviousRequiringFile() (*url.URL, error) {
var buf [1000]goja.StackFrame

frames := r.vu.Runtime().CaptureCallStack(1000, buf[:0])

for i, frame := range frames[1:] { // first one should be the current require
// TODO have this precalculated automatically
if frame.FuncName() == "go.k6.io/k6/js.(*requireImpl).require-fm" {
// we need to get the one *before* but as we skip the first one the index matches ;)
return url.Parse(frames[i].SrcName())
}
}
// hopefully nobody is calling `require` with 1000 big stack :crossedfingers:
if len(frames) == 1000 {
return nil, errors.New("stack too big")
}

// fallback
return url.Parse(frames[len(frames)-1].SrcName())
}

func (b *Bundle) setInitGlobals(rt *goja.Runtime, vu *moduleVUImpl, modSys *modules.ModuleSystem) {
Expand All @@ -390,8 +412,8 @@ func (b *Bundle) setInitGlobals(rt *goja.Runtime, vu *moduleVUImpl, modSys *modu
}

impl := requireImpl{
inInitContext: func() bool { return vu.state == nil },
internal: modules.NewLegacyRequireImpl(vu, modSys, *b.pwd),
vu: vu,
modSys: modSys,
}

mustSet("require", impl.require)
Expand All @@ -406,8 +428,12 @@ func (b *Bundle) setInitGlobals(rt *goja.Runtime, vu *moduleVUImpl, modSys *modu
return nil, errors.New("open() can't be used with an empty filename")
}
// This uses the pwd from the requireImpl
pwd := impl.internal.CurrentlyRequiredModule()
return openImpl(rt, b.filesystems["file"], &pwd, filename, args...)
requiringFile, err := impl.getPreviousRequiringFile()
if err != nil {
return nil, err // TODO:wrap
}
pwd := loader.Dir(requiringFile)
return openImpl(rt, b.filesystems["file"], pwd, filename, args...)
})
}

Expand Down
154 changes: 0 additions & 154 deletions js/modules/require_impl.go

This file was deleted.

41 changes: 36 additions & 5 deletions js/modules/resolution.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package modules

import (
"errors"
"fmt"
"net/url"
"strings"
Expand Down Expand Up @@ -148,6 +149,7 @@ type ModuleSystem struct {
vu VU
instanceCache map[module]moduleInstance
resolver *ModuleResolver
backupCWD *url.URL
}

// NewModuleSystem returns a new ModuleSystem for the provide VU using the provided resoluter
Expand All @@ -156,28 +158,56 @@ func NewModuleSystem(resolver *ModuleResolver, vu VU) *ModuleSystem {
resolver: resolver,
instanceCache: make(map[module]moduleInstance),
vu: vu,
backupCWD: vu.InitEnv().CWD,
}
}

// Require is called when a module/file needs to be loaded by a script
func (ms *ModuleSystem) Require(pwd *url.URL, arg string) (*goja.Object, error) {
mod, err := ms.resolver.resolve(pwd, arg)
func (ms *ModuleSystem) Require(specifier string) (*goja.Object, error) {
if specifier == "" {
return nil, errors.New("require() can't be used with an empty specifier")
}

currentModuleURL, err := ms.getCurrentModuleScript()
if err != nil {
return nil, err
}

dir := loader.Dir(currentModuleURL)
if currentModuleURL.String() == "file://-" {
dir = ms.backupCWD
}

mod, err := ms.resolver.resolve(dir, specifier)
if err != nil {
return nil, err
}
return ms.instantiate(mod)
}

// Require is called when a module/file needs to be loaded by a script
func (ms *ModuleSystem) instantiate(mod module) (*goja.Object, error) {
if instance, ok := ms.instanceCache[mod]; ok {
return instance.exports(), nil
}

instance := mod.instantiate(ms.vu)
ms.instanceCache[mod] = instance
if err = instance.execute(); err != nil {
if err := instance.execute(); err != nil {
return nil, err
}

return instance.exports(), nil
}

func (ms *ModuleSystem) getCurrentModuleScript() (*url.URL, error) {
var parent string
var buf [2]goja.StackFrame
frames := ms.vu.Runtime().CaptureCallStack(2, buf[:0])
parent = frames[1].SrcName()
return url.Parse(parent)
}

// RunSourceData runs the provided sourceData and adds it to the cache.
// If a module with the same specifier as the source is already cached
// it will be used instead of reevaluating the source from the provided SourceData.
Expand All @@ -187,8 +217,9 @@ func (ms *ModuleSystem) Require(pwd *url.URL, arg string) (*goja.Object, error)
func (ms *ModuleSystem) RunSourceData(source *loader.SourceData) (goja.Value, error) {
specifier := source.URL.String()
pwd := source.URL.JoinPath("../")
if _, err := ms.resolver.resolveLoaded(pwd, specifier, source.Data); err != nil {
mod, err := ms.resolver.resolveLoaded(pwd, specifier, source.Data)
if err != nil {
return nil, err // TODO wrap as this should never happen
}
return ms.Require(pwd, specifier)
return ms.instantiate(mod)
}
4 changes: 1 addition & 3 deletions js/modulestest/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package modulestest

import (
"context"
"net/url"
"testing"

"github.com/dop251/goja"
Expand Down Expand Up @@ -104,6 +103,5 @@ func (r *Runtime) RunOnEventLoop(code string) (value goja.Value, err error) {

func (r *Runtime) innerSetupModuleSystem() error {
ms := modules.NewModuleSystem(r.mr, r.VU)
impl := modules.NewLegacyRequireImpl(r.VU, ms, url.URL{})
return r.VU.RuntimeField.Set("require", impl.Require)
return r.VU.RuntimeField.Set("require", ms.Require)
}
Loading

0 comments on commit 05d8abf

Please sign in to comment.