SkillAgentSearch skills...

Otto

A JavaScript interpreter in Go (golang)

Install / Use

/learn @robertkrimen/Otto
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

otto

GoDoc Reference

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.

GoDoc Reference

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 strict will 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:

  1. There is an error during conversion of the argument list
  2. The property is not actually a function
  3. 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

View on GitHub
GitHub Stars8.4k
CategoryDevelopment
Updated14h ago
Forks600

Languages

Go

Security Score

95/100

Audited on Mar 25, 2026

No findings