package goja
import (
"fmt"
"reflect"
"github.com/dop251/goja/unistring"
)
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
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 {
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .