github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/func_test.go (about) 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 // Output: Test 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 }