quickjs

package module
v0.12.28 Latest Latest
Warning

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

Go to latest
Published: Apr 24, 2025 License: BSD-3-Clause Imports: 16 Imported by: 0

README

quickjs

Package quickjs is an idiomatic Go wrapper for modernc.org/libquickjs, an embeddable, CGo-free Javascript engine.

See also the original C quickjs library.

Supported platforms and architectures

These combinations of GOOS and GOARCH are currently supported

OS      Arch
-------------
linux   amd64
linux   loong64

Builders

Builder results are available at:

https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fquickjs

Performance

This package @ v0.8.0

vs https://pkg.go.dev/github.com/dop251/goja @v0.0.0-20240220182346-e401ed450204

go test -timeout 24h -run @ -bench . 2>&1 | tee log-benchmark
goos: linux
goarch: amd64
pkg: modernc.org/quickjs
cpu: AMD Ryzen 9 3900X 12-Core Processor            
BenchmarkArewefastyet/ccgo-24    1    115820232777 ns/op          22680 B/op            67 allocs/op
BenchmarkArewefastyet/goja-24    1    184796170244 ns/op    28111172328 B/op    1755493570 allocs/op
PASS
ok  modernc.org/quickjs 300.630s

Notes

Parts of the documentation were copied from the quickjs documentation, see LICENSE-QUICKJS for details.

Documentation

Overview

Package quickjs is an idiomatic Go wrapper for modernc.org/libquickjs, an embeddable, CGo-free Javascript engine.

See also the original C quickjs library.

Supported platforms and architectures

These combinations of GOOS and GOARCH are currently supported

OS      Arch
-------------
linux   386
linux   amd64
linux   arm64
linux   loong64

Builders

Builder results are available at:

https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fquickjs

Performance

This package @ v0.12.26

vs https://pkg.go.dev/github.com/dop251/goja@v0.0.0-20250309171923-bcd7cc6bf64c

jnml@3900x:~/src/modernc.org/quickjs/compare$ date ; make benchmark
Wed Apr 23 12:15:18 CEST 2025
go test -vet=off -timeout 24h -run @ -bench . 2>&1 | tee log-benchmark
goos: linux
goarch: amd64
pkg: modernc.org/quickjs/compare
cpu: AMD Ryzen 9 3900X 12-Core Processor
BenchmarkArewefastyet/ccgo-24  1  122005890899 ns/op       169424 B/op          69 allocs/op
BenchmarkArewefastyet/goja-24  1  178848393816 ns/op  25972563848 B/op  1495943715 allocs/op
PASS
ok  	modernc.org/quickjs/compare	300.876s
jnml@3900x:~/src/modernc.org/quickjs/compare$

Notes

Parts of the documentation were copied from the quickjs documentation, see LICENSE-QUICKJS for details.

Example (Ping)

Multiple concurrent Javascript virtual machines communicating via Go channels.

package main // import "modernc.org/quickjs"

import (
	"fmt"
)

// Multiple concurrent Javascript virtual machines communicating via Go channels.
func main() {
	tx := make(chan string, 1)
	rx := make(chan string, 1)
	client, _ := NewVM()
	defer client.Close()
	registerFuncs(client, tx, rx)
	go func() { // Start the server.
		server, _ := NewVM()
		defer server.Close()
		registerFuncs(server, rx, tx)
		server.Eval("send(receive()+' reply');", EvalGlobal)
	}()
	fmt.Println(client.Eval("send('ping'); receive();", EvalGlobal)) // Ping the server.
}

func registerFuncs(m *VM, tx, rx chan string) {
	m.RegisterFunc("send", func(s string) { tx <- s }, false)
	m.RegisterFunc("receive", func() string { return <-rx }, false)
}
Output:

ping reply <nil>

Index

Examples

Constants

View Source
const (
	EvalGlobal = lib.MJS_EVAL_TYPE_GLOBAL // global code
	EvalModule = lib.MJS_EVAL_TYPE_MODULE // module code
)

Eval flags.

Variables

View Source
var (

	// UndefinedValue is a Value representing Javascript value 'undefined'. It is
	// not associated with any particular VM.
	UndefinedValue = Value{/* contains filtered or unexported fields */}
)

Functions

This section is empty.

Types

type Atom added in v0.10.1

type Atom = lib.TJSAtom

Atom is an unique identifier of, for example, a string value. Atom values are VM-specific.

type Object added in v0.4.0

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

Object represents the value of a Javascript object, but not the javascript object instance itself. Do not compare instances of Object.

func (*Object) MarshalJSON added in v0.4.0

func (o *Object) MarshalJSON() (r []byte, err error)

MarshalJSON implements encoding/json.Marshaler.

Example

JSON marshalling.

m, _ := NewVM()
defer m.Close()
obj, _ := m.Eval("obj = {a: 42+314, b: 'foo'}; obj;", EvalGlobal)
s, _ := (obj.(*Object).MarshalJSON())
fmt.Printf("%s\n", s)
Output:

{"a":356,"b":"foo"}

func (*Object) String added in v0.6.0

func (o *Object) String() string

String implements fmt.Stringer.

Example

JSON marshalling.

m, _ := NewVM()
defer m.Close()
obj, _ := m.Eval("obj = {a: 42+314, b: 'foo'}; obj;", EvalGlobal)
fmt.Printf("%s\n", obj)
Output:

{"a":356,"b":"foo"}

type Undefined added in v0.1.0

type Undefined struct{}

Undefined represents the Javascript value "undefined".

func (Undefined) String added in v0.8.0

func (u Undefined) String() string

String implements fmt.Stringer.

type Unsupported added in v0.1.0

type Unsupported struct{}

Unsupported represents an unsupported Javascript value.

func (Unsupported) String added in v0.8.0

func (u Unsupported) String() string

String implements fmt.Stringer.

type VM added in v0.5.0

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

VM represents a Javascript context (or Realm). Each VM has its own global objects and system objects.

Note: VM is not safe for concurrent use by multiple goroutines.

func NewVM added in v0.5.0

func NewVM() (*VM, error)

NewVM returns a newly created VM.

func (*VM) AddIntrinsicBigDecimal added in v0.6.0

func (m *VM) AddIntrinsicBigDecimal()

AddIntrinsicBigDecimal adds the BigDecimal object to 'm'.

Example

Enable BigDecimal.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Eval("BigDecimal('1234567890.123456789');", EvalGlobal))
m.AddIntrinsicBigDecimal()
fmt.Println(m.Eval("BigDecimal('1234567890.123456789');", EvalGlobal))
Output:

<nil> ReferenceError: 'BigDecimal' is not defined
1234567890.123456789 <nil>

func (*VM) AddIntrinsicBigFloat added in v0.6.0

func (m *VM) AddIntrinsicBigFloat()

AddIntrinsicBigFloat adds the BigFloat object to 'm'.

Example

Enable BigFloat.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Eval("BigFloat('1234567890.123456789e+5');", EvalGlobal))
m.AddIntrinsicBigFloat()
fmt.Println(m.Eval("BigFloat('1234567890.123456789e+5');", EvalGlobal))
Output:

<nil> ReferenceError: 'BigFloat' is not defined
1.234567890123456789e+14 <nil>

func (*VM) AddStdHelpers added in v0.6.0

func (m *VM) AddStdHelpers() error

AddStdHelpers adds the 'print' and 'console' global objects to 'm'.

Example

Add std helpers.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Eval("console.toString();", EvalGlobal))
m.AddStdHelpers()
fmt.Println(m.Eval("console.toString();", EvalGlobal))
fmt.Println(m.Eval("console.log.toString();", EvalGlobal))
Output:

<nil> ReferenceError: 'console' is not defined
[object Object] <nil>
function log() {
    [native code]
} <nil>

func (*VM) Call added in v0.6.0

func (m *VM) Call(function string, args ...any) (r any, err error)

Call evaluates 'function(args...)' and returns the resulting (value, error).

Argument types must be one of:

Go argument type                        Javascript argument type
----------------------------------------------------------------
nil                                     null
Undefined                               undefined
string                                  string
int*/uint* (value in int32 range)       int
int*/uint* (value out of int32 range)   float
bool                                    bool
float64                                 float64
*math/big.Int                           BigInt
*math/big.Float                         BigFloat
github.com/shopspring/decimal.Decimal   BigDecimal
*Object                                 object
Value                                   native Javascript Value
any other type                          object from JSON produced by encoding.json/Marshall(arg)
Example (Function)

Call a Javascript function.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Call("parseInt", "1234"))
Output:

1234 <nil>
Example (Method)

Call a Javascript method.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Call("Math.abs", -1234))
Output:

1234 <nil>

func (*VM) CallValue added in v0.10.0

func (m *VM) CallValue(function string, args ...any) (r Value, err error)

CallValue is like Call but returns (Value, error) like EvalValue

If no error is returned, the caller must properly handle the returned Value using Dup/Free.

func (*VM) Close added in v0.5.0

func (m *VM) Close() error

Close releases the resources held by 'm'.

func (*VM) Eval added in v0.6.0

func (m *VM) Eval(javascript string, flags int) (r any, err error)

Eval evaluates a script or module source in 'javascript'.

Javascript result type  Go result type                          Go result error
-------------------------------------------------------------------------------
exception               nil                                     non-nil
null                    nil                                     nil
undefined               Undefined                               nil
string                  string                                  nil
int                     int                                     nil
bool                    bool                                    nil
float64                 float64                                 nil
BigInt                  *math/big.Int                           nil
BigFloat                *math/big.Float                         nil
BigDecimal              github.com/shopspring/decimal.Decimal   nil
object                  *Object                                 nil
any other type          Unsupported                             nil
Example (Exception)

Getting exception.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Eval("throw new Error('failed');", EvalGlobal))
Output:

<nil> Error: failed
Example (Expression)

Evaluate a simple Javascript expression.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Eval("1+2", EvalGlobal))
Output:

3 <nil>
Example (Object)

Object example.

m, _ := NewVM()
defer m.Close()
fmt.Println(m.Eval("obj = {a: 42+314, b: 'foo'}; obj;", EvalGlobal))
Output:

{"a":356,"b":"foo"} <nil>

func (*VM) EvalValue added in v0.10.0

func (m *VM) EvalValue(javascript string, flags int) (r Value, err error)

EvalValue evaluates a script or module source in 'javascript' and returns the resulting Value, or an error, if any.

Exceptions thrown during evaluation of the script are returned as Go errors.

If no error is returned, the caller must properly handle the returned Value using Dup/Free.

func (*VM) GetProperty added in v0.10.1

func (m *VM) GetProperty(this Value, prop Atom) (r any, err error)

GetProperty returns this.prop.

func (*VM) GetPropertyValue added in v0.10.1

func (m *VM) GetPropertyValue(this Value, prop Atom) (r Value, err error)

GetPropertyValue returns this.prop.

func (*VM) InitModuleStd added in v0.6.0

func (m *VM) InitModuleStd() error

InitModuleStd adds the "std" module to 'm'.

Example

Use the std module.

m, _ := NewVM()
defer m.Close()
m.InitModuleStd()
m.Eval(`
import * as std from 'std';
globalThis.std = std;
`, EvalModule)
fmt.Println(m.Call("std.sprintf", "%s %i", "hello", 42))
Output:

hello 42 <nil>

func (*VM) NewAtom added in v0.10.1

func (m *VM) NewAtom(s string) (r Atom, err error)

NewAtom returns an unique indentifier of 's' or an error, if any.

func (*VM) NewFloat64 added in v0.10.1

func (m *VM) NewFloat64(n float64) Value

NewFloat64 returns a new Value from 'n'.

func (*VM) NewInt added in v0.10.1

func (m *VM) NewInt(n int) Value

NewInt returns a new Value from 'n'.

func (*VM) NewString added in v0.10.1

func (m *VM) NewString(s string) (r Value, err error)

NewString returns a new Value from 's'.

func (*VM) RegisterFunc added in v0.6.0

func (m *VM) RegisterFunc(name string, f any, wantThis bool) (err error)

RegisterFunc registers a Go function 'f' and makes it callable from Javascript.

The 'f' argument can be a regular Go function, a closure, a method expression or a method value. All of them are called 'Go function' below.

The Go function can have zero or more parameters. If 'wantThis' is true then the first parameter of the Go function will get the Javascript value of 'this'. Depending on context, 'this' can be Javascript null or undefined.

Go functions with multiple results return them as an Javascript array.

Go nil errors are converted to Javascript null.

Go non-nil errors are converted to Javascript strings using the Error() method.

Any Go <-> Javascript failing type conversion between arguments/return values throws a Javascript type error exception.

There is a limit on the total number of currently registered Go functions.

Note: The 'name' argument should be a valid Javascript identifier. It is not currently enforced but this may change later.

Example (Error)

Call error returning Go function from Javascript.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(a int) error {
	if a < 0 {
		return fmt.Errorf("negative")
	}
	return nil
}, false)
fmt.Println(m.Eval("gofunc(-1)", EvalGlobal))
fmt.Println(m.Eval("gofunc(1)", EvalGlobal))
Output:

negative <nil>
<nil> <nil>
Example (MultipleReturn)

Call multiple return Go function from Javascript.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(a, b int) (int, int) { return 10 * a, 100 * b }, false)
fmt.Println(m.Eval("gofunc(2, 3)", EvalGlobal))
Output:

[20,300] <nil>
Example (SingleReturn)

Call single return Go function from Javascript.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(a, b, c int) int { return a + b*c }, false)
fmt.Println(m.Eval("gofunc(2, 3, 5)", EvalGlobal))
Output:

17 <nil>
Example (ThisNonNull)

Passing Javascript 'this' to a Go function.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(this any) any { return this }, true)
fmt.Println(m.Eval("var obj = { foo: 314, method: gofunc }; obj.method()", EvalGlobal))
Output:

{"foo":314} <nil>
Example (ThisNonNull2)

Passing Javascript 'this' to a Go function.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(this any, n int) (any, int) { return this, 10 * n }, true)
fmt.Println(m.Eval("var obj = { foo: 314, method: gofunc }; obj.method(42)", EvalGlobal))
Output:

[{"foo":314},420] <nil>
Example (ThisNull)

Passing undefined Javascript 'this' to a Go function.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(this any) any { return this }, true)
fmt.Println(m.Eval("gofunc()", EvalGlobal))
Output:

undefined <nil>
Example (ThisNull2)

Passing undefined Javascript 'this' to a Go function.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(this any, n int) (any, int) { return this, 10 * n }, true)
fmt.Println(m.Eval("gofunc(42)", EvalGlobal))
Output:

[null,420] <nil>
Example (Void)

Call void Go function from Javascript.

m, _ := NewVM()
defer m.Close()
m.RegisterFunc("gofunc", func(a, b, c int) { fmt.Println(a + b*c) }, false)
fmt.Println(m.Eval("gofunc(2, 3, 5)", EvalGlobal))
Output:

17
undefined <nil>

func (*VM) SetDefaultModuleLoader added in v0.9.0

func (m *VM) SetDefaultModuleLoader()

SetDefaultModuleLoader will enable loading module using the default module loader.

Example

Enabling the module loader.

m, _ := NewVM()
defer m.Close()
m.SetDefaultModuleLoader()
// testdata/power.js:
//  export const name = "Power";
//
//  export function square(x) {
//  	return x*x;
//  }
//
//  export function cube(x) {
//  	return x*x*x;
//  }
m.Eval("import * as Power from './testdata/power.js'; globalThis.Power = Power;", EvalModule)
fmt.Println(m.Eval("[Power.square(2), Power.cube(2)];", EvalGlobal))
Output:

[4,8] <nil>

func (*VM) SetProperty added in v0.10.1

func (m *VM) SetProperty(this Value, prop Atom, val any) (err error)

SetProperty sets this.prop = val.

func (*VM) SetPropertyValue added in v0.10.1

func (m *VM) SetPropertyValue(this Value, prop Atom, val Value) (err error)

SetPropertyValue sets this.prop = val.

type Value

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

Value represents a native Javascript value. Values are reference counted and their lifetime is managed by an independent Javascript garbage collector. To avoid memory corruption/leaks caused by tripping the Javascript GC, a Value must not

  • be copied. Use the Dup method instead.
  • become unreachable without calling its Free method.
  • be used after its Free() method was called.
  • outlive its VM.

It is recommended to use native Go values instead of Value where possible.

When passing a Value down the call stack use Dup. For example in main

v, _ := EvalValue(someScript)
defer v.Free()
foo(v.Dup()) // Instead of foo(v)

In 'foo' Free must be used. For example

func foo(v Value) {
	defer v.Free()
	...
}

This ensures the/only topmost Free marks 'v' eligible for garbage collection.

Beware that the correct setup/handling becomes more complicated when using closures, Values are sent through a channel etc. In particular, if a goroutine 1 passes a Dup of 'v' to goroutine 2 and goroutine 1 completes and thus frees 'v' before goroutine 2 completes, the reference counting mechanism will fail. In other words, every Free must be strictly paired with the Dup that preceded obtaining the Value and the Dup/Free calls must respect the original nesting. This is correct.

Dup             // in main
	Dup     // in foo
	Free    // in foo
Free            // in main

This will fail, for example in the above discussed goroutines scenario.

Dup            // in g1
	Dup    // in g2
Free           // in g1
        Free   // in g2

The fix might be in this case to arrange goroutine 1 to wait for goroutine 2 to complete before executing Free in goroutine 1.

func (Value) Any added in v0.11.0

func (v Value) Any() (r any, err error)

Any attemtps to convert 'v' to any using the same rules as there are for the return value of VM.Eval.

func (Value) Dup added in v0.10.0

func (v Value) Dup() Value

Dup returns a copy of 'v' while updating its reference count.

func (*Value) Free added in v0.10.0

func (v *Value) Free()

Free marks 'v' as no longer used and updates its reference count. 'v' must not be used afterwards.

func (Value) GetProperty added in v0.11.0

func (v Value) GetProperty(this Value, prop Atom) (r any, err error)

GetProperty returns v.prop.

func (Value) GetPropertyValue added in v0.11.0

func (v Value) GetPropertyValue(prop Atom) (r Value, err error)

GetPropertyValue returns v.prop.

func (Value) IsUndefined added in v0.11.0

func (v Value) IsUndefined() bool

IsUndefined reports whether 'v' represents the Javascript value 'undefined'.

func (Value) MarshalJSON added in v0.10.1

func (v Value) MarshalJSON() (r []byte, err error)

MarshalJSON implements encoding/json.Marshaler.

func (Value) SetProperty added in v0.11.0

func (v Value) SetProperty(prop Atom, val any) (err error)

SetProperty sets v.prop = val.

func (Value) SetPropertyValue added in v0.11.0

func (v Value) SetPropertyValue(prop Atom, val Value) (err error)

SetPropertyValue sets v.prop = val.

func (*Value) VM added in v0.11.0

func (v *Value) VM() *VM

VM returns the VM associated with 'v'.

Jump to

Keyboard shortcuts

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