-
Notifications
You must be signed in to change notification settings - Fork 2
/
examples_test.go
229 lines (199 loc) · 6.2 KB
/
examples_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package v8_test
import (
"fmt"
"log"
v8 "github.com/jviksne/v8go"
)
func Example() {
v8.Init("")
// Easy-peasy to create a new VM:
isol, err := v8.NewIsolate()
if err != nil {
log.Fatal(err)
}
ctx := isol.NewContext()
// You can load your js from a file, create it dynamically, whatever.
ctx.Eval(`
// This is javascript code!
add = (a,b)=>{ return a + b }; // whoa, ES6 arrow functions.
`, "add.js") // <-- supply filenames for stack traces
// State accumulates in a context. Add still exists.
// The last statements' value is returned to Go.
res, _ := ctx.Eval(`add(3,4)`, "compute.js") // don't ignore errors!
fmt.Println("add(3,4) =", res.String()) // I hope it's 7.
// You can also bind Go functions to javascript:
product := func(in v8.CallbackArgs) (*v8.Value, error) {
var result float64 = 1
for _, arg := range in.Args {
result *= arg.Float64()
}
return in.Context.Create(result) // ctx.Create is great for mapping Go -> JS.
}
cnt := ctx.Bind("product_function", product)
ctx.Global().Set("product", cnt)
res, _ = ctx.Eval(`
// Now we can call that function in JS
product(1,2,3,4,5)
`, "compute2.js")
fmt.Println("product(1,2,3,4,5) =", res.Int64())
_, err = ctx.Eval(`
// Sometimes there's a mistake in your js code:
functin broken(a,b) { return a+b; }
`, "ooops.js")
fmt.Println("Err:", err) // <-- get nice error messages
// output:
// add(3,4) = 7
// product(1,2,3,4,5) = 120
// Err: Uncaught exception: SyntaxError: Unexpected identifier
// at ooops.js:3:20
// functin broken(a,b) { return a+b; }
// ^^^^^^
// Stack trace: SyntaxError: Unexpected identifier
}
func Example_microtasks() {
// Microtasks are automatically run when the Eval'd js code has finished but
// before Eval returns.
v8.Init("")
isol, err := v8.NewIsolate()
if err != nil {
log.Fatal(err)
}
ctx := isol.NewContext()
// Register a simple log function in js.
ctx.Global().Set("log", ctx.Bind("log", func(in v8.CallbackArgs) (*v8.Value, error) {
fmt.Println("log>", in.Arg(0).String())
return nil, nil
}))
// Run some javascript that schedules microtasks, like promises.
output, err := ctx.Eval(`
log('start');
let p = new Promise(resolve => { // this is called immediately
log('resolve:5');
resolve(5);
});
log('promise created');
p.then(v => log('then:'+v)); // this is scheduled in a microtask
log('done'); // this is run before the microtask
'xyz' // this is the output of the script
`, "microtasks.js")
fmt.Println("output:", output)
fmt.Println("err:", err)
// output:
// log> start
// log> resolve:5
// log> promise created
// log> done
// log> then:5
// output: xyz
// err: <nil>
}
func ExampleContext_Create_basic() {
v8.Init("")
isol, err := v8.NewIsolate()
if err != nil {
log.Fatal(err)
}
ctx := isol.NewContext()
type Info struct{ Name, Email string }
val, _ := ctx.Create(map[string]interface{}{
"num": 3.7,
"str": "simple string",
"bool": true,
"struct": Info{"foo", "bar"},
"list": []int{1, 2, 3},
})
// val is now a *v8.Value that is associated with ctx but not yet accessible
// from the javascript scope.
_ = ctx.Global().Set("created_value", val)
res, _ := ctx.Eval(`
created_value.struct.Name = 'John';
JSON.stringify(created_value.struct)
`, `test.js`)
fmt.Println(res)
// output:
// {"Name":"John","Email":"bar"}
}
func ExampleContext_Create_callbacks() {
v8.Init("")
isol, err := v8.NewIsolate()
if err != nil {
log.Fatal(err)
}
ctx := isol.NewContext()
// A typical use of Create is to return values from callbacks:
var nextId int
getNextIdCallback := func(in v8.CallbackArgs) (*v8.Value, error) {
nextId++
return ctx.Create(nextId) // Return the created corresponding v8.Value or an error.
}
// Because Create will use reflection to map a Go value to a JS object, it
// can also be used to easily bind a complex object into the JS VM.
resetIdsCallback := func(in v8.CallbackArgs) (*v8.Value, error) {
nextId = 0
return nil, nil
}
myIdAPI, _ := ctx.Create(map[string]interface{}{
"next": getNextIdCallback,
"reset": resetIdsCallback,
// Can also include other stuff:
"my_api_version": "v1.2",
})
// now let's use those two callbacks and the api value:
_ = ctx.Global().Set("ids", myIdAPI)
var res *v8.Value
res, _ = ctx.Eval(`ids.my_api_version`, `test.js`)
fmt.Println(`ids.my_api_version =`, res)
res, _ = ctx.Eval(`ids.next()`, `test.js`)
fmt.Println(`ids.next() =`, res)
res, _ = ctx.Eval(`ids.next()`, `test.js`)
fmt.Println(`ids.next() =`, res)
res, _ = ctx.Eval(`ids.reset(); ids.next()`, `test.js`)
fmt.Println(`ids.reset()`)
fmt.Println(`ids.next() =`, res)
// output:
// ids.my_api_version = v1.2
// ids.next() = 1
// ids.next() = 2
// ids.reset()
// ids.next() = 1
}
func ExampleSnapshot() {
v8.Init("")
snapshot, err := v8.CreateSnapshot(`
// Concantenate all the scripts you want at startup, e.g. lodash, etc.
_ = { map: function() { /* ... */ }, etc: "etc, etc..." };
// Setup my per-context global state:
myGlobalState = {
init: function() { this.initialized = true; },
foo: 3,
};
// Run some functions:
myGlobalState.init();
`, true, nil)
if err != nil {
log.Fatal(err)
}
iso, err := v8.NewIsolateWithSnapshot(snapshot)
if err != nil {
log.Fatal(err)
}
// Create a context with the state from the snapshot:
ctx1 := iso.NewContext()
fmt.Println("Context 1:")
val, _ := ctx1.Eval("myGlobalState.foo = 37; myGlobalState.initialized", "")
fmt.Println("myGlobalState.initialized:", val)
val, _ = ctx1.Eval("myGlobalState.foo", "")
fmt.Println("myGlobalState.foo:", val)
// In the second context, the global state is reset to the state at the
// snapshot:
ctx2 := iso.NewContext()
fmt.Println("Context 2:")
val, _ = ctx2.Eval("myGlobalState.foo", "")
fmt.Println("myGlobalState.foo:", val)
// Output:
// Context 1:
// myGlobalState.initialized: true
// myGlobalState.foo: 37
// Context 2:
// myGlobalState.foo: 3
}