package goja import ( "fmt" "reflect" "apigo.cc/ai/ai/goja/unistring" ) // Proxy is a Go wrapper around ECMAScript Proxy. Calling Runtime.ToValue() on it // returns the underlying Proxy. Calling Export() on an ECMAScript Proxy returns a wrapper. // Use Runtime.NewProxy() to create one. type Proxy struct { proxy *proxyObject } var ( proxyType = reflect.TypeOf(Proxy{}) ) type proxyPropIter struct { p *proxyObject names []Value idx int } func (i *proxyPropIter) next() (propIterItem, iterNextFunc) { for i.idx < len(i.names) { name := i.names[i.idx] i.idx++ return propIterItem{name: name}, i.next } return propIterItem{}, nil } func (r *Runtime) newProxyObject(target, handler, proto *Object) *proxyObject { return r._newProxyObject(target, &jsProxyHandler{handler: handler}, proto) } func (r *Runtime) _newProxyObject(target *Object, handler proxyHandler, proto *Object) *proxyObject { v := &Object{runtime: r} p := &proxyObject{} v.self = p p.val = v p.class = classObject if proto == nil { p.prototype = r.global.ObjectPrototype } else { p.prototype = proto } p.extensible = false p.init() p.target = target p.handler = handler if call, ok := target.self.assertCallable(); ok { p.call = call } if ctor := target.self.assertConstructor(); ctor != nil { p.ctor = ctor } return p } func (p Proxy) Revoke() { p.proxy.revoke() } func (p Proxy) Handler() *Object { if handler := p.proxy.handler; handler != nil { return handler.toObject(p.proxy.val.runtime) } return nil } func (p Proxy) Target() *Object { return p.proxy.target } func (p Proxy) toValue(r *Runtime) Value { if p.proxy == nil { return _null } proxy := p.proxy.val if proxy.runtime != r { panic(r.NewTypeError("Illegal runtime transition of a Proxy")) } return proxy } type proxyTrap string const ( proxy_trap_getPrototypeOf = "getPrototypeOf" proxy_trap_setPrototypeOf = "setPrototypeOf" proxy_trap_isExtensible = "isExtensible" proxy_trap_preventExtensions = "preventExtensions" proxy_trap_getOwnPropertyDescriptor = "getOwnPropertyDescriptor" proxy_trap_defineProperty = "defineProperty" proxy_trap_has = "has" proxy_trap_get = "get" proxy_trap_set = "set" proxy_trap_deleteProperty = "deleteProperty" proxy_trap_ownKeys = "ownKeys" proxy_trap_apply = "apply" proxy_trap_construct = "construct" ) func (p proxyTrap) String() (name string) { return string(p) } type proxyHandler interface { getPrototypeOf(target *Object) (Value, bool) setPrototypeOf(target *Object, proto *Object) (bool, bool) isExtensible(target *Object) (bool, bool) preventExtensions(target *Object) (bool, bool) getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool) getOwnPropertyDescriptorIdx(target *Object, prop valueInt) (Value, bool) getOwnPropertyDescriptorSym(target *Object, prop *Symbol) (Value, bool) definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool) definePropertyIdx(target *Object, prop valueInt, desc PropertyDescriptor) (bool, bool) definePropertySym(target *Object, prop *Symbol, desc PropertyDescriptor) (bool, bool) hasStr(target *Object, prop unistring.String) (bool, bool) hasIdx(target *Object, prop valueInt) (bool, bool) hasSym(target *Object, prop *Symbol) (bool, bool) getStr(target *Object, prop unistring.String, receiver Value) (Value, bool) getIdx(target *Object, prop valueInt, receiver Value) (Value, bool) getSym(target *Object, prop *Symbol, receiver Value) (Value, bool) setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool) setIdx(target *Object, prop valueInt, value Value, receiver Value) (bool, bool) setSym(target *Object, prop *Symbol, value Value, receiver Value) (bool, bool) deleteStr(target *Object, prop unistring.String) (bool, bool) deleteIdx(target *Object, prop valueInt) (bool, bool) deleteSym(target *Object, prop *Symbol) (bool, bool) ownKeys(target *Object) (*Object, bool) apply(target *Object, this Value, args []Value) (Value, bool) construct(target *Object, args []Value, newTarget *Object) (Value, bool) toObject(*Runtime) *Object } type jsProxyHandler struct { handler *Object } func (h *jsProxyHandler) toObject(*Runtime) *Object { return h.handler } func (h *jsProxyHandler) proxyCall(trap proxyTrap, args ...Value) (Value, bool) { r := h.handler.runtime if m := toMethod(r.getVStr(h.handler, unistring.String(trap.String()))); m != nil { return m(FunctionCall{ This: h.handler, Arguments: args, }), true } return nil, false } func (h *jsProxyHandler) boolProxyCall(trap proxyTrap, args ...Value) (bool, bool) { if v, ok := h.proxyCall(trap, args...); ok { return v.ToBoolean(), true } return false, false } func (h *jsProxyHandler) getPrototypeOf(target *Object) (Value, bool) { return h.proxyCall(proxy_trap_getPrototypeOf, target) } func (h *jsProxyHandler) setPrototypeOf(target *Object, proto *Object) (bool, bool) { var protoVal Value if proto != nil { protoVal = proto } else { protoVal = _null } return h.boolProxyCall(proxy_trap_setPrototypeOf, target, protoVal) } func (h *jsProxyHandler) isExtensible(target *Object) (bool, bool) { return h.boolProxyCall(proxy_trap_isExtensible, target) } func (h *jsProxyHandler) preventExtensions(target *Object) (bool, bool) { return h.boolProxyCall(proxy_trap_preventExtensions, target) } func (h *jsProxyHandler) getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool) { return h.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, stringValueFromRaw(prop)) } func (h *jsProxyHandler) getOwnPropertyDescriptorIdx(target *Object, prop valueInt) (Value, bool) { return h.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, prop.toString()) } func (h *jsProxyHandler) getOwnPropertyDescriptorSym(target *Object, prop *Symbol) (Value, bool) { return h.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, prop) } func (h *jsProxyHandler) definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool) { return h.boolProxyCall(proxy_trap_defineProperty, target, stringValueFromRaw(prop), desc.toValue(h.handler.runtime)) } func (h *jsProxyHandler) definePropertyIdx(target *Object, prop valueInt, desc PropertyDescriptor) (bool, bool) { return h.boolProxyCall(proxy_trap_defineProperty, target, prop.toString(), desc.toValue(h.handler.runtime)) } func (h *jsProxyHandler) definePropertySym(target *Object, prop *Symbol, desc PropertyDescriptor) (bool, bool) { return h.boolProxyCall(proxy_trap_defineProperty, target, prop, desc.toValue(h.handler.runtime)) } func (h *jsProxyHandler) hasStr(target *Object, prop unistring.String) (bool, bool) { return h.boolProxyCall(proxy_trap_has, target, stringValueFromRaw(prop)) } func (h *jsProxyHandler) hasIdx(target *Object, prop valueInt) (bool, bool) { return h.boolProxyCall(proxy_trap_has, target, prop.toString()) } func (h *jsProxyHandler) hasSym(target *Object, prop *Symbol) (bool, bool) { return h.boolProxyCall(proxy_trap_has, target, prop) } func (h *jsProxyHandler) getStr(target *Object, prop unistring.String, receiver Value) (Value, bool) { return h.proxyCall(proxy_trap_get, target, stringValueFromRaw(prop), receiver) } func (h *jsProxyHandler) getIdx(target *Object, prop valueInt, receiver Value) (Value, bool) { return h.proxyCall(proxy_trap_get, target, prop.toString(), receiver) } func (h *jsProxyHandler) getSym(target *Object, prop *Symbol, receiver Value) (Value, bool) { return h.proxyCall(proxy_trap_get, target, prop, receiver) } func (h *jsProxyHandler) setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool) { return h.boolProxyCall(proxy_trap_set, target, stringValueFromRaw(prop), value, receiver) } func (h *jsProxyHandler) setIdx(target *Object, prop valueInt, value Value, receiver Value) (bool, bool) { return h.boolProxyCall(proxy_trap_set, target, prop.toString(), value, receiver) } func (h *jsProxyHandler) setSym(target *Object, prop *Symbol, value Value, receiver Value) (bool, bool) { return h.boolProxyCall(proxy_trap_set, target, prop, value, receiver) } func (h *jsProxyHandler) deleteStr(target *Object, prop unistring.String) (bool, bool) { return h.boolProxyCall(proxy_trap_deleteProperty, target, stringValueFromRaw(prop)) } func (h *jsProxyHandler) deleteIdx(target *Object, prop valueInt) (bool, bool) { return h.boolProxyCall(proxy_trap_deleteProperty, target, prop.toString()) } func (h *jsProxyHandler) deleteSym(target *Object, prop *Symbol) (bool, bool) { return h.boolProxyCall(proxy_trap_deleteProperty, target, prop) } func (h *jsProxyHandler) ownKeys(target *Object) (*Object, bool) { if v, ok := h.proxyCall(proxy_trap_ownKeys, target); ok { return h.handler.runtime.toObject(v), true } return nil, false } func (h *jsProxyHandler) apply(target *Object, this Value, args []Value) (Value, bool) { return h.proxyCall(proxy_trap_apply, target, this, h.handler.runtime.newArrayValues(args)) } func (h *jsProxyHandler) construct(target *Object, args []Value, newTarget *Object) (Value, bool) { return h.proxyCall(proxy_trap_construct, target, h.handler.runtime.newArrayValues(args), newTarget) } type proxyObject struct { baseObject target *Object handler proxyHandler call func(FunctionCall) Value ctor func(args []Value, newTarget *Object) *Object } func (p *proxyObject) checkHandler() proxyHandler { r := p.val.runtime if handler := p.handler; handler != nil { return handler } panic(r.NewTypeError("Proxy already revoked")) } func (p *proxyObject) proto() *Object { target := p.target if v, ok := p.checkHandler().getPrototypeOf(target); ok { var handlerProto *Object if v != _null { handlerProto = p.val.runtime.toObject(v) } if !target.self.isExtensible() && !p.__sameValue(handlerProto, target.self.proto()) { panic(p.val.runtime.NewTypeError("'getPrototypeOf' on proxy: proxy target is non-extensible but the trap did not return its actual prototype")) } return handlerProto } return target.self.proto() } func (p *proxyObject) setProto(proto *Object, throw bool) bool { target := p.target if v, ok := p.checkHandler().setPrototypeOf(target, proto); ok { if v { if !target.self.isExtensible() && !p.__sameValue(proto, target.self.proto()) { panic(p.val.runtime.NewTypeError("'setPrototypeOf' on proxy: trap returned truish for setting a new prototype on the non-extensible proxy target")) } return true } else { p.val.runtime.typeErrorResult(throw, "'setPrototypeOf' on proxy: trap returned falsish") return false } } return target.self.setProto(proto, throw) } func (p *proxyObject) isExtensible() bool { target := p.target if booleanTrapResult, ok := p.checkHandler().isExtensible(p.target); ok { if te := target.self.isExtensible(); booleanTrapResult != te { panic(p.val.runtime.NewTypeError("'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is '%v')", te)) } return booleanTrapResult } return target.self.isExtensible() } func (p *proxyObject) preventExtensions(throw bool) bool { target := p.target if booleanTrapResult, ok := p.checkHandler().preventExtensions(target); ok { if !booleanTrapResult { p.val.runtime.typeErrorResult(throw, "'preventExtensions' on proxy: trap returned falsish") return false } if te := target.self.isExtensible(); booleanTrapResult && te { panic(p.val.runtime.NewTypeError("'preventExtensions' on proxy: trap returned truish but the proxy target is extensible")) } } return target.self.preventExtensions(throw) } func propToValueProp(v Value) *valueProperty { if v == nil { return nil } if v, ok := v.(*valueProperty); ok { return v } return &valueProperty{ value: v, writable: true, configurable: true, enumerable: true, } } func (p *proxyObject) proxyDefineOwnPropertyPreCheck(trapResult, throw bool) bool { if !trapResult { p.val.runtime.typeErrorResult(throw, "'defineProperty' on proxy: trap returned falsish") return false } return true } func (p *proxyObject) proxyDefineOwnPropertyPostCheck(prop Value, target *Object, descr PropertyDescriptor) { targetDesc := propToValueProp(prop) extensibleTarget := target.self.isExtensible() settingConfigFalse := descr.Configurable == FLAG_FALSE if targetDesc == nil { if !extensibleTarget { panic(p.val.runtime.NewTypeError()) } if settingConfigFalse { panic(p.val.runtime.NewTypeError()) } } else { if !p.__isCompatibleDescriptor(extensibleTarget, &descr, targetDesc) { panic(p.val.runtime.NewTypeError()) } if settingConfigFalse && targetDesc.configurable { panic(p.val.runtime.NewTypeError()) } if targetDesc.value != nil && !targetDesc.configurable && targetDesc.writable { if descr.Writable == FLAG_FALSE { panic(p.val.runtime.NewTypeError()) } } } } func (p *proxyObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { target := p.target if booleanTrapResult, ok := p.checkHandler().definePropertyStr(target, name, descr); ok { if !p.proxyDefineOwnPropertyPreCheck(booleanTrapResult, throw) { return false } p.proxyDefineOwnPropertyPostCheck(target.self.getOwnPropStr(name), target, descr) return true } return target.self.defineOwnPropertyStr(name, descr, throw) } func (p *proxyObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { target := p.target if booleanTrapResult, ok := p.checkHandler().definePropertyIdx(target, idx, descr); ok { if !p.proxyDefineOwnPropertyPreCheck(booleanTrapResult, throw) { return false } p.proxyDefineOwnPropertyPostCheck(target.self.getOwnPropIdx(idx), target, descr) return true } return target.self.defineOwnPropertyIdx(idx, descr, throw) } func (p *proxyObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool { target := p.target if booleanTrapResult, ok := p.checkHandler().definePropertySym(target, s, descr); ok { if !p.proxyDefineOwnPropertyPreCheck(booleanTrapResult, throw) { return false } p.proxyDefineOwnPropertyPostCheck(target.self.getOwnPropSym(s), target, descr) return true } return target.self.defineOwnPropertySym(s, descr, throw) } func (p *proxyObject) proxyHasChecks(targetProp Value, target *Object, name fmt.Stringer) { targetDesc := propToValueProp(targetProp) if targetDesc != nil { if !targetDesc.configurable { 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())) } if !target.self.isExtensible() { panic(p.val.runtime.NewTypeError("'has' on proxy: trap returned falsish for property '%s' but the proxy target is not extensible", name.String())) } } } func (p *proxyObject) hasPropertyStr(name unistring.String) bool { target := p.target if b, ok := p.checkHandler().hasStr(target, name); ok { if !b { p.proxyHasChecks(target.self.getOwnPropStr(name), target, name) } return b } return target.self.hasPropertyStr(name) } func (p *proxyObject) hasPropertyIdx(idx valueInt) bool { target := p.target if b, ok := p.checkHandler().hasIdx(target, idx); ok { if !b { p.proxyHasChecks(target.self.getOwnPropIdx(idx), target, idx) } return b } return target.self.hasPropertyIdx(idx) } func (p *proxyObject) hasPropertySym(s *Symbol) bool { target := p.target if b, ok := p.checkHandler().hasSym(target, s); ok { if !b { p.proxyHasChecks(target.self.getOwnPropSym(s), target, s) } return b } return target.self.hasPropertySym(s) } func (p *proxyObject) hasOwnPropertyStr(name unistring.String) bool { return p.getOwnPropStr(name) != nil } func (p *proxyObject) hasOwnPropertyIdx(idx valueInt) bool { return p.getOwnPropIdx(idx) != nil } func (p *proxyObject) hasOwnPropertySym(s *Symbol) bool { return p.getOwnPropSym(s) != nil } func (p *proxyObject) proxyGetOwnPropertyDescriptor(targetProp Value, target *Object, trapResult Value, name fmt.Stringer) Value { r := p.val.runtime targetDesc := propToValueProp(targetProp) var trapResultObj *Object if trapResult != nil && trapResult != _undefined { if obj, ok := trapResult.(*Object); ok { trapResultObj = obj } else { panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap returned neither object nor undefined for property '%s'", name.String())) } } if trapResultObj == nil { if targetDesc == nil { return nil } if !targetDesc.configurable { panic(r.NewTypeError()) } if !target.self.isExtensible() { panic(r.NewTypeError()) } return nil } extensibleTarget := target.self.isExtensible() resultDesc := r.toPropertyDescriptor(trapResultObj) resultDesc.complete() if !p.__isCompatibleDescriptor(extensibleTarget, &resultDesc, targetDesc) { 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())) } if resultDesc.Configurable == FLAG_FALSE { if targetDesc == nil { panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property '%s' which is non-existent in the proxy target", name.String())) } if targetDesc.configurable { panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap reported non-configurability for property '%s' which is configurable in the proxy target", name.String())) } if resultDesc.Writable == FLAG_FALSE && targetDesc.writable { 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())) } } if resultDesc.Writable == FLAG_TRUE && resultDesc.Configurable == FLAG_TRUE && resultDesc.Enumerable == FLAG_TRUE { return resultDesc.Value } return r.toValueProp(trapResultObj) } func (p *proxyObject) getOwnPropStr(name unistring.String) Value { target := p.target if v, ok := p.checkHandler().getOwnPropertyDescriptorStr(target, name); ok { return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropStr(name), target, v, name) } return target.self.getOwnPropStr(name) } func (p *proxyObject) getOwnPropIdx(idx valueInt) Value { target := p.target if v, ok := p.checkHandler().getOwnPropertyDescriptorIdx(target, idx); ok { return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropIdx(idx), target, v, idx) } return target.self.getOwnPropIdx(idx) } func (p *proxyObject) getOwnPropSym(s *Symbol) Value { target := p.target if v, ok := p.checkHandler().getOwnPropertyDescriptorSym(target, s); ok { return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropSym(s), target, v, s) } return target.self.getOwnPropSym(s) } func (p *proxyObject) proxyGetChecks(targetProp, trapResult Value, name fmt.Stringer) { if targetDesc, ok := targetProp.(*valueProperty); ok { if !targetDesc.accessor { if !targetDesc.writable && !targetDesc.configurable && !trapResult.SameAs(targetDesc.value) { 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)) } } else { if !targetDesc.configurable && targetDesc.getterFunc == nil && trapResult != _undefined { 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)) } } } } func (p *proxyObject) getStr(name unistring.String, receiver Value) Value { target := p.target if receiver == nil { receiver = p.val } if v, ok := p.checkHandler().getStr(target, name, receiver); ok { p.proxyGetChecks(target.self.getOwnPropStr(name), v, name) return v } return target.self.getStr(name, receiver) } func (p *proxyObject) getIdx(idx valueInt, receiver Value) Value { target := p.target if receiver == nil { receiver = p.val } if v, ok := p.checkHandler().getIdx(target, idx, receiver); ok { p.proxyGetChecks(target.self.getOwnPropIdx(idx), v, idx) return v } return target.self.getIdx(idx, receiver) } func (p *proxyObject) getSym(s *Symbol, receiver Value) Value { target := p.target if receiver == nil { receiver = p.val } if v, ok := p.checkHandler().getSym(target, s, receiver); ok { p.proxyGetChecks(target.self.getOwnPropSym(s), v, s) return v } return target.self.getSym(s, receiver) } func (p *proxyObject) proxySetPreCheck(trapResult, throw bool, name fmt.Stringer) bool { if !trapResult { p.val.runtime.typeErrorResult(throw, "'set' on proxy: trap returned falsish for property '%s'", name.String()) } return trapResult } func (p *proxyObject) proxySetPostCheck(targetProp, value Value, name fmt.Stringer) { if prop, ok := targetProp.(*valueProperty); ok { if prop.accessor { if !prop.configurable && prop.setterFunc == nil { 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())) } } else if !prop.configurable && !prop.writable && !p.__sameValue(prop.value, value) { 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())) } } } func (p *proxyObject) proxySetStr(name unistring.String, value, receiver Value, throw bool) bool { target := p.target if v, ok := p.checkHandler().setStr(target, name, value, receiver); ok { if p.proxySetPreCheck(v, throw, name) { p.proxySetPostCheck(target.self.getOwnPropStr(name), value, name) return true } return false } return target.setStr(name, value, receiver, throw) } func (p *proxyObject) proxySetIdx(idx valueInt, value, receiver Value, throw bool) bool { target := p.target if v, ok := p.checkHandler().setIdx(target, idx, value, receiver); ok { if p.proxySetPreCheck(v, throw, idx) { p.proxySetPostCheck(target.self.getOwnPropIdx(idx), value, idx) return true } return false } return target.setIdx(idx, value, receiver, throw) } func (p *proxyObject) proxySetSym(s *Symbol, value, receiver Value, throw bool) bool { target := p.target if v, ok := p.checkHandler().setSym(target, s, value, receiver); ok { if p.proxySetPreCheck(v, throw, s) { p.proxySetPostCheck(target.self.getOwnPropSym(s), value, s) return true } return false } return target.setSym(s, value, receiver, throw) } func (p *proxyObject) setOwnStr(name unistring.String, v Value, throw bool) bool { return p.proxySetStr(name, v, p.val, throw) } func (p *proxyObject) setOwnIdx(idx valueInt, v Value, throw bool) bool { return p.proxySetIdx(idx, v, p.val, throw) } func (p *proxyObject) setOwnSym(s *Symbol, v Value, throw bool) bool { return p.proxySetSym(s, v, p.val, throw) } func (p *proxyObject) setForeignStr(name unistring.String, v, receiver Value, throw bool) (bool, bool) { return p.proxySetStr(name, v, receiver, throw), true } func (p *proxyObject) setForeignIdx(idx valueInt, v, receiver Value, throw bool) (bool, bool) { return p.proxySetIdx(idx, v, receiver, throw), true } func (p *proxyObject) setForeignSym(s *Symbol, v, receiver Value, throw bool) (bool, bool) { return p.proxySetSym(s, v, receiver, throw), true } func (p *proxyObject) proxyDeleteCheck(trapResult bool, targetProp Value, name fmt.Stringer, target *Object, throw bool) { if trapResult { if targetProp == nil { return } if targetDesc, ok := targetProp.(*valueProperty); ok { if !targetDesc.configurable { panic(p.val.runtime.NewTypeError("'deleteProperty' on proxy: property '%s' is a non-configurable property but the trap returned truish", name.String())) } } if !target.self.isExtensible() { panic(p.val.runtime.NewTypeError("'deleteProperty' on proxy: trap returned truish for property '%s' but the proxy target is non-extensible", name.String())) } } else { p.val.runtime.typeErrorResult(throw, "'deleteProperty' on proxy: trap returned falsish for property '%s'", name.String()) } } func (p *proxyObject) deleteStr(name unistring.String, throw bool) bool { target := p.target if v, ok := p.checkHandler().deleteStr(target, name); ok { p.proxyDeleteCheck(v, target.self.getOwnPropStr(name), name, target, throw) return v } return target.self.deleteStr(name, throw) } func (p *proxyObject) deleteIdx(idx valueInt, throw bool) bool { target := p.target if v, ok := p.checkHandler().deleteIdx(target, idx); ok { p.proxyDeleteCheck(v, target.self.getOwnPropIdx(idx), idx, target, throw) return v } return target.self.deleteIdx(idx, throw) } func (p *proxyObject) deleteSym(s *Symbol, throw bool) bool { target := p.target if v, ok := p.checkHandler().deleteSym(target, s); ok { p.proxyDeleteCheck(v, target.self.getOwnPropSym(s), s, target, throw) return v } return target.self.deleteSym(s, throw) } func (p *proxyObject) keys(all bool, _ []Value) []Value { if v, ok := p.proxyOwnKeys(); ok { if !all { k := 0 for i, key := range v { prop := p.val.getOwnProp(key) if prop == nil || prop == _undefined { continue } if prop, ok := prop.(*valueProperty); ok && !prop.enumerable { continue } if k != i { v[k] = v[i] } k++ } v = v[:k] } return v } return p.target.self.keys(all, nil) } func (p *proxyObject) proxyOwnKeys() ([]Value, bool) { target := p.target if v, ok := p.checkHandler().ownKeys(target); ok { keys := p.val.runtime.toObject(v) var keyList []Value keySet := make(map[Value]struct{}) l := toLength(keys.self.getStr("length", nil)) for k := int64(0); k < l; k++ { item := keys.self.getIdx(valueInt(k), nil) if _, ok := item.(String); !ok { if _, ok := item.(*Symbol); !ok { panic(p.val.runtime.NewTypeError("%s is not a valid property name", item.String())) } } if _, exists := keySet[item]; exists { panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap returned duplicate entries")) } keyList = append(keyList, item) keySet[item] = struct{}{} } ext := target.self.isExtensible() for item, next := target.self.iterateKeys()(); next != nil; item, next = next() { if _, exists := keySet[item.name]; exists { delete(keySet, item.name) } else { if !ext { panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include '%s'", item.name.String())) } var prop Value if item.value == nil { prop = target.getOwnProp(item.name) } else { prop = item.value } if prop, ok := prop.(*valueProperty); ok && !prop.configurable { panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap result did not include non-configurable '%s'", item.name.String())) } } } if !ext && len(keyList) > 0 && len(keySet) > 0 { panic(p.val.runtime.NewTypeError("'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible")) } return keyList, true } return nil, false } func (p *proxyObject) iterateStringKeys() iterNextFunc { return (&proxyPropIter{ p: p, names: p.stringKeys(true, nil), }).next } func (p *proxyObject) iterateSymbols() iterNextFunc { return (&proxyPropIter{ p: p, names: p.symbols(true, nil), }).next } func (p *proxyObject) iterateKeys() iterNextFunc { return (&proxyPropIter{ p: p, names: p.keys(true, nil), }).next } func (p *proxyObject) assertCallable() (call func(FunctionCall) Value, ok bool) { if p.call != nil { return func(call FunctionCall) Value { return p.apply(call) }, true } return nil, false } func (p *proxyObject) vmCall(vm *vm, n int) { vm.pushCtx() vm.prg = nil vm.sb = vm.sp - n // so that [sb-1] points to the callee ret := p.apply(FunctionCall{This: vm.stack[vm.sp-n-2], Arguments: vm.stack[vm.sp-n : vm.sp]}) if ret == nil { ret = _undefined } vm.stack[vm.sp-n-2] = ret vm.popCtx() vm.sp -= n + 1 vm.pc++ } func (p *proxyObject) assertConstructor() func(args []Value, newTarget *Object) *Object { if p.ctor != nil { return p.construct } return nil } func (p *proxyObject) apply(call FunctionCall) Value { if p.call == nil { panic(p.val.runtime.NewTypeError("proxy target is not a function")) } if v, ok := p.checkHandler().apply(p.target, nilSafe(call.This), call.Arguments); ok { return v } return p.call(call) } func (p *proxyObject) construct(args []Value, newTarget *Object) *Object { if p.ctor == nil { panic(p.val.runtime.NewTypeError("proxy target is not a constructor")) } if newTarget == nil { newTarget = p.val } if v, ok := p.checkHandler().construct(p.target, args, newTarget); ok { return p.val.runtime.toObject(v) } return p.ctor(args, newTarget) } func (p *proxyObject) __isCompatibleDescriptor(extensible bool, desc *PropertyDescriptor, current *valueProperty) bool { if current == nil { return extensible } if !current.configurable { if desc.Configurable == FLAG_TRUE { return false } if desc.Enumerable != FLAG_NOT_SET && desc.Enumerable.Bool() != current.enumerable { return false } if desc.IsGeneric() { return true } if desc.IsData() != !current.accessor { return desc.Configurable != FLAG_FALSE } if desc.IsData() && !current.accessor { if !current.configurable { if desc.Writable == FLAG_TRUE && !current.writable { return false } if !current.writable { if desc.Value != nil && !desc.Value.SameAs(current.value) { return false } } } return true } if desc.IsAccessor() && current.accessor { if !current.configurable { if desc.Setter != nil && desc.Setter.SameAs(current.setterFunc) { return false } if desc.Getter != nil && desc.Getter.SameAs(current.getterFunc) { return false } } } } return true } func (p *proxyObject) __sameValue(val1, val2 Value) bool { if val1 == nil && val2 == nil { return true } if val1 != nil { return val1.SameAs(val2) } return false } func (p *proxyObject) filterKeys(vals []Value, all, symbols bool) []Value { if !all { k := 0 for i, val := range vals { var prop Value if symbols { if s, ok := val.(*Symbol); ok { prop = p.getOwnPropSym(s) } else { continue } } else { if _, ok := val.(*Symbol); !ok { prop = p.getOwnPropStr(val.string()) } else { continue } } if prop == nil { continue } if prop, ok := prop.(*valueProperty); ok && !prop.enumerable { continue } if k != i { vals[k] = vals[i] } k++ } vals = vals[:k] } else { k := 0 for i, val := range vals { if _, ok := val.(*Symbol); ok != symbols { continue } if k != i { vals[k] = vals[i] } k++ } vals = vals[:k] } return vals } func (p *proxyObject) stringKeys(all bool, _ []Value) []Value { // we can assume accum is empty var keys []Value if vals, ok := p.proxyOwnKeys(); ok { keys = vals } else { keys = p.target.self.stringKeys(true, nil) } return p.filterKeys(keys, all, false) } func (p *proxyObject) symbols(all bool, accum []Value) []Value { var symbols []Value if vals, ok := p.proxyOwnKeys(); ok { symbols = vals } else { symbols = p.target.self.symbols(true, nil) } symbols = p.filterKeys(symbols, all, true) if accum == nil { return symbols } accum = append(accum, symbols...) return accum } func (p *proxyObject) className() string { if p.target == nil { panic(p.val.runtime.NewTypeError("proxy has been revoked")) } if p.call != nil || p.ctor != nil { return classFunction } return classObject } func (p *proxyObject) typeOf() String { if p.call == nil { return stringObjectC } return stringFunction } func (p *proxyObject) exportType() reflect.Type { return proxyType } func (p *proxyObject) export(*objectExportCtx) interface{} { return Proxy{ proxy: p, } } func (p *proxyObject) revoke() { p.handler = nil p.target = nil }