ECMAScript 5.1(+) implementation written in Go
Switch branches/tags
Nothing to show
Clone or download
Latest commit 2dd08a5 Nov 25, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
ast Support for sourcemaps (#66) Feb 25, 2018
file Initial commit Nov 4, 2016
goja Added readFile() Feb 16, 2017
parser Apply LEFT_BRACKET fix Sep 4, 2018
testdata Initial commit Nov 4, 2016
token Initial commit Nov 4, 2016
.gitignore Initial commit Nov 4, 2016
LICENSE Initial commit Nov 4, 2016
README.md Fixed native constuctor call signature (see #45) Feb 3, 2018
array.go Fixed type recusrive => recursive Mar 4, 2018
array_sparse.go Fixed type recusrive => recursive Mar 4, 2018
array_sparse_test.go Set objCount when switching from sparse to normal array (fixes #38) Sep 11, 2017
builtin_array.go Avoid integer underflow in arrayproto_pop_generic() Nov 25, 2018
builtin_boolean.go Initial commit Nov 4, 2016
builtin_date.go Date.getTimezoneOffset() now returns float value to support timezones… Nov 25, 2018
builtin_error.go Initial commit Nov 4, 2016
builtin_function.go Handle nil values in FunctionCall (fixes #20). Mar 9, 2017
builtin_global.go Initial commit Nov 4, 2016
builtin_global_test.go Initial commit Nov 4, 2016
builtin_json.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
builtin_json_test.go Use MarshalJSON() of the wrapped Go value if available Jun 30, 2017
builtin_math.go Initial commit Nov 4, 2016
builtin_number.go Initial commit Nov 4, 2016
builtin_object.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
builtin_regexp.go Initial commit Nov 4, 2016
builtin_string.go Fixed String.fromCharCode() Jul 19, 2018
builtin_string_test.go Fixed String.fromCharCode() Jul 19, 2018
builtin_typedarrays.go Initial commit Nov 4, 2016
builtin_typedarrays_test.go Initial commit Nov 4, 2016
compiler.go Sourcemap fixes. Feb 25, 2018
compiler_expr.go Support for labeled statements. Fixes for statement return values fol… Jan 13, 2018
compiler_stmt.go Support for labeled statements. Fixes for statement return values fol… Jan 13, 2018
compiler_test.go Support for labeled statements. Fixes for statement return values fol… Jan 13, 2018
date.go Date.parse() now returns a number. Switched to own date parser for be… Nov 25, 2018
date_parser.go Date.parse() now returns a number. Switched to own date parser for be… Nov 25, 2018
date_parser_test.go Date.parse() now returns a number. Switched to own date parser for be… Nov 25, 2018
date_test.go Avoid integer overflow in Date.setMilliseconds() Nov 25, 2018
dtoa.go Fixed operator precedence. Jul 19, 2018
func.go Added support for native constructors. See #45 Nov 3, 2017
ipow.go Initial commit Nov 4, 2016
object.go Fixed type recusrive => recursive Mar 4, 2018
object_args.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
object_gomap.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
object_gomap_reflect.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
object_gomap_reflect_test.go Custom map types with no methods converted to objectGoMapReflect (closes Nov 16, 2017
object_gomap_test.go Made wrapped simple maps extensible. Apr 23, 2017
object_goreflect.go Do not panic when trying to assign to a non-addressable reflect value… Nov 3, 2018
object_goreflect_test.go Do not panic when trying to assign to a non-addressable reflect value… Nov 3, 2018
object_goslice.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
object_goslice_reflect.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
object_goslice_reflect_test.go Custom map types with no methods converted to objectGoMapReflect (closes Nov 16, 2017
object_goslice_test.go Initial commit Nov 4, 2016
object_lazy.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
object_test.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
regexp.go Use FindAllSubmatchIndexASCII() instead of FindAllSubmatchIndexUTF8()… Feb 24, 2017
regexp_test.go Unescape non-ascii characters for re2 (closes #23) Mar 15, 2017
runtime.go Made Null() return the correct value. Fixes #60. Jan 3, 2018
runtime_test.go Unicode index() now considers the last character. Fixes #59. Jan 2, 2018
srcfile.go Sourcemap fixes. Feb 25, 2018
srcfile_test.go Support for sourcemaps (#66) Feb 25, 2018
string.go Fixed type recusrive => recursive Mar 4, 2018
string_ascii.go Fix for 32 bit platforms. Nov 9, 2016
string_unicode.go Unicode index() now considers the last character. Fixes #59. Jan 2, 2018
tc39_test.go Date.getTimezoneOffset() now returns float value to support timezones… Nov 25, 2018
value.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
vm.go Added Object.DefineDataProperty() and Object.DefineAccessorProperty()… Nov 5, 2017
vm_test.go Added Runtime.MustCompile() Apr 30, 2017

README.md

goja

ECMAScript 5.1(+) implementation in Go.

GoDoc

It is not a replacement for V8 or SpiderMonkey or any other general-purpose JavaScript engine as it is much slower. It can be used as an embedded scripting language where the cost of making a lot of cgo calls may outweight the benefits of a faster JavaScript engine or as a way to avoid having non-Go dependencies.

This project was largely inspired by otto.

Features

Current Status

  • API is still work in progress and is subject to change.
  • Some of the AnnexB functionality is missing.
  • No typed arrays yet.

Basic Example

vm := goja.New()
v, err := vm.RunString("2 + 2")
if err != nil {
    panic(err)
}
if num := v.Export().(int64); num != 4 {
    panic(num)
}

Passing Values to JS

Any Go value can be passed to JS using Runtime.ToValue() method. Primitive types (ints and uints, floats, string, bool) are converted to the corresponding JavaScript primitives.

func(FunctionCall) Value is treated as a native JavaScript function.

func(ConstructorCall) *Object is treated as a JavaScript constructor (see Native Constructors).

map[string]interface{} is converted into a host object that largely behaves like a JavaScript Object.

[]interface{} is converted into a host object that behaves largely like a JavaScript Array, however it's not extensible because extending it can change the pointer so it becomes detached from the original.

*[]interface{} is same as above, but the array becomes extensible.

A function is wrapped within a native JavaScript function. When called the arguments are automatically converted to the appropriate Go types. If conversion is not possible, a TypeError is thrown.

A slice type is converted into a generic reflect based host object that behaves similar to an unexpandable Array.

A map type with numeric or string keys and no methods is converted into a host object where properties are map keys.

A map type with methods is converted into a host object where properties are method names, the map values are not accessible. This is to avoid ambiguity between m["Property"] and m.Property.

Any other type is converted to a generic reflect based host object. Depending on the underlying type it behaves similar to a Number, String, Boolean or Object.

Note that these conversions wrap the original value which means any changes made inside JS are reflected on the value and calling Export() returns the original value. This applies to all reflect based types.

Exporting Values from JS

A JS value can be exported into its default Go representation using Value.Export() method.

Alternatively it can be exported into a specific Go variable using Runtime.ExportTo() method.

Native Constructors

In order to implement a constructor function in Go:

func MyObject(call goja.ConstructorCall) *Object {
    // call.This contains the newly created object as per http://www.ecma-international.org/ecma-262/5.1/index.html#sec-13.2.2
    // call.Arguments contain arguments passed to the function

    call.This.Set("method", method)

    //...

    // If return value is a non-nil *Object, it will be used instead of call.This
    // This way it is possible to return a Go struct or a map converted
    // into goja.Value using runtime.ToValue(), however in this case
    // instanceof will not work as expected.
    return nil
}

runtime.Set("MyObject", MyObject)

Then it can be used in JS as follows:

var o = new MyObject(arg);
var o1 = MyObject(arg); // same thing
o instanceof MyObject && o1 instanceof MyObject; // true

Regular Expressions

Goja uses the embedded Go regexp library where possible, otherwise it falls back to regexp2.

Exceptions

Any exception thrown in JavaScript is returned as an error of type *Exception. It is possible to extract the value thrown by using the Value() method:

vm := New()
_, err := vm.RunString(`

throw("Test");

`)

if jserr, ok := err.(*Exception); ok {
    if jserr.Value().Export() != "Test" {
        panic("wrong value")
    }
} else {
    panic("wrong type")
}

If a native Go function panics with a Value, it is thrown as a Javascript exception (and therefore can be caught):

var vm *Runtime

func Test() {
    panic(vm.ToValue("Error"))
}

vm = New()
vm.Set("Test", Test)
_, err := vm.RunString(`

try {
    Test();
} catch(e) {
    if (e !== "Error") {
        throw e;
    }
}

`)

if err != nil {
    panic(err)
}

Interrupting

func TestInterrupt(t *testing.T) {
    const SCRIPT = `
    var i = 0;
    for (;;) {
        i++;
    }
    `

    vm := New()
    time.AfterFunc(200 * time.Millisecond, func() {
        vm.Interrupt("halt")
    })

    _, err := vm.RunString(SCRIPT)
    if err == nil {
        t.Fatal("Err is nil")
    }
    // err is of type *InterruptError and its Value() method returns whatever has been passed to vm.Interrupt()
}

NodeJS Compatibility

There is a separate project aimed at providing some of the NodeJS functionality.