github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goslice_reflect_test.go (about) 1 package goja 2 3 import ( 4 "reflect" 5 "testing" 6 ) 7 8 func TestGoSliceReflectBasic(t *testing.T) { 9 const SCRIPT = ` 10 var sum = 0; 11 for (var i = 0; i < a.length; i++) { 12 sum += a[i]; 13 } 14 sum; 15 ` 16 r := New() 17 r.Set("a", []int{1, 2, 3, 4}) 18 v, err := r.RunString(SCRIPT) 19 if err != nil { 20 t.Fatal(err) 21 } 22 if i := v.ToInteger(); i != 10 { 23 t.Fatalf("Expected 10, got: %d", i) 24 } 25 26 } 27 28 func TestGoSliceReflectIn(t *testing.T) { 29 const SCRIPT = ` 30 var idx = ""; 31 for (var i in a) { 32 idx += i; 33 } 34 idx; 35 ` 36 r := New() 37 r.Set("a", []int{1, 2, 3, 4}) 38 v, err := r.RunString(SCRIPT) 39 if err != nil { 40 t.Fatal(err) 41 } 42 if i := v.String(); i != "0123" { 43 t.Fatalf("Expected '0123', got: '%s'", i) 44 } 45 } 46 47 func TestGoSliceReflectSet(t *testing.T) { 48 const SCRIPT = ` 49 a[0] = 33; 50 a[1] = 333; 51 a[2] = "42"; 52 a[3] = {}; 53 a[4] = 0; 54 ` 55 r := New() 56 a := []int8{1, 2, 3, 4} 57 r.Set("a", a) 58 _, err := r.RunString(SCRIPT) 59 if err != nil { 60 t.Fatal(err) 61 } 62 63 if a[0] != 33 { 64 t.Fatalf("a[0] = %d, expected 33", a[0]) 65 } 66 if a[1] != 77 { 67 t.Fatalf("a[1] = %d, expected 77", a[1]) 68 } 69 if a[2] != 42 { 70 t.Fatalf("a[2] = %d, expected 42", a[2]) 71 } 72 if a[3] != 0 { 73 t.Fatalf("a[3] = %d, expected 0", a[3]) 74 } 75 } 76 77 func TestGoSliceReflectPush(t *testing.T) { 78 79 r := New() 80 81 t.Run("Can push to array by array ptr", func(t *testing.T) { 82 a := []int8{1} 83 r.Set("a", &a) 84 _, err := r.RunString(`a.push (10)`) 85 if err != nil { 86 t.Fatal(err) 87 } 88 89 if a[1] != 10 { 90 t.Fatalf("a[1] = %d, expected 10", a[1]) 91 } 92 }) 93 94 t.Run("Can push to array by struct ptr", func(t *testing.T) { 95 type testStr struct { 96 A []int 97 } 98 a := testStr{ 99 A: []int{2}, 100 } 101 102 r.Set("a", &a) 103 _, err := r.RunString(`a.A.push (10)`) 104 if err != nil { 105 t.Fatal(err) 106 } 107 108 if a.A[1] != 10 { 109 t.Fatalf("a[1] = %v, expected 10", a) 110 } 111 }) 112 113 } 114 115 func TestGoSliceReflectStructField(t *testing.T) { 116 vm := New() 117 var s struct { 118 A []int 119 B *[]int 120 } 121 vm.Set("s", &s) 122 _, err := vm.RunString(` 123 'use strict'; 124 s.A.push(1); 125 if (s.B !== null) { 126 throw new Error("s.B is not null: " + s.B); 127 } 128 s.B = [2]; 129 `) 130 if err != nil { 131 t.Fatal(err) 132 } 133 if len(s.A) != 1 || s.A[0] != 1 { 134 t.Fatalf("s.A: %v", s.A) 135 } 136 if len(*s.B) != 1 || (*s.B)[0] != 2 { 137 t.Fatalf("s.B: %v", *s.B) 138 } 139 } 140 141 func TestGoSliceReflectExportToStructField(t *testing.T) { 142 vm := New() 143 v, err := vm.RunString(`({A: [1], B: [2]})`) 144 if err != nil { 145 t.Fatal(err) 146 } 147 var s struct { 148 A []int 149 B *[]int 150 } 151 err = vm.ExportTo(v, &s) 152 if err != nil { 153 t.Fatal(err) 154 } 155 if len(s.A) != 1 || s.A[0] != 1 { 156 t.Fatalf("s.A: %v", s.A) 157 } 158 if len(*s.B) != 1 || (*s.B)[0] != 2 { 159 t.Fatalf("s.B: %v", *s.B) 160 } 161 } 162 163 func TestGoSliceReflectProtoMethod(t *testing.T) { 164 const SCRIPT = ` 165 a.join(",") 166 ` 167 168 r := New() 169 a := []int8{1, 2, 3, 4} 170 r.Set("a", a) 171 ret, err := r.RunString(SCRIPT) 172 if err != nil { 173 t.Fatal(err) 174 } 175 if s := ret.String(); s != "1,2,3,4" { 176 t.Fatalf("Unexpected result: '%s'", s) 177 } 178 } 179 180 type gosliceReflect_withMethods []interface{} 181 182 func (s gosliceReflect_withMethods) Method() bool { 183 return true 184 } 185 186 func TestGoSliceReflectMethod(t *testing.T) { 187 const SCRIPT = ` 188 typeof a === "object" && a[0] === 42 && a.Method() === true; 189 ` 190 191 vm := New() 192 a := make(gosliceReflect_withMethods, 1) 193 a[0] = 42 194 vm.Set("a", a) 195 v, err := vm.RunString(SCRIPT) 196 if err != nil { 197 t.Fatal(err) 198 } 199 if !v.StrictEquals(valueTrue) { 200 t.Fatalf("Expected true, got %v", v) 201 } 202 203 } 204 205 func TestGoSliceReflectGetStr(t *testing.T) { 206 r := New() 207 v := r.ToValue([]string{"test"}) 208 if o, ok := v.(*Object); ok { 209 if e := o.Get("0").Export(); e != "test" { 210 t.Fatalf("Unexpected o.Get(\"0\"): %v", e) 211 } 212 } 213 } 214 215 func TestGoSliceReflectNilObjectIfaceVal(t *testing.T) { 216 r := New() 217 a := []Value{(*Object)(nil)} 218 r.Set("a", a) 219 ret, err := r.RunString(` 220 ""+a[0]; 221 `) 222 if err != nil { 223 t.Fatal(err) 224 } 225 if !asciiString("null").SameAs(ret) { 226 t.Fatalf("ret: %v", ret) 227 } 228 } 229 230 func TestGoSliceReflectSetLength(t *testing.T) { 231 r := New() 232 a := []int{1, 2, 3, 4} 233 b := []testing.TB{&testing.T{}, &testing.T{}, (*testing.T)(nil)} 234 r.Set("a", &a) 235 r.Set("b", &b) 236 _, err := r.RunString(` 237 'use strict'; 238 a.length = 3; 239 if (a.length !== 3) { 240 throw new Error("length="+a.length); 241 } 242 if (a[3] !== undefined) { 243 throw new Error("a[3]="+a[3]); 244 } 245 a.length = 5; 246 if (a.length !== 5) { 247 throw new Error("a.length="+a.length); 248 } 249 if (a[3] !== 0) { 250 throw new Error("a[3]="+a[3]); 251 } 252 if (a[4] !== 0) { 253 throw new Error("a[4]="+a[4]); 254 } 255 256 b.length = 3; 257 if (b.length !== 3) { 258 throw new Error("b.length="+b.length); 259 } 260 if (b[3] !== undefined) { 261 throw new Error("b[3]="+b[3]); 262 } 263 b.length = 5; 264 if (b.length !== 5) { 265 throw new Error("length="+b.length); 266 } 267 if (b[3] !== null) { 268 throw new Error("b[3]="+b[3]); 269 } 270 if (b[4] !== null) { 271 throw new Error("b[4]="+b[4]); 272 } 273 if (b[2] !== null) { 274 throw new Error("b[2]="+b[2]); 275 } 276 `) 277 if err != nil { 278 t.Fatal(err) 279 } 280 } 281 282 func TestGoSliceReflectProto(t *testing.T) { 283 r := New() 284 a := []*Object{{}, nil, {}} 285 r.Set("a", &a) 286 r.testScriptWithTestLib(` 287 var proto = [,2,,4]; 288 Object.setPrototypeOf(a, proto); 289 assert.sameValue(a[1], null, "a[1]"); 290 assert.sameValue(a[3], 4, "a[3]"); 291 var desc = Object.getOwnPropertyDescriptor(a, "1"); 292 assert.sameValue(desc.value, null, "desc.value"); 293 assert(desc.writable, "writable"); 294 assert(desc.enumerable, "enumerable"); 295 assert(!desc.configurable, "configurable"); 296 var v5; 297 Object.defineProperty(proto, "5", { 298 set: function(v) { 299 v5 = v; 300 } 301 }); 302 a[5] = "test"; 303 assert.sameValue(v5, "test", "v5"); 304 `, _undefined, t) 305 } 306 307 func TestGoSliceReflectProtoProto(t *testing.T) { 308 r := New() 309 a := []*Object{{}, nil, {}} 310 proto := []*Object{{}, {}, {}, {}} 311 r.Set("a", &a) 312 r.Set("proto", proto) 313 _, err := r.RunString(` 314 "use strict"; 315 var protoproto = {}; 316 Object.defineProperty(protoproto, "3", { 317 value: 42 318 }); 319 Object.setPrototypeOf(proto, protoproto); 320 Object.setPrototypeOf(a, proto); 321 if (a.hasOwnProperty("3")) { 322 throw new Error("a.hasOwnProperty(\"3\")"); 323 } 324 if (a[3] !== null) { 325 throw new Error("a[3]="+a[3]); 326 } 327 a[3] = null; 328 if (a[3] !== null) { 329 throw new Error("a[3]=" + a[3]); 330 } 331 `) 332 if err != nil { 333 t.Fatal(err) 334 } 335 336 } 337 338 func TestGoSliceReflectDelete(t *testing.T) { 339 r := New() 340 a := []*Object{{}, nil, {}} 341 r.Set("a", a) 342 v, err := r.RunString(` 343 delete a[0] && delete a[1] && delete a[3]; 344 `) 345 if err != nil { 346 t.Fatal(err) 347 } 348 if v != valueTrue { 349 t.Fatalf("not true: %v", v) 350 } 351 } 352 353 func TestGoSliceReflectPop(t *testing.T) { 354 r := New() 355 a := []string{"1", "", "3"} 356 r.Set("a", &a) 357 v, err := r.RunString(` 358 a.pop() 359 `) 360 if err != nil { 361 t.Fatal(err) 362 } 363 if !v.SameAs(asciiString("3")) { 364 t.Fatal(v) 365 } 366 } 367 368 func TestGoSliceReflectPopNoPtr(t *testing.T) { 369 r := New() 370 a := []string{"1", "", "3"} 371 r.Set("a", a) 372 v, err := r.RunString(` 373 a.pop() 374 `) 375 if err != nil { 376 t.Fatal(err) 377 } 378 if !v.SameAs(asciiString("3")) { 379 t.Fatal(v) 380 } 381 } 382 383 func TestGoSliceReflectLengthProperty(t *testing.T) { 384 vm := New() 385 vm.Set("s", []int{2, 3, 4}) 386 _, err := vm.RunString(` 387 if (!s.hasOwnProperty("length")) { 388 throw new Error("hasOwnProperty() returned false"); 389 } 390 let desc = Object.getOwnPropertyDescriptor(s, "length"); 391 if (desc.value !== 3 || !desc.writable || desc.enumerable || desc.configurable) { 392 throw new Error("incorrect property descriptor: " + JSON.stringify(desc)); 393 } 394 `) 395 if err != nil { 396 t.Fatal(err) 397 } 398 } 399 400 type testCustomSliceWithMethods []int 401 402 func (a testCustomSliceWithMethods) Method() bool { 403 return true 404 } 405 406 func TestGoSliceReflectMethods(t *testing.T) { 407 vm := New() 408 vm.Set("s", testCustomSliceWithMethods{1, 2, 3}) 409 _, err := vm.RunString(` 410 if (!s.hasOwnProperty("Method")) { 411 throw new Error("hasOwnProperty() returned false"); 412 } 413 let desc = Object.getOwnPropertyDescriptor(s, "Method"); 414 if (desc.value() !== true || desc.writable || !desc.enumerable || desc.configurable) { 415 throw new Error("incorrect property descriptor: " + JSON.stringify(desc)); 416 } 417 `) 418 if err != nil { 419 t.Fatal(err) 420 } 421 } 422 423 func TestGoSliceReflectExportAfterGrow(t *testing.T) { 424 vm := New() 425 vm.Set("a", []int{1}) 426 v, err := vm.RunString(` 427 a.push(2); 428 a; 429 `) 430 if err != nil { 431 t.Fatal(err) 432 } 433 exp := v.Export() 434 if a, ok := exp.([]int); ok { 435 if len(a) != 2 || a[0] != 1 || a[1] != 2 { 436 t.Fatal(a) 437 } 438 } else { 439 t.Fatalf("Wrong type: %T", exp) 440 } 441 } 442 443 func TestGoSliceReflectSort(t *testing.T) { 444 vm := New() 445 type Thing struct{ Name string } 446 vm.Set("v", []*Thing{ 447 {Name: "log"}, 448 {Name: "etc"}, 449 {Name: "test"}, 450 {Name: "bin"}, 451 }) 452 ret, err := vm.RunString(` 453 //v.sort((a, b) => a.Name.localeCompare(b.Name)).map((x) => x.Name); 454 const tmp = v[0]; 455 v[0] = v[1]; 456 v[1] = tmp; 457 v[0].Name + v[1].Name; 458 `) 459 if err != nil { 460 panic(err) 461 } 462 t.Log(ret.Export()) 463 } 464 465 func TestGoSliceReflect111(t *testing.T) { 466 vm := New() 467 vm.Set("v", []int32{ 468 1, 2, 469 }) 470 ret, err := vm.RunString(` 471 //v.sort((a, b) => a.Name.localeCompare(b.Name)).map((x) => x.Name); 472 const tmp = v[0]; 473 v[0] = v[1]; 474 v[1] = tmp; 475 "" + v[0] + v[1]; 476 `) 477 if err != nil { 478 panic(err) 479 } 480 t.Log(ret.Export()) 481 a := []int{1, 2} 482 a0 := reflect.ValueOf(a).Index(0) 483 a0.Set(reflect.ValueOf(0)) 484 t.Log(a[0]) 485 } 486 487 func TestGoSliceReflectExternalLenUpdate(t *testing.T) { 488 data := &[]int{1} 489 490 vm := New() 491 vm.Set("data", data) 492 vm.Set("append", func(a *[]int, v int) { 493 if a != data { 494 panic(vm.NewTypeError("a != data")) 495 } 496 *a = append(*a, v) 497 }) 498 499 vm.testScriptWithTestLib(` 500 assert.sameValue(data.length, 1); 501 502 // modify with js 503 data.push(1); 504 assert.sameValue(data.length, 2); 505 506 // modify with go 507 append(data, 2); 508 assert.sameValue(data.length, 3); 509 `, _undefined, t) 510 } 511 512 func BenchmarkGoSliceReflectSet(b *testing.B) { 513 vm := New() 514 a := vm.ToValue([]int{1}).(*Object) 515 b.ResetTimer() 516 v := intToValue(0) 517 for i := 0; i < b.N; i++ { 518 a.Set("0", v) 519 } 520 }