ECMAScript/JavaScript engine in pure Go

Overview

goja

ECMAScript 5.1(+) implementation in Go.

GoDoc

Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and performance.

This project was largely inspired by otto.

Minimum required Go version is 1.14.

Features

Known incompatibilities and caveats

WeakMap

WeakMap is implemented by embedding references to the values into the keys. This means that as long as the key is reachable all values associated with it in any weak maps also remain reachable and therefore cannot be garbage collected even if they are not otherwise referenced, even after the WeakMap is gone. The reference to the value is dropped either when the key is explicitly removed from the WeakMap or when the key becomes unreachable.

To illustrate this:

var m = new WeakMap();
var key = {};
var value = {/* a very large object */};
m.set(key, value);
value = undefined;
m = undefined; // The value does NOT become garbage-collectable at this point
key = undefined; // Now it does
// m.delete(key); // This would work too

The reason for it is the limitation of the Go runtime. At the time of writing (version 1.15) having a finalizer set on an object which is part of a reference cycle makes the whole cycle non-garbage-collectable. The solution above is the only reasonable way I can think of without involving finalizers. This is the third attempt (see https://github.com/dop251/goja/issues/250 and https://github.com/dop251/goja/issues/199 for more details).

Note, this does not have any effect on the application logic, but may cause a higher-than-expected memory usage.

FAQ

How fast is it?

Although it's faster than many scripting language implementations in Go I have seen (for example it's 6-7 times faster than otto on average) it is not a replacement for V8 or SpiderMonkey or any other general-purpose JavaScript engine. You can find some benchmarks here.

Why would I want to use it over a V8 wrapper?

It greatly depends on your usage scenario. If most of the work is done in javascript (for example crypto or any other heavy calculations) you are definitely better off with V8.

If you need a scripting language that drives an engine written in Go so that you need to make frequent calls between Go and javascript passing complex data structures then the cgo overhead may outweigh the benefits of having a faster javascript engine.

Because it's written in pure Go there are no cgo dependencies, it's very easy to build and it should run on any platform supported by Go.

It gives you a much better control over execution environment so can be useful for research.

Is it goroutine-safe?

No. An instance of goja.Runtime can only be used by a single goroutine at a time. You can create as many instances of Runtime as you like but it's not possible to pass object values between runtimes.

Where is setTimeout()?

setTimeout() assumes concurrent execution of code which requires an execution environment, for example an event loop similar to nodejs or a browser. There is a separate project aimed at providing some NodeJS functionality, and it includes an event loop.

Can you implement (feature X from ES6 or higher)?

Some ES6 functionality has been implemented. So far this is mostly built-ins, not syntax enhancements. See https://github.com/dop251/goja/milestone/1 for more details.

The ongoing work is done in the es6 branch which is merged into master when appropriate. Every commit in this branch represents a relatively stable state (i.e. it compiles and passes all enabled tc39 tests), however because the version of tc39 tests I use is quite old, it may be not as well tested as the ES5.1 functionality. Because ES6 is a superset of ES5.1 it should not break your existing code.

I will be adding features in their dependency order and as quickly as my time allows. Please do not ask for ETAs. Features that are open in the milestone are either in progress or will be worked on next.

How do I contribute?

Before submitting a pull request please make sure that:

  • You followed ECMA standard as close as possible. If adding a new feature make sure you've read the specification, do not just base it on a couple of examples that work fine.
  • Your change does not have a significant negative impact on performance (unless it's a bugfix and it's unavoidable)
  • It passes all relevant tc39 tests.

Current Status

  • There should be no breaking changes in the API, however it may be extended.
  • Some of the AnnexB functionality is missing.

Basic Example

Run JavaScript and get the result value.

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. See the method's documentation for more details.

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.

Within a single export operation the same Object will be represented by the same Go value (either the same map, slice or a pointer to the same struct). This includes circular objects and makes it possible to export them.

Calling JS functions from Go

There are 2 approaches:

vm := New()
_, err := vm.RunString(`
function sum(a, b) {
    return a+b;
}
`)
if err != nil {
    panic(err)
}
sum, ok := AssertFunction(vm.Get("sum"))
if !ok {
    panic("Not a function")
}

res, err := sum(Undefined(), vm.ToValue(40), vm.ToValue(2))
if err != nil {
    panic(err)
}
fmt.Println(res)
// Output: 42
const SCRIPT = `
function f(param) {
    return +param + 2;
}
`

vm := New()
_, err := vm.RunString(SCRIPT)
if err != nil {
    panic(err)
}

var fn func(string) string
err = vm.ExportTo(vm.Get("f"), &fn)
if err != nil {
    panic(err)
}

fmt.Println(fn("40")) // note, _this_ value in the function will be undefined.
// Output: 42

The first one is more low level and allows specifying this value, whereas the second one makes the function look like a normal Go function.

Mapping struct field and method names

By default, the names are passed through as is which means they are capitalised. This does not match the standard JavaScript naming convention, so if you need to make your JS code look more natural or if you are dealing with a 3rd party library, you can use a FieldNameMapper:

vm := New()
vm.SetFieldNameMapper(TagFieldNameMapper("json", true))
type S struct {
    Field int `json:"field"`
}
vm.Set("s", S{Field: 42})
res, _ := vm.RunString(`s.field`) // without the mapper it would have been s.Field
fmt.Println(res.Export())
// Output: 42

There are two standard mappers: TagFieldNameMapper and UncapFieldNameMapper, or you can use your own implementation.

Native Constructors

In order to implement a constructor function in Go use func (goja.ConstructorCall) *goja.Object. See Runtime.ToValue() documentation for more details.

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.

Issues
  • Adding native constructors

    Adding native constructors

    I've forked and started dabbling into allowing package users to create "native" constructors and instances.

    I'm implementing part of the Web API (Response, Request, Headers, etc.) and needed some way to create more complex instances that kept a state outside of goja.

    Here's my rough implementation so far:

    // constructor.go
    
    package goja
    
    type NativeFunction func(FunctionCall) Value
    type ConstructorFunction func(args []Value, inst *Object) *Object
    
    type Method struct {
    	call   NativeFunction
    	length int
    }
    
    type Constructor struct {
    	Name string
    	r    *Runtime
    
    	proto  *Object
    	static *Object
    
    	ctor   ConstructorFunction
    	length int
    
    	methods map[string]Method
    }
    
    func (r *Runtime) NewConstructor(name string, ctor ConstructorFunction, length int) *Constructor {
    	return &Constructor{
    		Name:    name,
    		r:       r,
    		ctor:    ctor,
    		length:  length,
    		methods: map[string]Method{},
    	}
    }
    
    func (c *Constructor) createProto(val *Object) objectImpl {
    	o := &baseObject{
    		class:      c.Name,
    		val:        val,
    		extensible: true,
    		prototype:  c.r.global.ObjectPrototype,
    	}
    	o.init()
    
    	for name, method := range c.methods {
    		o._putProp(name, c.r.newNativeFunc(method.call, nil, name, nil, method.length), true, false, true)
    	}
    
    	return o
    }
    
    func (c *Constructor) createStatic(val *Object) objectImpl {
    	o := c.r.newNativeFuncConstructObj(val, c.ctor, c.Name, c.proto, c.length)
    
    	// _putProp here...
    
    	return o
    }
    
    func (c *Constructor) NewInstance(args []Value, proto *Object) *Object {
    	bo := c.r.newBaseObject(proto, c.Name)
    	val := c.ctor(args, bo.val)
    	return val
    }
    
    func (c *Constructor) Init() {
    	c.proto = c.r.newLazyObject(c.createProto)
    	c.static = c.r.newLazyObject(c.createStatic)
    
    	c.r.addToGlobal(c.Name, c.static)
    }
    
    func (c *Constructor) DefineFunction(name string, call NativeFunction, length int) {
    	c.methods[name] = Method{call, length}
    }
    

    Surprisingly, this works ok. Here's an example of how I'm using it:

    func constructResponse(vm *goja.Runtime) goja.ConstructorFunction {
    	return func(args []goja.Value, res *goja.Object) *goja.Object {
    		res.Set("body", nil)
    		res.Set("bodyUsed", false)
    		res.Set("ok", true)
    		res.Set("redirected", false)
    		res.Set("status", 200)
    		res.Set("statusText", "OK")
    		res.Set("type", "default")
    		res.Set("url", "")
    
    		res.Set("text", bodyText(vm, strings.NewReader("")))
    
    		return res
    	}
    }
    
    func initResponse(vm *goja.Runtime) {
    	c := vm.NewConstructor("Response", constructResponse(vm), 2)
    	c.Init()
    }
    

    This works. Except I'm left with the same issue trying to keep state around. I have to create a bunch of closures.

    For instance, for the text() function: I'd like it to keep around a io.Reader and read from it from the text() function.

    As far as I can tell, there's already a similar pattern in goja. Namely, the newDateObject function seems to create a special kind of object that embed baseObject and a few more fields. It makes sense, except that's all unexported and I'm not sure how I can make it generic enough.

    I'm mostly looking for advice on how to proceed. Ideally, from a package user's perspective, the developer would only have to provide a constructor and a struct instance to make it work.

    I think maybe abstracting and exporting a few more structures would help. I guess I'd mostly need to create a few structs embedding baseObject or implementing objectImpl.

    Any thoughts?

    enhancement 
    opened by jeromegn 18
  • Misbehavior of ToValue() on map type with methods

    Misbehavior of ToValue() on map type with methods

    I found an issue while trying to enumerate the keys of an object that I created with ToValue() from a map type having helper methods.

    As an example, let's use http.Header type which is a Go map type with methods. When converting a http.Header value using ToValue(), this line of code in ToValue() https://github.com/dop251/goja/blob/master/runtime.go#L1067 prevents from making it a objectGoMapReflect and ends up as a objectGoReflect.

    But the its key enumeration only happens on methods and not on the map keys.

    Here is a test example that demonstrates the issue:

    	func TestMapTypeBinding(t *testing.T) {
    		headers := http.Header{
    			"k0": []string{},
    			"k1": []string{"v1"},
    			"k2": []string{"v21", "v22"},
    			"k3": []string{"v3"},
    		}
    		vm := goja.New()
    		vm.Set("headers", headers)
    		v, err := vm.RunString(`Object.keys(headers)`)
    		require.NoError(t, err)
    		result := v.Export()
    		require.Equal(t, []interface{}{"k0", "k1", "k2", "k3"}, result)
    	})
    

    Failing with:

    Expected :[]interface {}{"k0", "k1", "k2", "k3"} Actual :[]interface {}{"Add", "Del", "Get", "Set", "Write", "WriteSubset"}

    I'd be happy to provide a fix, but I'd like to understand first in which case it should be methods only as suggested by the current implementation.

    question wontfix 
    opened by Julio-Guerra 14
  • Multiple functions in my Javascript file

    Multiple functions in my Javascript file

    Hello, I come from Otto world and I want to migrate to this library because of very bad performance issues. I am using it for a normalisation pipeline.

    I used to put 4 or 5 functions into a JS file, and then call one of them into my Golang code.

    Example (here I call the function Exlcude) :

    // Run
    returnRaw, err := vm.Run(`Exclude(` + string(incidentJSON) + `);`)
    if err != nil {
    	return false, fmt.Errorf("Error while running the JS code : %s", err)
    }
    

    How can I do this with your lib please ? Thanks a lot ! :)

    question 
    opened by fallais 12
  • ES6 Proxy (WIP)

    ES6 Proxy (WIP)

    I'm working on an ES6 Proxy implementation. Traps can be implemented in JavaScript as in the specification or using a Go struct and the specific function implementations.

    Currently still work in progress, some tests are still missing, therefore most probably doesn't work yet in all specified situations.

    Anyhow would be happy to get some feedback, as this is my first bigger change to commit back to the official goja repository :-)

    I bet there would have to be changes here and there to integrate before it can be merged eventually

    opened by noctarius 12
  • Question: how feasible is it to share Freezed objects between goja.Runtimes

    Question: how feasible is it to share Freezed objects between goja.Runtimes

    The question is more or less in the title. The specific use case is an array with objects that mostly will contain strings, numbers, or other array/objects containing the same.

    Obviously having a function and calling that function will be bad and having a regex can trigger #214, but are there such situations for the other types, and are there other things that need to be taken into account ... apart from me thinking that this is a bad idea and that w/e answer you give will change over time.

    For more context, you can look at this discussion https://github.com/loadimpact/k6/pull/1739#discussion_r540119684

    question 
    opened by mstoykov 11
  • (*vm).Interrupt causes a data race

    (*vm).Interrupt causes a data race

    Per pull request #14, (*vm).Interrupt causes a data race. This results in undefined behavior and means that interrupts are currently unsafe to use. It can be resolved in the short term by using atomics (as in #14) or other explicit synchronization to signal the interrupt from a separate goroutine, and in the long term may be fixed in another manner.

    As discussed in #14, it is unacceptable for a data race to be included in a critical part of a VM, especially as part of an interrupt. According to @dop251, the data race and undefined behavior here is intentional in the pursuit of performance, and objects to the use of atomics to resolve this. The goal, then, is to find another method of causing an interrupt that both satisfies @dop251's performance requirements (currently unstated) and eliminates the data race, preferably without introducing unsafe code that cannot be checked by the race detector.

    This issue is intended to discuss other approaches to removing the interrupt data race, knowing that atomics are off the table (or somehow too slow).

    opened by nilium 11
  • Test for and fix (*vm).Interrupt data race

    Test for and fix (*vm).Interrupt data race

    This patch adds a test for a data race under (*vm).Interrupt, where an interrupt flag (boolean) is set to true inside of a lock but read outside of a lock. Since the lock is only taken at the end of (*vm).run to clear the interrupt value and un-set the interrupt flag, it doesn't make sense to lock for every check on the interrupt flag.

    I assume there's a reason for not using a channel here (even though it'd be simpler), but the most minimal change to be made right now is to replace the bool with a uint32 (or some other integer) and read/write it via atomics.

    The patch adds an (*vm).interrupted method to check the interrupt flag atomically and stores it only via atomics. The type change to uint32 also ensures that there are no other uses of the interrupt bool field (since a uint32 isn't ever treated as a bool and vice-versa). The lock remains in place to serialize setting and clearing the interrupt value.

    To test the race, checkout the first commit (73d6439) and run go test -race -run TestInterruptRace -- this should be enough to trigger it.

    opened by nilium 11
  • Structs

    Structs

    type str struct { A [5]float64 B [5]float64 C [5]float64 D [5]float64 }

    func main(){ mystr:= str{ A: [5]float64{11,22,333,444,55}, B: [5]float64{11,22,333,444,55}, C: [5]float64{11,22,333,444,55}, D: [5]float64{11,22,333,444,55}, } vm.Set("mystr", mystr) var prg = "" + "mystr.A[0];" } I get a nil from this. Am I not indexing the array properly? mystr.A works fine just not mystr.A[0] access.

    'mystr.A' yields [11 22 333 444 55]

    question 
    opened by konethmelathil 10
  • Memory leaks?

    Memory leaks?

    Hi

    I'm building a universal web app, where goja handles JS rendering on the server.

    Now I'm in the phase of doing performance tests, and for some reason goja seems to be leaking memory.

    I'm using artillery.io to perform load tests, and after the load test it seems vm.run is holding memory and not releasing it. I'm not too sure if that's goja fault (probably not), but maybe you know anything off the top of your head how to prevent memory leaks?

    Here's the heap pprof BEFORE the load test: http://jarosinski.uk/files/heap_base.svg

    And here's the one after the load test: http://jarosinski.uk/files/heap_current.svg

    And this is the code that handles VM creation: https://gist.github.com/iKonrad/a046de73c8e478b3cac07fa9e419af58

    Any help or suggestions will be appreciated.

    opened by iKonrad 10
  • Check if something is still running. Kill the VM?

    Check if something is still running. Kill the VM?

    Can we check if something is still running so maybe we can have a limited runtime time? So we could maybe check if the thing is finished executing? That also applies to the event loop. On top of that can we have something to just stop the execution mid-process?

    opened by JSH32 9
  • WeakMap memory leak

    WeakMap memory leak

    package gojatest
    
    import (
    	"runtime"
    	"testing"
    	"time"
    
    	"github.com/dop251/goja"
    )
    
    func TestWeakMap(t *testing.T) {
    	for i := 0; i < 100; i++ {
    		r := goja.New()
    		r.RunScript("", "var x = {}; new WeakMap().set(x, {})")
    		r = nil
    	}
    	runtime.GC()
    	time.Sleep(100 * time.Millisecond)
    	runtime.GC()
    	time.Sleep(100 * time.Millisecond)
    }
    
    $ go test weakmap_test.go -memprofile=/tmp/heap
    

    There are still tons of inuse_objects:

    Still have no idea why (it seems to be related with runtime.SetFinalizer).

    bug 
    opened by lujjjh 8
  • list features in docs, that jumped over ES5.1 (ES6)

    list features in docs, that jumped over ES5.1 (ES6)

    Class has been implemented at https://github.com/dop251/goja/commit/0b5d2105245750c4ba3305bf31081f36fdca9862. It is great feature, thank you.

    At this time, static/private was also implemented. this goes beyond ES6. People who come later, may missed this feature(even it's a great feature.).

    Do you plan to list features that jumped over ES5.1 (ES6) in the Readme? or is it ok to contribute to the Readme?

    opened by arukiidou 2
  • Some more tests not being ran, but likely should be.

    Some more tests not being ran, but likely should be.

    Due to https://github.com/dop251/goja/blob/a070957bbee3b9f34e92638b7bea615e5b074050/tc39_test.go#L588-L590 some tests aren't being ran.

    But I have found at least some that arguably should be. This was possible as with the classes supported added (thanks again :bow: ) the k6 tc39 tests, which run more tests than goja, are still failing for some tests and some of them seem like they should be passing.

    If you do remove it you will get the following list:

            --- FAIL: TestTC39/tc39/test/built-ins/Reflect/Symbol.toStringTag.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/prefix-decrement/S11.4.5_A6_T3.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/prefix-decrement/S11.4.5_A6_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/prefix-decrement/S11.4.5_A6_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/postfix-increment/S11.3.1_A6_T3.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/postfix-increment/S11.3.1_A6_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/postfix-increment/S11.3.1_A6_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/postfix-decrement/S11.3.2_A6_T3.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/postfix-decrement/S11.3.2_A6_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/postfix-decrement/S11.3.2_A6_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/prefix-increment/S11.4.4_A6_T3.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/prefix-increment/S11.4.4_A6_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/prefix-increment/S11.4.4_A6_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.9_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.9_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.9_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.8_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.8_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.8_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.7_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.7_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.7_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.6_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.6_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.6_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.5_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.5_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.5_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.4_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.3_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.4_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.4_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.3_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.3_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.2_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.2_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.2_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.1_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.1_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.11_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.11_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.1_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.11_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.10_T4.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.10_T2.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/compound-assignment/S11.13.2_A7.10_T1.js (0.00s)
            --- FAIL: TestTC39/tc39/test/language/expressions/assignment/S11.13.1_A7_T3.js (0.00s)
    

    All of them seem to be something that should work, and they also seem to be about the quantity or sequence of how stuff are being evaluated.

    opened by mstoykov 2
  • There is a method defined to struct pointer, how to call it from struct elem?

    There is a method defined to struct pointer, how to call it from struct elem?

    The go code is:

    type Obj struct {
    	Name string
    }
    
    func (o *Obj) RunInPtr() {
    	fmt.Printf("[%s] run in ptr\r\n", o.Name)
    }
    
    func (o Obj) RunInElem() {
    	fmt.Printf("[%s] run in elem\r\n", o.Name)
    }
    
    func newObjPtr(name string) *Obj {
    	return &Obj{
    		Name: name,
    	}
    }
    
    func newObj(name string) Obj {
    	return Obj{
    		Name: name,
    	}
    }
    
    func main() {
    	engine := goja.New()
    	engine.Set("newObj", newObj)
    	engine.Set("newObjPtr", newObjPtr)
    
    	buff, _ := ioutil.ReadFile(os.Args[1])
    	code := string(buff)
    	v, err := engine.RunScript(os.Args[1], code)
    }
    

    The test js is:

    let a = newObjPtr("ptr");
    a.RunInElem();
    a.RunInPtr();
    
    let b = newObj("elem");
    b.RunInElem();
    b.RunInPtr();
    

    Then I get the error:

    [ptr] run in elem
    [ptr] run in ptr
    [elem] run in elem
    TypeError: Object has no member 'RunInPtr' at test.js:7:11(27)
    

    PS: I have wrote a tool to import go package to goja. https://github.com/garfeng/gojatools

    The trouble is that, some function return the struct element instead of pointer of it.

    Calling methods defined on it's pointer causes error.

    opened by garfeng 0
  • How can I capture console logs from inside VM

    How can I capture console logs from inside VM

    Hello, For some requirements, I need to return the console logs. Had it been pure javascript, I could have done something like the below : var list = []; window.console.log = (val)=>{list.push(val)} // list will now contain all the console logs inside it I tried to add a custom method like print('something here') but was unable to define it. Any help would be appreciated. Thanks

    opened by vishaNagpal 4
  • Can I interact with a JS script while it is running?

    Can I interact with a JS script while it is running?

    For example, I have a javascript game loop, I use a Go grpc server to run it.

    When the player sends an operation request, the Go grpc server will change the status of javascript game loop.

    Can I do it with goji?

    opened by HZHgagaga 0
  • Comparing primitive pointers

    Comparing primitive pointers

    Goja will convert a go pointer of a primitive type to a JS object, and this prevents this value to be triple equaled in JavaScript. From my point of view this is counterintuitive because I'd expect that *uint16(nil) would be js(undefined or null) and a non nil pointer would be just the primitive value.

    I could manually create the object via vm.NewObject(), but this would create boilerplate, which the https://github.com/dop251/goja#mapping-struct-field-and-method-names tries to prevent.

    This issue is kinda a duplicate of #377, but I felt like it doesn't really describe the problems with this behavior. Feel free to close the issue regardless, as this probably makes the internal implementation of all primitive types more complex.

    Simple example of the behavior:

    type Thing struct {
    	X uint16  `json:"x"`
    	Y *uint16 `json:"y"`
    }
    
    func main() {
    	vm := goja.New()
    
    	vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
    	x := uint16(5)
    	vm.Set("thing", Thing{
    		X: x,
    		Y: &x,
    	})
    
    	fmt.Println("typeof thing.y  ", mustExec(vm, "typeof thing.y").String())
    	fmt.Println("typeof thing.x  ", mustExec(vm, "typeof thing.x").String())
    
    	fmt.Println("thing.x === 5  ", mustExec(vm, "thing.x === 5").ToBoolean())
    	fmt.Println("thing.y === 5  ", mustExec(vm, "thing.y === 5").ToBoolean())
    	fmt.Println("thing.x === thing.y  ", mustExec(vm, "thing.x === thing.y").ToBoolean())
    
    	// typeof thing.y   object
    	// typeof thing.x   number
    	// thing.x === 5   true
    	// thing.y === 5   false
    	// thing.x === thing.y   false
    }
    
    func mustExec(vm *goja.Runtime, s string) goja.Value {
    	v, err := vm.RunScript("myfile", s)
    	if err != nil {
    		panic(err)
    	}
    	return v
    }
    
    question 
    opened by jmattheis 4
Owner
Dmitry Panov
Dmitry Panov
🦁 A Super fast and lightweight runtime for JavaScript Scripts

Kimera.js - A super fast and lightweight JavaScript Runtime for Scripts.

Eliaz Bobadilla 20 May 25, 2022
Esprima-Go: A high performance JavaScript parser written in Go

Esprima-Go is a JavaScript parser written in Go. Esprima is a JavaScript parser written in TypeScript, it's widely used in javascript-realt

CHEN Yuan 0 Jan 9, 2022
Expression evaluation engine for Go: fast, non-Turing complete, dynamic typing, static typing

Expr Expr package provides an engine that can compile and evaluate expressions. An expression is a one-liner that returns a value (mostly, but not lim

Anton Medvedev 2.8k Aug 9, 2022
ECMAScript/JavaScript engine in pure Go

goja ECMAScript 5.1(+) implementation in Go. Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and performan

Dmitry Panov 3.1k Jul 29, 2022
ECMAScript/JavaScript engine in pure Go

goja ECMAScript 5.1(+) implementation in Go. Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and performan

Dmitry Panov 3.1k Aug 7, 2022
A full-featured regex engine in pure Go based on the .NET engine

regexp2 - full featured regular expressions for Go Regexp2 is a feature-rich RegExp engine for Go. It doesn't have constant time guarantees like the b

Doug Clark 574 Aug 8, 2022
Duktape JavaScript engine bindings for Go

Duktape bindings for Go(Golang) Duktape is a thin, embeddable javascript engine. Most of the api is implemented. The exceptions are listed here. Usage

Oleg Lebedev 781 Jul 13, 2022
v8 javascript engine binding for golang

Go-V8 V8 JavaScript engine bindings for Go. Features Thread safe Thorough and careful testing Boolean, Number, String, Object, Array, Regexp, Function

Hoping White 203 Aug 8, 2022
A Go API for the V8 javascript engine.

V8 Bindings for Go The v8 bindings allow a user to execute javascript from within a go executable. The bindings are tested to work with several recent

Augusto Roman 380 May 24, 2022
This library provides WebAssembly capability for goja Javascript engine

This module provides WebAssembly functions into goja javascript engine.

YC-L 1 Jan 10, 2022
A pure Go game engine

Oak A pure Go game engine Table of Contents Installation Motivation Features Support Quick Start Implementation and Examples Finished Games Installati

Oakmound Studio 1.3k Aug 4, 2022
A MySQL-compatible relational database with a storage agnostic query engine. Implemented in pure Go.

go-mysql-server is a SQL engine which parses standard SQL (based on MySQL syntax) and executes queries on data sources of your choice. A simple in-memory database and table implementation are provided, and you can query any data source you want by implementing a few interfaces.

DoltHub 862 Aug 5, 2022
A MySQL-compatible relational database with a storage agnostic query engine. Implemented in pure Go.

go-mysql-server go-mysql-server is a SQL engine which parses standard SQL (based on MySQL syntax) and executes queries on data sources of your choice.

DoltHub 861 Jul 29, 2022
Qt binding for Go (Golang) with support for Windows / macOS / Linux / FreeBSD / Android / iOS / Sailfish OS / Raspberry Pi / AsteroidOS / Ubuntu Touch / JavaScript / WebAssembly

Introduction Qt is a free and open-source widget toolkit for creating graphical user interfaces as well as cross-platform applications that run on var

null 9.4k Aug 8, 2022
Glue - Robust Go and Javascript Socket Library (Alternative to Socket.io)

Glue - Robust Go and Javascript Socket Library Glue is a real-time bidirectional socket library. It is a clean, robust and efficient alternative to so

DesertBit 402 May 25, 2022
Glue - Robust Go and Javascript Socket Library (Alternative to Socket.io)

Glue - Robust Go and Javascript Socket Library Glue is a real-time bidirectional socket library. It is a clean, robust and efficient alternative to so

DesertBit 402 May 25, 2022
A JavaScript interpreter in Go (golang)

otto -- import "github.com/robertkrimen/otto" Package otto is a JavaScript parser and interpreter written natively in Go. http://godoc.org/github.com/

Robert Krimen 6.8k Aug 3, 2022
A compiler from Go to JavaScript for running Go code in a browser

GopherJS - A compiler from Go to JavaScript GopherJS compiles Go code (golang.org) to pure JavaScript code. Its main purpose is to give you the opport

GopherJS 11.2k Aug 2, 2022
Golang->Haxe->CPP/CSharp/Java/JavaScript transpiler

TARDIS Go -> Haxe transpiler Haxe -> C++ / C# / Java / JavaScript Project status: a non-working curiosity, development currently on-ice The advent of

TARDIS Go 418 Jul 13, 2022
An extremely fast JavaScript bundler and minifier

An extremely fast JavaScript bundler and minifier

Evan Wallace 32.7k Aug 7, 2022
Execute JavaScript from Go

Execute JavaScript from Go Usage import "rogchap.com/v8go" Running a script ctx, _ := v8go.NewContext() // creates a new V8 context with a new Isolate

Roger Chapman 2.1k Aug 1, 2022
❄️ Elsa is a minimal, fast and secure runtime for JavaScript and TypeScript written in Go

Elsa Elsa is a minimal, fast and secure runtime for JavaScript and TypeScript written in Go, leveraging the power from QuickJS. Features URL based imp

Elsa 2.6k Jul 29, 2022
A JavaScript interpreter in Go (golang)

otto -- import "github.com/robertkrimen/otto" Package otto is a JavaScript parser and interpreter written natively in Go. http://godoc.org/github.com/

Robert Krimen 6.8k Aug 9, 2022
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers

A bridge and bindings for JS DOM API with Go WebAssembly. Written by Team Ortix - Hamza Ali and Chan Wen Xu. GOOS=js GOARCH=wasm go get -u github.com/

TeamOrtix 74 Jul 15, 2022
Go bindings to QuickJS: a fast, small, and embeddable ES2020 JavaScript interpreter.

quickjs Go bindings to QuickJS: a fast, small, and embeddable ES2020 JavaScript interpreter. These bindings are a WIP and do not match full parity wit

Kenta Iwasaki 126 Jul 25, 2022
jsPolicy - Easier & Faster Kubernetes Policies using JavaScript or TypeScript

Website • Getting Started Guide • Documentation • Blog • Twitter • Slack jsPolicy - Easier & Faster Kubernetes Policies using JavaScript or TypeScript

Loft Labs 209 Aug 7, 2022
Fetch web pages using headless Chrome, storing all fetched resources including JavaScript files

Fetch web pages using headless Chrome, storing all fetched resources including JavaScript files. Run arbitrary JavaScript on many web pages and see the returned values

Detectify 451 Aug 8, 2022
⛳ A minimal programming language inspired by Ink, JavaScript, and Python.

⛳ Golfcart My blog post: Creating the Golfcart Programming Language Getting Started Scope Rules Usage Building and tests Contributions License Golfcar

Andrew Healey 26 Feb 23, 2022
estruct traverses javascript projects and maps all the dependencies and relationships to a JSON. the output can be used to build network visualizations of the project and document the architecture.

EStruct traverses javascript projects and maps all the dependencies and relationships to a JSON. The output can be used to build network visualizations of the project and document the architecture.

Ray Luxembourg 11 Jan 27, 2022