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

Add GetJSModules support from #1858 #1911

Merged
merged 5 commits into from
Apr 9, 2021
Merged
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
7 changes: 3 additions & 4 deletions js/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,9 @@ func (b *Bundle) instantiate(logger logrus.FieldLogger, rt *goja.Runtime, init *
// TODO: get rid of the unused ctxPtr, use a real external context (so we
// can interrupt), build the common.InitEnvironment earlier and reuse it
initenv := &common.InitEnvironment{
SharedObjects: init.sharedObjects,
Logger: logger,
FileSystems: init.filesystems,
CWD: init.pwd,
Logger: logger,
FileSystems: init.filesystems,
CWD: init.pwd,
}
ctx := common.WithInitEnv(context.Background(), initenv)
*init.ctxPtr = common.WithRuntime(ctx, rt)
Expand Down
33 changes: 0 additions & 33 deletions js/common/initenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ package common
import (
"net/url"
"path/filepath"
"sync"

"github.com/sirupsen/logrus"
"github.com/spf13/afero"
Expand All @@ -38,7 +37,6 @@ type InitEnvironment struct {
// TODO: add RuntimeOptions and other properties, goja sources, etc.
// ideally, we should leave this as the only data structure necessary for
// executing the init context for all JS modules
SharedObjects *SharedObjects
}

// GetAbsFilePath should be used to access the FileSystems, since afero has a
Expand All @@ -62,34 +60,3 @@ func (ie *InitEnvironment) GetAbsFilePath(filename string) string {
}
return filename
}

// SharedObjects is a collection of general store for objects to be shared. It is mostly a wrapper
// around map[string]interface with a lock and stuff.
// The reason behind not just using sync.Map is that it still needs a lock when we want to only call
// the function constructor if there is no such key at which point you already need a lock so ...
type SharedObjects struct {
data map[string]interface{}
l sync.Mutex
}

// NewSharedObjects returns a new SharedObjects ready to use
func NewSharedObjects() *SharedObjects {
return &SharedObjects{
data: make(map[string]interface{}),
}
}

// GetOrCreateShare returns a shared value with the given name or sets it's value whatever
// createCallback returns and returns it.
func (so *SharedObjects) GetOrCreateShare(name string, createCallback func() interface{}) interface{} {
so.l.Lock()
defer so.l.Unlock()

value, ok := so.data[name]
if !ok {
value = createCallback()
so.data[name] = value
}

return value
}
16 changes: 11 additions & 5 deletions js/init_and_modules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ package js_test

import (
"context"
"fmt"
"net/url"
"sync/atomic"
"testing"
"time"

"github.com/loadimpact/k6/js"
"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/js/modules"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/lib/testutils"
"github.com/loadimpact/k6/loader"
Expand Down Expand Up @@ -59,19 +61,23 @@ func (cm *CheckModule) VuCtx(ctx context.Context) {
assert.NotNil(cm.t, lib.GetState(ctx))
}

var uniqueModuleNumber int64 //nolint:gochecknoglobals

func TestNewJSRunnerWithCustomModule(t *testing.T) {
t.Parallel()
checkModule := &CheckModule{t: t}
modules.Register("k6/check", checkModule)
moduleName := fmt.Sprintf("k6/x/check-%d", atomic.AddInt64(&uniqueModuleNumber, 1))
modules.Register(moduleName, checkModule)

script := `
var check = require("k6/check");
script := fmt.Sprintf(`
var check = require("%s");
check.initCtx();

module.exports.options = { vus: 1, iterations: 1 };
module.exports.default = function() {
check.vuCtx();
};
`
`, moduleName)

logger := testutils.NewLogger(t)
rtOptions := lib.RuntimeOptions{CompatibilityMode: null.StringFrom("base")}
Expand Down
15 changes: 9 additions & 6 deletions js/initcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/compiler"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/js/modules"
"github.com/loadimpact/k6/lib"
"github.com/loadimpact/k6/loader"
)
Expand Down Expand Up @@ -70,7 +70,7 @@ type InitContext struct {

logger logrus.FieldLogger

sharedObjects *common.SharedObjects
modules map[string]interface{}
}

// NewInitContext creates a new initcontext with the provided arguments
Expand All @@ -87,7 +87,7 @@ func NewInitContext(
programs: make(map[string]programWithSource),
compatibilityMode: compatMode,
logger: logger,
sharedObjects: common.NewSharedObjects(),
modules: modules.GetJSModules(),
}
}

Expand All @@ -113,7 +113,7 @@ func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Ru
programs: programs,
compatibilityMode: base.compatibilityMode,
logger: base.logger,
sharedObjects: base.sharedObjects,
modules: base.modules,
}
}

Expand All @@ -140,10 +140,13 @@ func (i *InitContext) Require(arg string) goja.Value {
}

func (i *InitContext) requireModule(name string) (goja.Value, error) {
mod := modules.Get(name)
if mod == nil {
mod, ok := i.modules[name]
if !ok {
return nil, fmt.Errorf("unknown module: %s", name)
}
if perInstance, ok := mod.(modules.HasModuleInstancePerVU); ok {
mod = perInstance.NewModuleInstancePerVU()
}
return i.runtime.ToValue(common.Bind(i.runtime, mod, i.ctxPtr)), nil
}

Expand Down
63 changes: 0 additions & 63 deletions js/internal/modules/modules.go

This file was deleted.

5 changes: 0 additions & 5 deletions js/modules/k6/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,8 @@ import (
"golang.org/x/crypto/ripemd160"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/crypto", New())
}

type Crypto struct{}

type Hasher struct {
Expand Down
7 changes: 1 addition & 6 deletions js/modules/k6/crypto/x509/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,8 @@ import (
"time"

"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
)

func init() {
modules.Register("k6/crypto/x509", New())
}

// X509 certificate functionality
type X509 struct{}

Expand Down Expand Up @@ -223,7 +218,7 @@ func iso8601(value time.Time) string {
}

func makeRdns(names []pkix.AttributeTypeAndValue) []RDN {
var result = make([]RDN, len(names))
result := make([]RDN, len(names))
for i, name := range names {
result[i] = makeRdn(name)
}
Expand Down
55 changes: 37 additions & 18 deletions js/modules/k6/data/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,49 @@ import (
"context"
"errors"
"strconv"
"sync"

"github.com/dop251/goja"
"github.com/loadimpact/k6/js/common"
"github.com/loadimpact/k6/js/internal/modules"
"github.com/loadimpact/k6/lib"
)

type data struct{}
type data struct {
shared sharedArrays
}

type sharedArrays struct {
data map[string]sharedArray
mu sync.RWMutex
}

func (s *sharedArrays) get(rt *goja.Runtime, name string, call goja.Callable) sharedArray {
s.mu.RLock()
array, ok := s.data[name]
s.mu.RUnlock()
if !ok {
s.mu.Lock()
array, ok = s.data[name]
if !ok {
imiric marked this conversation as resolved.
Show resolved Hide resolved
func() { // this is done for the defer below
defer s.mu.Unlock()
array = getShareArrayFromCall(rt, call)
s.data[name] = array
}()
}
}

func init() {
modules.Register("k6/data", new(data))
return array
}

const sharedArrayNamePrefix = "k6/data/SharedArray."
// New return a new Module instance
func New() interface{} {
return &data{
shared: sharedArrays{
data: make(map[string]sharedArray),
},
}
}

// XSharedArray is a constructor returning a shareable read-only array
// indentified by the name and having their contents be whatever the call returns
Expand All @@ -46,24 +75,14 @@ func (d *data) XSharedArray(ctx context.Context, name string, call goja.Callable
return nil, errors.New("new SharedArray must be called in the init context")
}

initEnv := common.GetInitEnv(ctx)
if initEnv == nil {
return nil, errors.New("missing init environment")
}
if len(name) == 0 {
return nil, errors.New("empty name provided to SharedArray's constructor")
}

name = sharedArrayNamePrefix + name
value := initEnv.SharedObjects.GetOrCreateShare(name, func() interface{} {
return getShareArrayFromCall(common.GetRuntime(ctx), call)
})
array, ok := value.(sharedArray)
if !ok { // TODO more info in the error?
return nil, errors.New("wrong type of shared object")
}
rt := common.GetRuntime(ctx)
array := d.shared.get(rt, name, call)

return array.wrap(common.GetRuntime(ctx)), nil
return array.wrap(rt), nil
}

func getShareArrayFromCall(rt *goja.Runtime, call goja.Callable) sharedArray {
Expand Down
Loading