1package goja
2
3import (
4	"github.com/dop251/goja/unistring"
5)
6
7type nativeProxyHandler struct {
8	handler *ProxyTrapConfig
9}
10
11func (h *nativeProxyHandler) getPrototypeOf(target *Object) (Value, bool) {
12	if trap := h.handler.GetPrototypeOf; trap != nil {
13		return trap(target), true
14	}
15	return nil, false
16}
17
18func (h *nativeProxyHandler) setPrototypeOf(target *Object, proto *Object) (bool, bool) {
19	if trap := h.handler.SetPrototypeOf; trap != nil {
20		return trap(target, proto), true
21	}
22	return false, false
23}
24
25func (h *nativeProxyHandler) isExtensible(target *Object) (bool, bool) {
26	if trap := h.handler.IsExtensible; trap != nil {
27		return trap(target), true
28	}
29	return false, false
30}
31
32func (h *nativeProxyHandler) preventExtensions(target *Object) (bool, bool) {
33	if trap := h.handler.PreventExtensions; trap != nil {
34		return trap(target), true
35	}
36	return false, false
37}
38
39func (h *nativeProxyHandler) getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool) {
40	if trap := h.handler.GetOwnPropertyDescriptorIdx; trap != nil {
41		if idx, ok := strToInt(prop); ok {
42			desc := trap(target, idx)
43			return desc.toValue(target.runtime), true
44		}
45	}
46	if trap := h.handler.GetOwnPropertyDescriptor; trap != nil {
47		desc := trap(target, prop.String())
48		return desc.toValue(target.runtime), true
49	}
50	return nil, false
51}
52
53func (h *nativeProxyHandler) getOwnPropertyDescriptorIdx(target *Object, prop valueInt) (Value, bool) {
54	if trap := h.handler.GetOwnPropertyDescriptorIdx; trap != nil {
55		desc := trap(target, toIntStrict(int64(prop)))
56		return desc.toValue(target.runtime), true
57	}
58	if trap := h.handler.GetOwnPropertyDescriptor; trap != nil {
59		desc := trap(target, prop.String())
60		return desc.toValue(target.runtime), true
61	}
62	return nil, false
63}
64
65func (h *nativeProxyHandler) getOwnPropertyDescriptorSym(target *Object, prop *Symbol) (Value, bool) {
66	if trap := h.handler.GetOwnPropertyDescriptorSym; trap != nil {
67		desc := trap(target, prop)
68		return desc.toValue(target.runtime), true
69	}
70	return nil, false
71}
72
73func (h *nativeProxyHandler) definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool) {
74	if trap := h.handler.DefinePropertyIdx; trap != nil {
75		if idx, ok := strToInt(prop); ok {
76			return trap(target, idx, desc), true
77		}
78	}
79	if trap := h.handler.DefineProperty; trap != nil {
80		return trap(target, prop.String(), desc), true
81	}
82	return false, false
83}
84
85func (h *nativeProxyHandler) definePropertyIdx(target *Object, prop valueInt, desc PropertyDescriptor) (bool, bool) {
86	if trap := h.handler.DefinePropertyIdx; trap != nil {
87		return trap(target, toIntStrict(int64(prop)), desc), true
88	}
89	if trap := h.handler.DefineProperty; trap != nil {
90		return trap(target, prop.String(), desc), true
91	}
92	return false, false
93}
94
95func (h *nativeProxyHandler) definePropertySym(target *Object, prop *Symbol, desc PropertyDescriptor) (bool, bool) {
96	if trap := h.handler.DefinePropertySym; trap != nil {
97		return trap(target, prop, desc), true
98	}
99	return false, false
100}
101
102func (h *nativeProxyHandler) hasStr(target *Object, prop unistring.String) (bool, bool) {
103	if trap := h.handler.HasIdx; trap != nil {
104		if idx, ok := strToInt(prop); ok {
105			return trap(target, idx), true
106		}
107	}
108	if trap := h.handler.Has; trap != nil {
109		return trap(target, prop.String()), true
110	}
111	return false, false
112}
113
114func (h *nativeProxyHandler) hasIdx(target *Object, prop valueInt) (bool, bool) {
115	if trap := h.handler.HasIdx; trap != nil {
116		return trap(target, toIntStrict(int64(prop))), true
117	}
118	if trap := h.handler.Has; trap != nil {
119		return trap(target, prop.String()), true
120	}
121	return false, false
122}
123
124func (h *nativeProxyHandler) hasSym(target *Object, prop *Symbol) (bool, bool) {
125	if trap := h.handler.HasSym; trap != nil {
126		return trap(target, prop), true
127	}
128	return false, false
129}
130
131func (h *nativeProxyHandler) getStr(target *Object, prop unistring.String, receiver Value) (Value, bool) {
132	if trap := h.handler.GetIdx; trap != nil {
133		if idx, ok := strToInt(prop); ok {
134			return trap(target, idx, receiver), true
135		}
136	}
137	if trap := h.handler.Get; trap != nil {
138		return trap(target, prop.String(), receiver), true
139	}
140	return nil, false
141}
142
143func (h *nativeProxyHandler) getIdx(target *Object, prop valueInt, receiver Value) (Value, bool) {
144	if trap := h.handler.GetIdx; trap != nil {
145		return trap(target, toIntStrict(int64(prop)), receiver), true
146	}
147	if trap := h.handler.Get; trap != nil {
148		return trap(target, prop.String(), receiver), true
149	}
150	return nil, false
151}
152
153func (h *nativeProxyHandler) getSym(target *Object, prop *Symbol, receiver Value) (Value, bool) {
154	if trap := h.handler.GetSym; trap != nil {
155		return trap(target, prop, receiver), true
156	}
157	return nil, false
158}
159
160func (h *nativeProxyHandler) setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool) {
161	if trap := h.handler.SetIdx; trap != nil {
162		if idx, ok := strToInt(prop); ok {
163			return trap(target, idx, value, receiver), true
164		}
165	}
166	if trap := h.handler.Set; trap != nil {
167		return trap(target, prop.String(), value, receiver), true
168	}
169	return false, false
170}
171
172func (h *nativeProxyHandler) setIdx(target *Object, prop valueInt, value Value, receiver Value) (bool, bool) {
173	if trap := h.handler.SetIdx; trap != nil {
174		return trap(target, toIntStrict(int64(prop)), value, receiver), true
175	}
176	if trap := h.handler.Set; trap != nil {
177		return trap(target, prop.String(), value, receiver), true
178	}
179	return false, false
180}
181
182func (h *nativeProxyHandler) setSym(target *Object, prop *Symbol, value Value, receiver Value) (bool, bool) {
183	if trap := h.handler.SetSym; trap != nil {
184		return trap(target, prop, value, receiver), true
185	}
186	return false, false
187}
188
189func (h *nativeProxyHandler) deleteStr(target *Object, prop unistring.String) (bool, bool) {
190	if trap := h.handler.DeletePropertyIdx; trap != nil {
191		if idx, ok := strToInt(prop); ok {
192			return trap(target, idx), true
193		}
194	}
195	if trap := h.handler.DeleteProperty; trap != nil {
196		return trap(target, prop.String()), true
197	}
198	return false, false
199}
200
201func (h *nativeProxyHandler) deleteIdx(target *Object, prop valueInt) (bool, bool) {
202	if trap := h.handler.DeletePropertyIdx; trap != nil {
203		return trap(target, toIntStrict(int64(prop))), true
204	}
205	if trap := h.handler.DeleteProperty; trap != nil {
206		return trap(target, prop.String()), true
207	}
208	return false, false
209}
210
211func (h *nativeProxyHandler) deleteSym(target *Object, prop *Symbol) (bool, bool) {
212	if trap := h.handler.DeletePropertySym; trap != nil {
213		return trap(target, prop), true
214	}
215	return false, false
216}
217
218func (h *nativeProxyHandler) ownKeys(target *Object) (*Object, bool) {
219	if trap := h.handler.OwnKeys; trap != nil {
220		return trap(target), true
221	}
222	return nil, false
223}
224
225func (h *nativeProxyHandler) apply(target *Object, this Value, args []Value) (Value, bool) {
226	if trap := h.handler.Apply; trap != nil {
227		return trap(target, this, args), true
228	}
229	return nil, false
230}
231
232func (h *nativeProxyHandler) construct(target *Object, args []Value, newTarget *Object) (Value, bool) {
233	if trap := h.handler.Construct; trap != nil {
234		return trap(target, args, newTarget), true
235	}
236	return nil, false
237}
238
239func (h *nativeProxyHandler) toObject(runtime *Runtime) *Object {
240	return runtime.ToValue(h.handler).ToObject(runtime)
241}
242
243func (r *Runtime) newNativeProxyHandler(nativeHandler *ProxyTrapConfig) proxyHandler {
244	return &nativeProxyHandler{handler: nativeHandler}
245}
246
247// ProxyTrapConfig provides a simplified Go-friendly API for implementing Proxy traps.
248// If an *Idx trap is defined it gets called for integer property keys, including negative ones. Note that
249// this only includes string property keys that represent a canonical integer
250// (i.e. "0", "123", but not "00", "01", " 1" or "-0").
251// For efficiency strings representing integers exceeding 2^53 are not checked to see if they are canonical,
252// i.e. the *Idx traps will receive "9007199254740993" as well as "9007199254740994", even though the former is not
253// a canonical representation in ECMAScript (Number("9007199254740993") === 9007199254740992).
254// See https://262.ecma-international.org/#sec-canonicalnumericindexstring
255// If an *Idx trap is not set, the corresponding string one is used.
256type ProxyTrapConfig struct {
257	// A trap for Object.getPrototypeOf, Reflect.getPrototypeOf, __proto__, Object.prototype.isPrototypeOf, instanceof
258	GetPrototypeOf func(target *Object) (prototype *Object)
259
260	// A trap for Object.setPrototypeOf, Reflect.setPrototypeOf
261	SetPrototypeOf func(target *Object, prototype *Object) (success bool)
262
263	// A trap for Object.isExtensible, Reflect.isExtensible
264	IsExtensible func(target *Object) (success bool)
265
266	// A trap for Object.preventExtensions, Reflect.preventExtensions
267	PreventExtensions func(target *Object) (success bool)
268
269	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor (string properties)
270	GetOwnPropertyDescriptor func(target *Object, prop string) (propertyDescriptor PropertyDescriptor)
271
272	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor (integer properties)
273	GetOwnPropertyDescriptorIdx func(target *Object, prop int) (propertyDescriptor PropertyDescriptor)
274
275	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor (Symbol properties)
276	GetOwnPropertyDescriptorSym func(target *Object, prop *Symbol) (propertyDescriptor PropertyDescriptor)
277
278	// A trap for Object.defineProperty, Reflect.defineProperty (string properties)
279	DefineProperty func(target *Object, key string, propertyDescriptor PropertyDescriptor) (success bool)
280
281	// A trap for Object.defineProperty, Reflect.defineProperty (integer properties)
282	DefinePropertyIdx func(target *Object, key int, propertyDescriptor PropertyDescriptor) (success bool)
283
284	// A trap for Object.defineProperty, Reflect.defineProperty (Symbol properties)
285	DefinePropertySym func(target *Object, key *Symbol, propertyDescriptor PropertyDescriptor) (success bool)
286
287	// A trap for the in operator, with operator, Reflect.has (string properties)
288	Has func(target *Object, property string) (available bool)
289
290	// A trap for the in operator, with operator, Reflect.has (integer properties)
291	HasIdx func(target *Object, property int) (available bool)
292
293	// A trap for the in operator, with operator, Reflect.has (Symbol properties)
294	HasSym func(target *Object, property *Symbol) (available bool)
295
296	// A trap for getting property values, Reflect.get (string properties)
297	Get func(target *Object, property string, receiver Value) (value Value)
298
299	// A trap for getting property values, Reflect.get (integer properties)
300	GetIdx func(target *Object, property int, receiver Value) (value Value)
301
302	// A trap for getting property values, Reflect.get (Symbol properties)
303	GetSym func(target *Object, property *Symbol, receiver Value) (value Value)
304
305	// A trap for setting property values, Reflect.set (string properties)
306	Set func(target *Object, property string, value Value, receiver Value) (success bool)
307
308	// A trap for setting property values, Reflect.set (integer properties)
309	SetIdx func(target *Object, property int, value Value, receiver Value) (success bool)
310
311	// A trap for setting property values, Reflect.set (Symbol properties)
312	SetSym func(target *Object, property *Symbol, value Value, receiver Value) (success bool)
313
314	// A trap for the delete operator, Reflect.deleteProperty (string properties)
315	DeleteProperty func(target *Object, property string) (success bool)
316
317	// A trap for the delete operator, Reflect.deleteProperty (integer properties)
318	DeletePropertyIdx func(target *Object, property int) (success bool)
319
320	// A trap for the delete operator, Reflect.deleteProperty (Symbol properties)
321	DeletePropertySym func(target *Object, property *Symbol) (success bool)
322
323	// A trap for Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.keys, Reflect.ownKeys
324	OwnKeys func(target *Object) (object *Object)
325
326	// A trap for a function call, Function.prototype.apply, Function.prototype.call, Reflect.apply
327	Apply func(target *Object, this Value, argumentsList []Value) (value Value)
328
329	// A trap for the new operator, Reflect.construct
330	Construct func(target *Object, argumentsList []Value, newTarget *Object) (value *Object)
331}
332
333func (r *Runtime) newProxy(args []Value, proto *Object) *Object {
334	if len(args) >= 2 {
335		if target, ok := args[0].(*Object); ok {
336			if proxyHandler, ok := args[1].(*Object); ok {
337				return r.newProxyObject(target, proxyHandler, proto).val
338			}
339		}
340	}
341	panic(r.NewTypeError("Cannot create proxy with a non-object as target or handler"))
342}
343
344func (r *Runtime) builtin_newProxy(args []Value, newTarget *Object) *Object {
345	if newTarget == nil {
346		panic(r.needNew("Proxy"))
347	}
348	return r.newProxy(args, r.getPrototypeFromCtor(newTarget, r.global.Proxy, r.global.ObjectPrototype))
349}
350
351func (r *Runtime) NewProxy(target *Object, nativeHandler *ProxyTrapConfig) Proxy {
352	if p, ok := target.self.(*proxyObject); ok {
353		if p.handler == nil {
354			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as target"))
355		}
356	}
357	handler := r.newNativeProxyHandler(nativeHandler)
358	proxy := r._newProxyObject(target, handler, nil)
359	return Proxy{proxy: proxy}
360}
361
362func (r *Runtime) builtin_proxy_revocable(call FunctionCall) Value {
363	if len(call.Arguments) >= 2 {
364		if target, ok := call.Argument(0).(*Object); ok {
365			if proxyHandler, ok := call.Argument(1).(*Object); ok {
366				proxy := r.newProxyObject(target, proxyHandler, nil)
367				revoke := r.newNativeFunc(func(FunctionCall) Value {
368					proxy.revoke()
369					return _undefined
370				}, nil, "", nil, 0)
371				ret := r.NewObject()
372				ret.self._putProp("proxy", proxy.val, true, true, true)
373				ret.self._putProp("revoke", revoke, true, true, true)
374				return ret
375			}
376		}
377	}
378	panic(r.NewTypeError("Cannot create proxy with a non-object as target or handler"))
379}
380
381func (r *Runtime) createProxy(val *Object) objectImpl {
382	o := r.newNativeConstructOnly(val, r.builtin_newProxy, nil, "Proxy", 2)
383
384	o._putProp("revocable", r.newNativeFunc(r.builtin_proxy_revocable, nil, "revocable", nil, 2), true, false, true)
385	return o
386}
387
388func (r *Runtime) initProxy() {
389	r.global.Proxy = r.newLazyObject(r.createProxy)
390	r.addToGlobal("Proxy", r.global.Proxy)
391}
392