Help us shape the future of Go! Take the 2026 Go Developer Survey

slowjs

package module
v0.28.4 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: BSD-3-Clause, MIT Imports: 22 Imported by: 0

README

slowjs

Run today's npm code unchanged inside one static Go binary.

slowjs renders React slower than Node and faster than any other pure Go JavaScript engine, with no cgo and no Node process. It is QuickJS translated to Go and tuned for server rendering, built for tifa, a Go framework for React pages.

It is for Go programs that need to run JavaScript, such as rendering React on the server. The engine runs ES2023 with modules, async generators, proxies, BigInt and Intl in every Chromium locale.

It is an engine, not Node: there is no require, fs or process. Code from npm runs unchanged once it is bundled as for a browser, as React is.

English · 简体中文

Install

go get github.com/liustack/slowjs

Go 1.27 or later. No cgo and no C compiler: it builds with CGO_ENABLED=0 and cross-compiles like any Go code.

Platforms

linux and darwin on amd64 and arm64, and windows on amd64. A build for any other target stops with an error that names these.

A first VM

package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/liustack/slowjs"
	"github.com/liustack/slowjs/intl"
)

func main() {
	vm, err := slowjs.NewVM()
	if err != nil {
		panic(err)
	}
	defer vm.Close()

	vm.SetMemoryLimit(64 << 20) // 64 MiB for this VM
	if err := vm.SetEvalTimeout(100 * time.Millisecond); err != nil {
		panic(err)
	}
	if err := intl.Register(vm); err != nil {
		panic(err)
	}

	v, err := vm.Eval(`new Date(0).toLocaleString('zh-CN', {timeZone: 'Asia/Shanghai'})`, slowjs.EvalGlobal)
	fmt.Println(v, err) // 1970/1/1 08:00:00 <nil>

	_, err = vm.Eval(`try { for (;;) {} } catch (e) { 'caught' }`, slowjs.EvalGlobal)
	var e *slowjs.Error
	if errors.As(err, &e) && e.Interrupted {
		fmt.Println("timed out, and no catch saw it")
	}
}

A VM is not safe for several goroutines at once: give each goroutine its own, or keep a pool. Intl and Temporal use embedded tz 2026e, so they need no host zoneinfo.

What slowjs adds to the API

The API is that of modernc.org/quickjs, documented at pkg.go.dev, with these additions:

  • NewBuiltin(name, length, fn) and VM.NewBuiltinFunction(b): a Go function javascript calls as it calls Math.max, with no reflection. fn reads its arguments from Args (Number, String, Bool, Value, This, VM, and Latin1 or UTF16 for the characters of a string as the engine holds them), which convert nothing, and returns a Value. A call that adds two numbers takes about 55 ns on an Apple M4, against about 470 ns through RegisterHostFunc. Make a Builtin once per process: VMs on several goroutines call it at once. An *Error it returns is thrown as the constructor its Name says (Error, TypeError, RangeError, SyntaxError, ReferenceError or InternalError), any other error as a TypeError. More
  • VM.NewArray(vals...): an array of values made in Go, made as an array literal makes one, so a setter a page put on Array.prototype never runs. More
  • VM.SetStrip(s) with type Strip: StripSource compiles functions without their source, so their toString shows [native code], and StripDebug leaves out line and column numbers. More
  • Error() of an *Error made in Go is Name: Message, as Error.prototype.toString writes it, where it was empty. An error taken from javascript keeps the engine's text. More
  • VM.RunGC(), VM.MemorySize() and VM.GCThreshold(): run a GC now, read the memory a VM takes and the threshold of its next GC, so a program can collect between two calls into javascript and set the threshold for the next one. More
  • VM.RunYoungGC(): collect only the objects made since the last GC, so a server that runs a young GC after each render and a full one now and then spends on each GC about as long as the garbage of one render. More
  • VM.OnClose(f): run f when the VM is closed, so a package that keeps something per VM in a table of its own can drop it then. More
  • NewSharedBytecode(bytecode) and VM.EvalSharedBytecode(b): VMs that load the same bytecode keep the source text and line tables of its functions in one copy, so each VM of tifa's example shop holds 2.36 MiB less, with the same toString and stacks. More
  • Package temporal: temporal.Register(vm) adds Temporal, every type and method V8 ships, computed in Go by go-intl with ICU's zones for tz 2026e built in, so the host's zoneinfo is never read. Temporal.Now reads the VM clock, and temporal.Clock overrides it for Temporal.Now alone. See the linked doc for current binary size measurements. More
  • *Error fields Uncatchable, OutOfMemory and Interrupted: why a call ended, which no script can fake. More
  • VM.SetClock(now) and VM.Now(): set and read the wall clock of a VM, which Date, Intl and Temporal all read. nil restores the live clock. More
  • VM.SetShapeCache(n) and DefaultShapeCache: how many shapes of plain objects (a shape is the engine's record of which keys an object has, in which order) a VM keeps for the next object with the same keys. More

The first five came in v0.26.17, the next two in v0.27.0. VM.SetClock and VM.Now are unreleased.

What changes for you

  • A limit ends the call. A script that runs past its eval timeout or its memory limit stops there, whatever it catches: no catch, finally, promise handler or Go function of the page gets to run on. *Error says whether it was a timeout or memory. A panic in a Go function you gave the VM comes back to your Go code, and a VM that ran out of memory still closes without a leak.
  • The same page on every machine. A script's local time is UTC in every VM, without setting TZ for the whole process. Intl formats in every locale Chromium has, compared with Chromium 156 on about 75,000 expressions.
  • Strings arrive whole. A Go string from javascript is always valid UTF-8, and a Go string with invalid bytes reads in javascript as Go itself decodes it.
  • Faster pages. On the React pages of the example shop of tifa, a Go framework that renders React on the server, the engine renders 1.63 to 1.66 times as fast as the original modernc.org/quickjs it started from (measured at v0.26.15), and several VMs on several cores no longer wait on one lock.

Why you can trust it

  • The engine is upstream QuickJS. lib/ is what ccgo makes from Bellard's C, merged from upstream libquickjs with its history. slowjs changes it only through internal/fixlib, which rewrites the generated Go mechanically, and through a C patch for a regeneration. The committed files are exactly what fixlib makes from the upstream files, byte for byte (how to check).
  • It stops when upstream moves. Every rewrite checks the code it touches against fingerprints or counts. When an upstream release changes that code, fixlib stops and names it, rather than rewriting code it has not seen.
  • Every change is written down. docs/fork-changes gives each change with why it is safe, what it costs in memory, what it gains on which machine, and its tests.
  • The tests are upstream's and more. test262 gives the same 58 known failures as upstream libquickjs, line for line. On top come over 300 tests of its own, and leak checks under libc's allocator that tracks every allocation.
  • Someone depends on it. It is kept for tifa, a Go framework that renders React pages in it, and each release is measured on tifa's pages before tifa takes it.

Where it comes from

The engine is QuickJS by Fabrice Bellard and Charlie Gordon, the release of 2026-06-04. Jan Mercl translated it to Go with ccgo as modernc.org/libquickjs (v0.13.1 here, under lib/), and the quickjs-go authors wrote the Go API over it, modernc.org/quickjs (v0.25.0 here, at the root). slowjs merges the two into one module, so that a fix to the engine ships with the Go API without waiting for an upstream release of either. docs/upstream.md has the exact versions, what the fork kept and changed, and what changes when you switch from modernc.org/quickjs.

Documentation

Doc Read it when
Fork changes You want to know what slowjs does differently from modernc.org/quickjs, and why
Performance You want to know how fast each release renders, or what was tried
Benchmarks You want to know how this engine compares with the other pure Go JavaScript engines
Changelog You are upgrading and want to know what changed
Architecture You are about to change the code
Testing You are about to commit, or want to check slowjs against upstream
Upgrading You are taking a new upstream release, or new locale data
Releasing, Commits You are making a release or a commit
Upstream You are switching from modernc.org/quickjs, or want to know where the code comes from

The API is documented at pkg.go.dev.

License

The files slowjs created are under the MIT license, and its source files carry an SPDX line. 14 of them repeat parts of the engine, and their SPDX line says MIT AND BSD-3-Clause. Everything that came from upstream keeps its own license: the Go API and the engine as ccgo translated it, including the generated files in lib/ and the fork's rewrites of them, are under BSD-3-Clause (LICENSE, lib/LICENSE) and QuickJS's MIT license (LICENSE-LIBQUICKJS). slowjs's changes to those files are offered under MIT as well, and the files stay usable under their upstream licenses. The locale data that intl embeds stays under the Unicode License V3 and the other notices in intl/LICENSE-DATA, and internal/gointl, a copy of go-intl, under go-intl's Unicode License V3, with the notices of the ICU data and of the V8 and Rust code that go-intl ported, which NOTICE.md lists.

A program that ships a binary built with this module has to carry the notices of these licenses. NOTICE.md says which license covers which path, and which texts to include.

Documentation

Overview

Package slowjs is a pure Go embeddable Javascript engine. It supports the ECMA script 14 (ES2023) specification including modules, asynchronous generators, proxies and BigInt.

See also the original C QuickJS library.

Internationalization

The engine has no Intl. ECMA-402 is not implemented by the C library this package wraps, so a page or bundle reaching for Intl.DateTimeFormat gets a ReferenceError, and the toLocale* methods it ships ignore both their locale and their timeZone argument. Package github.com/liustack/slowjs/intl adds Intl.DateTimeFormat, Intl.NumberFormat and Intl.PluralRules, with CLDR data for every locale Chromium has, and replaces those toLocale* methods, for a VM that opts in by calling its Register. With no locale argument the locale is en-US, and with no timeZone option the zone is UTC.

Performance

Geomeans of time/op over a set of benchmarks, relative to CCGO, lower number is better. Detailed results available in the testdata/benchmarks directory.

 CCGO: github.com/liustack/slowjs/lib
 GOJA: github.com/dop251/goja@v0.0.0-20260723142020-b4aef50fa347
  QJS: github.com/fastschema/qjs@v0.0.6

	                        CCGO     GOJA     QJS
	-----------------------------------------------
	        darwin/amd64    1.000    1.575    1.300
	        darwin/arm64    1.000    1.069    1.080
	         linux/amd64    1.000    1.419    1.213
	         linux/arm64    1.000    1.611    1.267
	       windows/amd64    1.000    2.254    1.438
	-----------------------------------------------
	                        CCGO     GOJA     QJS

Notes

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

Supported platforms and architectures

These combinations of GOOS and GOARCH are currently supported

OS      Arch
-------------
darwin  amd64
darwin  arm64
linux   amd64
linux   arm64
windows amd64

Change Log

2026-07-27: Upgrade to QuickJS release 2026-06-04. The bytecode format changed, code produced by VM.Compile using an older release will no longer load.

2025-10-10: Upgrade to QuickJS release 2025-09-13.

Example (Ping)

Multiple concurrent Javascript virtual machines communicating via Go channels.

package main // import "github.com/liustack/slowjs"

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 (
	DefaultRegExpCache      = lib.RegExpCacheEntries
	DefaultRegExpCacheBytes = lib.RegExpCacheBytes
)

DefaultRegExpCache and DefaultRegExpCacheBytes are how many compiled regexps a new VM keeps at most, and in how many bytes (see VM.SetRegExpCache).

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

Eval flags.

View Source
const DefaultShapeCache = 2048

DefaultShapeCache is the number of shapes a new VM keeps (see VM.SetShapeCache).

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

func Version

func Version() string

Version returns the underlying QuickJS version.

Types

type Args

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

Args are the arguments of a call of a Builtin, and the VM that called it. What they return is borrowed: a Value is valid for the duration of the call (see Value.Dup to keep one longer).

func (Args) Bool

func (a Args) Bool(i int) (bool, bool)

Bool returns argument i when it is a boolean.

func (Args) Latin1

func (a Args) Latin1(i int) (b []byte, ok bool)

Latin1 returns the characters of argument i, one byte each, when it is a string the engine holds that way: all its characters are below U+0100 and it was not left in pieces by concatenation (a rope). Nothing is copied: the bytes are the engine's, valid until the Builtin returns, and must not be changed. ok is false for any other argument, which Args.UTF16 or Args.String reads. A Builtin that looks at the characters of short strings, and often returns its argument as it is, reads them this way.

func (Args) Len

func (a Args) Len() int

Len returns the number of arguments of the call.

func (Args) Number

func (a Args) Number(i int) (float64, bool)

Number returns argument i when it is a number. It converts nothing: a string or a Number object is not a number.

func (Args) String

func (a Args) String(i int) (s string, ok bool)

String returns argument i when it is a string, as UTF-8 with U+FFFD for each lone surrogate. It converts nothing else. ok is false too when there is no memory for the conversion, and the call then fails with the out of memory error whatever the Builtin returns.

func (Args) This

func (a Args) This() Value

This returns the this value of the call.

func (Args) UTF16

func (a Args) UTF16(i int) (u []uint16, ok bool)

UTF16 returns the 16-bit units of argument i when it is a string the engine holds that way, lone surrogates as they are: a string with a character from U+0100 on, not left in pieces by concatenation (a rope). Nothing is copied: the units are the engine's, valid until the Builtin returns, and must not be changed. ok is false for any other argument, which Args.Latin1 or Args.String reads.

func (Args) VM

func (a Args) VM() *VM

VM returns the VM that called the Builtin. The Builtin may evaluate more javascript on it, which runs inside the time left to the call that got there (see VM.SetEvalTimeout).

func (Args) Value

func (a Args) Value(i int) Value

Value returns argument i, undefined when the call has no argument i.

func (Args) WTF8

func (a Args) WTF8(i int) (s string, ok bool)

WTF8 is String, but with each lone surrogate as the three bytes of its code point (0xED, then 0xA0 to 0xBF, then a continuation byte), the form the engine writes it in, which is not UTF-8. A surrogate pair is its one character in UTF-8, as in String. A Builtin that has to tell lone surrogates apart, as a collator orders them, reads its string this way.

type Atom

type Atom = lib.TJSAtom

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

type Builtin

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

A Builtin is a Go function that javascript calls as a built-in function: VM.NewBuiltinFunction makes it a function object shaped like the engine's own built-ins, such as Math.max. It has a name and a length and no other property of its own, no prototype property, it cannot be called with new, and its toString shows [native code] rather than source.

Unlike VM.RegisterFunc and VM.RegisterHostFunc, nothing is converted on the way in and no reflection is involved: the Go function reads the arguments it wants from Args, which give a number only for a number and a string only for a string, and returns a Value. That makes a call about as cheap as a call of a javascript function, which suits functions called once per row of a table, as the formatters of package intl are.

A Builtin belongs to no VM. It is made once, typically into a package level variable, and every VM makes its own function objects of it: Args says which VM called. So a process can have at most 32768 Builtins, and they are never released. VMs on several goroutines call the same Builtin at once, so its function has to be safe for concurrent use, and state it keeps for each VM is found by Args.VM.

func NewBuiltin

func NewBuiltin(name string, length int, fn func(Args) (Value, error)) *Builtin

NewBuiltin returns a Builtin calling fn, for function objects named name whose length property is length.

fn owns the Value it returns, which goes to javascript: a Value fn did not make itself, such as an argument, is returned as its Dup. UndefinedValue and the zero Value return undefined. A Value of another VM is neither returned nor freed: the call throws a TypeError.

A non-nil error is thrown instead, and a Value of the VM returned with it is freed. An *Error, found with errors.As, is thrown as a new error of the constructor its Name says when that is one of Error, TypeError, RangeError, SyntaxError, ReferenceError and InternalError, and as an Error for any other name, with its Message. Any other error is thrown as a TypeError with the error's text, as RegisterHostFunc throws it. The engine cuts the message of a TypeError, RangeError, SyntaxError, ReferenceError or InternalError to 255 bytes. The stack of each starts at the Builtin, as that of an error a built-in of the engine throws does.

A panic in fn goes on in the Go code that called javascript, as one in a function of RegisterHostFunc does.

NewBuiltin panics when name has a NUL byte, when length is not in 0 to 255, when fn is nil, and when the process already has 32768 Builtins: all mistakes of the code making them, which runs once.

type Error

type Error struct {
	Name         string // e.g. "ReferenceError", "TypeError"
	Message      string // the error message
	Stack        string // stack trace, one "    at ...\n" frame per line
	LineNumber   int    // line where the error occurred (parse errors only)
	ColumnNumber int    // column where the error occurred (parse errors only)
	FileName     string // file name where the error occurred (parse errors only)

	// Uncatchable reports that the call ended with an exception no catch
	// or finally block of the script ran for: the VM ran out of memory
	// (OutOfMemory), Interrupt or the eval timeout stopped it
	// (Interrupted), or a Go function that javascript called panicked
	// (neither of the two). Whatever a script throws itself is never
	// uncatchable, whatever its name and message.
	Uncatchable bool
	OutOfMemory bool // the VM ran out of memory (Uncatchable is true)
	Interrupted bool // Interrupt or the eval timeout stopped the script (Uncatchable is true)
	// contains filtered or unexported fields
}

Error holds structured information from a JavaScript exception. The error returned by VM.Eval, VM.Call and the other eval-family methods is an *Error; recover the fields with errors.As:

var e *slowjs.Error
if errors.As(err, &e) {
	fmt.Println(e.Stack)
}

Its Error method reports the same message string the engine has always returned, so existing code comparing the error text keeps working.

Only parse/syntax errors carry the FileName, LineNumber and ColumnNumber properties. For runtime errors those fields are zero and the source location is available only from Stack.

Uncatchable, OutOfMemory and Interrupted tell an error the engine ended the call with from one the script threw, without comparing names or messages, which a script can copy:

if errors.As(err, &e) && e.OutOfMemory {
	// the script needs a higher SetMemoryLimit
}

func ErrorFromValue

func ErrorFromValue(v Value) *Error

ErrorFromValue extracts structured error information from JavaScript error value 'v', typically an Error instance caught from a JS exception.

'v' is borrowed: ErrorFromValue does not free it and the caller remains responsible for its lifetime. Like all VM operations it must be called from the VM's own goroutine. See Error for which fields are populated.

A field that cannot be read is left empty: the string form of 'v' (the result of Error) when its toString throws, a property whose getter throws, and any string field when there is no memory for its UTF-8 copy. The exception that reading throws is dropped, so ErrorFromValue leaves no exception pending.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface. It returns the same string the engine produces for the exception, unchanged from earlier releases. For an Error made in Go, say to throw from a Builtin, it returns what javascript's Error.prototype.toString does: Name, or "Error" when Name is empty, then ": " and Message when Message is not empty.

type HostFunc

type HostFunc func(args []any) (any, error)

HostFunc is a Go function exposed to JavaScript by RegisterHostFunc. It receives the call arguments already converted to Go values and returns a single value to convert back to JS, or a non-nil error which is thrown as a JavaScript exception.

type ModuleLoaderFunc

type ModuleLoaderFunc func(vm *VM, moduleName string) (string, error)

ModuleLoaderFunc defines the signature for a custom module loader. It must return the module's source code (Javascript).

type ModuleNormalizeFunc

type ModuleNormalizeFunc func(vm *VM, moduleBaseName, moduleName string) (string, error)

ModuleNormalizeFunc defines the signature for a custom module normalizer. It receives the base name (the module making the import) and the requested name, and should return the normalized, absolute module name.

type Object

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) Into

func (o *Object) Into(v any) error

Into helps you quickly deserialize an Object into a native Go type. This uses encoding/json.Unmarshal internally, so the target type must be compatible with that package.

Example

JSON unmarshalling into native Go struct.

m, _ := NewVM()
defer m.Close()
obj, _ := m.Eval("obj = {a: 42+314, b: 'foo'}; obj;", EvalGlobal)
var dst struct {
	A int    `json:"a"`
	B string `json:"b"`
}
obj.(*Object).Into(&dst)
fmt.Printf("%#v\n", dst)
Output:
struct { A int "json:\"a\""; B string "json:\"b\"" }{A:356, B:"foo"}

func (*Object) MarshalJSON

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

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 PromiseCapability

type PromiseCapability struct {
	Promise Value
	Resolve Value
	Reject  Value
}

PromiseCapability holds a Promise along with its resolve and reject functions, obtained without evaluating JS source.

func (*PromiseCapability) Dup

Dup duplicates the capability — its promise and both resolving functions — returning a new PromiseCapability that must itself be released via Free.

func (*PromiseCapability) Free

func (pc *PromiseCapability) Free()

Free releases all values held by the capability.

type SharedBytecode

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

SharedBytecode is bytecode of VM.Compile that several VMs load with VM.EvalSharedBytecode from one copy. The source text of each function, which its toString returns, and the table that maps its bytecode to lines, which error stacks read, stay in that copy: a VM that loads it does not copy them into its own memory, as VM.EvalBytecode does. So they count once for the process, outside the memory of every VM: VM.SetMemoryLimit and VM.MemorySize do not count them. Everything else a VM reads from the bytecode, the code of its functions among it, is still its own.

The copy lives outside Go's heap. It stays while the SharedBytecode is open or a VM that loaded it is: Close gives up the hold of the SharedBytecode, and each VM gives up its own when it is closed. The last of them frees it.

A SharedBytecode is safe for use by several goroutines at once, and so several VMs on several goroutines can load it at the same time.

func NewSharedBytecode

func NewSharedBytecode(bytecode []byte) (*SharedBytecode, error)

NewSharedBytecode copies bytecode, which VM.Compile returned, once, for VMs to load with VM.EvalSharedBytecode. As with VM.EvalBytecode, the bytecode is not checked, so it must not come from an untrusted source.

func (*SharedBytecode) Close

func (b *SharedBytecode) Close() error

Close gives up the hold of b on its bytes: they are freed now if no VM that loaded them is open, or else when the last of them is closed. Loading b after Close fails. Closing b again does nothing.

func (*SharedBytecode) Len

func (b *SharedBytecode) Len() int

Len returns the size of the bytecode in bytes, the memory it takes once for the process.

type Strip

type Strip int

Strip says what a VM leaves out of the functions it compiles (see VM.SetStrip).

const (
	// StripSource leaves out the source text: the toString of a function
	// shows [native code] instead, as it does for the engine's built-ins.
	StripSource Strip = Strip(lib.MJS_STRIP_SOURCE)
	// StripDebug leaves out the file name, line and column numbers, so a
	// stack frame of the function shows only its name, and the source too.
	StripDebug Strip = Strip(lib.MJS_STRIP_DEBUG)
)

type Undefined

type Undefined struct{}

Undefined represents the Javascript value "undefined".

func (Undefined) String

func (u Undefined) String() string

String implements fmt.Stringer.

type Unsupported

type Unsupported struct{}

Unsupported represents an unsupported Javascript value.

func (Unsupported) String

func (u Unsupported) String() string

String implements fmt.Stringer.

type VM

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, except for VM.Interrupt.

func NewVM

func NewVM() (*VM, error)

NewVM returns a newly created VM.

func (*VM) Call

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
*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

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

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

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) Close

func (m *VM) Close() error

Close releases the resources held by 'm'. That includes the module loader and normalizer set by VM.SetModuleLoader: Close removes them, so a closed VM keeps nothing they close over reachable.

func (*VM) Compile

func (m *VM) Compile(javascript string, flags int) ([]byte, error)

Compile compiles the bytecode representation of the passed script.

The returned bytecode can be later executed using EvalBytecode or EvalBytecodeValue.

Note: The bytecode format is linked to a given QuickJS version.

func (*VM) Eval

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
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) EvalBytecode

func (m *VM) EvalBytecode(bytecode []byte) (r any, err error)

EvalBytecode is like Eval but uses QuickJS bytecode.

Note: The bytecode format is linked to a given QuickJS version. Moreover, no security check is done before its execution. Hence the bytecode should not be loaded from untrusted sources.

Note: Javascript 'this' is always set to the global context.

func (*VM) EvalBytecodeValue

func (m *VM) EvalBytecodeValue(bytecode []byte) (r Value, err error)

EvalBytecodeValue is like EvalValue but operates on QuickJS bytecode.

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.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) EvalSharedBytecode

func (m *VM) EvalSharedBytecode(b *SharedBytecode) (r any, err error)

EvalSharedBytecode is like VM.EvalBytecode, but loads b without copying the source text and line tables of its functions into m (see SharedBytecode). m holds b until it is closed.

func (*VM) EvalSharedBytecodeValue

func (m *VM) EvalSharedBytecodeValue(b *SharedBytecode) (r Value, err error)

EvalSharedBytecodeValue is like VM.EvalBytecodeValue, but loads b as VM.EvalSharedBytecode does.

func (*VM) EvalThis

func (m *VM) EvalThis(obj Value, javascript string, flags int) (r any, err error)

EvalThis is like eval but sets javascript 'this' to 'obj'.

func (*VM) EvalThisValue

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

EvalThisValue is like EvalValue but sets javascript 'this' to obj.

func (*VM) EvalValue

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.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) ExecutePendingJobs

func (m *VM) ExecutePendingJobs() (n int, err error)

ExecutePendingJobs runs queued Javascript jobs — Promise reactions (the .then/.catch/.finally callbacks) and async/await continuations — until the queue is empty, returning the number of jobs executed. The runtime does not run these itself: an embedding event loop calls ExecutePendingJobs at the end of each turn to drain the microtask queue (so a resolved Promise's .then runs, a pending await resumes, etc.). An exception thrown by a job is returned as an error — the first one — while the remaining jobs still run; a self-feeding job queue is bounded so it cannot hang the host.

func (*VM) GCThreshold

func (m *VM) GCThreshold() uintptr

GCThreshold returns the memory threshold at which a GC will be performed, in bytes. The engine checks it when it makes an object: when VM.MemorySize would pass it, the engine runs a GC and then sets the threshold to 1.5 times what the GC left. So a threshold other than the one VM.SetGCThreshold set last means the engine ran a GC since. VM.RunGC does not change it.

As with SetGCThreshold, no other goroutine may use m meanwhile (see VM), and a call after Close panics.

func (*VM) GetProperty

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

GetProperty returns this.prop.

func (*VM) GetPropertyValue

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

GetPropertyValue returns this.prop.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) GlobalObject

func (m *VM) GlobalObject() (r Value)

GlobalObject returns m's global object.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) Interrupt

func (m *VM) Interrupt()

Interrupt requests 'm' to interrupt Javascript evaluation. Interrupt can be called asynchronously, from any goroutine, at any time m is evaluating a script. The request stays pending until the outermost call into javascript returns, so javascript a host function evaluates on m meanwhile does not clear it. Interrupt is also safe while m closes and after Close, where it does nothing, so a watchdog need not know whether the script it guards has finished.

Example
const timeout = time.Second
m, _ := NewVM()
defer m.Close()

go func() {
	time.Sleep(timeout)
	m.Interrupt()
}()

t0 := time.Now()
r, err := m.Eval(`
function f() {
	var sink;
	for (var i = 0; i < 10000; i++) {
		sink += 42;
		sink -= 42;
	}
}

(function() {
	for (var i = 0; i < 10000; i++) {
		f();
	}
	return 42;
})();
`, EvalGlobal)
d := time.Since(t0)
step := timeout / 5
d = d / step * step
fmt.Println(r, err, d)
Output:
<nil> InternalError: interrupted 1s

func (*VM) MemorySize

func (m *VM) MemorySize() uintptr

MemorySize returns the memory m takes, in bytes, as VM.SetMemoryLimit and VM.SetGCThreshold count it. After VM.RunGC it is what the objects that javascript can still reach take, with the arenas they are in, plus the runtime's own structures and the object shapes it keeps for reuse.

As with SetGCThreshold, no other goroutine may use m meanwhile (see VM), and a call after Close panics.

func (*VM) NewArray

func (m *VM) NewArray(vals ...Value) (Value, error)

NewArray returns a new array of vals, made as an array literal makes one: its elements are own properties, which no setter a page put on Array.prototype sees. It borrows vals, as VM.SetPropertyValue and Value.Call do: the caller still owns them. The zero Value is undefined. A Value of another VM, a number too, is an error, as it is for a Builtin to return one.

func (*VM) NewAtom

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

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

func (*VM) NewBuiltinFunction

func (m *VM) NewBuiltinFunction(b *Builtin) (Value, error)

NewBuiltinFunction returns a new function object of b. The caller owns it: it passes it to javascript, for example as an argument of Value.Call or with VM.SetPropertyValue, and then frees it.

func (*VM) NewFloat64

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

NewFloat64 returns a new Value from 'n'.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) NewInt

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

NewInt returns a new Value from 'n'.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) NewObjectValue

func (m *VM) NewObjectValue() (r Value, err error)

NewObjectValue returns a new Value representing '{}'.

func (*VM) NewPromiseCapability

func (m *VM) NewPromiseCapability() (*PromiseCapability, error)

NewPromiseCapability creates a new JavaScript Promise along with its resolve and reject functions.

The returned PromiseCapability must be freed via Free when no longer needed.

To expose a promise to JS from a Go function registered with RegisterFunc, return pc.Promise from the Go function. The resolve/reject values captured by the closure need no Dup because the C API already holds a reference for each:

m.RegisterFunc("delayed", func(delay int) Value {
    pc, err := m.NewPromiseCapability()
    if err != nil { panic(err) }
    youreventloop.Schedule(func() {
        time.Sleep(time.Duration(delay) * time.Millisecond)
        pc.Resolve.Call(UndefinedValue, "done")
        pc.Resolve.Free()
        pc.Reject.Free()
        // pc.Promise.Free() // <- only if returned pc.Promise.Dup()!
    })
    return pc.Promise
}, false)

func (*VM) NewString

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

NewString returns a new Value from 's'.

Note: See the Value documentation for details about manual memory management of Value objects.

func (*VM) NewStringLatin1

func (m *VM) NewStringLatin1(b []byte) (Value, error)

NewStringLatin1 returns a new string of the characters b, one byte each: a byte is the character of its value, U+0000 to U+00FF. Unlike VM.NewString it decodes no UTF-8, so a Builtin that read its argument with Args.Latin1 returns what it made of it this way, as one copy.

func (*VM) NewStringUTF16

func (m *VM) NewStringUTF16(u []uint16) (Value, error)

NewStringUTF16 returns a new string of the 16-bit units u, lone surrogates as they are. A Builtin that read its argument with Args.UTF16 returns what it made of it this way, as one copy.

func (*VM) Now

func (m *VM) Now() time.Time

Now reads the VM's wall clock with time.Time precision. Date and Intl use whole milliseconds, while Temporal preserves nanoseconds. Like the VM, Now is not safe for concurrent use.

func (*VM) OnClose

func (m *VM) OnClose(f func())

OnClose arranges for f to run when Close runs, after the VM is released, so f must not use it. Close runs the functions once each, in the order OnClose was given them. Nothing else runs them: a VM that is never closed never runs them, and neither does the garbage collector.

A package that keeps something per VM in a table of its own, such as a closure that may reference the VM, removes it here, so that the table does not keep the VM, or what it closes over, reachable after Close.

func (*VM) RegisterFunc

func (m *VM) RegisterFunc(name string, f any, wantThis bool) 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.

The function becomes the property 'name' of the global object: writable, configurable and not enumerable. A configurable property already there, such as one a script assigned or a built-in like Array, is replaced. A property that is not configurable, such as undefined, NaN, Infinity or one a script declared with var or function, is kept and RegisterFunc returns an error, as it does when the global object is not extensible. Registering a name a Go function of this VM already has returns an error too, even when a script has since deleted or replaced that function. The name is the one javascript sees: invalid UTF-8 is replaced as Go decodes it (see Eval), so two Go strings can be the same name, and a NUL byte, which would end the name, is an error.

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

For binding host objects (a DOM, console, fetch, …) prefer RegisterHostFunc, which gives the more convenient single-return / error-throws-exception shape.

A function javascript calls often, say once per item of a list, or one that has to look like a built-in, is better a Builtin: it converts nothing and uses no reflection, so a call costs far less.

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) RegisterHostFunc

func (m *VM) RegisterHostFunc(name string, fn HostFunc) error

RegisterHostFunc exposes fn to JavaScript as a global function named name, using the calling convention an embedding host wants: every JS call argument arrives in the single []any slice (already converted to Go values), fn returns exactly one value to convert back to JS, and a non-nil error is thrown as a JavaScript exception.

This differs from RegisterFunc, whose reflection mapping turns a Go multiple-return into a JS array and converts a returned error like any other value (so a (value, error) pair becomes the array [value, error] rather than a value-or-throw). RegisterHostFunc is the better fit for binding host objects; RegisterFunc remains for direct, typed Go calls.

Each call converts every argument to a Go value through reflection. A function javascript calls often, or one that has to look like a built-in, is better a Builtin, which reads only the arguments it wants, as they are.

The name is handled as by RegisterFunc, including when the global object already has it.

func (*VM) RunGC

func (m *VM) RunGC()

RunGC performs a GC now. It frees what javascript can no longer reach, objects in reference cycles included, and gives the small blocks the runtime keeps for reuse back to their arenas. Unlike the GC the engine runs when VM.MemorySize passes VM.GCThreshold, it leaves the threshold as it is. It runs no javascript: the callbacks of a FinalizationRegistry whose targets it frees are queued and run at the next VM.ExecutePendingJobs, so what they hold goes at a later GC.

As with SetGCThreshold, no other goroutine may use m meanwhile (see VM), and a call after Close panics.

func (*VM) RunYoungGC

func (m *VM) RunYoungGC()

RunYoungGC performs a young GC now: it frees the reference cycles among the objects made since the last GC, young or full, and makes the objects it keeps old. It visits only those objects, so it takes about as long as the young objects are many, where VM.RunGC visits every object twice. It is meant to run after each unit of work, such as a page render, whose objects are mostly garbage once it ends.

It frees no object RunGC would keep. It is conservative where RunGC is not: a cycle that runs through an old object, and a value of a WeakMap entry, with what it holds, while its map is reachable or old, stay until the next RunGC or the GC the engine runs by itself. So memory can grow over many young GCs: run RunGC from time to time, such as when VM.MemorySize after a young GC has grown well past what it was after the last RunGC.

Like RunGC, it removes the weak references to what it frees, queues the callbacks of a FinalizationRegistry for the next VM.ExecutePendingJobs, gives the small blocks the runtime keeps for reuse back to their arenas, runs no javascript and leaves VM.GCThreshold as it is. No other goroutine may use m meanwhile (see VM), and a call after Close panics.

func (*VM) SetCanBlock

func (m *VM) SetCanBlock(value bool)

SetCanBlock configures m's blocking mode.

func (*VM) SetClock

func (m *VM) SetClock(now func() time.Time)

SetClock sets the wall clock of this VM. Date.now(), new Date(), Date(), Intl's current time and Temporal.Now (unless temporal.Clock overrides it) read now. nil restores time.Now. Set it only between calls into JavaScript, never concurrently with the VM or from a callback running inside it. The callback must not block. If several VMs share it, it must be safe for concurrent calls. Close releases it. It does not affect performance.now(), eval timeouts, Atomics.wait or the random seed.

func (*VM) SetDefaultModuleLoader

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) SetEvalTimeout

func (m *VM) SetEvalTimeout(d time.Duration) error

SetEvalTimeout sets the timeout for the various eval functions. Passing zero 'd' disables timeouts. The timeout starts with the outermost call into javascript: a host function that evaluates more javascript on m runs inside the time left to the call that got it there. The timeout runs on the monotonic clock, so setting the system clock does not move it. A timeout too long for that clock (about 292 years) never fires.

Example
m, _ := NewVM()
defer m.Close()

for _, timeout := range []time.Duration{100 * time.Millisecond, time.Second, 3 * time.Second} {
	m.SetEvalTimeout(timeout)
	t0 := time.Now()
	r, err := m.Eval(`
function f() {
	var sink;
	for (var i = 0; i < 10000; i++) {
		sink += 42;
		sink -= 42;
	}
}

(function() {
	for (var i = 0; i < 10000; i++) {
		f();
	}
	return 42;
})();
`, EvalGlobal)
	d := time.Since(t0)
	min := timeout / 2
	max := timeout * 3 / 2
	fmt.Println(r, err, timeout, d >= min && d <= max)
}
Output:
<nil> InternalError: interrupted 100ms true
<nil> InternalError: interrupted 1s true
<nil> InternalError: interrupted 3s true

func (*VM) SetGCThreshold

func (m *VM) SetGCThreshold(threshold uintptr)

SetGCThreshold sets the memory threshold at which a GC will be performed, in bytes.

func (*VM) SetMaxStackSize

func (m *VM) SetMaxStackSize(threshold uintptr)

SetMaxStackSize limits the JS call depth, in libc.TLS stack slots (~one per JS call frame in the CGo-free port). Deep recursion then throws a catchable RangeError instead of overflowing the Go goroutine stack. 0 restores the MaxStackSlots default.

func (*VM) SetMemoryLimit

func (m *VM) SetMemoryLimit(limit uintptr)

SetMemoryLimit limits m's maxmimum memory usage, in bytes. Small values of 'limit' are not honored because the VM needs to allocate memory also for the exception object itself. The particular value of "small" is unspecified and subject to change without notice.

func (*VM) SetModuleLoader

func (m *VM) SetModuleLoader(loader ModuleLoaderFunc, normalize ModuleNormalizeFunc)

SetModuleLoader configures custom Go functions to load and normalize Javascript modules. Passing nil for 'loader' removes the custom loader, and so does Close; until then the engine keeps 'loader' and 'normalize', and whatever they close over, reachable. If 'normalize' is nil, the engine falls back to the default QuickJS module normalizer.

func (*VM) SetProperty

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

SetProperty sets this.prop = val.

func (*VM) SetPropertyValue

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

SetPropertyValue sets this.prop = val.

func (*VM) SetRegExpCache

func (m *VM) SetRegExpCache(n, bytes int) error

SetRegExpCache sets how many compiled regexps m keeps at most, n, and in how many bytes, 0 for none, and lets go of the ones it kept. Compiling a regexp whose pattern and flags are strings (a literal, new RegExp, RegExp, RegExp.prototype.compile, and the copy split makes of its regexp) takes the bytecode m kept for the same pattern and flags instead of compiling again. Code that makes a new RegExp of the same pattern on every call, as a syntax highlighter made anew for each render does, then compiles each pattern once. When a new one does not fit, m lets go of the one used least recently. A new VM keeps DefaultRegExpCache regexps in DefaultRegExpCacheBytes bytes, and n can be at most math.MaxInt32/8.

What m keeps counts against the limit of SetMemoryLimit, the patterns and flags as well as the bytecode, and is not let go when an allocation fails. So a script that runs close to its memory limit can run out of memory with the cache on where it would not without it.

func (*VM) SetShapeCache

func (m *VM) SetShapeCache(n int) error

SetShapeCache sets how many shapes of plain objects m keeps after their last object is gone, 0 for none. The shape of an object is its keys in order: an object that gets the same keys in the same order as an object before it, as the objects a function returns often do, finds the shapes it goes through kept, instead of copying a shape for each key. m keeps the last n shapes it would have freed, of objects whose prototype is Object.prototype and that have at most 32 keys, none of them a symbol that is not registered. A new VM keeps DefaultShapeCache shapes.

Kept shapes take memory that counts against the limit of SetMemoryLimit, and each holds the atoms of its keys. A kept shape of a few keys takes about 100 bytes. A shape of 32 keys of about ten characters, that no other object has, takes about 3 KiB with its atoms, so 2048 of them take about 6 MiB, and the table of atoms m grew for them keeps its size after they are let go. So a script that runs close to its memory limit can run out of memory with the cache on where it would not without it: give such a VM a smaller cache, or 0. Kept shapes are not let go when an allocation fails.

func (*VM) SetStrip

func (m *VM) SetStrip(s Strip)

SetStrip sets what m leaves out of the functions it compiles from then on: StripSource, StripDebug, both or 0, the default, for nothing. That is every function m compiles: from source by Eval and the like, into bytecode by Compile, and in the page's own code by eval and new Function. Functions compiled before keep what they have. The bytecode of Compile keeps what is left out left out in any VM that loads it, which suits a script that plays a built-in, such as a polyfill whose functions should not show their source: compile it on a VM of its own, so that the page's functions keep their source.

func (*VM) StdAddHelpers

func (m *VM) StdAddHelpers() error

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

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.

See TestMemgrind for an example of how to detect memory leaks caused not only by improper Value use.

func (Value) Any

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) Call

func (v Value) Call(this Value, args ...any) (r any, err error)

Call evaluates 'v(args...)' with 'this' as the receiver and returns the resulting (value, error). 'v' must be a Javascript function. Pass UndefinedValue as 'this' when the function does not use it.

resolve.Call(slowjs.UndefinedValue, "done")
reject.Call(slowjs.UndefinedValue, err.Error())

Argument types are converted using the same rules as in VM.Call and the result is unpacked using the same rules as there are for the return value of VM.Eval. Note that a Go error is not one of the recognized types, so it is marshaled to JSON like any other value — and because the standard error types have no exported fields that yields '{}'. Pass err.Error() instead.

Note: A Value passed to a Go function registered by VM.RegisterFunc is valid only for the duration of that call. To call it later, for example to settle a promise after the Go function has returned, retain it using Value.Dup and release it using Value.Free when done.

Note: See the Value documentation for details about manual memory management of Value objects.

func (Value) CallValue

func (v Value) CallValue(this Value, args ...any) (r Value, err error)

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

Note: See the Value documentation for details about manual memory management of Value objects.

func (Value) Dup

func (v Value) Dup() Value

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

func (*Value) Free

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 deprecated

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

GetProperty returns v.prop. The 'this' argument is ignored.

Deprecated: The 'this' argument is vestigial, the property is always looked up on 'v'. Use Value.Property instead.

func (Value) GetPropertyValue

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

GetPropertyValue returns v.prop.

func (Value) IsUndefined

func (v Value) IsUndefined() bool

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

func (Value) MarshalJSON

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

MarshalJSON implements encoding/json.Marshaler.

func (Value) Property

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

Property returns v.prop. It is Value.GetProperty without the vestigial 'this' argument.

func (Value) SetProperty

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

SetProperty sets v.prop = val.

func (Value) SetPropertyValue

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

SetPropertyValue sets v.prop = val.

func (Value) Then

func (v Value) Then(onFulfilled, onRejected func(Value)) (_ Value, err error)

Then attaches fulfillment and rejection handlers to the Promise v, returning the new promise for chaining. Either onFulfilled or onRejected may be nil.

func (*Value) VM

func (v *Value) VM() *VM

VM returns the VM associated with 'v'.

Directories

Path Synopsis
Package fmt exposes some functionality of the Go stdlib fmt package to a slowjs.VM.
Package fmt exposes some functionality of the Go stdlib fmt package to a slowjs.VM.
internal
fixlib command
Command fixlib reapplies this fork's changes to the engine under lib/ after its files were taken from upstream libquickjs, as happens when a git subtree pull hits a conflict in a generated ccgo_*.go file and the upstream version is checked out.
Command fixlib reapplies this fork's changes to the engine under lib/ after its files were taken from upstream libquickjs, as happens when a git subtree pull hits a conflict in a generated ccgo_*.go file and the upstream version is checked out.
intlhook
Package intlhook connects the Intl and Temporal shims.
Package intlhook connects the Intl and Temporal shims.
Package intl implements ECMA-402, the ECMAScript Internationalization API, for a slowjs.VM.
Package intl implements ECMA-402, the ECMAScript Internationalization API, for a slowjs.VM.
Package libquickjs is a pure Go embeddable Javascript engine.
Package libquickjs is a pure Go embeddable Javascript engine.
Package temporal implements Temporal, the ECMAScript date and time API, for a slowjs.VM.
Package temporal implements Temporal, the ECMAScript date and time API, for a slowjs.VM.

Jump to

Keyboard shortcuts

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