object_goreflect_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. package goja
  2. import (
  3. "reflect"
  4. "strings"
  5. "testing"
  6. )
  7. func TestGoReflectGet(t *testing.T) {
  8. const SCRIPT = `
  9. o.X + o.Y;
  10. `
  11. type O struct {
  12. X int
  13. Y string
  14. }
  15. r := New()
  16. o := O{X: 4, Y: "2"}
  17. r.Set("o", o)
  18. v, err := r.RunString(SCRIPT)
  19. if err != nil {
  20. t.Fatal(err)
  21. }
  22. if s, ok := v.assertString(); ok {
  23. if s.String() != "42" {
  24. t.Fatalf("Unexpected string: %s", s)
  25. }
  26. } else {
  27. t.Fatalf("Unexpected type: %s", v)
  28. }
  29. }
  30. func TestGoReflectSet(t *testing.T) {
  31. const SCRIPT = `
  32. o.X++;
  33. o.Y += "P";
  34. `
  35. type O struct {
  36. X int
  37. Y string
  38. }
  39. r := New()
  40. o := O{X: 4, Y: "2"}
  41. r.Set("o", &o)
  42. _, err := r.RunString(SCRIPT)
  43. if err != nil {
  44. t.Fatal(err)
  45. }
  46. if o.X != 5 {
  47. t.Fatalf("Unexpected X: %d", o.X)
  48. }
  49. if o.Y != "2P" {
  50. t.Fatalf("Unexpected Y: %s", o.Y)
  51. }
  52. }
  53. type TestGoReflectMethod_Struct struct {
  54. }
  55. func (s *TestGoReflectMethod_Struct) M() int {
  56. return 42
  57. }
  58. func TestGoReflectEnumerate(t *testing.T) {
  59. const SCRIPT = `
  60. var hasX = false;
  61. var hasY = false;
  62. for (var key in o) {
  63. switch (key) {
  64. case "X":
  65. if (hasX) {
  66. throw "Already have X";
  67. }
  68. hasX = true;
  69. break;
  70. case "Y":
  71. if (hasY) {
  72. throw "Already have Y";
  73. }
  74. hasY = true;
  75. break;
  76. default:
  77. throw "Unexpected property: " + key;
  78. }
  79. }
  80. hasX && hasY;
  81. `
  82. type S struct {
  83. X, Y int
  84. }
  85. r := New()
  86. r.Set("o", S{X: 40, Y: 2})
  87. v, err := r.RunString(SCRIPT)
  88. if err != nil {
  89. t.Fatal(err)
  90. }
  91. if !v.StrictEquals(valueTrue) {
  92. t.Fatalf("Expected true, got %v", v)
  93. }
  94. }
  95. func TestGoReflectCustomIntUnbox(t *testing.T) {
  96. const SCRIPT = `
  97. i + 2;
  98. `
  99. type CustomInt int
  100. var i CustomInt = 40
  101. r := New()
  102. r.Set("i", i)
  103. v, err := r.RunString(SCRIPT)
  104. if err != nil {
  105. t.Fatal(err)
  106. }
  107. if !v.StrictEquals(intToValue(42)) {
  108. t.Fatalf("Expected int 42, got %v", v)
  109. }
  110. }
  111. func TestGoReflectPreserveCustomType(t *testing.T) {
  112. const SCRIPT = `
  113. i;
  114. `
  115. type CustomInt int
  116. var i CustomInt = 42
  117. r := New()
  118. r.Set("i", i)
  119. v, err := r.RunString(SCRIPT)
  120. if err != nil {
  121. t.Fatal(err)
  122. }
  123. ve := v.Export()
  124. if ii, ok := ve.(CustomInt); ok {
  125. if ii != i {
  126. t.Fatalf("Wrong value: %v", ii)
  127. }
  128. } else {
  129. t.Fatalf("Wrong type: %v", ve)
  130. }
  131. }
  132. func TestGoReflectCustomIntValueOf(t *testing.T) {
  133. const SCRIPT = `
  134. if (i instanceof Number) {
  135. i.valueOf();
  136. } else {
  137. throw new Error("Value is not a number");
  138. }
  139. `
  140. type CustomInt int
  141. var i CustomInt = 42
  142. r := New()
  143. r.Set("i", i)
  144. v, err := r.RunString(SCRIPT)
  145. if err != nil {
  146. t.Fatal(err)
  147. }
  148. if !v.StrictEquals(intToValue(42)) {
  149. t.Fatalf("Expected int 42, got %v", v)
  150. }
  151. }
  152. func TestGoReflectEqual(t *testing.T) {
  153. const SCRIPT = `
  154. x === y;
  155. `
  156. type CustomInt int
  157. var x CustomInt = 42
  158. var y CustomInt = 42
  159. r := New()
  160. r.Set("x", x)
  161. r.Set("y", y)
  162. v, err := r.RunString(SCRIPT)
  163. if err != nil {
  164. t.Fatal(err)
  165. }
  166. if !v.StrictEquals(valueTrue) {
  167. t.Fatalf("Expected true, got %v", v)
  168. }
  169. }
  170. type testGoReflectMethod_O struct {
  171. field string
  172. Test string
  173. }
  174. func (o testGoReflectMethod_O) Method(s string) string {
  175. return o.field + s
  176. }
  177. func TestGoReflectMethod(t *testing.T) {
  178. const SCRIPT = `
  179. o.Method(" 123")
  180. `
  181. o := testGoReflectMethod_O{
  182. field: "test",
  183. }
  184. r := New()
  185. r.Set("o", &o)
  186. v, err := r.RunString(SCRIPT)
  187. if err != nil {
  188. t.Fatal(err)
  189. }
  190. if !v.StrictEquals(asciiString("test 123")) {
  191. t.Fatalf("Expected 'test 123', got %v", v)
  192. }
  193. }
  194. func (o *testGoReflectMethod_O) Set(s string) {
  195. o.field = s
  196. }
  197. func (o *testGoReflectMethod_O) Get() string {
  198. return o.field
  199. }
  200. func TestGoReflectMethodPtr(t *testing.T) {
  201. const SCRIPT = `
  202. o.Set("42")
  203. o.Get()
  204. `
  205. o := testGoReflectMethod_O{
  206. field: "test",
  207. }
  208. r := New()
  209. r.Set("o", &o)
  210. v, err := r.RunString(SCRIPT)
  211. if err != nil {
  212. t.Fatal(err)
  213. }
  214. if !v.StrictEquals(asciiString("42")) {
  215. t.Fatalf("Expected '42', got %v", v)
  216. }
  217. }
  218. func TestGoReflectProp(t *testing.T) {
  219. const SCRIPT = `
  220. var d1 = Object.getOwnPropertyDescriptor(o, "Get");
  221. var d2 = Object.getOwnPropertyDescriptor(o, "Test");
  222. !d1.writable && !d1.configurable && d2.writable && !d2.configurable;
  223. `
  224. o := testGoReflectMethod_O{
  225. field: "test",
  226. }
  227. r := New()
  228. r.Set("o", &o)
  229. v, err := r.RunString(SCRIPT)
  230. if err != nil {
  231. t.Fatal(err)
  232. }
  233. if !v.StrictEquals(valueTrue) {
  234. t.Fatalf("Expected true, got %v", v)
  235. }
  236. }
  237. func TestGoReflectRedefineFieldSuccess(t *testing.T) {
  238. const SCRIPT = `
  239. Object.defineProperty(o, "Test", {value: "AAA"}) === o;
  240. `
  241. o := testGoReflectMethod_O{}
  242. r := New()
  243. r.Set("o", &o)
  244. v, err := r.RunString(SCRIPT)
  245. if err != nil {
  246. t.Fatal(err)
  247. }
  248. if !v.StrictEquals(valueTrue) {
  249. t.Fatalf("Expected true, got %v", v)
  250. }
  251. if o.Test != "AAA" {
  252. t.Fatalf("Expected 'AAA', got '%s'", o.Test)
  253. }
  254. }
  255. func TestGoReflectRedefineFieldNonWritable(t *testing.T) {
  256. const SCRIPT = `
  257. var thrown = false;
  258. try {
  259. Object.defineProperty(o, "Test", {value: "AAA", writable: false});
  260. } catch (e) {
  261. if (e instanceof TypeError) {
  262. thrown = true;
  263. } else {
  264. throw e;
  265. }
  266. }
  267. thrown;
  268. `
  269. o := testGoReflectMethod_O{Test: "Test"}
  270. r := New()
  271. r.Set("o", &o)
  272. v, err := r.RunString(SCRIPT)
  273. if err != nil {
  274. t.Fatal(err)
  275. }
  276. if !v.StrictEquals(valueTrue) {
  277. t.Fatalf("Expected true, got %v", v)
  278. }
  279. if o.Test != "Test" {
  280. t.Fatalf("Expected 'Test', got: '%s'", o.Test)
  281. }
  282. }
  283. func TestGoReflectRedefineFieldConfigurable(t *testing.T) {
  284. const SCRIPT = `
  285. var thrown = false;
  286. try {
  287. Object.defineProperty(o, "Test", {value: "AAA", configurable: true});
  288. } catch (e) {
  289. if (e instanceof TypeError) {
  290. thrown = true;
  291. } else {
  292. throw e;
  293. }
  294. }
  295. thrown;
  296. `
  297. o := testGoReflectMethod_O{Test: "Test"}
  298. r := New()
  299. r.Set("o", &o)
  300. v, err := r.RunString(SCRIPT)
  301. if err != nil {
  302. t.Fatal(err)
  303. }
  304. if !v.StrictEquals(valueTrue) {
  305. t.Fatalf("Expected true, got %v", v)
  306. }
  307. if o.Test != "Test" {
  308. t.Fatalf("Expected 'Test', got: '%s'", o.Test)
  309. }
  310. }
  311. func TestGoReflectRedefineMethod(t *testing.T) {
  312. const SCRIPT = `
  313. var thrown = false;
  314. try {
  315. Object.defineProperty(o, "Method", {value: "AAA", configurable: true});
  316. } catch (e) {
  317. if (e instanceof TypeError) {
  318. thrown = true;
  319. } else {
  320. throw e;
  321. }
  322. }
  323. thrown;
  324. `
  325. o := testGoReflectMethod_O{Test: "Test"}
  326. r := New()
  327. r.Set("o", &o)
  328. v, err := r.RunString(SCRIPT)
  329. if err != nil {
  330. t.Fatal(err)
  331. }
  332. if !v.StrictEquals(valueTrue) {
  333. t.Fatalf("Expected true, got %v", v)
  334. }
  335. }
  336. func TestGoReflectEmbeddedStruct(t *testing.T) {
  337. const SCRIPT = `
  338. if (o.ParentField2 !== "ParentField2") {
  339. throw new Error("ParentField2 = " + o.ParentField2);
  340. }
  341. if (o.Parent.ParentField2 !== 2) {
  342. throw new Error("o.Parent.ParentField2 = " + o.Parent.ParentField2);
  343. }
  344. if (o.ParentField1 !== 1) {
  345. throw new Error("o.ParentField1 = " + o.ParentField1);
  346. }
  347. if (o.ChildField !== 3) {
  348. throw new Error("o.ChildField = " + o.ChildField);
  349. }
  350. var keys = {};
  351. for (var k in o) {
  352. if (keys[k]) {
  353. throw new Error("Duplicate key: " + k);
  354. }
  355. keys[k] = true;
  356. }
  357. var expectedKeys = ["ParentField2", "ParentField1", "Parent", "ChildField"];
  358. for (var i in expectedKeys) {
  359. if (!keys[expectedKeys[i]]) {
  360. throw new Error("Missing key in enumeration: " + expectedKeys[i]);
  361. }
  362. delete keys[expectedKeys[i]];
  363. }
  364. var remainingKeys = Object.keys(keys);
  365. if (remainingKeys.length > 0) {
  366. throw new Error("Unexpected keys: " + remainingKeys);
  367. }
  368. o.ParentField2 = "ParentField22";
  369. o.Parent.ParentField2 = 22;
  370. o.ParentField1 = 11;
  371. o.ChildField = 33;
  372. `
  373. type Parent struct {
  374. ParentField1 int
  375. ParentField2 int
  376. }
  377. type Child struct {
  378. ParentField2 string
  379. Parent
  380. ChildField int
  381. }
  382. vm := New()
  383. o := Child{
  384. Parent: Parent{
  385. ParentField1: 1,
  386. ParentField2: 2,
  387. },
  388. ParentField2: "ParentField2",
  389. ChildField: 3,
  390. }
  391. vm.Set("o", &o)
  392. _, err := vm.RunString(SCRIPT)
  393. if err != nil {
  394. t.Fatal(err)
  395. }
  396. if o.ParentField2 != "ParentField22" {
  397. t.Fatalf("ParentField2 = %q", o.ParentField2)
  398. }
  399. if o.Parent.ParentField2 != 22 {
  400. t.Fatalf("Parent.ParentField2 = %d", o.Parent.ParentField2)
  401. }
  402. if o.ParentField1 != 11 {
  403. t.Fatalf("ParentField1 = %d", o.ParentField1)
  404. }
  405. if o.ChildField != 33 {
  406. t.Fatalf("ChildField = %d", o.ChildField)
  407. }
  408. }
  409. type jsonTagNamer struct{}
  410. func (*jsonTagNamer) FieldName(t reflect.Type, field reflect.StructField) string {
  411. if jsonTag := field.Tag.Get("json"); jsonTag != "" {
  412. return jsonTag
  413. }
  414. return field.Name
  415. }
  416. func (*jsonTagNamer) MethodName(t reflect.Type, method reflect.Method) string {
  417. return method.Name
  418. }
  419. func TestGoReflectCustomNaming(t *testing.T) {
  420. type testStructWithJsonTags struct {
  421. A string `json:"b"` // <-- script sees field "A" as property "b"
  422. }
  423. o := &testStructWithJsonTags{"Hello world"}
  424. r := New()
  425. r.SetFieldNameMapper(&jsonTagNamer{})
  426. r.Set("fn", func() *testStructWithJsonTags { return o })
  427. t.Run("get property", func(t *testing.T) {
  428. v, err := r.RunString(`fn().b`)
  429. if err != nil {
  430. t.Fatal(err)
  431. }
  432. if !v.StrictEquals(newStringValue(o.A)) {
  433. t.Fatalf("Expected %q, got %v", o.A, v)
  434. }
  435. })
  436. t.Run("set property", func(t *testing.T) {
  437. _, err := r.RunString(`fn().b = "Hello universe"`)
  438. if err != nil {
  439. t.Fatal(err)
  440. }
  441. if o.A != "Hello universe" {
  442. t.Fatalf("Expected \"Hello universe\", got %q", o.A)
  443. }
  444. })
  445. t.Run("enumerate properties", func(t *testing.T) {
  446. v, err := r.RunString(`Object.keys(fn())`)
  447. if err != nil {
  448. t.Fatal(err)
  449. }
  450. if !reflect.DeepEqual(v.Export(), []interface{}{"b"}) {
  451. t.Fatalf("Expected [\"b\"], got %v", v.Export())
  452. }
  453. })
  454. }
  455. type fieldNameMapper1 struct{}
  456. func (fieldNameMapper1) FieldName(t reflect.Type, f reflect.StructField) string {
  457. return strings.ToLower(f.Name)
  458. }
  459. func (fieldNameMapper1) MethodName(t reflect.Type, m reflect.Method) string {
  460. return m.Name
  461. }
  462. func TestNonStructAnonFields(t *testing.T) {
  463. type Test1 struct {
  464. M bool
  465. }
  466. type test3 []int
  467. type Test4 []int
  468. type Test2 struct {
  469. test3
  470. Test4
  471. *Test1
  472. }
  473. const SCRIPT = `
  474. JSON.stringify(a);
  475. a.m && a.test3 === undefined && a.test4.length === 2
  476. `
  477. vm := New()
  478. vm.SetFieldNameMapper(fieldNameMapper1{})
  479. vm.Set("a", &Test2{Test1: &Test1{M: true}, Test4: []int{1, 2}})
  480. v, err := vm.RunString(SCRIPT)
  481. if err != nil {
  482. t.Fatal(err)
  483. }
  484. if !v.StrictEquals(valueTrue) {
  485. t.Fatalf("Unexepected result: %v", v)
  486. }
  487. }
  488. func BenchmarkGoReflectGet(b *testing.B) {
  489. type parent struct {
  490. field, Test1, Test2, Test3, Test4, Test5, Test string
  491. }
  492. type child struct {
  493. parent
  494. Test6 string
  495. }
  496. b.StopTimer()
  497. vm := New()
  498. b.StartTimer()
  499. for i := 0; i < b.N; i++ {
  500. v := vm.ToValue(child{parent: parent{Test: "Test"}}).(*Object)
  501. v.Get("Test")
  502. }
  503. }