diff --git a/js/initcontext.go b/js/initcontext.go index 6a5efb35915..e1422fdd8c1 100644 --- a/js/initcontext.go +++ b/js/initcontext.go @@ -71,6 +71,8 @@ type InitContext struct { logger logrus.FieldLogger sharedObjects *common.SharedObjects + + modules map[string]modules.HasModuleInstancePerVU } // NewInitContext creates a new initcontext with the provided arguments @@ -88,6 +90,7 @@ func NewInitContext( compatibilityMode: compatMode, logger: logger, sharedObjects: common.NewSharedObjects(), + modules: modules.GetJSModules(), } } @@ -114,6 +117,7 @@ func newBoundInitContext(base *InitContext, ctxPtr *context.Context, rt *goja.Ru compatibilityMode: base.compatibilityMode, logger: base.logger, sharedObjects: base.sharedObjects, + modules: base.modules, } } @@ -140,11 +144,11 @@ 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, errors.Errorf("unknown module: %s", name) } - return i.runtime.ToValue(common.Bind(i.runtime, mod, i.ctxPtr)), nil + return i.runtime.ToValue(common.Bind(i.runtime, mod.NewModuleInstancePerVU(), i.ctxPtr)), nil } func (i *InitContext) requireFile(name string) (goja.Value, error) { diff --git a/js/internal/modules/modules.go b/js/internal/modules/modules.go index 01f8d69baed..67c10a4fac1 100644 --- a/js/internal/modules/modules.go +++ b/js/internal/modules/modules.go @@ -23,6 +23,17 @@ package modules import ( "fmt" "sync" + + "github.com/loadimpact/k6/js/modules/k6" + "github.com/loadimpact/k6/js/modules/k6/crypto" + "github.com/loadimpact/k6/js/modules/k6/crypto/x509" + "github.com/loadimpact/k6/js/modules/k6/data" + "github.com/loadimpact/k6/js/modules/k6/encoding" + "github.com/loadimpact/k6/js/modules/k6/grpc" + "github.com/loadimpact/k6/js/modules/k6/html" + "github.com/loadimpact/k6/js/modules/k6/http" + "github.com/loadimpact/k6/js/modules/k6/metrics" + "github.com/loadimpact/k6/js/modules/k6/ws" ) //nolint:gochecknoglobals @@ -31,17 +42,6 @@ var ( mx sync.RWMutex ) -// Get returns the module registered with name. -func Get(name string) interface{} { - mx.RLock() - defer mx.RUnlock() - mod := modules[name] - if i, ok := mod.(HasModuleInstancePerVU); ok { - return i.NewModuleInstancePerVU() - } - return mod -} - // HasModuleInstancePerVU should be implemented by all native Golang modules that // would require per-VU state. k6 will call their NewModuleInstancePerVU() methods // every time a VU imports the module and use its result as the returned object. @@ -61,3 +61,42 @@ func Register(name string, mod interface{}) { } modules[name] = mod } + +// GetJSModules returns a map of all js modules +func GetJSModules() map[string]HasModuleInstancePerVU { + result := map[string]HasModuleInstancePerVU{ + // TODO add others + "k6": HasModuleInstancePerVUDummyWrapper{Module: k6.New()}, + "k6/crypto": HasModuleInstancePerVUDummyWrapper{Module: crypto.New()}, + "k6/crypto/x509": HasModuleInstancePerVUDummyWrapper{Module: x509.New()}, + "k6/data": HasModuleInstancePerVUDummyWrapper{Module: data.New()}, + "k6/encoding": HasModuleInstancePerVUDummyWrapper{Module: encoding.New()}, + "k6/net/grpc": HasModuleInstancePerVUDummyWrapper{Module: grpc.New()}, + "k6/html": HasModuleInstancePerVUDummyWrapper{Module: html.New()}, + "k6/http": http.New(), + "k6/metrics": HasModuleInstancePerVUDummyWrapper{Module: metrics.New()}, + "k6/ws": HasModuleInstancePerVUDummyWrapper{Module: ws.New()}, + } + + for name, module := range modules { + if perInstance, ok := module.(HasModuleInstancePerVU); ok { + result[name] = perInstance + } else { + result[name] = HasModuleInstancePerVUDummyWrapper{Module: module} + } + } + return result +} + +// HasModuleInstancePerVUDummyWrapper is a wrapper to be used around an module that doesn't support +// HasModuleIntancePerVU, but needs to be used in places where it is required. +type HasModuleInstancePerVUDummyWrapper struct { + Module interface{} +} + +// NewModuleInstancePerVU implements HasModuleInstancePerVU +func (h HasModuleInstancePerVUDummyWrapper) NewModuleInstancePerVU() interface{} { + return h.Module +} + +var _ HasModuleInstancePerVU = HasModuleInstancePerVUDummyWrapper{} diff --git a/js/modules/k6/crypto/crypto.go b/js/modules/k6/crypto/crypto.go index 226e1412100..94910f2cf9e 100644 --- a/js/modules/k6/crypto/crypto.go +++ b/js/modules/k6/crypto/crypto.go @@ -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 { diff --git a/js/modules/k6/crypto/x509/x509.go b/js/modules/k6/crypto/x509/x509.go index cb3201faae0..e505fff52ef 100644 --- a/js/modules/k6/crypto/x509/x509.go +++ b/js/modules/k6/crypto/x509/x509.go @@ -35,13 +35,8 @@ import ( "github.com/pkg/errors" "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{} @@ -224,7 +219,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) } diff --git a/js/modules/k6/data/data.go b/js/modules/k6/data/data.go index 3b8ac3c7b8f..757f92a5de3 100644 --- a/js/modules/k6/data/data.go +++ b/js/modules/k6/data/data.go @@ -26,15 +26,15 @@ import ( "github.com/dop251/goja" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" "github.com/loadimpact/k6/lib" "github.com/pkg/errors" ) type data struct{} -func init() { - modules.Register("k6/data", new(data)) +// New return a new Module instance +func New() interface{} { + return new(data) } const sharedArrayNamePrefix = "k6/data/SharedArray." diff --git a/js/modules/k6/encoding/encoding.go b/js/modules/k6/encoding/encoding.go index 914d2981e7a..8c2893dc233 100644 --- a/js/modules/k6/encoding/encoding.go +++ b/js/modules/k6/encoding/encoding.go @@ -25,13 +25,8 @@ import ( "encoding/base64" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" ) -func init() { - modules.Register("k6/encoding", New()) -} - type Encoding struct{} func New() *Encoding { diff --git a/js/modules/k6/grpc/grpc.go b/js/modules/k6/grpc/grpc.go index 5864e96ecea..83e41bfaff8 100644 --- a/js/modules/k6/grpc/grpc.go +++ b/js/modules/k6/grpc/grpc.go @@ -22,14 +22,8 @@ package grpc import ( "google.golang.org/grpc/codes" - - "github.com/loadimpact/k6/js/internal/modules" ) -func init() { - modules.Register("k6/net/grpc", New()) -} - // GRPC represents the gRPC protocol module for k6 type GRPC struct { StatusOK codes.Code `js:"StatusOK"` diff --git a/js/modules/k6/html/html.go b/js/modules/k6/html/html.go index 34ccb09689a..7ac4b614308 100644 --- a/js/modules/k6/html/html.go +++ b/js/modules/k6/html/html.go @@ -31,13 +31,8 @@ import ( gohtml "golang.org/x/net/html" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" ) -func init() { - modules.Register("k6/html", New()) -} - type HTML struct{} func New() *HTML { @@ -78,7 +73,6 @@ func (s Selection) varargFnCall(arg interface{}, strFilter func(string) *goquery.Selection, selFilter func(*goquery.Selection) *goquery.Selection, nodeFilter func(...*gohtml.Node) *goquery.Selection) Selection { - switch v := arg.(type) { case Selection: return Selection{s.rt, selFilter(v.sel), s.URL} @@ -113,7 +107,6 @@ func (s Selection) adjacentUntil(until func(string) *goquery.Selection, filteredUntil func(string, string) *goquery.Selection, filteredUntilSelection func(string, *goquery.Selection) *goquery.Selection, def ...goja.Value) Selection { - switch len(def) { case 0: return Selection{s.rt, until(""), s.URL} diff --git a/js/modules/k6/http/http.go b/js/modules/k6/http/http.go index 412cbd72f92..de4f0ff9bdf 100644 --- a/js/modules/k6/http/http.go +++ b/js/modules/k6/http/http.go @@ -24,15 +24,10 @@ import ( "context" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" "github.com/loadimpact/k6/lib" "github.com/loadimpact/k6/lib/netext" ) -func init() { - modules.Register("k6/http", new(GlobalHTTP)) -} - const ( HTTP_METHOD_GET = "GET" HTTP_METHOD_POST = "POST" @@ -46,11 +41,14 @@ const ( // ErrJarForbiddenInInitContext is used when a cookie jar was made in the init context var ErrJarForbiddenInInitContext = common.NewInitContextError("Making cookie jars in the init context is not supported") +// New returns a new global module instance +func New() *GlobalHTTP { + return &GlobalHTTP{} +} + // GlobalHTTP is a global HTTP module for a k6 instance/test run type GlobalHTTP struct{} -var _ modules.HasModuleInstancePerVU = new(GlobalHTTP) - // NewModuleInstancePerVU returns an HTTP instance for each VU func (g *GlobalHTTP) NewModuleInstancePerVU() interface{} { // this here needs to return interface{} return &HTTP{ // change the below fields to be not writable or not fields diff --git a/js/modules/k6/k6.go b/js/modules/k6/k6.go index 32a2cafe58b..7bbfddd5e94 100644 --- a/js/modules/k6/k6.go +++ b/js/modules/k6/k6.go @@ -30,16 +30,11 @@ import ( "github.com/pkg/errors" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" "github.com/loadimpact/k6/lib" "github.com/loadimpact/k6/lib/metrics" "github.com/loadimpact/k6/stats" ) -func init() { - modules.Register("k6", New()) -} - type K6 struct{} // ErrGroupInInitContext is returned when group() are using in the init context diff --git a/js/modules/k6/metrics/metrics.go b/js/modules/k6/metrics/metrics.go index 9f3d3cc491e..de4e51a3991 100644 --- a/js/modules/k6/metrics/metrics.go +++ b/js/modules/k6/metrics/metrics.go @@ -30,15 +30,10 @@ import ( "github.com/dop251/goja" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" "github.com/loadimpact/k6/lib" "github.com/loadimpact/k6/stats" ) -func init() { - modules.Register("k6/metrics", New()) -} - var nameRegexString = "^[\\p{L}\\p{N}\\._ !\\?/&#\\(\\)<>%-]{1,128}$" var compileNameRegex = regexp.MustCompile(nameRegexString) @@ -59,7 +54,7 @@ func newMetric(ctxPtr *context.Context, name string, t stats.MetricType, isTime return nil, errors.New("metrics must be declared in the init context") } - //TODO: move verification outside the JS + // TODO: move verification outside the JS if !checkName(name) { return nil, common.NewInitContextError(fmt.Sprintf("Invalid metric name: '%s'", name)) } diff --git a/js/modules/k6/ws/ws.go b/js/modules/k6/ws/ws.go index 8a54be1fb75..e19813897bc 100644 --- a/js/modules/k6/ws/ws.go +++ b/js/modules/k6/ws/ws.go @@ -37,16 +37,11 @@ import ( "github.com/gorilla/websocket" "github.com/loadimpact/k6/js/common" - "github.com/loadimpact/k6/js/internal/modules" "github.com/loadimpact/k6/lib" "github.com/loadimpact/k6/lib/metrics" "github.com/loadimpact/k6/stats" ) -func init() { - modules.Register("k6/ws", New()) -} - // ErrWSInInitContext is returned when websockets are using in the init context var ErrWSInInitContext = common.NewInitContextError("using websockets in the init context is not supported") diff --git a/js/modules/modules.go b/js/modules/modules.go index fa7ebdd9fa6..c1ddcb83623 100644 --- a/js/modules/modules.go +++ b/js/modules/modules.go @@ -29,11 +29,6 @@ import ( const extPrefix string = "k6/x/" -// Get returns the module registered with name. -func Get(name string) interface{} { - return modules.Get(name) -} - // Register the given mod as an external JavaScript module that can be imported // by name. The name must be unique across all registered modules and must be // prefixed with "k6/x/", otherwise this function will panic.