object_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. package goja
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. "testing"
  7. )
  8. func TestDefineProperty(t *testing.T) {
  9. r := New()
  10. o := r.NewObject()
  11. err := o.DefineDataProperty("data", r.ToValue(42), FLAG_TRUE, FLAG_TRUE, FLAG_TRUE)
  12. if err != nil {
  13. t.Fatal(err)
  14. }
  15. err = o.DefineAccessorProperty("accessor_ro", r.ToValue(func() int {
  16. return 1
  17. }), nil, FLAG_TRUE, FLAG_TRUE)
  18. if err != nil {
  19. t.Fatal(err)
  20. }
  21. err = o.DefineAccessorProperty("accessor_rw",
  22. r.ToValue(func(call FunctionCall) Value {
  23. return o.Get("__hidden")
  24. }),
  25. r.ToValue(func(call FunctionCall) (ret Value) {
  26. o.Set("__hidden", call.Argument(0))
  27. return
  28. }),
  29. FLAG_TRUE, FLAG_TRUE)
  30. if err != nil {
  31. t.Fatal(err)
  32. }
  33. if v := o.Get("accessor_ro"); v.ToInteger() != 1 {
  34. t.Fatalf("Unexpected accessor value: %v", v)
  35. }
  36. err = o.Set("accessor_ro", r.ToValue(2))
  37. if err == nil {
  38. t.Fatal("Expected an error")
  39. }
  40. if ex, ok := err.(*Exception); ok {
  41. if msg := ex.Error(); msg != "TypeError: Cannot assign to read only property 'accessor_ro'" {
  42. t.Fatalf("Unexpected error: '%s'", msg)
  43. }
  44. } else {
  45. t.Fatalf("Unexected error type: %T", err)
  46. }
  47. err = o.Set("accessor_rw", 42)
  48. if err != nil {
  49. t.Fatal(err)
  50. }
  51. if v := o.Get("accessor_rw"); v.ToInteger() != 42 {
  52. t.Fatalf("Unexpected value: %v", v)
  53. }
  54. }
  55. func TestPropertyOrder(t *testing.T) {
  56. const SCRIPT = `
  57. var o = {};
  58. var sym1 = Symbol(1);
  59. var sym2 = Symbol(2);
  60. o[sym2] = 1;
  61. o[4294967294] = 1;
  62. o[2] = 1;
  63. o[1] = 1;
  64. o[0] = 1;
  65. o["02"] = 1;
  66. o[4294967295] = 1;
  67. o["01"] = 1;
  68. o["00"] = 1;
  69. o[sym1] = 1;
  70. var expected = ["0", "1", "2", "4294967294", "02", "4294967295", "01", "00", sym2, sym1];
  71. var actual = Reflect.ownKeys(o);
  72. if (actual.length !== expected.length) {
  73. throw new Error("Unexpected length: "+actual.length);
  74. }
  75. for (var i = 0; i < actual.length; i++) {
  76. if (actual[i] !== expected[i]) {
  77. throw new Error("Unexpected list: " + actual);
  78. }
  79. }
  80. `
  81. testScript(SCRIPT, _undefined, t)
  82. }
  83. func TestDefinePropertiesSymbol(t *testing.T) {
  84. const SCRIPT = `
  85. var desc = {};
  86. desc[Symbol.toStringTag] = {value: "Test"};
  87. var o = {};
  88. Object.defineProperties(o, desc);
  89. o[Symbol.toStringTag] === "Test";
  90. `
  91. testScript(SCRIPT, valueTrue, t)
  92. }
  93. func TestObjectShorthandProperties(t *testing.T) {
  94. const SCRIPT = `
  95. var b = 1;
  96. var a = {b, get() {return "c"}};
  97. assert.sameValue(a.b, b, "#1");
  98. assert.sameValue(a.get(), "c", "#2");
  99. var obj = {
  100. w\u0069th() { return 42; }
  101. };
  102. assert.sameValue(obj['with'](), 42, 'property exists');
  103. `
  104. testScriptWithTestLib(SCRIPT, _undefined, t)
  105. }
  106. func TestObjectAssign(t *testing.T) {
  107. const SCRIPT = `
  108. assert.sameValue(Object.assign({ b: 1 }, { get a() {
  109. Object.defineProperty(this, "b", {
  110. value: 3,
  111. enumerable: false
  112. });
  113. }, b: 2 }).b, 1, "#1");
  114. assert.sameValue(Object.assign({ b: 1 }, { get a() {
  115. delete this.b;
  116. }, b: 2 }).b, 1, "#2");
  117. `
  118. testScriptWithTestLib(SCRIPT, _undefined, t)
  119. }
  120. func TestExportCircular(t *testing.T) {
  121. vm := New()
  122. o := vm.NewObject()
  123. o.Set("o", o)
  124. v := o.Export()
  125. if m, ok := v.(map[string]interface{}); ok {
  126. if reflect.ValueOf(m["o"]).Pointer() != reflect.ValueOf(v).Pointer() {
  127. t.Fatal("Unexpected value")
  128. }
  129. } else {
  130. t.Fatal("Unexpected type")
  131. }
  132. res, err := vm.RunString(`var a = []; a[0] = a;`)
  133. if err != nil {
  134. t.Fatal(err)
  135. }
  136. v = res.Export()
  137. if a, ok := v.([]interface{}); ok {
  138. if reflect.ValueOf(a[0]).Pointer() != reflect.ValueOf(v).Pointer() {
  139. t.Fatal("Unexpected value")
  140. }
  141. } else {
  142. t.Fatal("Unexpected type")
  143. }
  144. }
  145. type test_s struct {
  146. S *test_s1
  147. }
  148. type test_s1 struct {
  149. S *test_s
  150. }
  151. func TestExportToCircular(t *testing.T) {
  152. vm := New()
  153. o := vm.NewObject()
  154. o.Set("o", o)
  155. var m map[string]interface{}
  156. err := vm.ExportTo(o, &m)
  157. if err != nil {
  158. t.Fatal(err)
  159. }
  160. type K string
  161. type T map[K]T
  162. var m1 T
  163. err = vm.ExportTo(o, &m1)
  164. if err != nil {
  165. t.Fatal(err)
  166. }
  167. type A []A
  168. var a A
  169. res, err := vm.RunString("var a = []; a[0] = a;")
  170. if err != nil {
  171. t.Fatal(err)
  172. }
  173. err = vm.ExportTo(res, &a)
  174. if err != nil {
  175. t.Fatal(err)
  176. }
  177. if &a[0] != &a[0][0] {
  178. t.Fatal("values do not match")
  179. }
  180. o = vm.NewObject()
  181. o.Set("S", o)
  182. var s test_s
  183. err = vm.ExportTo(o, &s)
  184. if err != nil {
  185. t.Fatal(err)
  186. }
  187. if s.S.S != &s {
  188. t.Fatalf("values do not match: %v, %v", s.S.S, &s)
  189. }
  190. type test_s2 struct {
  191. S interface{}
  192. S1 *test_s2
  193. }
  194. var s2 test_s2
  195. o.Set("S1", o)
  196. err = vm.ExportTo(o, &s2)
  197. if err != nil {
  198. t.Fatal(err)
  199. }
  200. if m, ok := s2.S.(map[string]interface{}); ok {
  201. if reflect.ValueOf(m["S"]).Pointer() != reflect.ValueOf(m).Pointer() {
  202. t.Fatal("Unexpected m.S")
  203. }
  204. } else {
  205. t.Fatalf("Unexpected s2.S type: %T", s2.S)
  206. }
  207. if s2.S1 != &s2 {
  208. t.Fatal("Unexpected s2.S1")
  209. }
  210. o1 := vm.NewObject()
  211. o1.Set("S", o)
  212. o1.Set("S1", o)
  213. err = vm.ExportTo(o1, &s2)
  214. if err != nil {
  215. t.Fatal(err)
  216. }
  217. if s2.S1.S1 != s2.S1 {
  218. t.Fatal("Unexpected s2.S1.S1")
  219. }
  220. }
  221. func TestExportWrappedMap(t *testing.T) {
  222. vm := New()
  223. m := map[string]interface{}{
  224. "test": "failed",
  225. }
  226. exported := vm.ToValue(m).Export()
  227. if exportedMap, ok := exported.(map[string]interface{}); ok {
  228. exportedMap["test"] = "passed"
  229. if v := m["test"]; v != "passed" {
  230. t.Fatalf("Unexpected m[\"test\"]: %v", v)
  231. }
  232. } else {
  233. t.Fatalf("Unexpected export type: %T", exported)
  234. }
  235. }
  236. func TestExportToWrappedMap(t *testing.T) {
  237. vm := New()
  238. m := map[string]interface{}{
  239. "test": "failed",
  240. }
  241. var exported map[string]interface{}
  242. err := vm.ExportTo(vm.ToValue(m), &exported)
  243. if err != nil {
  244. t.Fatal(err)
  245. }
  246. exported["test"] = "passed"
  247. if v := m["test"]; v != "passed" {
  248. t.Fatalf("Unexpected m[\"test\"]: %v", v)
  249. }
  250. }
  251. func TestExportToWrappedMapCustom(t *testing.T) {
  252. type CustomMap map[string]bool
  253. vm := New()
  254. m := CustomMap{}
  255. var exported CustomMap
  256. err := vm.ExportTo(vm.ToValue(m), &exported)
  257. if err != nil {
  258. t.Fatal(err)
  259. }
  260. exported["test"] = true
  261. if v := m["test"]; v != true {
  262. t.Fatalf("Unexpected m[\"test\"]: %v", v)
  263. }
  264. }
  265. func TestExportToSliceNonIterable(t *testing.T) {
  266. vm := New()
  267. o := vm.NewObject()
  268. var a []interface{}
  269. err := vm.ExportTo(o, &a)
  270. if err == nil {
  271. t.Fatal("Expected an error")
  272. }
  273. if len(a) != 0 {
  274. t.Fatalf("a: %v", a)
  275. }
  276. if msg := err.Error(); msg != "cannot convert [object Object] to []interface {}: not an array or iterable" {
  277. t.Fatalf("Unexpected error: %v", err)
  278. }
  279. }
  280. func ExampleRuntime_ExportTo_iterableToSlice() {
  281. vm := New()
  282. v, err := vm.RunString(`
  283. function reverseIterator() {
  284. const arr = this;
  285. let idx = arr.length;
  286. return {
  287. next: () => idx > 0 ? {value: arr[--idx]} : {done: true}
  288. }
  289. }
  290. const arr = [1,2,3];
  291. arr[Symbol.iterator] = reverseIterator;
  292. arr;
  293. `)
  294. if err != nil {
  295. panic(err)
  296. }
  297. var arr []int
  298. err = vm.ExportTo(v, &arr)
  299. if err != nil {
  300. panic(err)
  301. }
  302. fmt.Println(arr)
  303. // Output: [3 2 1]
  304. }
  305. func TestRuntime_ExportTo_proxiedIterableToSlice(t *testing.T) {
  306. vm := New()
  307. v, err := vm.RunString(`
  308. function reverseIterator() {
  309. const arr = this;
  310. let idx = arr.length;
  311. return {
  312. next: () => idx > 0 ? {value: arr[--idx]} : {done: true}
  313. }
  314. }
  315. const arr = [1,2,3];
  316. arr[Symbol.iterator] = reverseIterator;
  317. new Proxy(arr, {});
  318. `)
  319. if err != nil {
  320. t.Fatal(err)
  321. }
  322. var arr []int
  323. err = vm.ExportTo(v, &arr)
  324. if err != nil {
  325. t.Fatal(err)
  326. }
  327. if out := fmt.Sprint(arr); out != "[3 2 1]" {
  328. t.Fatal(out)
  329. }
  330. }
  331. func ExampleRuntime_ExportTo_arrayLikeToSlice() {
  332. vm := New()
  333. v, err := vm.RunString(`
  334. ({
  335. length: 3,
  336. 0: 1,
  337. 1: 2,
  338. 2: 3
  339. });
  340. `)
  341. if err != nil {
  342. panic(err)
  343. }
  344. var arr []int
  345. err = vm.ExportTo(v, &arr)
  346. if err != nil {
  347. panic(err)
  348. }
  349. fmt.Println(arr)
  350. // Output: [1 2 3]
  351. }
  352. func TestExportArrayToArrayMismatchedLengths(t *testing.T) {
  353. vm := New()
  354. a := vm.NewArray(1, 2)
  355. var a1 [3]int
  356. err := vm.ExportTo(a, &a1)
  357. if err == nil {
  358. t.Fatal("expected error")
  359. }
  360. if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
  361. t.Fatalf("unexpected error: %v", err)
  362. }
  363. }
  364. func TestExportIterableToArrayMismatchedLengths(t *testing.T) {
  365. vm := New()
  366. a, err := vm.RunString(`
  367. new Map([[1, true], [2, true]]);
  368. `)
  369. if err != nil {
  370. t.Fatal(err)
  371. }
  372. var a1 [3]interface{}
  373. err = vm.ExportTo(a, &a1)
  374. if err == nil {
  375. t.Fatal("expected error")
  376. }
  377. if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
  378. t.Fatalf("unexpected error: %v", err)
  379. }
  380. }
  381. func TestExportArrayLikeToArrayMismatchedLengths(t *testing.T) {
  382. vm := New()
  383. a, err := vm.RunString(`
  384. ({
  385. length: 2,
  386. 0: true,
  387. 1: true
  388. });
  389. `)
  390. if err != nil {
  391. t.Fatal(err)
  392. }
  393. var a1 [3]interface{}
  394. err = vm.ExportTo(a, &a1)
  395. if err == nil {
  396. t.Fatal("expected error")
  397. }
  398. if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
  399. t.Fatalf("unexpected error: %v", err)
  400. }
  401. }
  402. func TestSetForeignReturnValue(t *testing.T) {
  403. const SCRIPT = `
  404. var array = [1, 2, 3];
  405. var arrayTarget = new Proxy(array, {});
  406. Object.preventExtensions(array);
  407. !Reflect.set(arrayTarget, "foo", 2);
  408. `
  409. testScript(SCRIPT, valueTrue, t)
  410. }
  411. func TestDefinePropertiesUndefinedVal(t *testing.T) {
  412. const SCRIPT = `
  413. var target = {};
  414. var sym = Symbol();
  415. target[sym] = 1;
  416. target.foo = 2;
  417. target[0] = 3;
  418. var getOwnKeys = [];
  419. var proxy = new Proxy(target, {
  420. getOwnPropertyDescriptor: function(_target, key) {
  421. getOwnKeys.push(key);
  422. },
  423. });
  424. Object.defineProperties({}, proxy);
  425. true;
  426. `
  427. testScript(SCRIPT, valueTrue, t)
  428. }
  429. func ExampleObject_Delete() {
  430. vm := New()
  431. obj := vm.NewObject()
  432. _ = obj.Set("test", true)
  433. before := obj.Get("test")
  434. _ = obj.Delete("test")
  435. after := obj.Get("test")
  436. fmt.Printf("before: %v, after: %v", before, after)
  437. // Output: before: true, after: <nil>
  438. }
  439. func ExampleObject_GetOwnPropertyNames() {
  440. vm := New()
  441. obj := vm.NewObject()
  442. obj.DefineDataProperty("test", vm.ToValue(true), FLAG_TRUE, FLAG_TRUE, FLAG_FALSE) // define a non-enumerable property that Keys() would not return
  443. fmt.Print(obj.GetOwnPropertyNames())
  444. // Output: [test]
  445. }
  446. func TestObjectEquality(t *testing.T) {
  447. type CustomInt int
  448. type S struct {
  449. F CustomInt
  450. }
  451. vm := New()
  452. vm.Set("s", S{})
  453. // indexOf() and includes() use different equality checks (StrictEquals and SameValueZero respectively),
  454. // but for objects they must behave the same. De-referencing s.F creates a new wrapper each time.
  455. vm.testScriptWithTestLib(`
  456. assert.sameValue([s.F].indexOf(s.F), 0, "indexOf");
  457. assert([s.F].includes(s.F));
  458. `, _undefined, t)
  459. }
  460. func BenchmarkPut(b *testing.B) {
  461. v := &Object{}
  462. o := &baseObject{
  463. val: v,
  464. extensible: true,
  465. }
  466. v.self = o
  467. o.init()
  468. var key Value = asciiString("test")
  469. var val Value = valueInt(123)
  470. for i := 0; i < b.N; i++ {
  471. v.setOwn(key, val, false)
  472. }
  473. }
  474. func BenchmarkPutStr(b *testing.B) {
  475. v := &Object{}
  476. o := &baseObject{
  477. val: v,
  478. extensible: true,
  479. }
  480. o.init()
  481. v.self = o
  482. var val Value = valueInt(123)
  483. for i := 0; i < b.N; i++ {
  484. o.setOwnStr("test", val, false)
  485. }
  486. }
  487. func BenchmarkGet(b *testing.B) {
  488. v := &Object{}
  489. o := &baseObject{
  490. val: v,
  491. extensible: true,
  492. }
  493. o.init()
  494. v.self = o
  495. var n Value = asciiString("test")
  496. for i := 0; i < b.N; i++ {
  497. v.get(n, nil)
  498. }
  499. }
  500. func BenchmarkGetStr(b *testing.B) {
  501. v := &Object{}
  502. o := &baseObject{
  503. val: v,
  504. extensible: true,
  505. }
  506. v.self = o
  507. o.init()
  508. for i := 0; i < b.N; i++ {
  509. o.getStr("test", nil)
  510. }
  511. }
  512. func __toString(v Value) string {
  513. switch v := v.(type) {
  514. case asciiString:
  515. return string(v)
  516. default:
  517. return ""
  518. }
  519. }
  520. func BenchmarkToString1(b *testing.B) {
  521. v := asciiString("test")
  522. for i := 0; i < b.N; i++ {
  523. v.toString()
  524. }
  525. }
  526. func BenchmarkToString2(b *testing.B) {
  527. v := asciiString("test")
  528. for i := 0; i < b.N; i++ {
  529. __toString(v)
  530. }
  531. }
  532. func BenchmarkConv(b *testing.B) {
  533. count := int64(0)
  534. for i := 0; i < b.N; i++ {
  535. count += valueInt(123).ToInteger()
  536. }
  537. if count == 0 {
  538. b.Fatal("zero")
  539. }
  540. }
  541. func BenchmarkToUTF8String(b *testing.B) {
  542. var s String = asciiString("test")
  543. for i := 0; i < b.N; i++ {
  544. _ = s.String()
  545. }
  546. }
  547. func BenchmarkAdd(b *testing.B) {
  548. var x, y Value
  549. x = valueInt(2)
  550. y = valueInt(2)
  551. for i := 0; i < b.N; i++ {
  552. if xi, ok := x.(valueInt); ok {
  553. if yi, ok := y.(valueInt); ok {
  554. x = xi + yi
  555. }
  556. }
  557. }
  558. }
  559. func BenchmarkAddString(b *testing.B) {
  560. var x, y Value
  561. tst := asciiString("22")
  562. x = asciiString("2")
  563. y = asciiString("2")
  564. for i := 0; i < b.N; i++ {
  565. var z Value
  566. if xi, ok := x.(String); ok {
  567. if yi, ok := y.(String); ok {
  568. z = xi.Concat(yi)
  569. }
  570. }
  571. if !z.StrictEquals(tst) {
  572. b.Fatalf("Unexpected result %v", x)
  573. }
  574. }
  575. }