js

package
v0.4.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2023 License: AGPL-3.0 Imports: 27 Imported by: 1

Documentation

Overview

Package js the JavaScript implementation

Index

Constants

View Source
const (
	// DefaultMaxTimeToWaitGetVM default retries time
	DefaultMaxTimeToWaitGetVM = 500 * time.Millisecond
	// DefaultMaxRetriesGetVM default retries times
	DefaultMaxRetriesGetVM = 3
)

Variables

View Source
var (
	// ErrInvalidModule module is invalid
	ErrInvalidModule = errors.New("invalid module")
	// ErrIllegalModuleName module name is illegal
	ErrIllegalModuleName = errors.New("illegal module name")
	// ErrModuleFileDoesNotExist module not exist
	ErrModuleFileDoesNotExist = errors.New("module file does not exist")
)
View Source
var (

	// ErrSchedulerClosed the scheduler is closed error
	ErrSchedulerClosed = errors.New("scheduler is closed")
)

Functions

func EnableConsole

func EnableConsole(vm *goja.Runtime)

EnableConsole enables the console

func EnableRequire added in v0.4.0

func EnableRequire(vm *goja.Runtime, path ...string)

EnableRequire set runtime require module

func Format added in v0.4.0

func Format(call goja.FunctionCall, vm *goja.Runtime) goja.Value

Format implements js format

func InitGlobalModule added in v0.4.0

func InitGlobalModule(runtime *goja.Runtime)

InitGlobalModule init all global modules

func NewPromise added in v0.4.1

func NewPromise(runtime *goja.Runtime, asyncFunc func() (any, error)) *goja.Promise

NewPromise returns the new promise with the async function. must be called on the EventLoop. like this:

func main() {
	vm := js.NewVM()
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	goFunc := func(call goja.FunctionCall, vm *goja.Runtime) goja.Value {
		return vm.ToValue(js.NewPromise(vm, func() (any, error) {
			time.Sleep(time.Second)
			return call.Argument(0).ToInteger() + call.Argument(1).ToInteger(), nil
		}))
	}
	_ = vm.Runtime().Set("asyncAdd", goFunc)

	start := time.Now()

	result, err := vm.RunString(ctx, `asyncAdd(1, 2)`)
	if err != nil {
		panic(err)
	}
	value, err := js.Unwrap(result)
	if err != nil {
		panic(err)
	}

	fmt.Println(value)
	fmt.Println(time.Now().Sub(start))
}

func Run

func Run(ctx context.Context, p Program) (goja.Value, error)

Run the js program

func RunString

func RunString(ctx context.Context, script string) (goja.Value, error)

RunString the js string

func SetOptions added in v0.4.0

func SetOptions(opt Options)

SetOptions set the default js Options.

func Throw added in v0.4.0

func Throw(vm *goja.Runtime, err error)

Throw js exception

func ToBytes added in v0.4.0

func ToBytes(data any) ([]byte, error)

ToBytes tries to return a byte slice from compatible types.

func ToStrings added in v0.4.0

func ToStrings(data any) (s any, err error)

ToStrings tries to return a string slice or string from compatible types.

func Unwrap added in v0.4.0

func Unwrap(value goja.Value) (any, error)

Unwrap the goja.Value to the raw value

func VMContext added in v0.4.0

func VMContext(runtime *goja.Runtime) context.Context

VMContext returns the current context of the goja.Runtime

Types

type Cat

type Cat struct {
	BaseURL string
	URL     string `js:"url"`
	// contains filtered or unexported fields
}

Cat an analyzer context

func NewCat

func NewCat(ctx *plugin.Context) *Cat

NewCat returns a new Cat instance

func (*Cat) Cancel

func (c *Cat) Cancel()

Cancel this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.

func (*Cat) ClearVar

func (c *Cat) ClearVar()

ClearVar clean all values

func (*Cat) GetElement

func (c *Cat) GetElement(key string, rule string, content any) (ret string, err error)

GetElement gets the string of the content with the given arguments

func (*Cat) GetElements

func (c *Cat) GetElements(key string, rule string, content any) (ret []string, err error)

GetElements gets the string of the content with the given arguments

func (*Cat) GetString

func (c *Cat) GetString(key string, rule string, content any) (ret string, err error)

GetString gets the string of the content with the given arguments

func (*Cat) GetStrings

func (c *Cat) GetStrings(key string, rule string, content any) (ret []string, err error)

GetStrings gets the string of the content with the given arguments

func (*Cat) GetVar

func (c *Cat) GetVar(call goja.FunctionCall, vm *goja.Runtime) goja.Value

GetVar returns the value associated with this context for key, or nil if no value is associated with key.

func (*Cat) Log

func (c *Cat) Log(call goja.FunctionCall, vm *goja.Runtime) goja.Value

Log print the msg to logger

func (*Cat) SetVar

func (c *Cat) SetVar(key string, value goja.Value) error

SetVar value associated with key is val.

type EnqueueCallback added in v0.4.1

type EnqueueCallback func(func() error)

func NewEnqueueCallback added in v0.4.2

func NewEnqueueCallback(runtime *goja.Runtime) EnqueueCallback

NewEnqueueCallback signals to the event loop that you are going to do some asynchronous work off the main thread and that you may need to execute some code back on the main thread when you are done. see EventLoop.RegisterCallback.

func doAsyncWork(runtime *goja.Runtime) *goja.Promise {
	enqueueCallback := js.NewEnqueueCallback(runtime)
	promise, resolve, reject := runtime.NewPromise()

	// Do the actual async work in a new independent goroutine, but make
	// sure that the Promise resolution is done on the main thread:

	go func() {
		// Also make sure to abort early if the context is cancelled, so
		// the VM is not stuck when the scenario ends or Ctrl+C is used:
		result, err := doTheActualAsyncWork()
		enqueueCallback(func() error {
			if err != nil {
				reject(err)
			} else {
				resolve(result)
			}
			return nil // do not abort the iteration
		})
	}()
	return promise
}

type EventLoop added in v0.4.1

type EventLoop struct {
	// contains filtered or unexported fields
}

EventLoop implements an event with handling of unhandled rejected promises.

A specific thing about this event loop is that it will wait to return not only until the queue is empty but until nothing is registered that it will run in the future. This is in contrast with more common behaviours where it only returns on a specific event/action or when the loop is empty. This is required as in k6 iterations (for which event loop will be primary used) are supposed to be independent and any work started in them needs to finish, but also they need to end when all the instructions are done. Additionally because of this on any error while the event loop will exit it's required to wait on the event loop to be empty before the execution can continue.

func NewEventLoop added in v0.4.1

func NewEventLoop(runtime *goja.Runtime) *EventLoop

NewEventLoop returns a new event loop with a few helpers attached to it: - adding setTimeout javascript implementation - reporting (and aborting on) unhandled promise rejections

func (*EventLoop) RegisterCallback added in v0.4.1

func (e *EventLoop) RegisterCallback() EnqueueCallback

RegisterCallback signals to the event loop that you are going to do some asynchronous work off the main thread and that you may need to execute some code back on the main thread when you are done. So, once you call this method, the event loop will wait for you to finish and give it the callback it needs to run back on the main thread before it can end the whole current script iteration.

RegisterCallback() *must* be called from the main runtime thread, but its result enqueueCallback() is thread-safe and can be called from any goroutine. enqueueCallback() ensures that its callback parameter is added to the VM runtime's tasks queue, to be executed on the main runtime thread eventually, when the VM is done with the other tasks before it. Unless the whole event loop has been stopped, invoking enqueueCallback() will queue its argument and "wake up" the loop (if it was idle, but not stopped).

Keep in mind that once you call RegisterCallback(), you *must* also call enqueueCallback() exactly once, even if don't actually need to run any code on the main thread. If that's the case, you can pass an empty no-op callback to it, but you must call it! The event loop will wait for the enqueueCallback() invocation and the k6 iteration won't finish and will be stuck until the VM itself has been stopped (e.g. because the whole test or scenario has ended). Any error returned by any callback on the main thread will abort the current iteration and no further event loop callbacks will be executed in the same iteration.

A common pattern for async work is something like this:

func doAsyncWork(vm js.VM) *goja.Promise {
    enqueueCallback := vm.Runtime().GlobalObject().GetSymbol(enqueueCallbackSymbol).Export().(func() EnqueueCallback)()
    p, resolve, reject := vm.Runtime().NewPromise()

    // Do the actual async work in a new independent goroutine, but make
    // sure that the Promise resolution is done on the main thread:
    go func() {
        // Also make sure to abort early if the context is cancelled, so
        // the VM is not stuck when the scenario ends or Ctrl+C is used:
        result, err := doTheActualAsyncWork()
        enqueueCallback(func() error {
            if err != nil {
                reject(err)
            } else {
                resolve(result)
            }
            return nil  // do not abort the iteration
        })
    }()

    return p
}

This ensures that the actual work happens asynchronously, while the Promise is immediately returned and the main thread resumes execution. It also ensures that the Promise resolution happens safely back on the main thread once the async work is done, as required by goja and all other JS runtimes.

TODO: rename to ReservePendingCallback or something more appropriate?

func (*EventLoop) Start added in v0.4.1

func (e *EventLoop) Start(firstCallback func() error) error

Start will run the event loop until it's empty and there are no uninvoked registered callbacks or a queued function returns an error. The provided firstCallback will be the first thing executed. After Start returns the event loop can be reused as long as waitOnRegistered is called.

func (*EventLoop) WaitOnRegistered added in v0.4.1

func (e *EventLoop) WaitOnRegistered()

WaitOnRegistered waits on all registered callbacks so we know nothing is still doing work. This does call back the callbacks and more can be queued over time. A different mechanism needs to be used to tell the users that the event loop has errored out or winding down for a different reason.

type FieldNameMapper added in v0.4.0

type FieldNameMapper struct{}

FieldNameMapper provides custom mapping between Go and JavaScript property names.

func (FieldNameMapper) FieldName added in v0.4.0

FieldName returns a JavaScript name for the given struct field in the given type. If this method returns "" the field becomes hidden.

func (FieldNameMapper) MethodName added in v0.4.0

func (FieldNameMapper) MethodName(_ reflect.Type, m reflect.Method) string

MethodName returns a JavaScript name for the given method in the given type. If this method returns "" the method becomes hidden.

type Options

type Options struct {
	InitialVMs         int           `yaml:"initial-vms"`
	MaxVMs             int           `yaml:"max-vms"`
	MaxRetriesGetVM    int           `yaml:"max-retries-get-vm"`
	MaxTimeToWaitGetVM time.Duration `yaml:"max-time-to-wait-get-vm"`
	ModulePath         []string      `yaml:"module-path"`
}

Options Scheduler options

type Program added in v0.4.0

type Program struct {
	Code string
	Args map[string]any
}

Program The js program

type Scheduler

type Scheduler interface {
	// Get the VM
	Get() (VM, error)
	// Release the VM
	Release(VM)
	// Close the scheduler
	Close() error
}

Scheduler the VM scheduler

func GetScheduler

func GetScheduler() Scheduler

GetScheduler returns the default Scheduler.

func NewScheduler

func NewScheduler(opt Options) Scheduler

NewScheduler returns a new Scheduler

type VM

type VM interface {
	// Run the js program
	Run(context.Context, Program) (goja.Value, error)
	// RunString the js string
	RunString(context.Context, string) (goja.Value, error)
	// Runtime the js runtime
	Runtime() *goja.Runtime
}

VM the js runtime. An instance of VM can only be used by a single goroutine at a time.

func NewVM added in v0.4.1

func NewVM(modulePath ...string) VM

NewVM creates a new JavaScript VM Initialize the EventLoop, require, global module, console

Directories

Path Synopsis
cache
Package cache the cache JS implementation
Package cache the cache JS implementation
cookie
Package cookie the cookie JS implementation
Package cookie the cookie JS implementation
crypto
Package crypto the crypto JS implementation
Package crypto the crypto JS implementation
encoding
Package encoding the encoding JS implementation
Package encoding the encoding JS implementation
http
Package http the http JS implementation
Package http the http JS implementation
Package modulestest the module test vm
Package modulestest the module test vm

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL