Go bindings to Mozilla Javascript Engine SpiderMonkey.
OS | CPU | Version |
---|---|---|
Darwin | arm64 | macOS 14 |
FreeBSD | amd64 | FreeBSD 14 |
Linux | amd64 | Debian 12 |
NetBSD | amd64 | NetBSD 9 |
OpenBSD | amd64 | OpenBSD 7 |
$ go get github.com/bhuisgen/gomonkey
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// lock the goroutine to its own OS thread
runtime.LockOSThread()
defer runtime.UnlockOSThread()
// create the context
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy() // destroy after usage
// use the context only inside this goroutine
}()
wg.Wait()
var wg sync.WaitGroup
wg.Add(1)
go func() {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
// get the global object ...
global, err := ctx.Global()
if err != nil {
return
}
defer global.Release() // release after usage
// ... or create a new object ...
object, err := gomonkey.NewObject(ctx)
if err != nil {
return
}
defer object.Release() // release after usage
// ... add a property ...
propValue1, err := gomonkey.NewValueString(ctx, "string")
if err != nil {
return
}
defer propValue1.Release() // release after usage
if err := object.Set("key1", propValue1); err != nil {
return
}
// ... check if a property exists ...
b := object.Has("key2")
_ = b // use result
// ... get a property value ...
propValue3, err := object.Get("key3")
if err != nil {
return
}
defer propValue3.Release() // release after usage
// ... delete a property ...
if err := object.Delete("key4"); err != nil {
return
}
}()
wg.Wait()
To evaluate some JS code, evaluate it directly:
var wg sync.WaitGroup
wg.Add(1)
go func() {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
result, err := ctx.Evaluate([]byte("(() => { return 'result'; })()"))
if err != nil {
return
}
defer result.Release() // release after usage
if !result.IsString() { // check value type
return
}
_ = result.String() // use the value
}()
wg.Wait()
If you need to execute multiple times the same JS code, compile it as a script and execute it as many times as needed:
var wg sync.WaitGroup
wg.Add(1)
go func() {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
// load your script ...
code, err := os.ReadFile("script.js")
if err != nil {
return
}
// compile the script ...
script, err := ctx.CompileScript("script.js", code)
if err != nil {
return
}
defer script.Release() // release after usage
// .. and execute it ...
result1, err := ctx.ExecuteScript(script)
if err != nil {
return
}
defer result1.Release() // release after usage
if !result1.IsString() { // check value type
return
}
_ = result1.String() // use the value
// ... one more time please ...
result2, err := ctx.ExecuteScript(script)
if err != nil {
return
}
defer result2.Release() // release after usage
if !result2.IsString() { // check value type
return
}
_ = result2.String() // use the value
}()
wg.Wait()
To share a script between several contexts, compile it as a stencil:
var wg sync.WaitGroup
ch := make(chan *gomonkey.Stencil)
// compile script as a stencil in a frontend context ...
wg.Add(1)
go func(ch chan<- *gomonkey.Stencil) {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewFrontendContext()
if err != nil {
return
}
defer ctx.Destroy()
code, err := os.ReadFile("script.js")
if err != nil {
return
}
stencil, err := ctx.CompileScriptToStencil("script.js", code)
if err != nil {
return
}
ch <- stencil
close(ch)
}(ch)
// ... and execute it from another context ...
wg.Add(1)
go func(ch <-chan *gomonkey.Stencil) {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
stencil := <-ch // share the stencil
defer stencil.Release() // release the stencil from the last goroutine
for i := 0; i < 10; i++ {
value, err := ctx.ExecuteScriptFromStencil(stencil)
if err != nil {
return
}
defer value.Release() // release after usage
}
}(ch)
wg.Wait()
var wg sync.WaitGroup
wg.Add(1)
go func() {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
// get the global object ...
global, err := ctx.Global()
if err != nil {
return
}
defer global.Release() // release after usage
// ... implement your Go function ...
hello := func(args []*gomonkey.Value) (*gomonkey.Value, error) {
// parse any arguments but do not release them
for _, arg := range args {
_ = arg
}
// create the returned value
retValue, err := gomonkey.NewValueString(ctx, "hello world!")
if err != nil {
return nil, errors.New("create value")
}
return retValue, nil // do not release it
}
// ... and register it as a JS function
if err := ctx.DefineFunction(global, "hello", hello, 0, 0); err != nil {
return
}
}()
wg.Wait()
var wg sync.WaitGroup
wg.Add(1)
go func() {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
// create a new function ...
fn, err := gomonkey.NewFunction(ctx, "name", func(args []*gomonkey.Value) (*gomonkey.Value, error) {
// parse any arguments but do not release them
for _, arg := range args {
_ = arg
}
// create the returned value
retValue, err := gomonkey.NewValueString(ctx, "hello world!")
if err != nil {
return nil, errors.New("create value")
}
return retValue, nil // do not release it
})
if err != nil {
return
}
defer fn.Release() // release after usage
// ... you can call it with a receiver (object) ...
receiver, err := gomonkey.NewObject(ctx)
if err != nil {
return
}
defer receiver.Release() // release after usage
arg0, err := gomonkey.NewValueString(ctx, "value")
if err != nil {
return
}
defer arg0.Release() // release after usage
result, err := fn.Call(receiver, arg0)
if err != nil {
return
}
defer result.Release() // release after usage
}()
wg.Wait()
var wg sync.WaitGroup
wg.Add(1)
go func() {
runtime.LockOSThread()
defer func() {
runtime.UnlockOSThread()
wg.Done()
}()
ctx, err := gomonkey.NewContext()
if err != nil {
return
}
defer ctx.Destroy()
// create a new object ...
object, err := gomonkey.NewObject(ctx)
if err != nil {
return
}
defer object.Release() // release after usage
// ... create a function ...
fn, err := gomonkey.NewFunction(ctx, "hello", func(args []*gomonkey.Value) (*gomonkey.Value, error) {
// parse any arguments but do not release them
for _, arg := range args {
_ = arg
}
// create the returned value
retValue, err := gomonkey.NewValueString(ctx, "hello world!")
if err != nil {
return nil, errors.New("create value")
}
return retValue, nil // do not release it
})
if err != nil {
return
}
// ... set it as a property on the object ...
if err := object.Set("showHello", fn.AsValue()); err != nil {
return
}
// ... and call this new method ...
result, err := object.Call("showHello")
if err != nil {
return
}
defer result.Release() // release after usage
}()
wg.Wait()
The shared library libmozjs-115.so
is required for compilation and execution.
Prebuilt libraries are available in the deps/lib directory. If your system is not available, you have to build the library following the instructions in the build document.
Darwin:
Copy the library in the same directory as the binary file:
$ cp deps/lib/freebsd_amd64/release/lib/libmozjs-115.so .
Check the current reference to the library from the binary file:
$ otool -l ./binary | grep libmozjs
Update the reference to the new libary path:
$ install_name_tool -change /Users/boris/Projects/gomonkey/deps/lib/darwin_arm64/release/lib/libmozjs-115.dylib @rpath/libmozjs-115.dylib ./binary
FreeBSD:
Copy the library in your system library path:
$ sudo cp deps/lib/freebsd_amd64/release/lib/libmozjs-115.so /usr/lib/
To troubleshoot any missing system libraries, use the ldd
command.
Linux:
Copy the library in your system library path:
$ sudo cp deps/lib/linux_amd64/release/lib/libmozjs-115.so /usr/lib/x86_64-linux-gnu/
To troubleshoot any missing system libraries, use the ldd
command.
NetBSD:
Copy the library in your system library path:
$ sudo cp deps/lib/netbsd_amd64/release/lib/libmozjs-115.so /usr/lib/
To troubleshoot any missing system libraries, use the ldd
command.
OpenBSD:
Copy the library in your system library path:
$ sudo cp deps/lib/openbsd_amd64/release/lib/libmozjs-115.so.1.0 /usr/lib/
To troubleshoot any missing system libraries, use the ldd
command.
The SpiderMonkey library should be built with debugging support during development. Please refer to the instructions in the build document.
First step is to run the unit tests:
$ go test ./tests/...
Then run the smoke tests:
$ go run cmd/smoke/main.go