Goja - ECMAScript/JavaScript engine in pure Go
#scripting #script-engine #ecma #js #javascript

Dmitry Panov 034fb74b51 Unicode index() now considers the last character. Fixes #59. vor 7 Jahren
ast 25773609b6 Initial commit vor 9 Jahren
file 25773609b6 Initial commit vor 9 Jahren
goja 77b8a67e92 Added readFile() vor 8 Jahren
parser 6672e9d1ee Add missing idx to ast.ForStatement / ast.ForInStatement vor 8 Jahren
testdata 25773609b6 Initial commit vor 9 Jahren
token 25773609b6 Initial commit vor 9 Jahren
.gitignore 25773609b6 Initial commit vor 9 Jahren
LICENSE 25773609b6 Initial commit vor 9 Jahren
README.md 4e76d88204 Custom map types with no methods converted to objectGoMapReflect (closes #47). vor 8 Jahren
array.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
array_sparse.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
array_sparse_test.go 0aeff7545e Set objCount when switching from sparse to normal array (fixes #38) vor 8 Jahren
builtin_array.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
builtin_boolean.go 25773609b6 Initial commit vor 9 Jahren
builtin_date.go da19abe48a Fixed date setters. vor 8 Jahren
builtin_error.go 25773609b6 Initial commit vor 9 Jahren
builtin_function.go 8fa3e12080 Handle nil values in FunctionCall (fixes #20). vor 8 Jahren
builtin_global.go 25773609b6 Initial commit vor 9 Jahren
builtin_global_test.go 25773609b6 Initial commit vor 9 Jahren
builtin_json.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
builtin_json_test.go 8f3380f012 Use MarshalJSON() of the wrapped Go value if available vor 8 Jahren
builtin_math.go 25773609b6 Initial commit vor 9 Jahren
builtin_number.go 25773609b6 Initial commit vor 9 Jahren
builtin_object.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
builtin_regexp.go 25773609b6 Initial commit vor 9 Jahren
builtin_string.go df256e035d Fix for 32 bit platforms. vor 9 Jahren
builtin_string_test.go 25773609b6 Initial commit vor 9 Jahren
builtin_typedarrays.go 25773609b6 Initial commit vor 9 Jahren
builtin_typedarrays_test.go 25773609b6 Initial commit vor 9 Jahren
compiler.go 9e958edb3c Used sort.Search() in sourceOffset() (fixes #31) vor 8 Jahren
compiler_expr.go 2e54e35d67 compiler: create eval vm lazily vor 8 Jahren
compiler_stmt.go d3092335fb Add DebuggerStatement to compiler.compileStatement vor 9 Jahren
compiler_test.go 9e958edb3c Used sort.Search() in sourceOffset() (fixes #31) vor 8 Jahren
date.go 25773609b6 Initial commit vor 9 Jahren
date_test.go da19abe48a Fixed date setters. vor 8 Jahren
dtoa.go 25773609b6 Initial commit vor 9 Jahren
func.go 4ed8e66074 Added support for native constructors. See #45 vor 8 Jahren
ipow.go 25773609b6 Initial commit vor 9 Jahren
object.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_args.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_gomap.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_gomap_reflect.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_gomap_reflect_test.go 4e76d88204 Custom map types with no methods converted to objectGoMapReflect (closes #47). vor 8 Jahren
object_gomap_test.go c77e15cd9a Made wrapped simple maps extensible. vor 8 Jahren
object_goreflect.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_goreflect_test.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_goslice.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_goslice_reflect.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_goslice_reflect_test.go 4e76d88204 Custom map types with no methods converted to objectGoMapReflect (closes #47). vor 8 Jahren
object_goslice_test.go 25773609b6 Initial commit vor 9 Jahren
object_lazy.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
object_test.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
regexp.go 2acb68835d Use FindAllSubmatchIndexASCII() instead of FindAllSubmatchIndexUTF8() for non-Unicode strings. This improves performance a bit. vor 8 Jahren
regexp_test.go c99b0db935 Unescape non-ascii characters for re2 (closes #23) vor 8 Jahren
runtime.go f24d0f0d86 Add Error() and String() to InterruptedError (#52) vor 7 Jahren
runtime_test.go 034fb74b51 Unicode index() now considers the last character. Fixes #59. vor 7 Jahren
srcfile.go 25773609b6 Initial commit vor 9 Jahren
srcfile_test.go 25773609b6 Initial commit vor 9 Jahren
string.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
string_ascii.go df256e035d Fix for 32 bit platforms. vor 9 Jahren
string_unicode.go 034fb74b51 Unicode index() now considers the last character. Fixes #59. vor 7 Jahren
tc39_test.go dafe23d0fc Added a note about the last supported tc39 tests commit. vor 8 Jahren
value.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
vm.go 9bbb616c12 Added Object.DefineDataProperty() and Object.DefineAccessorProperty(). See #45 vor 8 Jahren
vm_test.go d382686fd2 Added Runtime.MustCompile() vor 8 Jahren

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) Value 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) Value {
    // 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.