1 package goja
2
3 import (
4 "errors"
5 "fmt"
6 "reflect"
7 "testing"
8 )
9
10 func TestFuncProto(t *testing.T) {
11 const SCRIPT = `
12 "use strict";
13 function A() {}
14 A.__proto__ = Object;
15 A.prototype = {};
16
17 function B() {}
18 B.__proto__ = Object.create(null);
19 var thrown = false;
20 try {
21 delete B.prototype;
22 } catch (e) {
23 thrown = e instanceof TypeError;
24 }
25 thrown;
26 `
27 testScript(SCRIPT, valueTrue, t)
28 }
29
30 func TestFuncPrototypeRedefine(t *testing.T) {
31 const SCRIPT = `
32 let thrown = false;
33 try {
34 Object.defineProperty(function() {}, "prototype", {
35 set: function(_value) {},
36 });
37 } catch (e) {
38 if (e instanceof TypeError) {
39 thrown = true;
40 } else {
41 throw e;
42 }
43 }
44 thrown;
45 `
46
47 testScript(SCRIPT, valueTrue, t)
48 }
49
50 func TestFuncExport(t *testing.T) {
51 vm := New()
52 typ := reflect.TypeOf((func(FunctionCall) Value)(nil))
53
54 f := func(expr string, t *testing.T) {
55 v, err := vm.RunString(expr)
56 if err != nil {
57 t.Fatal(err)
58 }
59 if actualTyp := v.ExportType(); actualTyp != typ {
60 t.Fatalf("Invalid export type: %v", actualTyp)
61 }
62 ev := v.Export()
63 if actualTyp := reflect.TypeOf(ev); actualTyp != typ {
64 t.Fatalf("Invalid export value: %v", ev)
65 }
66 }
67
68 t.Run("regular function", func(t *testing.T) {
69 f("(function() {})", t)
70 })
71
72 t.Run("arrow function", func(t *testing.T) {
73 f("(()=>{})", t)
74 })
75
76 t.Run("method", func(t *testing.T) {
77 f("({m() {}}).m", t)
78 })
79
80 t.Run("class", func(t *testing.T) {
81 f("(class {})", t)
82 })
83 }
84
85 func TestFuncWrapUnwrap(t *testing.T) {
86 vm := New()
87 f := func(a int, b string) bool {
88 return a > 0 && b != ""
89 }
90 var f1 func(int, string) bool
91 v := vm.ToValue(f)
92 if et := v.ExportType(); et != reflect.TypeOf(f1) {
93 t.Fatal(et)
94 }
95 err := vm.ExportTo(v, &f1)
96 if err != nil {
97 t.Fatal(err)
98 }
99 if !f1(1, "a") {
100 t.Fatal("not true")
101 }
102 }
103
104 func TestWrappedFunc(t *testing.T) {
105 vm := New()
106 f := func(a int, b string) bool {
107 return a > 0 && b != ""
108 }
109 vm.Set("f", f)
110 const SCRIPT = `
111 assert.sameValue(typeof f, "function");
112 const s = f.toString()
113 assert(s.endsWith("TestWrappedFunc.func1() { [native code] }"), s);
114 assert(f(1, "a"));
115 assert(!f(0, ""));
116 `
117 vm.testScriptWithTestLib(SCRIPT, _undefined, t)
118 }
119
120 func TestWrappedFuncErrorPassthrough(t *testing.T) {
121 vm := New()
122 e := errors.New("test")
123 f := func(a int) error {
124 if a > 0 {
125 return e
126 }
127 return nil
128 }
129
130 var f1 func(a int64) error
131 err := vm.ExportTo(vm.ToValue(f), &f1)
132 if err != nil {
133 t.Fatal(err)
134 }
135 if err := f1(1); err != e {
136 t.Fatal(err)
137 }
138 }
139
140 func ExampleAssertConstructor() {
141 vm := New()
142 res, err := vm.RunString(`
143 (class C {
144 constructor(x) {
145 this.x = x;
146 }
147 })
148 `)
149 if err != nil {
150 panic(err)
151 }
152 if ctor, ok := AssertConstructor(res); ok {
153 obj, err := ctor(nil, vm.ToValue("Test"))
154 if err != nil {
155 panic(err)
156 }
157 fmt.Print(obj.Get("x"))
158 } else {
159 panic("Not a constructor")
160 }
161
162 }
163
164 type testAsyncCtx struct {
165 group string
166 refCount int
167 }
168
169 type testAsyncContextTracker struct {
170 ctx *testAsyncCtx
171 logFunc func(...interface{})
172 resumed bool
173 }
174
175 func (s *testAsyncContextTracker) Grab() interface{} {
176 ctx := s.ctx
177 if ctx != nil {
178 s.logFunc("Grab", ctx.group)
179 ctx.refCount++
180 }
181 return ctx
182 }
183
184 func (s *testAsyncContextTracker) Resumed(trackingObj interface{}) {
185 s.logFunc("Resumed", trackingObj)
186 if s.resumed {
187 panic("Nested Resumed() calls")
188 }
189 s.ctx = trackingObj.(*testAsyncCtx)
190 s.resumed = true
191 }
192
193 func (s *testAsyncContextTracker) releaseCtx() {
194 s.ctx.refCount--
195 if s.ctx.refCount < 0 {
196 panic("refCount < 0")
197 }
198 if s.ctx.refCount == 0 {
199 s.logFunc(s.ctx.group, "is finished")
200 }
201 }
202
203 func (s *testAsyncContextTracker) Exited() {
204 s.logFunc("Exited")
205 if s.ctx != nil {
206 s.releaseCtx()
207 s.ctx = nil
208 }
209 s.resumed = false
210 }
211
212 func TestAsyncContextTracker(t *testing.T) {
213 r := New()
214 var tracker testAsyncContextTracker
215 tracker.logFunc = t.Log
216
217 group := func(name string, asyncFunc func(FunctionCall) Value) Value {
218 prevCtx := tracker.ctx
219 defer func() {
220 t.Log("Returned", name)
221 tracker.releaseCtx()
222 tracker.ctx = prevCtx
223 }()
224 tracker.ctx = &testAsyncCtx{
225 group: name,
226 refCount: 1,
227 }
228 t.Log("Set", name)
229 return asyncFunc(FunctionCall{})
230 }
231 r.SetAsyncContextTracker(&tracker)
232 r.Set("group", group)
233 r.Set("check", func(expectedGroup, msg string) {
234 var groupName string
235 if tracker.ctx != nil {
236 groupName = tracker.ctx.group
237 }
238 if groupName != expectedGroup {
239 t.Fatalf("Unexpected group (%q), expected %q in %s", groupName, expectedGroup, msg)
240 }
241 t.Log("In", msg)
242 })
243
244 t.Run("", func(t *testing.T) {
245 _, err := r.RunString(`
246 group("1", async () => {
247 check("1", "line A");
248 await 3;
249 check("1", "line B");
250 group("2", async () => {
251 check("2", "line C");
252 await 4;
253 check("2", "line D");
254 })
255 }).then(() => {
256 check("", "line E");
257 })
258 `)
259 if err != nil {
260 t.Fatal(err)
261 }
262 })
263
264 t.Run("", func(t *testing.T) {
265 _, err := r.RunString(`
266 group("some", async () => {
267 check("some", "line A");
268 (async () => {
269 check("some", "line B");
270 await 1;
271 check("some", "line C");
272 await 2;
273 check("some", "line D");
274 })();
275 check("some", "line E");
276 });
277 `)
278 if err != nil {
279 t.Fatal(err)
280 }
281 })
282
283 t.Run("", func(t *testing.T) {
284 _, err := r.RunString(`
285 group("Main", async () => {
286 check("Main", "0.1");
287 await Promise.all([
288 group("A", async () => {
289 check("A", "1.1");
290 await 1;
291 check("A", "1.2");
292 }),
293 (async () => {
294 check("Main", "3.1");
295 })(),
296 group("B", async () => {
297 check("B", "2.1");
298 await 2;
299 check("B", "2.2");
300 })
301 ]);
302 check("Main", "0.2");
303 });
304 `)
305 if err != nil {
306 t.Fatal(err)
307 }
308 })
309 }
310
View as plain text