Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop legacy require support #3694

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions js/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ 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)
Expand Down Expand Up @@ -369,17 +370,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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// getPreviousRequiringFile is a helper that is currently need for the implemnetation of `open`.
// getPreviousRequiringFile is a helper that is currently need for the implementation 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 +414,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 +430,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.

46 changes: 40 additions & 6 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 @@ -150,6 +151,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 @@ -158,28 +160,59 @@ 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])
if len(frames) == 0 {
return &url.URL{Scheme: "file", Path: "/-"}, nil
}
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 @@ -189,16 +222,17 @@ 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)
}

// ExportGloballyModule sets all exports of the provided module name on the globalThis.
// effectively making them globally available
func ExportGloballyModule(rt *goja.Runtime, modSys *ModuleSystem, moduleName string) {
t, _ := modSys.Require(nil, moduleName)
t, _ := modSys.Require(moduleName)

for _, key := range t.Keys() {
if err := rt.Set(key, t.Get(key)); err != nil {
Expand Down
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 @@ -113,7 +112,6 @@ 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{})
modules.ExportGloballyModule(r.VU.RuntimeField, ms, "k6/timers")
return r.VU.RuntimeField.Set("require", impl.Require)
return r.VU.RuntimeField.Set("require", ms.Require)
}
Loading
Loading