1package goja
2
3import (
4	"strconv"
5	"testing"
6)
7
8func TestProxy_Object_target_getPrototypeOf(t *testing.T) {
9	const SCRIPT = `
10    var proto = {};
11	var obj = Object.create(proto);
12	var proxy = new Proxy(obj, {});
13	var p = Object.getPrototypeOf(proxy);
14	assert.sameValue(proto, p);
15	`
16
17	testScript1(TESTLIB+SCRIPT, _undefined, t)
18}
19
20func TestProxy_Object_proxy_getPrototypeOf(t *testing.T) {
21	const SCRIPT = `
22    var proto = {};
23	var proto2 = {};
24	var obj = Object.create(proto);
25	var proxy = new Proxy(obj, {
26		getPrototypeOf: function(target) {
27			return proto2;
28		}
29	});
30	var p = Object.getPrototypeOf(proxy);
31	assert.sameValue(proto2, p);
32	`
33
34	testScript1(TESTLIB+SCRIPT, _undefined, t)
35}
36
37func TestProxy_Object_native_proxy_getPrototypeOf(t *testing.T) {
38	const SCRIPT = `
39	var p = Object.getPrototypeOf(proxy);
40	assert.sameValue(proto, p);
41	`
42
43	runtime := New()
44
45	prototype := runtime.NewObject()
46	runtime.Set("proto", prototype)
47
48	target := runtime.NewObject()
49	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
50		GetPrototypeOf: func(target *Object) *Object {
51			return prototype
52		},
53	})
54	runtime.Set("proxy", proxy)
55
56	_, err := runtime.RunString(TESTLIB + SCRIPT)
57	if err != nil {
58		t.Fatal(err)
59	}
60}
61
62func TestProxy_Object_target_setPrototypeOf(t *testing.T) {
63	const SCRIPT = `
64    var proto = {};
65	var obj = {};
66	Object.setPrototypeOf(obj, proto);
67	var proxy = new Proxy(obj, {});
68	var p = Object.getPrototypeOf(proxy);
69	assert.sameValue(proto, p);
70	`
71
72	testScript1(TESTLIB+SCRIPT, _undefined, t)
73}
74
75func TestProxy_Object_proxy_setPrototypeOf(t *testing.T) {
76	const SCRIPT = `
77    var proto = {};
78	var proto2 = {};
79	var obj = {};
80	Object.setPrototypeOf(obj, proto);
81	var proxy = new Proxy(obj, {
82		setPrototypeOf: function(target, prototype) {
83			return Object.setPrototypeOf(target, proto2);
84		}
85	});
86	Object.setPrototypeOf(proxy, null);
87	var p = Object.getPrototypeOf(proxy);
88	assert.sameValue(proto2, p);
89	`
90
91	testScript1(TESTLIB+SCRIPT, _undefined, t)
92}
93
94func TestProxy_Object_target_isExtensible(t *testing.T) {
95	const SCRIPT = `
96	var obj = {};
97	Object.seal(obj);
98	var proxy = new Proxy(obj, {});
99	Object.isExtensible(proxy);
100	`
101
102	testScript1(SCRIPT, valueFalse, t)
103}
104
105func TestProxy_proxy_isExtensible(t *testing.T) {
106	const SCRIPT = `
107	var obj = {};
108	Object.seal(obj);
109	var proxy = new Proxy(obj, {
110		isExtensible: function(target) {
111			return false;
112		}
113	});
114	Object.isExtensible(proxy);
115	`
116
117	testScript1(SCRIPT, valueFalse, t)
118}
119
120func TestProxy_native_proxy_isExtensible(t *testing.T) {
121	const SCRIPT = `
122	(function() {
123		Object.preventExtensions(target);
124		return Object.isExtensible(proxy);
125	})();
126	`
127
128	runtime := New()
129
130	target := runtime.NewObject()
131	runtime.Set("target", target)
132
133	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
134		IsExtensible: func(target *Object) (success bool) {
135			return false
136		},
137	})
138	runtime.Set("proxy", proxy)
139
140	val, err := runtime.RunString(SCRIPT)
141	if err != nil {
142		t.Fatal(err)
143	}
144	if val.ToBoolean() {
145		t.Fatal()
146	}
147}
148
149func TestProxy_Object_target_preventExtensions(t *testing.T) {
150	const SCRIPT = `
151	var obj = {
152		canEvolve: true
153	};
154	var proxy = new Proxy(obj, {});
155	Object.preventExtensions(proxy);
156	proxy.canEvolve
157	`
158
159	testScript1(SCRIPT, valueTrue, t)
160}
161
162func TestProxy_proxy_preventExtensions(t *testing.T) {
163	const SCRIPT = `
164	var obj = {
165		canEvolve: true
166	};
167	var proxy = new Proxy(obj, {
168		preventExtensions: function(target) {
169			target.canEvolve = false;
170			return false;
171		}
172	});
173	Object.preventExtensions(proxy);
174	proxy.canEvolve;
175	`
176
177	testScript1(SCRIPT, valueFalse, t)
178}
179
180func TestProxy_native_proxy_preventExtensions(t *testing.T) {
181	const SCRIPT = `
182	(function() {
183		Object.preventExtensions(proxy);
184		return proxy.canEvolve;
185	})();
186	`
187
188	runtime := New()
189
190	target := runtime.NewObject()
191	target.Set("canEvolve", true)
192	runtime.Set("target", target)
193
194	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
195		PreventExtensions: func(target *Object) (success bool) {
196			target.Set("canEvolve", false)
197			return false
198		},
199	})
200	runtime.Set("proxy", proxy)
201
202	val, err := runtime.RunString(SCRIPT)
203	if err != nil {
204		t.Fatal(err)
205	}
206	if val.ToBoolean() {
207		t.Fatal()
208	}
209}
210
211func TestProxy_Object_target_getOwnPropertyDescriptor(t *testing.T) {
212	const SCRIPT = `
213	var desc = {
214		configurable: false,
215		enumerable: false,
216		value: 42,
217		writable: false
218	};
219
220	var obj = {};
221	Object.defineProperty(obj, "foo", desc);
222
223	var proxy = new Proxy(obj, {});
224
225	var desc2 = Object.getOwnPropertyDescriptor(proxy, "foo");
226	desc2.value
227	`
228
229	testScript1(SCRIPT, valueInt(42), t)
230}
231
232func TestProxy_proxy_getOwnPropertyDescriptor(t *testing.T) {
233	const SCRIPT = `
234	var desc = {
235		configurable: false,
236		enumerable: false,
237		value: 42,
238		writable: false
239	};
240	var proxy_desc = {
241		configurable: false,
242		enumerable: false,
243		value: 24,
244		writable: false
245	};
246
247	var obj = {};
248	Object.defineProperty(obj, "foo", desc);
249
250	var proxy = new Proxy(obj, {
251		getOwnPropertyDescriptor: function(target, property) {
252			return proxy_desc;
253		}
254	});
255
256	assert.throws(TypeError, function() {
257		Object.getOwnPropertyDescriptor(proxy, "foo");
258	});
259	undefined;
260	`
261
262	testScript1(TESTLIB+SCRIPT, _undefined, t)
263}
264
265func TestProxy_native_proxy_getOwnPropertyDescriptor(t *testing.T) {
266	const SCRIPT = `
267	(function() {
268		var desc = {
269			configurable: true,
270			enumerable: false,
271			value: 42,
272			writable: false
273		};
274		var proxy_desc = {
275			configurable: true,
276			enumerable: false,
277			value: 24,
278			writable: false
279		};
280
281		var obj = {};
282		Object.defineProperty(obj, "foo", desc);
283
284		return function(constructor) {
285			var proxy = constructor(obj, proxy_desc);
286
287			var desc2 = Object.getOwnPropertyDescriptor(proxy, "foo");
288			return desc2.value
289		}
290	})();
291	`
292
293	runtime := New()
294
295	constructor := func(call FunctionCall) Value {
296		target := call.Argument(0).(*Object)
297		proxyDesc := call.Argument(1).(*Object)
298
299		return runtime.NewProxy(target, &ProxyTrapConfig{
300			GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
301				return runtime.toPropertyDescriptor(proxyDesc)
302			},
303		}).proxy.val
304	}
305
306	val, err := runtime.RunString(SCRIPT)
307	if err != nil {
308		t.Fatal(err)
309	}
310
311	if c, ok := val.(*Object).self.assertCallable(); ok {
312		val := c(FunctionCall{
313			This:      val,
314			Arguments: []Value{runtime.ToValue(constructor)},
315		})
316		if i := val.ToInteger(); i != 24 {
317			t.Fatalf("val: %d", i)
318		}
319	} else {
320		t.Fatal("not a function")
321	}
322}
323
324func TestProxy_native_proxy_getOwnPropertyDescriptorIdx(t *testing.T) {
325	vm := New()
326	a := vm.NewArray()
327	proxy1 := vm.NewProxy(a, &ProxyTrapConfig{
328		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
329			panic(vm.NewTypeError("GetOwnPropertyDescriptor was called for %q", prop))
330		},
331		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
332			if prop >= -1 && prop <= 1 {
333				return PropertyDescriptor{
334					Value:        vm.ToValue(prop),
335					Configurable: FLAG_TRUE,
336				}
337			}
338			return PropertyDescriptor{}
339		},
340	})
341
342	proxy2 := vm.NewProxy(a, &ProxyTrapConfig{
343		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
344			switch prop {
345			case "-1", "0", "1":
346				return PropertyDescriptor{
347					Value:        vm.ToValue(prop),
348					Configurable: FLAG_TRUE,
349				}
350			}
351			return PropertyDescriptor{}
352		},
353	})
354
355	proxy3 := vm.NewProxy(a, &ProxyTrapConfig{
356		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
357			return PropertyDescriptor{
358				Value:        vm.ToValue(prop),
359				Configurable: FLAG_TRUE,
360			}
361		},
362		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
363			panic(vm.NewTypeError("GetOwnPropertyDescriptorIdx was called for %d", prop))
364		},
365	})
366
367	vm.Set("proxy1", proxy1)
368	vm.Set("proxy2", proxy2)
369	vm.Set("proxy3", proxy3)
370	_, err := vm.RunString(TESTLIBX + `
371	var desc;
372	for (var i = -1; i <= 1; i++) {
373		desc = Object.getOwnPropertyDescriptor(proxy1, i);
374		assert(deepEqual(desc, {value: i, writable: false, enumerable: false, configurable: true}), "1. int "+i);
375
376		desc = Object.getOwnPropertyDescriptor(proxy1, ""+i);
377		assert(deepEqual(desc, {value: i, writable: false, enumerable: false, configurable: true}), "1. str "+i);
378
379		desc = Object.getOwnPropertyDescriptor(proxy2, i);
380		assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. int "+i);
381
382		desc = Object.getOwnPropertyDescriptor(proxy2, ""+i);
383		assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. str "+i);
384	}
385
386	for (const prop of ["00", " 0", "-0", "01"]) {
387		desc = Object.getOwnPropertyDescriptor(proxy3, prop);
388		assert(deepEqual(desc, {value: prop, writable: false, enumerable: false, configurable: true}), "3. "+prop);
389	}
390	`)
391	if err != nil {
392		t.Fatal(err)
393	}
394}
395
396func TestProxy_native_proxy_getOwnPropertyDescriptorSym(t *testing.T) {
397	vm := New()
398	o := vm.NewObject()
399	sym := NewSymbol("42")
400	vm.Set("sym", sym)
401	proxy := vm.NewProxy(o, &ProxyTrapConfig{
402		GetOwnPropertyDescriptorSym: func(target *Object, s *Symbol) PropertyDescriptor {
403			if target != o {
404				panic(vm.NewTypeError("Invalid target"))
405			}
406			if s == sym {
407				return PropertyDescriptor{
408					Value:        vm.ToValue("passed"),
409					Writable:     FLAG_TRUE,
410					Configurable: FLAG_TRUE,
411				}
412			}
413			return PropertyDescriptor{}
414		},
415	})
416
417	vm.Set("proxy", proxy)
418	_, err := vm.RunString(TESTLIBX + `
419	var desc = Object.getOwnPropertyDescriptor(proxy, sym);
420	assert(deepEqual(desc, {value: "passed", writable: true, enumerable: false, configurable: true}));
421	assert.sameValue(Object.getOwnPropertyDescriptor(proxy, Symbol.iterator), undefined);
422	`)
423	if err != nil {
424		t.Fatal(err)
425	}
426}
427
428func TestProxy_native_proxy_getOwnPropertyDescriptor_non_existing(t *testing.T) {
429	vm := New()
430	proxy := vm.NewProxy(vm.NewObject(), &ProxyTrapConfig{
431		GetOwnPropertyDescriptor: func(target *Object, prop string) (propertyDescriptor PropertyDescriptor) {
432			return // empty PropertyDescriptor
433		},
434	})
435	vm.Set("proxy", proxy)
436	res, err := vm.RunString(`Object.getOwnPropertyDescriptor(proxy, "foo") === undefined`)
437	if err != nil {
438		t.Fatal(err)
439	}
440	if res != valueTrue {
441		t.Fatal(res)
442	}
443}
444
445func TestProxy_Object_target_defineProperty(t *testing.T) {
446	const SCRIPT = `
447	var obj = {};
448	var proxy = new Proxy(obj, {});
449	Object.defineProperty(proxy, "foo", {
450		value: "test123"
451	});
452	proxy.foo;
453	`
454
455	testScript1(SCRIPT, asciiString("test123"), t)
456}
457
458func TestProxy_proxy_defineProperty(t *testing.T) {
459	const SCRIPT = `
460	var obj = {};
461	var proxy = new Proxy(obj, {
462		defineProperty: function(target, prop, descriptor) {
463			target.foo = "321tset";
464			return true;
465		}
466	});
467	Object.defineProperty(proxy, "foo", {
468		value: "test123"
469	});
470	proxy.foo;
471	`
472
473	testScript1(SCRIPT, asciiString("321tset"), t)
474}
475
476func TestProxy_native_proxy_defineProperty(t *testing.T) {
477	const SCRIPT = `
478	Object.defineProperty(proxy, "foo", {
479		value: "teststr"
480	});
481	Object.defineProperty(proxy, "0", {
482		value: "testidx"
483	});
484	Object.defineProperty(proxy, Symbol.toStringTag, {
485		value: "testsym"
486	});
487	assert.sameValue(proxy.foo, "teststr-passed-str");
488	assert.sameValue(proxy[0], "testidx-passed-idx");
489	assert.sameValue(proxy[Symbol.toStringTag], "testsym-passed-sym");
490	`
491
492	runtime := New()
493
494	target := runtime.NewObject()
495
496	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
497		DefineProperty: func(target *Object, key string, propertyDescriptor PropertyDescriptor) (success bool) {
498			target.Set(key, propertyDescriptor.Value.String()+"-passed-str")
499			return true
500		},
501		DefinePropertyIdx: func(target *Object, key int, propertyDescriptor PropertyDescriptor) (success bool) {
502			target.Set(strconv.Itoa(key), propertyDescriptor.Value.String()+"-passed-idx")
503			return true
504		},
505		DefinePropertySym: func(target *Object, key *Symbol, propertyDescriptor PropertyDescriptor) (success bool) {
506			target.SetSymbol(key, propertyDescriptor.Value.String()+"-passed-sym")
507			return true
508		},
509	})
510	runtime.Set("proxy", proxy)
511
512	_, err := runtime.RunString(TESTLIB + SCRIPT)
513	if err != nil {
514		t.Fatal(err)
515	}
516}
517
518func TestProxy_target_has_in(t *testing.T) {
519	const SCRIPT = `
520	var obj = {
521		secret: true
522	};
523	var proxy = new Proxy(obj, {});
524
525	"secret" in proxy
526	`
527
528	testScript1(SCRIPT, valueTrue, t)
529}
530
531func TestProxy_proxy_has_in(t *testing.T) {
532	const SCRIPT = `
533	var obj = {
534		secret: true
535	};
536	var proxy = new Proxy(obj, {
537		has: function(target, key) {
538			return key !== "secret";
539		}
540	});
541
542	"secret" in proxy
543	`
544
545	testScript1(SCRIPT, valueFalse, t)
546}
547
548func TestProxy_target_has_with(t *testing.T) {
549	const SCRIPT = `
550	var obj = {
551		secret: true
552	};
553	var proxy = new Proxy(obj, {});
554
555	with(proxy) {
556		(secret);
557	}
558	`
559
560	testScript1(SCRIPT, valueTrue, t)
561}
562
563func TestProxy_proxy_has_with(t *testing.T) {
564	const SCRIPT = `
565	var obj = {
566		secret: true
567	};
568	var proxy = new Proxy(obj, {
569		has: function(target, key) {
570			return key !== "secret";
571		}
572	});
573
574	var thrown = false;
575	try {
576		with(proxy) {
577			(secret);
578		}
579	} catch (e) {
580		if (e instanceof ReferenceError) {
581			thrown = true;
582		} else {
583			throw e;
584		}
585	}
586	thrown;
587	`
588
589	testScript1(SCRIPT, valueTrue, t)
590}
591
592func TestProxy_target_get(t *testing.T) {
593	const SCRIPT = `
594	var obj = {};
595	var proxy = new Proxy(obj, {});
596	Object.defineProperty(proxy, "foo", {
597		value: "test123"
598	});
599	proxy.foo;
600	`
601
602	testScript1(SCRIPT, asciiString("test123"), t)
603}
604
605func TestProxy_proxy_get(t *testing.T) {
606	const SCRIPT = `
607	var obj = {};
608	var proxy = new Proxy(obj, {
609		get: function(target, prop, receiver) {
610			return "321tset"
611		}
612	});
613	Object.defineProperty(proxy, "foo", {
614		value: "test123",
615		configurable: true,
616	});
617	proxy.foo;
618	`
619
620	testScript1(SCRIPT, asciiString("321tset"), t)
621}
622
623func TestProxy_proxy_get_json_stringify(t *testing.T) {
624	const SCRIPT = `
625	var obj = {};
626	var propValue = "321tset";
627	var _handler, _target, _prop, _receiver;
628	var proxy = new Proxy(obj, {
629		ownKeys: function() {
630			return ["foo"];
631		},
632		getOwnPropertyDescriptor: function(target, prop) {
633			if (prop === "foo") {
634				return {
635					value: propValue,
636					enumerable: true,
637					configurable: true
638				}
639			}
640		},
641		get: function(target, prop, receiver) {
642			if (prop === "foo") {
643				_prop = prop;
644				_receiver = receiver;
645				return propValue;
646			}
647			return obj[prop];
648		}
649	});
650	var res = JSON.stringify(proxy);
651	assert.sameValue(res, '{"foo":"321tset"}');
652	assert.sameValue(_prop, "foo");
653	assert.sameValue(_receiver, proxy);
654	`
655
656	testScript1(TESTLIB+SCRIPT, _undefined, t)
657}
658
659func TestProxy_native_proxy_get(t *testing.T) {
660	vm := New()
661	propValueStr := vm.ToValue("321tset")
662	propValueIdx := vm.ToValue("idx")
663	propValueSym := vm.ToValue("sym")
664	sym := NewSymbol("test")
665	obj := vm.NewObject()
666	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
667		OwnKeys: func(*Object) *Object {
668			return vm.NewArray("0", "foo")
669		},
670		GetOwnPropertyDescriptor: func(target *Object, prop string) (propertyDescriptor PropertyDescriptor) {
671			if prop == "foo" {
672				return PropertyDescriptor{
673					Value:        propValueStr,
674					Enumerable:   FLAG_TRUE,
675					Configurable: FLAG_TRUE,
676				}
677			}
678			if prop == "0" {
679				panic(vm.NewTypeError("GetOwnPropertyDescriptor(0) was called"))
680			}
681			return
682		},
683		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) (propertyDescriptor PropertyDescriptor) {
684			if prop == 0 {
685				return PropertyDescriptor{
686					Value:        propValueIdx,
687					Enumerable:   FLAG_TRUE,
688					Configurable: FLAG_TRUE,
689				}
690			}
691			return
692		},
693		Get: func(target *Object, property string, receiver Value) (value Value) {
694			if property == "foo" {
695				return propValueStr
696			}
697			if property == "0" {
698				panic(vm.NewTypeError("Get(0) was called"))
699			}
700			return obj.Get(property)
701		},
702		GetIdx: func(target *Object, property int, receiver Value) (value Value) {
703			if property == 0 {
704				return propValueIdx
705			}
706			return obj.Get(strconv.Itoa(property))
707		},
708		GetSym: func(target *Object, property *Symbol, receiver Value) (value Value) {
709			if property == sym {
710				return propValueSym
711			}
712			return obj.GetSymbol(property)
713		},
714	})
715	vm.Set("proxy", proxy)
716	res, err := vm.RunString(`JSON.stringify(proxy)`)
717	if err != nil {
718		t.Fatal(err)
719	}
720	if !res.SameAs(asciiString(`{"0":"idx","foo":"321tset"}`)) {
721		t.Fatalf("res: %v", res)
722	}
723	res, err = vm.RunString(`proxy[Symbol.toPrimitive]`)
724	if err != nil {
725		t.Fatal(err)
726	}
727	if !IsUndefined(res) {
728		t.Fatalf("res: %v", res)
729	}
730
731	res, err = vm.RunString(`proxy.hasOwnProperty(Symbol.toPrimitive)`)
732	if err != nil {
733		t.Fatal(err)
734	}
735	if !res.SameAs(valueFalse) {
736		t.Fatalf("res: %v", res)
737	}
738
739	if val := vm.ToValue(proxy).(*Object).GetSymbol(sym); val == nil || !val.SameAs(propValueSym) {
740		t.Fatalf("Get(symbol): %v", val)
741	}
742
743	res, err = vm.RunString(`proxy.toString()`)
744	if err != nil {
745		t.Fatal(err)
746	}
747	if !res.SameAs(asciiString(`[object Object]`)) {
748		t.Fatalf("res: %v", res)
749	}
750}
751
752func TestProxy_native_proxy_set(t *testing.T) {
753	vm := New()
754	propValueStr := vm.ToValue("321tset")
755	propValueIdx := vm.ToValue("idx")
756	propValueSym := vm.ToValue("sym")
757	sym := NewSymbol("test")
758	obj := vm.NewObject()
759	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
760		Set: func(target *Object, property string, value Value, receiver Value) (success bool) {
761			if property == "str" {
762				obj.Set(property, propValueStr)
763				return true
764			}
765			panic(vm.NewTypeError("Setter for unexpected property: %q", property))
766		},
767		SetIdx: func(target *Object, property int, value Value, receiver Value) (success bool) {
768			if property == 0 {
769				obj.Set(strconv.Itoa(property), propValueIdx)
770				return true
771			}
772			panic(vm.NewTypeError("Setter for unexpected idx property: %d", property))
773		},
774		SetSym: func(target *Object, property *Symbol, value Value, receiver Value) (success bool) {
775			if property == sym {
776				obj.SetSymbol(property, propValueSym)
777				return true
778			}
779			panic(vm.NewTypeError("Setter for unexpected sym property: %q", property.String()))
780		},
781	})
782	proxyObj := vm.ToValue(proxy).ToObject(vm)
783	err := proxyObj.Set("str", "")
784	if err != nil {
785		t.Fatal(err)
786	}
787	err = proxyObj.Set("0", "")
788	if err != nil {
789		t.Fatal(err)
790	}
791	err = proxyObj.SetSymbol(sym, "")
792	if err != nil {
793		t.Fatal(err)
794	}
795	if v := obj.Get("str"); !propValueStr.SameAs(v) {
796		t.Fatal(v)
797	}
798	if v := obj.Get("0"); !propValueIdx.SameAs(v) {
799		t.Fatal(v)
800	}
801	if v := obj.GetSymbol(sym); !propValueSym.SameAs(v) {
802		t.Fatal(v)
803	}
804}
805
806func TestProxy_target_set_prop(t *testing.T) {
807	const SCRIPT = `
808	var obj = {};
809	var proxy = new Proxy(obj, {});
810	proxy.foo = "test123";
811	proxy.foo;
812	`
813
814	testScript1(SCRIPT, asciiString("test123"), t)
815}
816
817func TestProxy_proxy_set_prop(t *testing.T) {
818	const SCRIPT = `
819	var obj = {};
820	var proxy = new Proxy(obj, {
821		set: function(target, prop, receiver) {
822			target.foo = "321tset";
823			return true;
824		}
825	});
826	proxy.foo = "test123";
827	proxy.foo;
828	`
829
830	testScript1(SCRIPT, asciiString("321tset"), t)
831}
832func TestProxy_target_set_associative(t *testing.T) {
833	const SCRIPT = `
834	var obj = {};
835	var proxy = new Proxy(obj, {});
836	proxy["foo"] = "test123";
837	proxy.foo;
838	`
839
840	testScript1(SCRIPT, asciiString("test123"), t)
841}
842
843func TestProxy_proxy_set_associative(t *testing.T) {
844	const SCRIPT = `
845	var obj = {};
846	var proxy = new Proxy(obj, {
847		set: function(target, property, value, receiver) {
848			target["foo"] = "321tset";
849			return true;
850		}
851	});
852	proxy["foo"] = "test123";
853	proxy.foo;
854	`
855
856	testScript1(SCRIPT, asciiString("321tset"), t)
857}
858
859func TestProxy_target_delete(t *testing.T) {
860	const SCRIPT = `
861	var obj = {
862		foo: "test"
863	};
864	var proxy = new Proxy(obj, {});
865	delete proxy.foo;
866
867	proxy.foo;
868	`
869
870	testScript1(SCRIPT, _undefined, t)
871}
872
873func TestProxy_proxy_delete(t *testing.T) {
874	const SCRIPT = `
875	var obj = {
876		foo: "test"
877	};
878	var proxy = new Proxy(obj, {
879		deleteProperty: function(target, prop) {
880			return true;
881		}
882	});
883	delete proxy.foo;
884
885	proxy.foo;
886	`
887
888	testScript1(SCRIPT, asciiString("test"), t)
889}
890
891func TestProxy_native_delete(t *testing.T) {
892	vm := New()
893	sym := NewSymbol("test")
894	obj := vm.NewObject()
895	var strCalled, idxCalled, symCalled, strNegCalled, idxNegCalled, symNegCalled bool
896	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
897		DeleteProperty: func(target *Object, property string) (success bool) {
898			if property == "str" {
899				strCalled = true
900				return true
901			}
902			if property == "strNeg" {
903				strNegCalled = true
904				return false
905			}
906			panic(vm.NewTypeError("DeleteProperty for unexpected property: %q", property))
907		},
908		DeletePropertyIdx: func(target *Object, property int) (success bool) {
909			if property == 0 {
910				idxCalled = true
911				return true
912			}
913			if property == 1 {
914				idxNegCalled = true
915				return false
916			}
917			panic(vm.NewTypeError("DeletePropertyIdx for unexpected idx property: %d", property))
918		},
919		DeletePropertySym: func(target *Object, property *Symbol) (success bool) {
920			if property == sym {
921				symCalled = true
922				return true
923			}
924			if property == SymIterator {
925				symNegCalled = true
926				return false
927			}
928			panic(vm.NewTypeError("DeletePropertySym for unexpected sym property: %q", property.String()))
929		},
930	})
931	proxyObj := vm.ToValue(proxy).ToObject(vm)
932	err := proxyObj.Delete("str")
933	if err != nil {
934		t.Fatal(err)
935	}
936	err = proxyObj.Delete("0")
937	if err != nil {
938		t.Fatal(err)
939	}
940	err = proxyObj.DeleteSymbol(sym)
941	if err != nil {
942		t.Fatal(err)
943	}
944	if !strCalled {
945		t.Fatal("str")
946	}
947	if !idxCalled {
948		t.Fatal("idx")
949	}
950	if !symCalled {
951		t.Fatal("sym")
952	}
953	vm.Set("proxy", proxy)
954	_, err = vm.RunString(`
955	if (delete proxy.strNeg) {
956		throw new Error("strNeg");
957	}
958	if (delete proxy[1]) {
959		throw new Error("idxNeg");
960	}
961	if (delete proxy[Symbol.iterator]) {
962		throw new Error("symNeg");
963	}
964	`)
965	if err != nil {
966		t.Fatal(err)
967	}
968	if !strNegCalled {
969		t.Fatal("strNeg")
970	}
971	if !idxNegCalled {
972		t.Fatal("idxNeg")
973	}
974	if !symNegCalled {
975		t.Fatal("symNeg")
976	}
977}
978
979func TestProxy_target_keys(t *testing.T) {
980	const SCRIPT = `
981	var obj = {
982		foo: "test"
983	};
984	var proxy = new Proxy(obj, {});
985
986	var keys = Object.keys(proxy);
987	if (keys.length != 1) {
988		throw new Error("assertion error");
989	}
990	`
991
992	testScript1(SCRIPT, _undefined, t)
993}
994
995func TestProxy_proxy_keys(t *testing.T) {
996	const SCRIPT = `
997	var obj = {
998		foo: "test"
999	};
1000	var proxy = new Proxy(obj, {
1001		ownKeys: function(target) {
1002			return ["foo", "bar"];
1003		}
1004	});
1005
1006	var keys = Object.keys(proxy);
1007	if (keys.length !== 1) {
1008		throw new Error("length is "+keys.length);
1009	}
1010	if (keys[0] !== "foo") {
1011		throw new Error("keys[0] is "+keys[0]);
1012	}
1013	`
1014
1015	testScript1(SCRIPT, _undefined, t)
1016}
1017
1018func TestProxy_target_call(t *testing.T) {
1019	const SCRIPT = `
1020	var obj = function() {
1021		return "test"
1022	}
1023
1024	var proxy = new Proxy(obj, {});
1025
1026	proxy();
1027	`
1028
1029	testScript1(SCRIPT, asciiString("test"), t)
1030}
1031
1032func TestProxy_proxy_call(t *testing.T) {
1033	const SCRIPT = `
1034	var obj = function() {
1035		return "test"
1036	}
1037
1038	var proxy = new Proxy(obj, {
1039		apply: function(target, thisArg, args) {
1040			return "tset"
1041		}
1042	});
1043
1044	proxy();
1045	`
1046
1047	testScript1(SCRIPT, asciiString("tset"), t)
1048}
1049
1050func TestProxy_target_func_apply(t *testing.T) {
1051	const SCRIPT = `
1052	var obj = function() {
1053		return "test"
1054	}
1055
1056	var proxy = new Proxy(obj, {});
1057
1058	proxy.apply();
1059	`
1060
1061	testScript1(SCRIPT, asciiString("test"), t)
1062}
1063
1064func TestProxy_proxy_func_apply(t *testing.T) {
1065	const SCRIPT = `
1066	var obj = function() {
1067		return "test"
1068	}
1069
1070	var proxy = new Proxy(obj, {
1071		apply: function(target, thisArg, args) {
1072			return "tset"
1073		}
1074	});
1075
1076	proxy.apply();
1077	`
1078
1079	testScript1(SCRIPT, asciiString("tset"), t)
1080}
1081
1082func TestProxy_target_func_call(t *testing.T) {
1083	const SCRIPT = `
1084	var obj = function() {
1085		return "test"
1086	}
1087
1088	var proxy = new Proxy(obj, {});
1089
1090	proxy.call();
1091	`
1092
1093	testScript1(SCRIPT, asciiString("test"), t)
1094}
1095
1096func TestProxy_proxy_func_call(t *testing.T) {
1097	const SCRIPT = `
1098	var obj = function() {
1099		return "test"
1100	}
1101
1102	var proxy = new Proxy(obj, {
1103		apply: function(target, thisArg, args) {
1104			return "tset"
1105		}
1106	});
1107
1108	proxy.call();
1109	`
1110
1111	testScript1(SCRIPT, asciiString("tset"), t)
1112}
1113
1114func TestProxy_target_new(t *testing.T) {
1115	const SCRIPT = `
1116	var obj = function(word) {
1117		this.foo = function() {
1118			return word;
1119		}
1120	}
1121
1122	var proxy = new Proxy(obj, {});
1123
1124	var instance = new proxy("test");
1125	instance.foo();
1126	`
1127
1128	testScript1(SCRIPT, asciiString("test"), t)
1129}
1130
1131func TestProxy_proxy_new(t *testing.T) {
1132	const SCRIPT = `
1133	var obj = function(word) {
1134		this.foo = function() {
1135			return word;
1136		}
1137	}
1138
1139	var proxy = new Proxy(obj, {
1140		construct: function(target, args, newTarget) {
1141			var word = args[0];
1142			return {
1143				foo: function() {
1144					return "caught-" + word
1145				}
1146			}
1147		}
1148	});
1149
1150	var instance = new proxy("test");
1151	instance.foo();
1152	`
1153
1154	testScript1(SCRIPT, asciiString("caught-test"), t)
1155}
1156
1157func TestProxy_Object_native_proxy_ownKeys(t *testing.T) {
1158	headers := map[string][]string{
1159		"k0": {},
1160	}
1161	vm := New()
1162	proxy := vm.NewProxy(vm.NewObject(), &ProxyTrapConfig{
1163		OwnKeys: func(target *Object) (object *Object) {
1164			keys := make([]interface{}, 0, len(headers))
1165			for k := range headers {
1166				keys = append(keys, k)
1167			}
1168			return vm.ToValue(keys).ToObject(vm)
1169		},
1170		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
1171			v, exists := headers[prop]
1172			if exists {
1173				return PropertyDescriptor{
1174					Value:        vm.ToValue(v),
1175					Enumerable:   FLAG_TRUE,
1176					Configurable: FLAG_TRUE,
1177				}
1178			}
1179			return PropertyDescriptor{}
1180		},
1181	})
1182	vm.Set("headers", proxy)
1183	v, err := vm.RunString(`
1184		var keys = Object.keys(headers);
1185		keys.length === 1 && keys[0] === "k0";
1186		`)
1187	if err != nil {
1188		t.Fatal(err)
1189	}
1190	if v != valueTrue {
1191		t.Fatal("not true", v)
1192	}
1193}
1194
1195func TestProxy_proxy_forIn(t *testing.T) {
1196	const SCRIPT = `
1197	var proto = {
1198		a: 2,
1199		protoProp: 1
1200	}
1201	Object.defineProperty(proto, "protoNonEnum", {
1202		value: 2,
1203		writable: true,
1204		configurable: true
1205	});
1206	var target = Object.create(proto);
1207	var proxy = new Proxy(target, {
1208		ownKeys: function() {
1209			return ["a", "b"];
1210		},
1211		getOwnPropertyDescriptor: function(target, p) {
1212			switch (p) {
1213			case "a":
1214			case "b":
1215				return {
1216					value: 42,
1217					enumerable: true,
1218					configurable: true
1219				}
1220			}
1221		},
1222	});
1223
1224	var forInResult = [];
1225	for (var key in proxy) {
1226		if (forInResult.indexOf(key) !== -1) {
1227			throw new Error("Duplicate property "+key);
1228		}
1229		forInResult.push(key);
1230	}
1231	forInResult.length === 3 && forInResult[0] === "a" && forInResult[1] === "b" && forInResult[2] === "protoProp";
1232	`
1233
1234	testScript1(SCRIPT, valueTrue, t)
1235}
1236
1237func TestProxyExport(t *testing.T) {
1238	vm := New()
1239	v, err := vm.RunString(`
1240	new Proxy({}, {});
1241	`)
1242	if err != nil {
1243		t.Fatal(err)
1244	}
1245	v1 := v.Export()
1246	if _, ok := v1.(Proxy); !ok {
1247		t.Fatalf("Export returned unexpected type: %T", v1)
1248	}
1249}
1250
1251func TestProxy_proxy_createTargetNotCallable(t *testing.T) {
1252	// from https://github.com/tc39/test262/blob/main/test/built-ins/Proxy/create-target-is-not-callable.js
1253	const SCRIPT = `
1254	var p = new Proxy({}, {});
1255
1256	assert.throws(TypeError, function() {
1257		  p();
1258	});
1259	`
1260
1261	testScript1(TESTLIB+SCRIPT, _undefined, t)
1262}
1263
1264func TestProxyEnumerableSymbols(t *testing.T) {
1265	const SCRIPT = `
1266	var getOwnKeys = [];
1267	var ownKeysResult = [Symbol(), "foo", "0"];
1268	var proxy = new Proxy({}, {
1269	  getOwnPropertyDescriptor: function(_target, key) {
1270		getOwnKeys.push(key);
1271	  },
1272	  ownKeys: function() {
1273		return ownKeysResult;
1274	  },
1275	});
1276
1277	let {...$} = proxy;
1278	compareArray(getOwnKeys, ownKeysResult);
1279	`
1280
1281	testScript1(TESTLIB+SCRIPT, valueTrue, t)
1282}
1283