Otto
A JavaScript interpreter in Go (golang)
Install / Use
/learn @robertkrimen/OttoREADME
otto
Basic Usage
Package otto is a JavaScript parser and interpreter written natively in Go.
To use import it with the following:
import (
"github.com/robertkrimen/otto"
)
Run something in the VM
vm := otto.New()
vm.Run(`
abc = 2 + 2;
console.log("The value of abc is " + abc); // 4
`)
Get a value out of the VM
if value, err := vm.Get("abc"); err == nil {
if value_int, err := value.ToInteger(); err == nil {
fmt.Println(value_int)
} else {
fmt.Printf("Error during conversion: %v\n", err)
}
} else {
fmt.Printf("Error getting value: %v\n", err)
}
Set a number
vm.Set("def", 11)
vm.Run(`
console.log("The value of def is " + def);
// The value of def is 11
`)
Set a string
vm.Set("xyzzy", "Nothing happens.")
vm.Run(`
console.log(xyzzy.length); // 16
`)
Get the value of an expression
value, _ = vm.Run("xyzzy.length")
{
// value is an int64 with a value of 16
value, _ := value.ToInteger()
}
An error happens
_, err = vm.Run("abcdefghijlmnopqrstuvwxyz.length")
if err != nil {
// err = ReferenceError: abcdefghijlmnopqrstuvwxyz is not defined
// If there is an error, then value.IsUndefined() is true
...
}
Set a Go function
vm.Set("sayHello", func(call otto.FunctionCall) otto.Value {
fmt.Printf("Hello, %s.\n", call.Argument(0).String())
return otto.Value{}
})
Set a Go function that returns something useful
vm.Set("twoPlus", func(call otto.FunctionCall) otto.Value {
right, _ := call.Argument(0).ToInteger()
result, _ := vm.ToValue(2 + right)
return result
})
Use the functions in JavaScript
result, _ = vm.Run(`
sayHello("Xyzzy"); // Hello, Xyzzy.
sayHello(); // Hello, undefined
result = twoPlus(2.0); // 4
`)
Parser
A separate parser is available in the parser package if you're interested in only building an AST.
Parse and return an AST
filename := "" // A filename is optional
src := `
// Sample xyzzy example
(function(){
if (3.14159 > 0) {
console.log("Hello, World.");
return;
}
var xyzzy = NaN;
console.log("Nothing happens.");
return xyzzy;
})();
`
// Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
program, err := parser.ParseFile(nil, filename, src, 0)
Setup
You can run (Go) JavaScript from the command line with otto.
go install github.com/robertkrimen/otto/otto@latest
Run JavaScript by entering some source on stdin or by giving otto a filename:
otto example.js
Underscore
Optionally include the JavaScript utility-belt library, underscore, with this import:
import (
"github.com/robertkrimen/otto"
_ "github.com/robertkrimen/otto/underscore"
)
// Now every otto runtime will be initialized with Underscore.
For more information: underscore
Caveat Emptor
The following are some limitations with otto:
use strictwill parse, but does nothing.- The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification.
- Otto targets ES5. Some ES6 features, e.g. Typed Arrays, are not supported. Pull requests to add functionality are always welcome.
Regular Expression Incompatibility
Go translates JavaScript-style regular expressions into something that is
"regexp" compatible via parser.TransformRegExp. Unfortunately, RegExp requires
backtracking for some patterns, and backtracking is not supported by Go
re2.
Therefore, the following syntax is incompatible:
(?=) // Lookahead (positive), currently a parsing error
(?!) // Lookahead (backhead), currently a parsing error
\1 // Backreference (\1, \2, \3, ...), currently a parsing error
A brief discussion of these limitations: Regexp (?!re)
More information about re2
In addition to the above, re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript definition, on the other hand, also includes \v, Unicode
"Separator, Space", etc.
Halting Problem
If you want to stop long running executions (like third-party code), you can use the interrupt channel to do this:
package main
import (
"errors"
"fmt"
"os"
"time"
"github.com/robertkrimen/otto"
)
var halt = errors.New("Stahp")
func main() {
runUnsafe(`var abc = [];`)
runUnsafe(`
while (true) {
// Loop forever
}`)
}
func runUnsafe(unsafe string) {
start := time.Now()
defer func() {
duration := time.Since(start)
if caught := recover(); caught != nil {
if caught == halt {
fmt.Fprintf(os.Stderr, "Some code took too long! Stopping after: %v\n", duration)
return
}
panic(caught) // Something else happened, repanic!
}
fmt.Fprintf(os.Stderr, "Ran code successfully: %v\n", duration)
}()
vm := otto.New()
vm.Interrupt = make(chan func(), 1) // The buffer prevents blocking
watchdogCleanup := make(chan struct{})
defer close(watchdogCleanup)
go func() {
select {
case <-time.After(2 * time.Second): // Stop after two seconds
vm.Interrupt <- func() {
panic(halt)
}
case <-watchdogCleanup:
}
close(vm.Interrupt)
}()
vm.Run(unsafe) // Here be dragons (risky code)
}
Where is setTimeout / setInterval?
These timing functions are not part of the ECMA-262 specification. They typically belong to the window object in a browser environment. While it is possible to implement similar functionality in Go, it generally requires wrapping Otto in an event loop.
For an example of how this could be done in Go with otto, see natto.
Here is some more discussion of the issue:
Usage
var ErrVersion = errors.New("version mismatch")
type Error
type Error struct {}
An Error represents a runtime error, e.g. a TypeError, a ReferenceError, etc.
func (Error) Error
func (err Error) Error() string
Error returns a string representation of the error
TypeError: 'def' is not a function
func (Error) String
func (err Error) String() string
String returns a description of the error and a trace of where the error occurred.
TypeError: 'def' is not a function
at xyz (<anonymous>:3:9)
at <anonymous>:7:1/
type FunctionCall
type FunctionCall struct {
This Value
ArgumentList []Value
Otto *Otto
}
FunctionCall is an encapsulation of a JavaScript function call.
func (FunctionCall) Argument
func (self FunctionCall) Argument(index int) Value
Argument will return the value of the argument at the given index.
If no such argument exists, undefined is returned.
type Object
type Object struct {}
Object is the representation of a JavaScript object.
func (Object) Call
func (self Object) Call(name string, argumentList ...interface{}) (Value, error)
Call a method on the object.
It is essentially equivalent to:
var method, _ := object.Get(name)
method.Call(object, argumentList...)
An undefined value and an error will result if:
- There is an error during conversion of the argument list
- The property is not actually a function
- An (uncaught) exception is thrown
func (Object) Class
func (self Object) Class() string
Class will return the class string of the object.
The return value will (generally) be one of:
Object
Function
Array
String
Number
Boolean
Date
RegExp
func (Object) Get
func (self Object) Get(name string) (Value, error)
Get the value of the property with the given name.
func (Object) Keys
func (self Object) Keys() []string
Get the keys for the object
This is equivalent to calling Object.keys on the object.
func (Object) Set
func (self Object) Set(name string, value interface{}) error
Set the property of the given name to the given value.
An error will result if setting the property triggers an exception (e.g. read-only) or if there is an error during conversion of the given value.
func (Object) Value
func (self Object) Value() Value
Value will return self as a value.
type Otto
type Otto struct {
// Interrupt is a channel for interrupting the runtime. You can use this to halt a long running execution, for example.
// See "Halting Problem" for more information.
Interrupt chan func()
}
Otto is the representation of the JavaScript runtime. Each instance of Otto has a self-contained namespace.
func New
func New() *Otto
New will allocate a new JavaScript runtime
func Run
func Run(src interface{}) (*Otto, Value, error)
Run will allocate a new JavaScript runtime, run the given source on the allocated
Related Skills
node-connect
336.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
336.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.0kCommit, push, and open a PR
