1package goja
2
3import (
4	"fmt"
5	"reflect"
6
7	"github.com/dop251/goja/unistring"
8)
9
10// Proxy is a Go wrapper around ECMAScript Proxy. Calling Runtime.ToValue() on it
11// returns the underlying Proxy. Calling Export() on an ECMAScript Proxy returns a wrapper.
12// Use Runtime.NewProxy() to create one.
13type Proxy struct {
14	proxy *proxyObject
15}
16
17var (
18	proxyType = reflect.TypeOf(Proxy{})
19)
20
21type proxyPropIter struct {
22	p     *proxyObject
23	names []Value
24	idx   int
25}
26
27func (i *proxyPropIter) next() (propIterItem, iterNextFunc) {
28	for i.idx < len(i.names) {
29		name := i.names[i.idx]
30		i.idx++
31		if prop := i.p.val.getOwnProp(name); prop != nil {
32			return propIterItem{name: name.string(), value: prop}, i.next
33		}
34	}
35	return propIterItem{}, nil
36}
37
38func (r *Runtime) newProxyObject(target, handler, proto *Object) *proxyObject {
39	if p, ok := target.self.(*proxyObject); ok {
40		if p.handler == nil {
41			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as target"))
42		}
43	}
44	if p, ok := handler.self.(*proxyObject); ok {
45		if p.handler == nil {
46			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as handler"))
47		}
48	}
49	return r._newProxyObject(target, &jsProxyHandler{handler: handler}, proto)
50}
51
52func (r *Runtime) _newProxyObject(target *Object, handler proxyHandler, proto *Object) *proxyObject {
53	v := &Object{runtime: r}
54	p := &proxyObject{}
55	v.self = p
56	p.val = v
57	p.class = classObject
58	if proto == nil {
59		p.prototype = r.global.ObjectPrototype
60	} else {
61		p.prototype = proto
62	}
63	p.extensible = false
64	p.init()
65	p.target = target
66	p.handler = handler
67	if call, ok := target.self.assertCallable(); ok {
68		p.call = call
69	}
70	if ctor := target.self.assertConstructor(); ctor != nil {
71		p.ctor = ctor
72	}
73	return p
74}
75
76func (p Proxy) Revoke() {
77	p.proxy.revoke()
78}
79
80func (p Proxy) Handler() *Object {
81	if handler := p.proxy.handler; handler != nil {
82		return handler.toObject(p.proxy.val.runtime)
83	}
84	return nil
85}
86
87func (p Proxy) Target() *Object {
88	return p.proxy.target
89}
90
91func (p Proxy) toValue(r *Runtime) Value {
92	if p.proxy == nil {
93		return _null
94	}
95	proxy := p.proxy.val
96	if proxy.runtime != r {
97		panic(r.NewTypeError("Illegal runtime transition of a Proxy"))
98	}
99	return proxy
100}
101
102type proxyTrap string
103
104const (
105	proxy_trap_getPrototypeOf           = "getPrototypeOf"
106	proxy_trap_setPrototypeOf           = "setPrototypeOf"
107	proxy_trap_isExtensible             = "isExtensible"
108	proxy_trap_preventExtensions        = "preventExtensions"
109	proxy_trap_getOwnPropertyDescriptor = "getOwnPropertyDescriptor"
110	proxy_trap_defineProperty           = "defineProperty"
111	proxy_trap_has                      = "has"
112	proxy_trap_get                      = "get"
113	proxy_trap_set                      = "set"
114	proxy_trap_deleteProperty           = "deleteProperty"
115	proxy_trap_ownKeys                  = "ownKeys"
116	proxy_trap_apply                    = "apply"
117	proxy_trap_construct                = "construct"
118)
119
120func (p proxyTrap) String() (name string) {
121	return string(p)
122}
123
124type proxyHandler interface {
125	getPrototypeOf(target *Object) (Value, bool)
126	setPrototypeOf(target *Object, proto *Object) (bool, bool)
127	isExtensible(target *Object) (bool, bool)
128	preventExtensions(target *Object) (bool, bool)
129
130	getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool)
131	getOwnPropertyDescriptorIdx(target *Object, prop valueInt) (Value, bool)
132	getOwnPropertyDescriptorSym(target *Object, prop *Symbol) (Value, bool)
133
134	definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool)
135	definePropertyIdx(target *Object, prop valueInt, desc PropertyDescriptor) (bool, bool)
136	definePropertySym(target *Object, prop *Symbol, desc PropertyDescriptor) (bool, bool)
137
138	hasStr(target *Object, prop unistring.String) (bool, bool)
139	hasIdx(target *Object, prop valueInt) (bool, bool)
140	hasSym(target *Object, prop *Symbol) (bool, bool)
141
142	getStr(target *Object, prop unistring.String, receiver Value) (Value, bool)
143	getIdx(target *Object, prop valueInt, receiver Value) (Value, bool)
144	getSym(target *Object, prop *Symbol, receiver Value) (Value, bool)
145
146	setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool)
147	setIdx(target *Object, prop valueInt, value Value, receiver Value) (bool, bool)
148	setSym(target *Object, prop *Symbol, value Value, receiver Value) (bool, bool)
149
150	deleteStr(target *Object, prop unistring.String) (bool, bool)
151	deleteIdx(target *Object, prop valueInt) (bool, bool)
152	deleteSym(target *Object, prop *Symbol) (bool, bool)
153
154	ownKeys(target *Object) (*Object, bool)
155	apply(target *Object, this Value, args []Value) (Value, bool)
156	construct(target *Object, args []Value, newTarget *Object) (Value, bool)
157
158	toObject(*Runtime) *Object
159}
160
161type jsProxyHandler struct {
162	handler *Object
163}
164
165func (h *jsProxyHandler) toObject(*Runtime) *Object {
166	return h.handler
167}
168
169func (h *jsProxyHandler) proxyCall(trap proxyTrap, args ...Value) (Value, bool) {
170	r := h.handler.runtime
171
172	if m := toMethod(r.getVStr(h.handler, unistring.String(trap.String()))); m != nil {
173		return m(FunctionCall{
174			This:      h.handler,
175			Arguments: args,
176		}), true
177	}
178
179	return nil, false
180}
181
182func (h *jsProxyHandler) boolProxyCall(trap proxyTrap, args ...Value) (bool, bool) {
183	if v, ok := h.proxyCall(trap, args...); ok {
184		return v.ToBoolean(), true
185	}
186	return false, false
187}
188
189func (h *jsProxyHandler) getPrototypeOf(target *Object) (Value, bool) {
190	return h.proxyCall(proxy_trap_getPrototypeOf, target)
191}
192
193func (h *jsProxyHandler) setPrototypeOf(target *Object, proto *Object) (bool, bool) {
194	var protoVal Value
195	if proto != nil {
196		protoVal = proto
197	} else {
198		protoVal = _null
199	}
200	return h.boolProxyCall(proxy_trap_setPrototypeOf, target, protoVal)
201}
202
203func (h *jsProxyHandler) isExtensible(target *Object) (bool, bool) {
204	return h.boolProxyCall(proxy_trap_isExtensible, target)
205}
206
207func (h *jsProxyHandler) preventExtensions(target *Object) (bool, bool) {
208	return h.boolProxyCall(proxy_trap_preventExtensions, target)
209}
210
211func (h *jsProxyHandler) getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool) {
212	return h.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, stringValueFromRaw(prop))
213}
214
215func (h *jsProxyHandler) getOwnPropertyDescriptorIdx(target *Object, prop valueInt) (Value, bool) {
216	return h.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, prop.toString())
217}
218
219func (h *jsProxyHandler) getOwnPropertyDescriptorSym(target *Object, prop *Symbol) (Value, bool) {
220	return h.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, prop)
221}
222
223func (h *jsProxyHandler) definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool) {
224	return h.boolProxyCall(proxy_trap_defineProperty, target, stringValueFromRaw(prop), desc.toValue(h.handler.runtime))
225}
226
227func (h *jsProxyHandler) definePropertyIdx(target *Object, prop valueInt, desc PropertyDescriptor) (bool, bool) {
228	return h.boolProxyCall(proxy_trap_defineProperty, target, prop.toString(), desc.toValue(h.handler.runtime))
229}
230
231func (h *jsProxyHandler) definePropertySym(target *Object, prop *Symbol, desc PropertyDescriptor) (bool, bool) {
232	return h.boolProxyCall(proxy_trap_defineProperty, target, prop, desc.toValue(h.handler.runtime))
233}
234
235func (h *jsProxyHandler) hasStr(target *Object, prop unistring.String) (bool, bool) {
236	return h.boolProxyCall(proxy_trap_has, target, stringValueFromRaw(prop))
237}
238
239func (h *jsProxyHandler) hasIdx(target *Object, prop valueInt) (bool, bool) {
240	return h.boolProxyCall(proxy_trap_has, target, prop.toString())
241}
242
243func (h *jsProxyHandler) hasSym(target *Object, prop *Symbol) (bool, bool) {
244	return h.boolProxyCall(proxy_trap_has, target, prop)
245}
246
247func (h *jsProxyHandler) getStr(target *Object, prop unistring.String, receiver Value) (Value, bool) {
248	return h.proxyCall(proxy_trap_get, target, stringValueFromRaw(prop), receiver)
249}
250
251func (h *jsProxyHandler) getIdx(target *Object, prop valueInt, receiver Value) (Value, bool) {
252	return h.proxyCall(proxy_trap_get, target, prop.toString(), receiver)
253}
254
255func (h *jsProxyHandler) getSym(target *Object, prop *Symbol, receiver Value) (Value, bool) {
256	return h.proxyCall(proxy_trap_get, target, prop, receiver)
257}
258
259func (h *jsProxyHandler) setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool) {
260	return h.boolProxyCall(proxy_trap_set, target, stringValueFromRaw(prop), value, receiver)
261}
262
263func (h *jsProxyHandler) setIdx(target *Object, prop valueInt, value Value, receiver Value) (bool, bool) {
264	return h.boolProxyCall(proxy_trap_set, target, prop.toString(), value, receiver)
265}
266
267func (h *jsProxyHandler) setSym(target *Object, prop *Symbol, value Value, receiver Value) (bool, bool) {
268	return h.boolProxyCall(proxy_trap_set, target, prop, value, receiver)
269}
270
271func (h *jsProxyHandler) deleteStr(target *Object, prop unistring.String) (bool, bool) {
272	return h.boolProxyCall(proxy_trap_deleteProperty, target, stringValueFromRaw(prop))
273}
274
275func (h *jsProxyHandler) deleteIdx(target *Object, prop valueInt) (bool, bool) {
276	return h.boolProxyCall(proxy_trap_deleteProperty, target, prop.toString())
277}
278
279func (h *jsProxyHandler) deleteSym(target *Object, prop *Symbol) (bool, bool) {
280	return h.boolProxyCall(proxy_trap_deleteProperty, target, prop)
281}
282
283func (h *jsProxyHandler) ownKeys(target *Object) (*Object, bool) {
284	if v, ok := h.proxyCall(proxy_trap_ownKeys, target); ok {
285		return h.handler.runtime.toObject(v), true
286	}
287	return nil, false
288}
289
290func (h *jsProxyHandler) apply(target *Object, this Value, args []Value) (Value, bool) {
291	return h.proxyCall(proxy_trap_apply, target, this, h.handler.runtime.newArrayValues(args))
292}
293
294func (h *jsProxyHandler) construct(target *Object, args []Value, newTarget *Object) (Value, bool) {
295	return h.proxyCall(proxy_trap_construct, target, h.handler.runtime.newArrayValues(args), newTarget)
296}
297
298type proxyObject struct {
299	baseObject
300	target  *Object
301	handler proxyHandler
302	call    func(FunctionCall) Value
303	ctor    func(args []Value, newTarget *Object) *Object
304}
305
306func (p *proxyObject) checkHandler() proxyHandler {
307	r := p.val.runtime
308	if handler := p.handler; handler != nil {
309		return handler
310	}
311	panic(r.NewTypeError("Proxy already revoked"))
312}
313
314func (p *proxyObject) proto() *Object {
315	target := p.target
316	if v, ok := p.checkHandler().getPrototypeOf(target); ok {
317		var handlerProto *Object
318		if v != _null {
319			handlerProto = p.val.runtime.toObject(v)
320		}
321		if !target.self.isExtensible() && !p.__sameValue(handlerProto, target.self.proto()) {
322			panic(p.val.runtime.NewTypeError("'getPrototypeOf' on proxy: proxy target is non-extensible but the trap did not return its actual prototype"))
323		}
324		return handlerProto
325	}
326
327	return target.self.proto()
328}
329
330func (p *proxyObject) setProto(proto *Object, throw bool) bool {
331	target := p.target
332	if v, ok := p.checkHandler().setPrototypeOf(target, proto); ok {
333		if v {
334			if !target.self.isExtensible() && !p.__sameValue(proto, target.self.proto()) {
335				panic(p.val.runtime.NewTypeError("'setPrototypeOf' on proxy: trap returned truish for setting a new prototype on the non-extensible proxy target"))
336			}
337			return true
338		} else {
339			p.val.runtime.typeErrorResult(throw, "'setPrototypeOf' on proxy: trap returned falsish")
340			return false
341		}
342	}
343
344	return target.self.setProto(proto, throw)
345}
346
347func (p *proxyObject) isExtensible() bool {
348	target := p.target
349	if booleanTrapResult, ok := p.checkHandler().isExtensible(p.target); ok {
350		if te := target.self.isExtensible(); booleanTrapResult != te {
351			panic(p.val.runtime.NewTypeError("'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is '%v')", te))
352		}
353		return booleanTrapResult
354	}
355
356	return target.self.isExtensible()
357}
358
359func (p *proxyObject) preventExtensions(throw bool) bool {
360	target := p.target
361	if booleanTrapResult, ok := p.checkHandler().preventExtensions(target); ok {
362		if !booleanTrapResult {
363			p.val.runtime.typeErrorResult(throw, "'preventExtensions' on proxy: trap returned falsish")
364			return false
365		}
366		if te := target.self.isExtensible(); booleanTrapResult && te {
367			panic(p.val.runtime.NewTypeError("'preventExtensions' on proxy: trap returned truish but the proxy target is extensible"))
368		}
369	}
370
371	return target.self.preventExtensions(throw)
372}
373
374func propToValueProp(v Value) *valueProperty {
375	if v == nil {
376		return nil
377	}
378	if v, ok := v.(*valueProperty); ok {
379		return v
380	}
381	return &valueProperty{
382		value:        v,
383		writable:     true,
384		configurable: true,
385		enumerable:   true,
386	}
387}
388
389func (p *proxyObject) proxyDefineOwnPropertyPreCheck(trapResult, throw bool) bool {
390	if !trapResult {
391		p.val.runtime.typeErrorResult(throw, "'defineProperty' on proxy: trap returned falsish")
392		return false
393	}
394	return true
395}
396
397func (p *proxyObject) proxyDefineOwnPropertyPostCheck(prop Value, target *Object, descr PropertyDescriptor) {
398	targetDesc := propToValueProp(prop)
399	extensibleTarget := target.self.isExtensible()
400	settingConfigFalse := descr.Configurable == FLAG_FALSE
401	if targetDesc == nil {
402		if !extensibleTarget {
403			panic(p.val.runtime.NewTypeError())
404		}
405		if settingConfigFalse {
406			panic(p.val.runtime.NewTypeError())
407		}
408	} else {
409		if !p.__isCompatibleDescriptor(extensibleTarget, &descr, targetDesc) {
410			panic(p.val.runtime.NewTypeError())
411		}
412		if settingConfigFalse && targetDesc.configurable {
413			panic(p.val.runtime.NewTypeError())
414		}
415		if targetDesc.value != nil && !targetDesc.configurable && targetDesc.writable {
416			if descr.Writable == FLAG_FALSE {
417				panic(p.val.runtime.NewTypeError())
418			}
419		}
420	}
421}
422
423func (p *proxyObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
424	target := p.target
425	if booleanTrapResult, ok := p.checkHandler().definePropertyStr(target, name, descr); ok {
426		if !p.proxyDefineOwnPropertyPreCheck(booleanTrapResult, throw) {
427			return false
428		}
429		p.proxyDefineOwnPropertyPostCheck(target.self.getOwnPropStr(name), target, descr)
430		return true
431	}
432	return target.self.defineOwnPropertyStr(name, descr, throw)
433}
434
435func (p *proxyObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
436	target := p.target
437	if booleanTrapResult, ok := p.checkHandler().definePropertyIdx(target, idx, descr); ok {
438		if !p.proxyDefineOwnPropertyPreCheck(booleanTrapResult, throw) {
439			return false
440		}
441		p.proxyDefineOwnPropertyPostCheck(target.self.getOwnPropIdx(idx), target, descr)
442		return true
443	}
444
445	return target.self.defineOwnPropertyIdx(idx, descr, throw)
446}
447
448func (p *proxyObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool {
449	target := p.target
450	if booleanTrapResult, ok := p.checkHandler().definePropertySym(target, s, descr); ok {
451		if !p.proxyDefineOwnPropertyPreCheck(booleanTrapResult, throw) {
452			return false
453		}
454		p.proxyDefineOwnPropertyPostCheck(target.self.getOwnPropSym(s), target, descr)
455		return true
456	}
457
458	return target.self.defineOwnPropertySym(s, descr, throw)
459}
460
461func (p *proxyObject) proxyHasChecks(targetProp Value, target *Object, name fmt.Stringer) {
462	targetDesc := propToValueProp(targetProp)
463	if targetDesc != nil {
464		if !targetDesc.configurable {
465			panic(p.val.runtime.NewTypeError("'has' on proxy: trap returned falsish for property '%s' which exists in the proxy target as non-configurable", name.String()))
466		}
467		if !target.self.isExtensible() {
468			panic(p.val.runtime.NewTypeError("'has' on proxy: trap returned falsish for property '%s' but the proxy target is not extensible", name.String()))
469		}
470	}
471}
472
473func (p *proxyObject) hasPropertyStr(name unistring.String) bool {
474	target := p.target
475	if b, ok := p.checkHandler().hasStr(target, name); ok {
476		if !b {
477			p.proxyHasChecks(target.self.getOwnPropStr(name), target, name)
478		}
479		return b
480	}
481
482	return target.self.hasPropertyStr(name)
483}
484
485func (p *proxyObject) hasPropertyIdx(idx valueInt) bool {
486	target := p.target
487	if b, ok := p.checkHandler().hasIdx(target, idx); ok {
488		if !b {
489			p.proxyHasChecks(target.self.getOwnPropIdx(idx), target, idx)
490		}
491		return b
492	}
493
494	return target.self.hasPropertyIdx(idx)
495}
496
497func (p *proxyObject) hasPropertySym(s *Symbol) bool {
498	target := p.target
499	if b, ok := p.checkHandler().hasSym(target, s); ok {
500		if !b {
501			p.proxyHasChecks(target.self.getOwnPropSym(s), target, s)
502		}
503		return b
504	}
505
506	return target.self.hasPropertySym(s)
507}
508
509func (p *proxyObject) hasOwnPropertyStr(name unistring.String) bool {
510	return p.getOwnPropStr(name) != nil
511}
512
513func (p *proxyObject) hasOwnPropertyIdx(idx valueInt) bool {
514	return p.getOwnPropIdx(idx) != nil
515}
516
517func (p *proxyObject) hasOwnPropertySym(s *Symbol) bool {
518	return p.getOwnPropSym(s) != nil
519}
520
521func (p *proxyObject) proxyGetOwnPropertyDescriptor(targetProp Value, target *Object, trapResult Value, name fmt.Stringer) Value {
522	r := p.val.runtime
523	targetDesc := propToValueProp(targetProp)
524	var trapResultObj *Object
525	if trapResult != nil && trapResult != _undefined {
526		if obj, ok := trapResult.(*Object); ok {
527			trapResultObj = obj
528		} else {
529			panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap returned neither object nor undefined for property '%s'", name.String()))
530		}
531	}
532	if trapResultObj == nil {
533		if targetDesc == nil {
534			return nil
535		}
536		if !targetDesc.configurable {
537			panic(r.NewTypeError())
538		}
539		if !target.self.isExtensible() {
540			panic(r.NewTypeError())
541		}
542		return nil
543	}
544	extensibleTarget := target.self.isExtensible()
545	resultDesc := r.toPropertyDescriptor(trapResultObj)
546	resultDesc.complete()
547	if !p.__isCompatibleDescriptor(extensibleTarget, &resultDesc, targetDesc) {
548		panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap returned descriptor for property '%s' that is incompatible with the existing property in the proxy target", name.String()))
549	}
550
551	if resultDesc.Configurable == FLAG_FALSE {
552		if targetDesc == nil {
553			panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property '%s' which is non-existent in the proxy target", name.String()))
554		}
555
556		if targetDesc.configurable {
557			panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property '%s' which is configurable in the proxy target", name.String()))
558		}
559
560		if resultDesc.Writable == FLAG_FALSE && targetDesc.writable {
561			panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap reported non-configurable and writable for property '%s' which is non-configurable, non-writable in the proxy target", name.String()))
562		}
563	}
564
565	if resultDesc.Writable == FLAG_TRUE && resultDesc.Configurable == FLAG_TRUE &&
566		resultDesc.Enumerable == FLAG_TRUE {
567		return resultDesc.Value
568	}
569	return r.toValueProp(trapResultObj)
570}
571
572func (p *proxyObject) getOwnPropStr(name unistring.String) Value {
573	target := p.target
574	if v, ok := p.checkHandler().getOwnPropertyDescriptorStr(target, name); ok {
575		return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropStr(name), target, v, name)
576	}
577
578	return target.self.getOwnPropStr(name)
579}
580
581func (p *proxyObject) getOwnPropIdx(idx valueInt) Value {
582	target := p.target
583	if v, ok := p.checkHandler().getOwnPropertyDescriptorIdx(target, idx); ok {
584		return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropIdx(idx), target, v, idx)
585	}
586
587	return target.self.getOwnPropIdx(idx)
588}
589
590func (p *proxyObject) getOwnPropSym(s *Symbol) Value {
591	target := p.target
592	if v, ok := p.checkHandler().getOwnPropertyDescriptorSym(target, s); ok {
593		return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropSym(s), target, v, s)
594	}
595
596	return target.self.getOwnPropSym(s)
597}
598
599func (p *proxyObject) proxyGetChecks(targetProp, trapResult Value, name fmt.Stringer) {
600	if targetDesc, ok := targetProp.(*valueProperty); ok {
601		if !targetDesc.accessor {
602			if !targetDesc.writable && !targetDesc.configurable && !trapResult.SameAs(targetDesc.value) {
603				panic(p.val.runtime.NewTypeError("'get' on proxy: property '%s' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected '%s' but got '%s')", name.String(), nilSafe(targetDesc.value), ret))
604			}
605		} else {
606			if !targetDesc.configurable && targetDesc.getterFunc == nil && trapResult != _undefined {
607				panic(p.val.runtime.NewTypeError("'get' on proxy: property '%s' is a non-configurable accessor property on the proxy target and does not have a getter function, but the trap did not return 'undefined' (got '%s')", name.String(), ret))
608			}
609		}
610	}
611}
612
613func (p *proxyObject) getStr(name unistring.String, receiver Value) Value {
614	target := p.target
615	if receiver == nil {
616		receiver = p.val
617	}
618	if v, ok := p.checkHandler().getStr(target, name, receiver); ok {
619		p.proxyGetChecks(target.self.getOwnPropStr(name), v, name)
620		return v
621	}
622	return target.self.getStr(name, receiver)
623}
624
625func (p *proxyObject) getIdx(idx valueInt, receiver Value) Value {
626	target := p.target
627	if receiver == nil {
628		receiver = p.val
629	}
630	if v, ok := p.checkHandler().getIdx(target, idx, receiver); ok {
631		p.proxyGetChecks(target.self.getOwnPropIdx(idx), v, idx)
632		return v
633	}
634	return target.self.getIdx(idx, receiver)
635}
636
637func (p *proxyObject) getSym(s *Symbol, receiver Value) Value {
638	target := p.target
639	if receiver == nil {
640		receiver = p.val
641	}
642	if v, ok := p.checkHandler().getSym(target, s, receiver); ok {
643		p.proxyGetChecks(target.self.getOwnPropSym(s), v, s)
644		return v
645	}
646
647	return target.self.getSym(s, receiver)
648}
649
650func (p *proxyObject) proxySetPreCheck(trapResult, throw bool, name fmt.Stringer) bool {
651	if !trapResult {
652		p.val.runtime.typeErrorResult(throw, "'set' on proxy: trap returned falsish for property '%s'", name.String())
653	}
654	return trapResult
655}
656
657func (p *proxyObject) proxySetPostCheck(targetProp, value Value, name fmt.Stringer) {
658	if prop, ok := targetProp.(*valueProperty); ok {
659		if prop.accessor {
660			if !prop.configurable && prop.setterFunc == nil {
661				panic(p.val.runtime.NewTypeError("'set' on proxy: trap returned truish for property '%s' which exists in the proxy target as a non-configurable and non-writable accessor property without a setter", name.String()))
662			}
663		} else if !prop.configurable && !prop.writable && !p.__sameValue(prop.value, value) {
664			panic(p.val.runtime.NewTypeError("'set' on proxy: trap returned truish for property '%s' which exists in the proxy target as a non-configurable and non-writable data property with a different value", name.String()))
665		}
666	}
667}
668
669func (p *proxyObject) proxySetStr(name unistring.String, value, receiver Value, throw bool) bool {
670	target := p.target
671	if v, ok := p.checkHandler().setStr(target, name, value, receiver); ok {
672		if p.proxySetPreCheck(v, throw, name) {
673			p.proxySetPostCheck(target.self.getOwnPropStr(name), value, name)
674			return true
675		}
676		return false
677	}
678	return target.setStr(name, value, receiver, throw)
679}
680
681func (p *proxyObject) proxySetIdx(idx valueInt, value, receiver Value, throw bool) bool {
682	target := p.target
683	if v, ok := p.checkHandler().setIdx(target, idx, value, receiver); ok {
684		if p.proxySetPreCheck(v, throw, idx) {
685			p.proxySetPostCheck(target.self.getOwnPropIdx(idx), value, idx)
686			return true
687		}
688		return false
689	}
690	return target.setIdx(idx, value, receiver, throw)
691}
692
693func (p *proxyObject) proxySetSym(s *Symbol, value, receiver Value, throw bool) bool {
694	target := p.target
695	if v, ok := p.checkHandler().setSym(target, s, value, receiver); ok {
696		if p.proxySetPreCheck(v, throw, s) {
697			p.proxySetPostCheck(target.self.getOwnPropSym(s), value, s)
698			return true
699		}
700		return false
701	}
702	return target.setSym(s, value, receiver, throw)
703}
704
705func (p *proxyObject) setOwnStr(name unistring.String, v Value, throw bool) bool {
706	return p.proxySetStr(name, v, p.val, throw)
707}
708
709func (p *proxyObject) setOwnIdx(idx valueInt, v Value, throw bool) bool {
710	return p.proxySetIdx(idx, v, p.val, throw)
711}
712
713func (p *proxyObject) setOwnSym(s *Symbol, v Value, throw bool) bool {
714	return p.proxySetSym(s, v, p.val, throw)
715}
716
717func (p *proxyObject) setForeignStr(name unistring.String, v, receiver Value, throw bool) (bool, bool) {
718	return p.proxySetStr(name, v, receiver, throw), true
719}
720
721func (p *proxyObject) setForeignIdx(idx valueInt, v, receiver Value, throw bool) (bool, bool) {
722	return p.proxySetIdx(idx, v, receiver, throw), true
723}
724
725func (p *proxyObject) setForeignSym(s *Symbol, v, receiver Value, throw bool) (bool, bool) {
726	return p.proxySetSym(s, v, receiver, throw), true
727}
728
729func (p *proxyObject) proxyDeleteCheck(trapResult bool, targetProp Value, name fmt.Stringer, target *Object) {
730	if trapResult {
731		if targetProp == nil {
732			return
733		}
734		if targetDesc, ok := targetProp.(*valueProperty); ok {
735			if !targetDesc.configurable {
736				panic(p.val.runtime.NewTypeError("'deleteProperty' on proxy: property '%s' is a non-configurable property but the trap returned truish", name.String()))
737			}
738		}
739		if !target.self.isExtensible() {
740			panic(p.val.runtime.NewTypeError("'deleteProperty' on proxy: trap returned truish for property '%s' but the proxy target is non-extensible", name.String()))
741		}
742	}
743}
744
745func (p *proxyObject) deleteStr(name unistring.String, throw bool) bool {
746	target := p.target
747	if v, ok := p.checkHandler().deleteStr(target, name); ok {
748		p.proxyDeleteCheck(v, target.self.getOwnPropStr(name), name, target)
749		return v
750	}
751
752	return target.self.deleteStr(name, throw)
753}
754
755func (p *proxyObject) deleteIdx(idx valueInt, throw bool) bool {
756	target := p.target
757	if v, ok := p.checkHandler().deleteIdx(target, idx); ok {
758		p.proxyDeleteCheck(v, target.self.getOwnPropIdx(idx), idx, target)
759		return v
760	}
761
762	return target.self.deleteIdx(idx, throw)
763}
764
765func (p *proxyObject) deleteSym(s *Symbol, throw bool) bool {
766	target := p.target
767	if v, ok := p.checkHandler().deleteSym(target, s); ok {
768		p.proxyDeleteCheck(v, target.self.getOwnPropSym(s), s, target)
769		return v
770	}
771
772	return target.self.deleteSym(s, throw)
773}
774
775func (p *proxyObject) ownPropertyKeys(all bool, _ []Value) []Value {
776	if v, ok := p.proxyOwnKeys(); ok {
777		if !all {
778			k := 0
779			for i, key := range v {
780				prop := p.val.getOwnProp(key)
781				if prop == nil {
782					continue
783				}
784				if prop, ok := prop.(*valueProperty); ok && !prop.enumerable {
785					continue
786				}
787				if k != i {
788					v[k] = v[i]
789				}
790				k++
791			}
792			v = v[:k]
793		}
794		return v
795	}
796	return p.target.self.ownPropertyKeys(all, nil)
797}
798
799func (p *proxyObject) proxyOwnKeys() ([]Value, bool) {
800	target := p.target
801	if v, ok := p.checkHandler().ownKeys(target); ok {
802		keys := p.val.runtime.toObject(v)
803		var keyList []Value
804		keySet := make(map[Value]struct{})
805		l := toLength(keys.self.getStr("length", nil))
806		for k := int64(0); k < l; k++ {
807			item := keys.self.getIdx(valueInt(k), nil)
808			if _, ok := item.(valueString); !ok {
809				if _, ok := item.(*Symbol); !ok {
810					panic(p.val.runtime.NewTypeError("%s is not a valid property name", item.String()))
811				}
812			}
813			if _, exists := keySet[item]; exists {
814				panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap returned duplicate entries"))
815			}
816			keyList = append(keyList, item)
817			keySet[item] = struct{}{}
818		}
819		ext := target.self.isExtensible()
820		for _, itemName := range target.self.ownPropertyKeys(true, nil) {
821			if _, exists := keySet[itemName]; exists {
822				delete(keySet, itemName)
823			} else {
824				if !ext {
825					panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include '%s'", itemName.String()))
826				}
827				prop := target.getOwnProp(itemName)
828				if prop, ok := prop.(*valueProperty); ok && !prop.configurable {
829					panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include non-configurable '%s'", itemName.String()))
830				}
831			}
832		}
833		if !ext && len(keyList) > 0 && len(keySet) > 0 {
834			panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible"))
835		}
836
837		return keyList, true
838	}
839
840	return nil, false
841}
842
843func (p *proxyObject) enumerateOwnKeys() iterNextFunc {
844	return (&proxyPropIter{
845		p:     p,
846		names: p.ownKeys(true, nil),
847	}).next
848}
849
850func (p *proxyObject) assertCallable() (call func(FunctionCall) Value, ok bool) {
851	if p.call != nil {
852		return func(call FunctionCall) Value {
853			return p.apply(call)
854		}, true
855	}
856	return nil, false
857}
858
859func (p *proxyObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
860	if p.ctor != nil {
861		return p.construct
862	}
863	return nil
864}
865
866func (p *proxyObject) apply(call FunctionCall) Value {
867	if p.call == nil {
868		panic(p.val.runtime.NewTypeError("proxy target is not a function"))
869	}
870	if v, ok := p.checkHandler().apply(p.target, nilSafe(call.This), call.Arguments); ok {
871		return v
872	}
873	return p.call(call)
874}
875
876func (p *proxyObject) construct(args []Value, newTarget *Object) *Object {
877	if p.ctor == nil {
878		panic(p.val.runtime.NewTypeError("proxy target is not a constructor"))
879	}
880	if newTarget == nil {
881		newTarget = p.val
882	}
883	if v, ok := p.checkHandler().construct(p.target, args, newTarget); ok {
884		return p.val.runtime.toObject(v)
885	}
886	return p.ctor(args, newTarget)
887}
888
889func (p *proxyObject) __isCompatibleDescriptor(extensible bool, desc *PropertyDescriptor, current *valueProperty) bool {
890	if current == nil {
891		return extensible
892	}
893
894	/*if desc.Empty() {
895		return true
896	}*/
897
898	/*if p.__isEquivalentDescriptor(desc, current) {
899		return true
900	}*/
901
902	if !current.configurable {
903		if desc.Configurable == FLAG_TRUE {
904			return false
905		}
906
907		if desc.Enumerable != FLAG_NOT_SET && desc.Enumerable.Bool() != current.enumerable {
908			return false
909		}
910
911		if desc.IsGeneric() {
912			return true
913		}
914
915		if desc.IsData() != !current.accessor {
916			return desc.Configurable != FLAG_FALSE
917		}
918
919		if desc.IsData() && !current.accessor {
920			if !current.configurable {
921				if desc.Writable == FLAG_TRUE && !current.writable {
922					return false
923				}
924				if !current.writable {
925					if desc.Value != nil && !desc.Value.SameAs(current.value) {
926						return false
927					}
928				}
929			}
930			return true
931		}
932		if desc.IsAccessor() && current.accessor {
933			if !current.configurable {
934				if desc.Setter != nil && desc.Setter.SameAs(current.setterFunc) {
935					return false
936				}
937				if desc.Getter != nil && desc.Getter.SameAs(current.getterFunc) {
938					return false
939				}
940			}
941		}
942	}
943	return true
944}
945
946func (p *proxyObject) __sameValue(val1, val2 Value) bool {
947	if val1 == nil && val2 == nil {
948		return true
949	}
950	if val1 != nil {
951		return val1.SameAs(val2)
952	}
953	return false
954}
955
956func (p *proxyObject) filterKeys(vals []Value, all, symbols bool) []Value {
957	if !all {
958		k := 0
959		for i, val := range vals {
960			var prop Value
961			if symbols {
962				if s, ok := val.(*Symbol); ok {
963					prop = p.getOwnPropSym(s)
964				} else {
965					continue
966				}
967			} else {
968				if _, ok := val.(*Symbol); !ok {
969					prop = p.getOwnPropStr(val.string())
970				} else {
971					continue
972				}
973			}
974			if prop == nil {
975				continue
976			}
977			if prop, ok := prop.(*valueProperty); ok && !prop.enumerable {
978				continue
979			}
980			if k != i {
981				vals[k] = vals[i]
982			}
983			k++
984		}
985		vals = vals[:k]
986	} else {
987		k := 0
988		for i, val := range vals {
989			if _, ok := val.(*Symbol); ok != symbols {
990				continue
991			}
992			if k != i {
993				vals[k] = vals[i]
994			}
995			k++
996		}
997		vals = vals[:k]
998	}
999	return vals
1000}
1001
1002func (p *proxyObject) ownKeys(all bool, _ []Value) []Value { // we can assume accum is empty
1003	if vals, ok := p.proxyOwnKeys(); ok {
1004		return p.filterKeys(vals, all, false)
1005	}
1006
1007	return p.target.self.ownKeys(all, nil)
1008}
1009
1010func (p *proxyObject) ownSymbols(all bool, accum []Value) []Value {
1011	if vals, ok := p.proxyOwnKeys(); ok {
1012		res := p.filterKeys(vals, all, true)
1013		if accum == nil {
1014			return res
1015		}
1016		accum = append(accum, res...)
1017		return accum
1018	}
1019
1020	return p.target.self.ownSymbols(all, accum)
1021}
1022
1023func (p *proxyObject) className() string {
1024	if p.target == nil {
1025		panic(p.val.runtime.NewTypeError("proxy has been revoked"))
1026	}
1027	if p.call != nil || p.ctor != nil {
1028		return classFunction
1029	}
1030	return classObject
1031}
1032
1033func (p *proxyObject) exportType() reflect.Type {
1034	return proxyType
1035}
1036
1037func (p *proxyObject) export(*objectExportCtx) interface{} {
1038	return Proxy{
1039		proxy: p,
1040	}
1041}
1042
1043func (p *proxyObject) revoke() {
1044	p.handler = nil
1045	p.target = nil
1046}
1047