• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.github/workflows/H11-Oct-2021-

ast/H11-Oct-2021-

file/H11-Oct-2021-

ftoa/H11-Oct-2021-

goja/H11-Oct-2021-

parser/H11-Oct-2021-

testdata/H03-May-2022-

token/H11-Oct-2021-

unistring/H11-Oct-2021-

.gitignoreH A D11-Oct-202129

.tc39_test262_checkout.shH A D11-Oct-2021285

LICENSEH A D11-Oct-20211.1 KiB

README.mdH A D11-Oct-202110.1 KiB

array.goH A D11-Oct-202112.4 KiB

array_sparse.goH A D11-Oct-202111.3 KiB

array_sparse_test.goH A D11-Oct-20214.8 KiB

array_test.goH A D11-Oct-20212.1 KiB

builtin_array.goH A D11-Oct-202138.2 KiB

builtin_arrray_test.goH A D11-Oct-20217.9 KiB

builtin_boolean.goH A D11-Oct-20211.4 KiB

builtin_date.goH A D11-Oct-202129.5 KiB

builtin_error.goH A D11-Oct-20212.5 KiB

builtin_function.goH A D11-Oct-20215.4 KiB

builtin_global.goH A D11-Oct-202111.1 KiB

builtin_global_test.goH A D11-Oct-2021409

builtin_json.goH A D11-Oct-202111.1 KiB

builtin_json_test.goH A D11-Oct-20211.9 KiB

builtin_map.goH A D11-Oct-20217.7 KiB

builtin_map_test.goH A D11-Oct-20211.7 KiB

builtin_math.goH A D11-Oct-202110 KiB

builtin_number.goH A D11-Oct-20216 KiB

builtin_object.goH A D11-Oct-202116.6 KiB

builtin_proxy.goH A D11-Oct-202113.9 KiB

builtin_proxy_test.goH A D11-Oct-202126.7 KiB

builtin_reflect.goH A D11-Oct-20215 KiB

builtin_regexp.goH A D11-Oct-202132.6 KiB

builtin_set.goH A D11-Oct-20216.8 KiB

builtin_set_test.goH A D11-Oct-2021342

builtin_string.goH A D11-Oct-202126.7 KiB

builtin_string_test.goH A D11-Oct-20217.3 KiB

builtin_symbol.goH A D11-Oct-20214 KiB

builtin_typedarrays.goH A D11-Oct-202151.3 KiB

builtin_typedarrays_test.goH A D11-Oct-20216.9 KiB

builtin_weakmap.goH A D11-Oct-20214.7 KiB

builtin_weakmap_test.goH A D11-Oct-2021679

builtin_weakset.goH A D11-Oct-20213.3 KiB

builtin_weakset_test.goH A D11-Oct-20211.2 KiB

compiler.goH A D11-Oct-202125.1 KiB

compiler_expr.goH A D11-Oct-202153 KiB

compiler_stmt.goH A D11-Oct-202126.8 KiB

compiler_test.goH A D11-Oct-202167.7 KiB

date.goH A D11-Oct-20213.4 KiB

date_parser.goH A D11-Oct-202123.9 KiB

date_parser_test.goH A D11-Oct-20211,014

date_test.goH A D11-Oct-202111.5 KiB

destruct.goH A D11-Oct-20217 KiB

func.goH A D11-Oct-20216.7 KiB

go.modH A D11-Oct-2021394

go.sumH A D11-Oct-20211.9 KiB

ipow.goH A D11-Oct-20212.1 KiB

map.goH A D11-Oct-20213 KiB

map_test.goH A D11-Oct-20214.6 KiB

object.goH A D11-Oct-202137.8 KiB

object_args.goH A D11-Oct-20213.3 KiB

object_dynamic.goH A D11-Oct-202120 KiB

object_dynamic_test.goH A D11-Oct-20215.5 KiB

object_gomap.goH A D11-Oct-20213.9 KiB

object_gomap_reflect.goH A D11-Oct-20216.9 KiB

object_gomap_reflect_test.goH A D11-Oct-20214.9 KiB

object_gomap_test.goH A D11-Oct-20215.8 KiB

object_goreflect.goH A D11-Oct-202113.9 KiB

object_goreflect_test.goH A D11-Oct-202121.6 KiB

object_goslice.goH A D11-Oct-20217.4 KiB

object_goslice_reflect.goH A D11-Oct-20218.3 KiB

object_goslice_reflect_test.goH A D11-Oct-20217 KiB

object_goslice_test.goH A D11-Oct-20214.5 KiB

object_lazy.goH A D11-Oct-20216.6 KiB

object_test.goH A D11-Oct-20218.2 KiB

proxy.goH A D11-Oct-202131.2 KiB

regexp.goH A D11-Oct-202115.5 KiB

regexp_test.goH A D11-Oct-202127.9 KiB

runtime.goH A D11-Oct-202163.8 KiB

runtime_test.goH A D11-Oct-202143.6 KiB

string.goH A D11-Oct-20218.6 KiB

string_ascii.goH A D11-Oct-20215.9 KiB

string_unicode.goH A D11-Oct-202110.5 KiB

tc39_norace_test.goH A D11-Oct-2021238

tc39_race_test.goH A D11-Oct-2021622

tc39_test.goH A D11-Oct-202136.9 KiB

typedarrays.goH A D11-Oct-202121.6 KiB

typedarrays_test.goH A D11-Oct-20218.6 KiB

value.goH A D11-Oct-202122.9 KiB

vm.goH A D11-Oct-202173.2 KiB

vm_test.goH A D11-Oct-20215.4 KiB

README.md

1goja
2====
3
4ECMAScript 5.1(+) implementation in Go.
5
6[![GoDoc](https://godoc.org/github.com/dop251/goja?status.svg)](https://godoc.org/github.com/dop251/goja)
7
8Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and
9performance.
10
11This project was largely inspired by [otto](https://github.com/robertkrimen/otto).
12
13Minimum required Go version is 1.14.
14
15Features
16--------
17
18 * Full ECMAScript 5.1 support (including regex and strict mode).
19 * Passes nearly all [tc39 tests](https://github.com/tc39/test262) tagged with es5id. The goal is to pass all of them.
20   Note, the current working commit is https://github.com/tc39/test262/commit/ddfe24afe3043388827aa220ef623b8540958bbd.
21   The next commit removed most of the es5id tags which made it impossible to distinguish which tests to run.
22 * Capable of running Babel, Typescript compiler and pretty much anything written in ES5.
23 * Sourcemaps.
24 * Some ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1
25
26Known incompatibilities and caveats
27-----------------------------------
28
29### WeakMap
30WeakMap is implemented by embedding references to the values into the keys. This means that as long
31as the key is reachable all values associated with it in any weak maps also remain reachable and therefore
32cannot be garbage collected even if they are not otherwise referenced, even after the WeakMap is gone.
33The reference to the value is dropped either when the key is explicitly removed from the WeakMap or when the
34key becomes unreachable.
35
36To illustrate this:
37
38```javascript
39var m = new WeakMap();
40var key = {};
41var value = {/* a very large object */};
42m.set(key, value);
43value = undefined;
44m = undefined; // The value does NOT become garbage-collectable at this point
45key = undefined; // Now it does
46// m.delete(key); // This would work too
47```
48
49The reason for it is the limitation of the Go runtime. At the time of writing (version 1.15) having a finalizer
50set on an object which is part of a reference cycle makes the whole cycle non-garbage-collectable. The solution
51above is the only reasonable way I can think of without involving finalizers. This is the third attempt
52(see https://github.com/dop251/goja/issues/250 and https://github.com/dop251/goja/issues/199 for more details).
53
54Note, this does not have any effect on the application logic, but may cause a higher-than-expected memory usage.
55
56FAQ
57---
58
59### How fast is it?
60
61Although it's faster than many scripting language implementations in Go I have seen
62(for example it's 6-7 times faster than otto on average) it is not a
63replacement for V8 or SpiderMonkey or any other general-purpose JavaScript engine.
64You can find some benchmarks [here](https://github.com/dop251/goja/issues/2).
65
66### Why would I want to use it over a V8 wrapper?
67
68It greatly depends on your usage scenario. If most of the work is done in javascript
69(for example crypto or any other heavy calculations) you are definitely better off with V8.
70
71If you need a scripting language that drives an engine written in Go so that
72you need to make frequent calls between Go and javascript passing complex data structures
73then the cgo overhead may outweigh the benefits of having a faster javascript engine.
74
75Because it's written in pure Go there are no cgo dependencies, it's very easy to build and it
76should run on any platform supported by Go.
77
78It gives you a much better control over execution environment so can be useful for research.
79
80### Is it goroutine-safe?
81
82No. An instance of goja.Runtime can only be used by a single goroutine
83at a time. You can create as many instances of Runtime as you like but
84it's not possible to pass object values between runtimes.
85
86### Where is setTimeout()?
87
88setTimeout() assumes concurrent execution of code which requires an execution
89environment, for example an event loop similar to nodejs or a browser.
90There is a [separate project](https://github.com/dop251/goja_nodejs) aimed at providing some NodeJS functionality,
91and it includes an event loop.
92
93### Can you implement (feature X from ES6 or higher)?
94
95I will be adding features in their dependency order and as quickly as time permits. Please do not ask
96for ETAs. Features that are open in the [milestone](https://github.com/dop251/goja/milestone/1) are either in progress
97or will be worked on next.
98
99The ongoing work is done in separate feature branches which are merged into master when appropriate.
100Every commit in these branches represents a relatively stable state (i.e. it compiles and passes all enabled tc39 tests),
101however because the version of tc39 tests I use is quite old, it may be not as well tested as the ES5.1 functionality. Because there are (usually) no major breaking changes between ECMAScript revisions
102it should not break your existing code. You are encouraged to give it a try and report any bugs found. Please do not submit fixes though without discussing it first, as the code could be changed in the meantime.
103
104### How do I contribute?
105
106Before submitting a pull request please make sure that:
107
108- You followed ECMA standard as close as possible. If adding a new feature make sure you've read the specification,
109do not just base it on a couple of examples that work fine.
110- Your change does not have a significant negative impact on performance (unless it's a bugfix and it's unavoidable)
111- It passes all relevant tc39 tests.
112
113Current Status
114--------------
115
116 * There should be no breaking changes in the API, however it may be extended.
117 * Some of the AnnexB functionality is missing.
118
119Basic Example
120-------------
121
122Run JavaScript and get the result value.
123
124```go
125vm := goja.New()
126v, err := vm.RunString("2 + 2")
127if err != nil {
128    panic(err)
129}
130if num := v.Export().(int64); num != 4 {
131    panic(num)
132}
133```
134
135Passing Values to JS
136--------------------
137Any Go value can be passed to JS using Runtime.ToValue() method. See the method's [documentation](https://godoc.org/github.com/dop251/goja#Runtime.ToValue) for more details.
138
139Exporting Values from JS
140------------------------
141A JS value can be exported into its default Go representation using Value.Export() method.
142
143Alternatively it can be exported into a specific Go variable using [Runtime.ExportTo()](https://godoc.org/github.com/dop251/goja#Runtime.ExportTo) method.
144
145Within a single export operation the same Object will be represented by the same Go value (either the same map, slice or
146a pointer to the same struct). This includes circular objects and makes it possible to export them.
147
148Calling JS functions from Go
149----------------------------
150There are 2 approaches:
151
152- Using [AssertFunction()](https://godoc.org/github.com/dop251/goja#AssertFunction):
153```go
154vm := New()
155_, err := vm.RunString(`
156function sum(a, b) {
157    return a+b;
158}
159`)
160if err != nil {
161    panic(err)
162}
163sum, ok := AssertFunction(vm.Get("sum"))
164if !ok {
165    panic("Not a function")
166}
167
168res, err := sum(Undefined(), vm.ToValue(40), vm.ToValue(2))
169if err != nil {
170    panic(err)
171}
172fmt.Println(res)
173// Output: 42
174```
175- Using [Runtime.ExportTo()](https://godoc.org/github.com/dop251/goja#Runtime.ExportTo):
176```go
177const SCRIPT = `
178function f(param) {
179    return +param + 2;
180}
181`
182
183vm := New()
184_, err := vm.RunString(SCRIPT)
185if err != nil {
186    panic(err)
187}
188
189var fn func(string) string
190err = vm.ExportTo(vm.Get("f"), &fn)
191if err != nil {
192    panic(err)
193}
194
195fmt.Println(fn("40")) // note, _this_ value in the function will be undefined.
196// Output: 42
197```
198
199The first one is more low level and allows specifying _this_ value, whereas the second one makes the function look like
200a normal Go function.
201
202Mapping struct field and method names
203-------------------------------------
204By default, the names are passed through as is which means they are capitalised. This does not match
205the standard JavaScript naming convention, so if you need to make your JS code look more natural or if you are
206dealing with a 3rd party library, you can use a [FieldNameMapper](https://godoc.org/github.com/dop251/goja#FieldNameMapper):
207
208```go
209vm := New()
210vm.SetFieldNameMapper(TagFieldNameMapper("json", true))
211type S struct {
212    Field int `json:"field"`
213}
214vm.Set("s", S{Field: 42})
215res, _ := vm.RunString(`s.field`) // without the mapper it would have been s.Field
216fmt.Println(res.Export())
217// Output: 42
218```
219
220There are two standard mappers: [TagFieldNameMapper](https://godoc.org/github.com/dop251/goja#TagFieldNameMapper) and
221[UncapFieldNameMapper](https://godoc.org/github.com/dop251/goja#UncapFieldNameMapper), or you can use your own implementation.
222
223Native Constructors
224-------------------
225
226In order to implement a constructor function in Go use `func (goja.ConstructorCall) *goja.Object`.
227See [Runtime.ToValue()](https://godoc.org/github.com/dop251/goja#Runtime.ToValue) documentation for more details.
228
229Regular Expressions
230-------------------
231
232Goja uses the embedded Go regexp library where possible, otherwise it falls back to [regexp2](https://github.com/dlclark/regexp2).
233
234Exceptions
235----------
236
237Any exception thrown in JavaScript is returned as an error of type *Exception. It is possible to extract the value thrown
238by using the Value() method:
239
240```go
241vm := New()
242_, err := vm.RunString(`
243
244throw("Test");
245
246`)
247
248if jserr, ok := err.(*Exception); ok {
249    if jserr.Value().Export() != "Test" {
250        panic("wrong value")
251    }
252} else {
253    panic("wrong type")
254}
255```
256
257If a native Go function panics with a Value, it is thrown as a Javascript exception (and therefore can be caught):
258
259```go
260var vm *Runtime
261
262func Test() {
263    panic(vm.ToValue("Error"))
264}
265
266vm = New()
267vm.Set("Test", Test)
268_, err := vm.RunString(`
269
270try {
271    Test();
272} catch(e) {
273    if (e !== "Error") {
274        throw e;
275    }
276}
277
278`)
279
280if err != nil {
281    panic(err)
282}
283```
284
285Interrupting
286------------
287
288```go
289func TestInterrupt(t *testing.T) {
290    const SCRIPT = `
291    var i = 0;
292    for (;;) {
293        i++;
294    }
295    `
296
297    vm := New()
298    time.AfterFunc(200 * time.Millisecond, func() {
299        vm.Interrupt("halt")
300    })
301
302    _, err := vm.RunString(SCRIPT)
303    if err == nil {
304        t.Fatal("Err is nil")
305    }
306    // err is of type *InterruptError and its Value() method returns whatever has been passed to vm.Interrupt()
307}
308```
309
310NodeJS Compatibility
311--------------------
312
313There is a [separate project](https://github.com/dop251/goja_nodejs) aimed at providing some of the NodeJS functionality.
314