Browse Source

Refactored proxy implementation to improve the efficiency of the native handler. Includes a minor breaking API change in ProxyTrapConfig. Closes #227

Dmitry Panov 4 years ago
parent
commit
e933a54917
5 changed files with 1036 additions and 441 deletions
  1. 294 205
      builtin_proxy.go
  2. 291 14
      builtin_proxy_test.go
  3. 3 1
      object.go
  4. 447 220
      proxy.go
  5. 1 1
      tc39_test.go

+ 294 - 205
builtin_proxy.go

@@ -1,215 +1,256 @@
 package goja
 
 import (
-	"fmt"
+	"strconv"
 
 	"github.com/dop251/goja/unistring"
 )
 
-func (r *Runtime) newNativeProxyHandler(nativeHandler *ProxyTrapConfig) *Object {
-	handler := r.NewObject()
-	r.proxyproto_nativehandler_gen_obj_obj(proxy_trap_getPrototypeOf, nativeHandler.GetPrototypeOf, handler)
-	r.proxyproto_nativehandler_setPrototypeOf(nativeHandler.SetPrototypeOf, handler)
-	r.proxyproto_nativehandler_gen_obj_bool(proxy_trap_isExtensible, nativeHandler.IsExtensible, handler)
-	r.proxyproto_nativehandler_gen_obj_bool(proxy_trap_preventExtensions, nativeHandler.PreventExtensions, handler)
-	r.proxyproto_nativehandler_getOwnPropertyDescriptor(nativeHandler.GetOwnPropertyDescriptor, handler)
-	r.proxyproto_nativehandler_defineProperty(nativeHandler.DefineProperty, handler)
-	r.proxyproto_nativehandler_gen_obj_string_bool(proxy_trap_has, nativeHandler.Has, handler)
-	r.proxyproto_nativehandler_get(nativeHandler.Get, handler)
-	r.proxyproto_nativehandler_set(nativeHandler.Set, handler)
-	r.proxyproto_nativehandler_gen_obj_string_bool(proxy_trap_deleteProperty, nativeHandler.DeleteProperty, handler)
-	r.proxyproto_nativehandler_gen_obj_obj(proxy_trap_ownKeys, nativeHandler.OwnKeys, handler)
-	r.proxyproto_nativehandler_apply(nativeHandler.Apply, handler)
-	r.proxyproto_nativehandler_construct(nativeHandler.Construct, handler)
-	return handler
-}
-
-func (r *Runtime) proxyproto_nativehandler_gen_obj_obj(name proxyTrap, native func(*Object) *Object, handler *Object) {
-	if native != nil {
-		handler.self._putProp(unistring.String(name), r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 1 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					return native(t)
-				}
-			}
-			panic(r.NewTypeError("%s needs to be called with target as Object", name))
-		}, nil, unistring.String(fmt.Sprintf("[native %s]", name)), nil, 1), true, true, true)
+type nativeProxyHandler struct {
+	handler *ProxyTrapConfig
+}
+
+func (h *nativeProxyHandler) getPrototypeOf(target *Object) (Value, bool) {
+	if trap := h.handler.GetPrototypeOf; trap != nil {
+		return trap(target), true
 	}
+	return nil, false
 }
 
-func (r *Runtime) proxyproto_nativehandler_setPrototypeOf(native func(*Object, *Object) bool, handler *Object) {
-	if native != nil {
-		handler.self._putProp("setPrototypeOf", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 2 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					if p, ok := call.Argument(1).(*Object); ok {
-						s := native(t, p)
-						return r.ToValue(s)
-					}
-				}
-			}
-			panic(r.NewTypeError("setPrototypeOf needs to be called with target and prototype as Object"))
-		}, nil, "[native setPrototypeOf]", nil, 2), true, true, true)
+func (h *nativeProxyHandler) setPrototypeOf(target *Object, proto *Object) (bool, bool) {
+	if trap := h.handler.SetPrototypeOf; trap != nil {
+		return trap(target, proto), true
 	}
+	return false, false
 }
 
-func (r *Runtime) proxyproto_nativehandler_gen_obj_bool(name proxyTrap, native func(*Object) bool, handler *Object) {
-	if native != nil {
-		handler.self._putProp(unistring.String(name), r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 1 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					s := native(t)
-					return r.ToValue(s)
-				}
-			}
-			panic(r.NewTypeError("%s needs to be called with target as Object", name))
-		}, nil, unistring.String(fmt.Sprintf("[native %s]", name)), nil, 1), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_getOwnPropertyDescriptor(native func(*Object, string) PropertyDescriptor, handler *Object) {
-	if native != nil {
-		handler.self._putProp("getOwnPropertyDescriptor", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 2 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					switch p := call.Argument(1).(type) {
-					case *Symbol:
-						return _undefined
-					default:
-						desc := native(t, p.String())
-						if desc.Empty() {
-							return _undefined
-						}
-						return desc.toValue(r)
-					}
-				}
-			}
-			panic(r.NewTypeError("getOwnPropertyDescriptor needs to be called with target as Object and prop as string"))
-		}, nil, "[native getOwnPropertyDescriptor]", nil, 2), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_defineProperty(native func(*Object, string, PropertyDescriptor) bool, handler *Object) {
-	if native != nil {
-		handler.self._putProp("defineProperty", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 3 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					if k, ok := call.Argument(1).(valueString); ok {
-						propertyDescriptor := r.toPropertyDescriptor(call.Argument(2))
-						s := native(t, k.String(), propertyDescriptor)
-						return r.ToValue(s)
-					}
-				}
-			}
-			panic(r.NewTypeError("defineProperty needs to be called with target as Object and propertyDescriptor as string and key as string"))
-		}, nil, "[native defineProperty]", nil, 3), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_gen_obj_string_bool(name proxyTrap, native func(*Object, string) bool, handler *Object) {
-	if native != nil {
-		handler.self._putProp(unistring.String(name), r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 2 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					switch p := call.Argument(1).(type) {
-					case *Symbol:
-						return valueFalse
-					default:
-						o := native(t, p.String())
-						return r.ToValue(o)
-					}
-				}
-			}
-			panic(r.NewTypeError("%s needs to be called with target as Object and property as string", name))
-		}, nil, unistring.String(fmt.Sprintf("[native %s]", name)), nil, 2), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_get(native func(*Object, string, *Object) Value, handler *Object) {
-	if native != nil {
-		handler.self._putProp("get", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 3 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					if r, ok := call.Argument(2).(*Object); ok {
-						switch p := call.Argument(1).(type) {
-						case *Symbol:
-							return _undefined
-						default:
-							return native(t, p.String(), r)
-						}
-					}
-				}
-			}
-			panic(r.NewTypeError("get needs to be called with target and receiver as Object and property as string"))
-		}, nil, "[native get]", nil, 3), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_set(native func(*Object, string, Value, *Object) bool, handler *Object) {
-	if native != nil {
-		handler.self._putProp("set", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 4 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					if p, ok := call.Argument(1).(valueString); ok {
-						v := call.Argument(2)
-						if re, ok := call.Argument(3).(*Object); ok {
-							s := native(t, p.String(), v, re)
-							return r.ToValue(s)
-						}
-					}
-				}
-			}
-			panic(r.NewTypeError("set needs to be called with target and receiver as Object, property as string and value as a legal javascript value"))
-		}, nil, "[native set]", nil, 4), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_apply(native func(*Object, *Object, []Value) Value, handler *Object) {
-	if native != nil {
-		handler.self._putProp("apply", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 3 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					if this, ok := call.Argument(1).(*Object); ok {
-						if v, ok := call.Argument(2).(*Object); ok {
-							if a, ok := v.self.(*arrayObject); ok {
-								v := native(t, this, a.values)
-								return r.ToValue(v)
-							}
-						}
-					}
-				}
-			}
-			panic(r.NewTypeError("apply needs to be called with target and this as Object and argumentsList as an array of legal javascript values"))
-		}, nil, "[native apply]", nil, 3), true, true, true)
-	}
-}
-
-func (r *Runtime) proxyproto_nativehandler_construct(native func(*Object, []Value, *Object) *Object, handler *Object) {
-	if native != nil {
-		handler.self._putProp("construct", r.newNativeFunc(func(call FunctionCall) Value {
-			if len(call.Arguments) >= 3 {
-				if t, ok := call.Argument(0).(*Object); ok {
-					if v, ok := call.Argument(1).(*Object); ok {
-						if newTarget, ok := call.Argument(2).(*Object); ok {
-							if a, ok := v.self.(*arrayObject); ok {
-								return native(t, a.values, newTarget)
-							}
-						}
-					}
-				}
-			}
-			panic(r.NewTypeError("construct needs to be called with target and newTarget as Object and argumentsList as an array of legal javascript values"))
-		}, nil, "[native construct]", nil, 3), true, true, true)
+func (h *nativeProxyHandler) isExtensible(target *Object) (bool, bool) {
+	if trap := h.handler.IsExtensible; trap != nil {
+		return trap(target), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) preventExtensions(target *Object) (bool, bool) {
+	if trap := h.handler.PreventExtensions; trap != nil {
+		return trap(target), true
 	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) getOwnPropertyDescriptorStr(target *Object, prop unistring.String) (Value, bool) {
+	if trap := h.handler.GetOwnPropertyDescriptorIdx; trap != nil {
+		if idx, ok := strPropToInt(prop); ok {
+			desc := trap(target, idx)
+			return desc.toValue(target.runtime), true
+		}
+	}
+	if trap := h.handler.GetOwnPropertyDescriptor; trap != nil {
+		desc := trap(target, prop.String())
+		return desc.toValue(target.runtime), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) getOwnPropertyDescriptorIdx(target *Object, prop valueInt) (Value, bool) {
+	if trap := h.handler.GetOwnPropertyDescriptorIdx; trap != nil {
+		desc := trap(target, toIntStrict(int64(prop)))
+		return desc.toValue(target.runtime), true
+	}
+	if trap := h.handler.GetOwnPropertyDescriptor; trap != nil {
+		desc := trap(target, prop.String())
+		return desc.toValue(target.runtime), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) getOwnPropertyDescriptorSym(target *Object, prop *Symbol) (Value, bool) {
+	if trap := h.handler.GetOwnPropertyDescriptorSym; trap != nil {
+		desc := trap(target, prop)
+		return desc.toValue(target.runtime), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) definePropertyStr(target *Object, prop unistring.String, desc PropertyDescriptor) (bool, bool) {
+	if trap := h.handler.DefinePropertyIdx; trap != nil {
+		if idx, ok := strPropToInt(prop); ok {
+			return trap(target, idx, desc), true
+		}
+	}
+	if trap := h.handler.DefineProperty; trap != nil {
+		return trap(target, prop.String(), desc), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) definePropertyIdx(target *Object, prop valueInt, desc PropertyDescriptor) (bool, bool) {
+	if trap := h.handler.DefinePropertyIdx; trap != nil {
+		return trap(target, toIntStrict(int64(prop)), desc), true
+	}
+	if trap := h.handler.DefineProperty; trap != nil {
+		return trap(target, prop.String(), desc), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) definePropertySym(target *Object, prop *Symbol, desc PropertyDescriptor) (bool, bool) {
+	if trap := h.handler.DefinePropertySym; trap != nil {
+		return trap(target, prop, desc), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) hasStr(target *Object, prop unistring.String) (bool, bool) {
+	if trap := h.handler.HasIdx; trap != nil {
+		if idx, ok := strPropToInt(prop); ok {
+			return trap(target, idx), true
+		}
+	}
+	if trap := h.handler.Has; trap != nil {
+		return trap(target, prop.String()), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) hasIdx(target *Object, prop valueInt) (bool, bool) {
+	if trap := h.handler.HasIdx; trap != nil {
+		return trap(target, toIntStrict(int64(prop))), true
+	}
+	if trap := h.handler.Has; trap != nil {
+		return trap(target, prop.String()), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) hasSym(target *Object, prop *Symbol) (bool, bool) {
+	if trap := h.handler.HasSym; trap != nil {
+		return trap(target, prop), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) getStr(target *Object, prop unistring.String, receiver Value) (Value, bool) {
+	if trap := h.handler.GetIdx; trap != nil {
+		if idx, ok := strPropToInt(prop); ok {
+			return trap(target, idx, receiver), true
+		}
+	}
+	if trap := h.handler.Get; trap != nil {
+		return trap(target, prop.String(), receiver), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) getIdx(target *Object, prop valueInt, receiver Value) (Value, bool) {
+	if trap := h.handler.GetIdx; trap != nil {
+		return trap(target, toIntStrict(int64(prop)), receiver), true
+	}
+	if trap := h.handler.Get; trap != nil {
+		return trap(target, prop.String(), receiver), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) getSym(target *Object, prop *Symbol, receiver Value) (Value, bool) {
+	if trap := h.handler.GetSym; trap != nil {
+		return trap(target, prop, receiver), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) setStr(target *Object, prop unistring.String, value Value, receiver Value) (bool, bool) {
+	if trap := h.handler.SetIdx; trap != nil {
+		if idx, ok := strPropToInt(prop); ok {
+			return trap(target, idx, value, receiver), true
+		}
+	}
+	if trap := h.handler.Set; trap != nil {
+		return trap(target, prop.String(), value, receiver), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) setIdx(target *Object, prop valueInt, value Value, receiver Value) (bool, bool) {
+	if trap := h.handler.SetIdx; trap != nil {
+		return trap(target, toIntStrict(int64(prop)), value, receiver), true
+	}
+	if trap := h.handler.Set; trap != nil {
+		return trap(target, prop.String(), value, receiver), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) setSym(target *Object, prop *Symbol, value Value, receiver Value) (bool, bool) {
+	if trap := h.handler.SetSym; trap != nil {
+		return trap(target, prop, value, receiver), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) deleteStr(target *Object, prop unistring.String) (bool, bool) {
+	if trap := h.handler.DeletePropertyIdx; trap != nil {
+		if idx, ok := strPropToInt(prop); ok {
+			return trap(target, idx), true
+		}
+	}
+	if trap := h.handler.DeleteProperty; trap != nil {
+		return trap(target, prop.String()), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) deleteIdx(target *Object, prop valueInt) (bool, bool) {
+	if trap := h.handler.DeletePropertyIdx; trap != nil {
+		return trap(target, toIntStrict(int64(prop))), true
+	}
+	if trap := h.handler.DeleteProperty; trap != nil {
+		return trap(target, prop.String()), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) deleteSym(target *Object, prop *Symbol) (bool, bool) {
+	if trap := h.handler.DeletePropertySym; trap != nil {
+		return trap(target, prop), true
+	}
+	return false, false
+}
+
+func (h *nativeProxyHandler) ownKeys(target *Object) (*Object, bool) {
+	if trap := h.handler.OwnKeys; trap != nil {
+		return trap(target), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) apply(target *Object, this Value, args []Value) (Value, bool) {
+	if trap := h.handler.Apply; trap != nil {
+		return trap(target, this, args), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) construct(target *Object, args []Value, newTarget *Object) (Value, bool) {
+	if trap := h.handler.Construct; trap != nil {
+		return trap(target, args, newTarget), true
+	}
+	return nil, false
+}
+
+func (h *nativeProxyHandler) toObject(runtime *Runtime) *Object {
+	return runtime.ToValue(h.handler).ToObject(runtime)
+}
+
+func (r *Runtime) newNativeProxyHandler(nativeHandler *ProxyTrapConfig) proxyHandler {
+	return &nativeProxyHandler{handler: nativeHandler}
 }
 
 // ProxyTrapConfig provides a simplified Go-friendly API for implementing Proxy traps.
-// Note that the Proxy may not have Symbol properties when using this as a handler because property keys are
-// passed as strings.
-// get() and getOwnPropertyDescriptor() for Symbol properties will always return undefined;
-// has() and deleteProperty() for Symbol properties will always return false;
-// set() and defineProperty() for Symbol properties will throw a TypeError.
-// If you need Symbol properties implement the handler in JavaScript.
+// If an *Idx trap is defined it gets called for integer property keys, including negative ones. Note that
+// this also includes string property keys that can be parsed into an integer. This allows more efficient
+// array operations.
+// If an *Idx trap is not set, the corresponding string one is used.
 type ProxyTrapConfig struct {
 	// A trap for Object.getPrototypeOf, Reflect.getPrototypeOf, __proto__, Object.prototype.isPrototypeOf, instanceof
 	GetPrototypeOf func(target *Object) (prototype *Object)
@@ -223,29 +264,65 @@ type ProxyTrapConfig struct {
 	// A trap for Object.preventExtensions, Reflect.preventExtensions
 	PreventExtensions func(target *Object) (success bool)
 
-	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor
+	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor (string properties)
 	GetOwnPropertyDescriptor func(target *Object, prop string) (propertyDescriptor PropertyDescriptor)
 
-	// A trap for Object.defineProperty, Reflect.defineProperty
+	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor (integer properties)
+	GetOwnPropertyDescriptorIdx func(target *Object, prop int) (propertyDescriptor PropertyDescriptor)
+
+	// A trap for Object.getOwnPropertyDescriptor, Reflect.getOwnPropertyDescriptor (Symbol properties)
+	GetOwnPropertyDescriptorSym func(target *Object, prop *Symbol) (propertyDescriptor PropertyDescriptor)
+
+	// A trap for Object.defineProperty, Reflect.defineProperty (string properties)
 	DefineProperty func(target *Object, key string, propertyDescriptor PropertyDescriptor) (success bool)
 
-	// A trap for the in operator, with operator, Reflect.has
+	// A trap for Object.defineProperty, Reflect.defineProperty (integer properties)
+	DefinePropertyIdx func(target *Object, key int, propertyDescriptor PropertyDescriptor) (success bool)
+
+	// A trap for Object.defineProperty, Reflect.defineProperty (Symbol properties)
+	DefinePropertySym func(target *Object, key *Symbol, propertyDescriptor PropertyDescriptor) (success bool)
+
+	// A trap for the in operator, with operator, Reflect.has (string properties)
 	Has func(target *Object, property string) (available bool)
 
-	// A trap for getting property values, Reflect.get
-	Get func(target *Object, property string, receiver *Object) (value Value)
+	// A trap for the in operator, with operator, Reflect.has (integer properties)
+	HasIdx func(target *Object, property int) (available bool)
+
+	// A trap for the in operator, with operator, Reflect.has (Symbol properties)
+	HasSym func(target *Object, property *Symbol) (available bool)
+
+	// A trap for getting property values, Reflect.get (string properties)
+	Get func(target *Object, property string, receiver Value) (value Value)
+
+	// A trap for getting property values, Reflect.get (integer properties)
+	GetIdx func(target *Object, property int, receiver Value) (value Value)
+
+	// A trap for getting property values, Reflect.get (Symbol properties)
+	GetSym func(target *Object, property *Symbol, receiver Value) (value Value)
+
+	// A trap for setting property values, Reflect.set (string properties)
+	Set func(target *Object, property string, value Value, receiver Value) (success bool)
+
+	// A trap for setting property values, Reflect.set (integer properties)
+	SetIdx func(target *Object, property int, value Value, receiver Value) (success bool)
 
-	// A trap for setting property values, Reflect.set
-	Set func(target *Object, property string, value Value, receiver *Object) (success bool)
+	// A trap for setting property values, Reflect.set (Symbol properties)
+	SetSym func(target *Object, property *Symbol, value Value, receiver Value) (success bool)
 
-	// A trap for the delete operator, Reflect.deleteProperty
+	// A trap for the delete operator, Reflect.deleteProperty (string properties)
 	DeleteProperty func(target *Object, property string) (success bool)
 
+	// A trap for the delete operator, Reflect.deleteProperty (integer properties)
+	DeletePropertyIdx func(target *Object, property int) (success bool)
+
+	// A trap for the delete operator, Reflect.deleteProperty (Symbol properties)
+	DeletePropertySym func(target *Object, property *Symbol) (success bool)
+
 	// A trap for Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.keys, Reflect.ownKeys
 	OwnKeys func(target *Object) (object *Object)
 
 	// A trap for a function call, Function.prototype.apply, Function.prototype.call, Reflect.apply
-	Apply func(target *Object, this *Object, argumentsList []Value) (value Value)
+	Apply func(target *Object, this Value, argumentsList []Value) (value Value)
 
 	// A trap for the new operator, Reflect.construct
 	Construct func(target *Object, argumentsList []Value, newTarget *Object) (value *Object)
@@ -270,8 +347,13 @@ func (r *Runtime) builtin_newProxy(args []Value, newTarget *Object) *Object {
 }
 
 func (r *Runtime) NewProxy(target *Object, nativeHandler *ProxyTrapConfig) Proxy {
+	if p, ok := target.self.(*proxyObject); ok {
+		if p.handler == nil {
+			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as target"))
+		}
+	}
 	handler := r.newNativeProxyHandler(nativeHandler)
-	proxy := r.newProxyObject(target, handler, nil)
+	proxy := r._newProxyObject(target, handler, nil)
 	return Proxy{proxy: proxy}
 }
 
@@ -305,3 +387,10 @@ func (r *Runtime) initProxy() {
 	r.global.Proxy = r.newLazyObject(r.createProxy)
 	r.addToGlobal("Proxy", r.global.Proxy)
 }
+
+func strPropToInt(s unistring.String) (int, bool) {
+	if res, err := strconv.Atoi(string(s)); err == nil {
+		return res, true
+	}
+	return 0, false
+}

+ 291 - 14
builtin_proxy_test.go

@@ -1,6 +1,7 @@
 package goja
 
 import (
+	"strconv"
 	"testing"
 )
 
@@ -320,6 +321,92 @@ func TestProxy_native_proxy_getOwnPropertyDescriptor(t *testing.T) {
 	}
 }
 
+func TestProxy_native_proxy_getOwnPropertyDescriptorIdx(t *testing.T) {
+	vm := New()
+	a := vm.NewArray()
+	proxy1 := vm.NewProxy(a, &ProxyTrapConfig{
+		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
+			panic(vm.NewTypeError("GetOwnPropertyDescriptor was called"))
+		},
+		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
+			if prop >= -1 && prop <= 1 {
+				return PropertyDescriptor{
+					Value:        vm.ToValue(prop),
+					Configurable: FLAG_TRUE,
+				}
+			}
+			return PropertyDescriptor{}
+		},
+	})
+
+	proxy2 := vm.NewProxy(a, &ProxyTrapConfig{
+		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
+			switch prop {
+			case "-1", "0", "1":
+				return PropertyDescriptor{
+					Value:        vm.ToValue(prop),
+					Configurable: FLAG_TRUE,
+				}
+			}
+			return PropertyDescriptor{}
+		},
+	})
+
+	vm.Set("proxy1", proxy1)
+	vm.Set("proxy2", proxy2)
+	_, err := vm.RunString(TESTLIBX + `
+	var desc;
+	for (var i = -1; i <= 1; i++) {
+		desc = Object.getOwnPropertyDescriptor(proxy1, i);
+		assert(deepEqual(desc, {value: i, writable: false, enumerable: false, configurable: true}), "1. int "+i);
+
+		desc = Object.getOwnPropertyDescriptor(proxy1, ""+i);
+		assert(deepEqual(desc, {value: i, writable: false, enumerable: false, configurable: true}), "1. str "+i);
+
+		desc = Object.getOwnPropertyDescriptor(proxy2, i);
+		assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. int "+i);
+
+		desc = Object.getOwnPropertyDescriptor(proxy2, ""+i);
+		assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. str "+i);
+	}
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestProxy_native_proxy_getOwnPropertyDescriptorSym(t *testing.T) {
+	vm := New()
+	o := vm.NewObject()
+	sym := NewSymbol("42")
+	vm.Set("sym", sym)
+	proxy := vm.NewProxy(o, &ProxyTrapConfig{
+		GetOwnPropertyDescriptorSym: func(target *Object, s *Symbol) PropertyDescriptor {
+			if target != o {
+				panic(vm.NewTypeError("Invalid target"))
+			}
+			if s == sym {
+				return PropertyDescriptor{
+					Value:        vm.ToValue("passed"),
+					Writable:     FLAG_TRUE,
+					Configurable: FLAG_TRUE,
+				}
+			}
+			return PropertyDescriptor{}
+		},
+	})
+
+	vm.Set("proxy", proxy)
+	_, err := vm.RunString(TESTLIBX + `
+	var desc = Object.getOwnPropertyDescriptor(proxy, sym);
+	assert(deepEqual(desc, {value: "passed", writable: true, enumerable: false, configurable: true}));
+	assert.sameValue(Object.getOwnPropertyDescriptor(proxy, Symbol.iterator), undefined);
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
 func TestProxy_native_proxy_getOwnPropertyDescriptor_non_existing(t *testing.T) {
 	vm := New()
 	proxy := vm.NewProxy(vm.NewObject(), &ProxyTrapConfig{
@@ -371,9 +458,17 @@ func TestProxy_proxy_defineProperty(t *testing.T) {
 func TestProxy_native_proxy_defineProperty(t *testing.T) {
 	const SCRIPT = `
 	Object.defineProperty(proxy, "foo", {
-		value: "test123"
+		value: "teststr"
 	});
-	proxy.foo;
+	Object.defineProperty(proxy, "0", {
+		value: "testidx"
+	});
+	Object.defineProperty(proxy, Symbol.toStringTag, {
+		value: "testsym"
+	});
+	assert.sameValue(proxy.foo, "teststr-passed-str");
+	assert.sameValue(proxy[0], "testidx-passed-idx");
+	assert.sameValue(proxy[Symbol.toStringTag], "testsym-passed-sym");
 	`
 
 	runtime := New()
@@ -382,19 +477,24 @@ func TestProxy_native_proxy_defineProperty(t *testing.T) {
 
 	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
 		DefineProperty: func(target *Object, key string, propertyDescriptor PropertyDescriptor) (success bool) {
-			target.Set("foo", "321tset")
+			target.Set(key, propertyDescriptor.Value.String()+"-passed-str")
+			return true
+		},
+		DefinePropertyIdx: func(target *Object, key int, propertyDescriptor PropertyDescriptor) (success bool) {
+			target.Set(strconv.Itoa(key), propertyDescriptor.Value.String()+"-passed-idx")
+			return true
+		},
+		DefinePropertySym: func(target *Object, key *Symbol, propertyDescriptor PropertyDescriptor) (success bool) {
+			target.SetSymbol(key, propertyDescriptor.Value.String()+"-passed-sym")
 			return true
 		},
 	})
 	runtime.Set("proxy", proxy)
 
-	val, err := runtime.RunString(SCRIPT)
+	_, err := runtime.RunString(TESTLIB + SCRIPT)
 	if err != nil {
 		t.Fatal(err)
 	}
-	if s := val.String(); s != "321tset" {
-		t.Fatalf("val: %s", s)
-	}
 }
 
 func TestProxy_target_has_in(t *testing.T) {
@@ -540,35 +640,66 @@ func TestProxy_proxy_get_json_stringify(t *testing.T) {
 
 func TestProxy_native_proxy_get(t *testing.T) {
 	vm := New()
-	propValue := vm.ToValue("321tset")
+	propValueStr := vm.ToValue("321tset")
+	propValueIdx := vm.ToValue("idx")
+	propValueSym := vm.ToValue("sym")
+	sym := NewSymbol("test")
 	obj := vm.NewObject()
 	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
 		OwnKeys: func(*Object) *Object {
-			return vm.newArrayValues([]Value{vm.ToValue("foo")})
+			return vm.NewArray("0", "foo")
 		},
 		GetOwnPropertyDescriptor: func(target *Object, prop string) (propertyDescriptor PropertyDescriptor) {
 			if prop == "foo" {
 				return PropertyDescriptor{
-					Value:        propValue,
+					Value:        propValueStr,
 					Enumerable:   FLAG_TRUE,
 					Configurable: FLAG_TRUE,
 				}
 			}
-			return PropertyDescriptor{}
+			if prop == "0" {
+				panic(vm.NewTypeError("GetOwnPropertyDescriptor(0) was called"))
+			}
+			return
 		},
-		Get: func(target *Object, property string, receiver *Object) (value Value) {
+		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) (propertyDescriptor PropertyDescriptor) {
+			if prop == 0 {
+				return PropertyDescriptor{
+					Value:        propValueIdx,
+					Enumerable:   FLAG_TRUE,
+					Configurable: FLAG_TRUE,
+				}
+			}
+			return
+		},
+		Get: func(target *Object, property string, receiver Value) (value Value) {
 			if property == "foo" {
-				return propValue
+				return propValueStr
+			}
+			if property == "0" {
+				panic(vm.NewTypeError("Get(0) was called"))
 			}
 			return obj.Get(property)
 		},
+		GetIdx: func(target *Object, property int, receiver Value) (value Value) {
+			if property == 0 {
+				return propValueIdx
+			}
+			return obj.Get(strconv.Itoa(property))
+		},
+		GetSym: func(target *Object, property *Symbol, receiver Value) (value Value) {
+			if property == sym {
+				return propValueSym
+			}
+			return obj.GetSymbol(property)
+		},
 	})
 	vm.Set("proxy", proxy)
 	res, err := vm.RunString(`JSON.stringify(proxy)`)
 	if err != nil {
 		t.Fatal(err)
 	}
-	if !res.SameAs(asciiString(`{"foo":"321tset"}`)) {
+	if !res.SameAs(asciiString(`{"0":"idx","foo":"321tset"}`)) {
 		t.Fatalf("res: %v", res)
 	}
 	res, err = vm.RunString(`proxy[Symbol.toPrimitive]`)
@@ -587,6 +718,10 @@ func TestProxy_native_proxy_get(t *testing.T) {
 		t.Fatalf("res: %v", res)
 	}
 
+	if val := vm.ToValue(proxy).(*Object).GetSymbol(sym); val == nil || !val.SameAs(propValueSym) {
+		t.Fatalf("Get(symbol): %v", val)
+	}
+
 	res, err = vm.RunString(`proxy.toString()`)
 	if err != nil {
 		t.Fatal(err)
@@ -596,6 +731,60 @@ func TestProxy_native_proxy_get(t *testing.T) {
 	}
 }
 
+func TestProxy_native_proxy_set(t *testing.T) {
+	vm := New()
+	propValueStr := vm.ToValue("321tset")
+	propValueIdx := vm.ToValue("idx")
+	propValueSym := vm.ToValue("sym")
+	sym := NewSymbol("test")
+	obj := vm.NewObject()
+	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
+		Set: func(target *Object, property string, value Value, receiver Value) (success bool) {
+			if property == "str" {
+				obj.Set(property, propValueStr)
+				return true
+			}
+			panic(vm.NewTypeError("Setter for unexpected property: %q", property))
+		},
+		SetIdx: func(target *Object, property int, value Value, receiver Value) (success bool) {
+			if property == 0 {
+				obj.Set(strconv.Itoa(property), propValueIdx)
+				return true
+			}
+			panic(vm.NewTypeError("Setter for unexpected idx property: %d", property))
+		},
+		SetSym: func(target *Object, property *Symbol, value Value, receiver Value) (success bool) {
+			if property == sym {
+				obj.SetSymbol(property, propValueSym)
+				return true
+			}
+			panic(vm.NewTypeError("Setter for unexpected sym property: %q", property.String()))
+		},
+	})
+	proxyObj := vm.ToValue(proxy).ToObject(vm)
+	err := proxyObj.Set("str", "")
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = proxyObj.Set("0", "")
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = proxyObj.SetSymbol(sym, "")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if v := obj.Get("str"); !propValueStr.SameAs(v) {
+		t.Fatal(v)
+	}
+	if v := obj.Get("0"); !propValueIdx.SameAs(v) {
+		t.Fatal(v)
+	}
+	if v := obj.GetSymbol(sym); !propValueSym.SameAs(v) {
+		t.Fatal(v)
+	}
+}
+
 func TestProxy_target_set_prop(t *testing.T) {
 	const SCRIPT = `
 	var obj = {};
@@ -681,6 +870,94 @@ func TestProxy_proxy_delete(t *testing.T) {
 	testScript1(SCRIPT, asciiString("test"), t)
 }
 
+func TestProxy_native_delete(t *testing.T) {
+	vm := New()
+	sym := NewSymbol("test")
+	obj := vm.NewObject()
+	var strCalled, idxCalled, symCalled, strNegCalled, idxNegCalled, symNegCalled bool
+	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
+		DeleteProperty: func(target *Object, property string) (success bool) {
+			if property == "str" {
+				strCalled = true
+				return true
+			}
+			if property == "strNeg" {
+				strNegCalled = true
+				return false
+			}
+			panic(vm.NewTypeError("DeleteProperty for unexpected property: %q", property))
+		},
+		DeletePropertyIdx: func(target *Object, property int) (success bool) {
+			if property == 0 {
+				idxCalled = true
+				return true
+			}
+			if property == 1 {
+				idxNegCalled = true
+				return false
+			}
+			panic(vm.NewTypeError("DeletePropertyIdx for unexpected idx property: %d", property))
+		},
+		DeletePropertySym: func(target *Object, property *Symbol) (success bool) {
+			if property == sym {
+				symCalled = true
+				return true
+			}
+			if property == SymIterator {
+				symNegCalled = true
+				return false
+			}
+			panic(vm.NewTypeError("DeletePropertySym for unexpected sym property: %q", property.String()))
+		},
+	})
+	proxyObj := vm.ToValue(proxy).ToObject(vm)
+	err := proxyObj.Delete("str")
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = proxyObj.Delete("0")
+	if err != nil {
+		t.Fatal(err)
+	}
+	err = proxyObj.DeleteSymbol(sym)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !strCalled {
+		t.Fatal("str")
+	}
+	if !idxCalled {
+		t.Fatal("idx")
+	}
+	if !symCalled {
+		t.Fatal("sym")
+	}
+	vm.Set("proxy", proxy)
+	_, err = vm.RunString(`
+	if (delete proxy.strNeg) {
+		throw new Error("strNeg");
+	}
+	if (delete proxy[1]) {
+		throw new Error("idxNeg");
+	}
+	if (delete proxy[Symbol.iterator]) {
+		throw new Error("symNeg");
+	}
+	`)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !strNegCalled {
+		t.Fatal("strNeg")
+	}
+	if !idxNegCalled {
+		t.Fatal("idxNeg")
+	}
+	if !symNegCalled {
+		t.Fatal("symNeg")
+	}
+}
+
 func TestProxy_target_keys(t *testing.T) {
 	const SCRIPT = `
 	var obj = {

+ 3 - 1
object.go

@@ -138,7 +138,9 @@ func (p *PropertyDescriptor) toValue(r *Runtime) Value {
 	if p.jsDescriptor != nil {
 		return p.jsDescriptor
 	}
-
+	if p.Empty() {
+		return _undefined
+	}
 	o := r.NewObject()
 	s := o.self
 

+ 447 - 220
proxy.go

@@ -1,6 +1,7 @@
 package goja
 
 import (
+	"fmt"
 	"reflect"
 
 	"github.com/dop251/goja/unistring"
@@ -45,6 +46,10 @@ func (r *Runtime) newProxyObject(target, handler, proto *Object) *proxyObject {
 			panic(r.NewTypeError("Cannot create proxy with a revoked proxy as handler"))
 		}
 	}
+	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
@@ -73,7 +78,10 @@ func (p Proxy) Revoke() {
 }
 
 func (p Proxy) Handler() *Object {
-	return p.proxy.handler
+	if handler := p.proxy.handler; handler != nil {
+		return handler.toObject(p.proxy.val.runtime)
+	}
+	return nil
 }
 
 func (p Proxy) Target() *Object {
@@ -113,23 +121,57 @@ func (p proxyTrap) String() (name string) {
 	return string(p)
 }
 
-type proxyObject struct {
-	baseObject
-	target  *Object
+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
-	call    func(FunctionCall) Value
-	ctor    func(args []Value, newTarget *Object) *Object
 }
 
-func (p *proxyObject) proxyCall(trap proxyTrap, args ...Value) (Value, bool) {
-	r := p.val.runtime
-	if p.handler == nil {
-		panic(r.NewTypeError("Proxy already revoked"))
-	}
+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(p.handler, unistring.String(trap.String()))); m != nil {
+	if m := toMethod(r.getVStr(h.handler, unistring.String(trap.String()))); m != nil {
 		return m(FunctionCall{
-			This:      p.handler,
+			This:      h.handler,
 			Arguments: args,
 		}), true
 	}
@@ -137,61 +179,196 @@ func (p *proxyObject) proxyCall(trap proxyTrap, args ...Value) (Value, bool) {
 	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 {
-	if v, ok := p.proxyCall(proxy_trap_getPrototypeOf, p.target); ok {
+	target := p.target
+	if v, ok := p.checkHandler().getPrototypeOf(target); ok {
 		var handlerProto *Object
 		if v != _null {
 			handlerProto = p.val.runtime.toObject(v)
 		}
-		if !p.target.self.isExtensible() && !p.__sameValue(handlerProto, p.target.self.proto()) {
+		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 p.target.self.proto()
+	return target.self.proto()
 }
 
 func (p *proxyObject) setProto(proto *Object, throw bool) bool {
-	if v, ok := p.proxyCall(proxy_trap_setPrototypeOf, p.target, proto); ok {
-		if v.ToBoolean() {
-			if !p.target.self.isExtensible() && !p.__sameValue(proto, p.target.self.proto()) {
+	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 p.target.self.setProto(proto, throw)
+	return target.self.setProto(proto, throw)
 }
 
 func (p *proxyObject) isExtensible() bool {
-	if v, ok := p.proxyCall(proxy_trap_isExtensible, p.target); ok {
-		booleanTrapResult := v.ToBoolean()
-		if te := p.target.self.isExtensible(); booleanTrapResult != te {
+	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 p.target.self.isExtensible()
+	return target.self.isExtensible()
 }
 
 func (p *proxyObject) preventExtensions(throw bool) bool {
-	if v, ok := p.proxyCall(proxy_trap_preventExtensions, p.target); ok {
-		booleanTrapResult := v.ToBoolean()
+	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 := p.target.self.isExtensible(); booleanTrapResult && te {
+		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 p.target.self.preventExtensions(throw)
+	return target.self.preventExtensions(throw)
 }
 
 func propToValueProp(v Value) *valueProperty {
@@ -209,99 +386,124 @@ func propToValueProp(v Value) *valueProperty {
 	}
 }
 
-func (p *proxyObject) proxyDefineOwnProperty(name Value, descr PropertyDescriptor, throw bool) (bool, bool) {
-	if v, ok := p.proxyCall(proxy_trap_defineProperty, p.target, name, descr.toValue(p.val.runtime)); ok {
-		booleanTrapResult := v.ToBoolean()
-		if !booleanTrapResult {
-			p.val.runtime.typeErrorResult(throw, "'defineProperty' on proxy: trap returned falsish")
-			return false, 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())
 		}
-		targetDesc := propToValueProp(p.target.getOwnProp(name))
-		extensibleTarget := p.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 {
+		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())
 			}
 		}
-		return booleanTrapResult, true
 	}
-	return false, false
 }
 
 func (p *proxyObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
-	if v, ok := p.proxyDefineOwnProperty(stringValueFromRaw(name), descr, throw); ok {
-		return v
+	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 p.target.self.defineOwnPropertyStr(name, descr, throw)
+	return target.self.defineOwnPropertyStr(name, descr, throw)
 }
 
 func (p *proxyObject) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
-	if v, ok := p.proxyDefineOwnProperty(idx, descr, throw); ok {
-		return v
+	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 p.target.self.defineOwnPropertyIdx(idx, descr, throw)
+
+	return target.self.defineOwnPropertyIdx(idx, descr, throw)
 }
 
 func (p *proxyObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool {
-	if v, ok := p.proxyDefineOwnProperty(s, descr, throw); ok {
-		return v
+	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 p.target.self.defineOwnPropertySym(s, descr, throw)
+
+	return target.self.defineOwnPropertySym(s, descr, throw)
 }
 
-func (p *proxyObject) proxyHas(name Value) (bool, bool) {
-	if v, ok := p.proxyCall(proxy_trap_has, p.target, name); ok {
-		booleanTrapResult := v.ToBoolean()
-		if !booleanTrapResult {
-			targetDesc := propToValueProp(p.target.getOwnProp(name))
-			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 !p.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) 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()))
 		}
-		return booleanTrapResult, true
 	}
-
-	return false, false
 }
 
 func (p *proxyObject) hasPropertyStr(name unistring.String) bool {
-	if b, ok := p.proxyHas(stringValueFromRaw(name)); ok {
+	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 p.target.self.hasPropertyStr(name)
+	return target.self.hasPropertyStr(name)
 }
 
 func (p *proxyObject) hasPropertyIdx(idx valueInt) bool {
-	if b, ok := p.proxyHas(idx); ok {
+	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 p.target.self.hasPropertyIdx(idx)
+	return target.self.hasPropertyIdx(idx)
 }
 
 func (p *proxyObject) hasPropertySym(s *Symbol) bool {
-	if b, ok := p.proxyHas(s); ok {
+	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 p.target.self.hasPropertySym(s)
+	return target.self.hasPropertySym(s)
 }
 
 func (p *proxyObject) hasOwnPropertyStr(name unistring.String) bool {
@@ -316,233 +518,258 @@ func (p *proxyObject) hasOwnPropertySym(s *Symbol) bool {
 	return p.getOwnPropSym(s) != nil
 }
 
-func (p *proxyObject) proxyGetOwnPropertyDescriptor(name Value) (Value, bool) {
-	target := p.target
-	if v, ok := p.proxyCall(proxy_trap_getOwnPropertyDescriptor, target, name); ok {
-		r := p.val.runtime
-
-		targetDesc := propToValueProp(target.getOwnProp(name))
-
-		var trapResultObj *Object
-		if v != nil && v != _undefined {
-			if obj, ok := v.(*Object); ok {
-				trapResultObj = obj
-			} else {
-				panic(r.NewTypeError("'getOwnPropertyDescriptor' on proxy: trap returned neither object nor undefined for property '%s'", name.String()))
-			}
+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, true
-			}
-			if !targetDesc.configurable {
-				panic(r.NewTypeError())
-			}
-			if !target.self.isExtensible() {
-				panic(r.NewTypeError())
-			}
-			return nil, true
+	}
+	if trapResultObj == nil {
+		if targetDesc == nil {
+			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 !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 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 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_TRUE && resultDesc.Configurable == FLAG_TRUE &&
-			resultDesc.Enumerable == FLAG_TRUE {
-			return resultDesc.Value, true
+		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()))
 		}
-		return r.toValueProp(trapResultObj), true
 	}
 
-	return nil, false
+	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 {
-	if v, ok := p.proxyGetOwnPropertyDescriptor(stringValueFromRaw(name)); ok {
-		return v
+	target := p.target
+	if v, ok := p.checkHandler().getOwnPropertyDescriptorStr(target, name); ok {
+		return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropStr(name), target, v, name)
 	}
 
-	return p.target.self.getOwnPropStr(name)
+	return target.self.getOwnPropStr(name)
 }
 
 func (p *proxyObject) getOwnPropIdx(idx valueInt) Value {
-	if v, ok := p.proxyGetOwnPropertyDescriptor(idx.toString()); ok {
-		return v
+	target := p.target
+	if v, ok := p.checkHandler().getOwnPropertyDescriptorIdx(target, idx); ok {
+		return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropIdx(idx), target, v, idx)
 	}
 
-	return p.target.self.getOwnPropIdx(idx)
+	return target.self.getOwnPropIdx(idx)
 }
 
 func (p *proxyObject) getOwnPropSym(s *Symbol) Value {
-	if v, ok := p.proxyGetOwnPropertyDescriptor(s); ok {
-		return v
+	target := p.target
+	if v, ok := p.checkHandler().getOwnPropertyDescriptorSym(target, s); ok {
+		return p.proxyGetOwnPropertyDescriptor(target.self.getOwnPropSym(s), target, v, s)
 	}
 
-	return p.target.self.getOwnPropSym(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 {
-	if v, ok := p.proxyGet(stringValueFromRaw(name), receiver); ok {
+	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 p.target.self.getStr(name, receiver)
+	return target.self.getStr(name, receiver)
 }
 
 func (p *proxyObject) getIdx(idx valueInt, receiver Value) Value {
-	if v, ok := p.proxyGet(idx.toString(), receiver); ok {
+	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 p.target.self.getIdx(idx, receiver)
+	return target.self.getIdx(idx, receiver)
 }
 
 func (p *proxyObject) getSym(s *Symbol, receiver Value) Value {
-	if v, ok := p.proxyGet(s, receiver); ok {
+	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 p.target.self.getSym(s, receiver)
 
+	return target.self.getSym(s, receiver)
 }
 
-func (p *proxyObject) proxyGet(name, receiver Value) (Value, bool) {
-	target := p.target
-	if receiver == nil {
-		receiver = p.val
+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())
 	}
-	if v, ok := p.proxyCall(proxy_trap_get, target, name, receiver); ok {
-		if targetDesc, ok := target.getOwnProp(name).(*valueProperty); ok {
-			if !targetDesc.accessor {
-				if !targetDesc.writable && !targetDesc.configurable && !v.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 && v != _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))
-				}
+	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()))
 		}
-		return v, true
 	}
-
-	return nil, false
 }
 
-func (p *proxyObject) proxySet(name, value, receiver Value, throw bool) (bool, bool) {
+func (p *proxyObject) proxySetStr(name unistring.String, value, receiver Value, throw bool) bool {
 	target := p.target
-	if v, ok := p.proxyCall(proxy_trap_set, target, name, value, receiver); ok {
-		if v.ToBoolean() {
-			if prop, ok := target.getOwnProp(name).(*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()))
-				}
-			}
-			return true, true
+	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
 		}
-		if throw {
-			panic(p.val.runtime.NewTypeError("'set' on proxy: trap returned falsish for property '%s'", name.String()))
+		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, true
+		return false
 	}
+	return target.setIdx(idx, value, receiver, throw)
+}
 
-	return false, false
+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 {
-	if res, ok := p.proxySet(stringValueFromRaw(name), v, p.val, throw); ok {
-		return res
-	}
-	return p.target.setStr(name, v, p.val, throw)
+	return p.proxySetStr(name, v, p.val, throw)
 }
 
 func (p *proxyObject) setOwnIdx(idx valueInt, v Value, throw bool) bool {
-	if res, ok := p.proxySet(idx.toString(), v, p.val, throw); ok {
-		return res
-	}
-	return p.target.setIdx(idx, v, p.val, throw)
+	return p.proxySetIdx(idx, v, p.val, throw)
 }
 
 func (p *proxyObject) setOwnSym(s *Symbol, v Value, throw bool) bool {
-	if res, ok := p.proxySet(s, v, p.val, throw); ok {
-		return res
-	}
-	return p.target.setSym(s, v, p.val, throw)
+	return p.proxySetSym(s, v, p.val, throw)
 }
 
 func (p *proxyObject) setForeignStr(name unistring.String, v, receiver Value, throw bool) (bool, bool) {
-	if res, ok := p.proxySet(stringValueFromRaw(name), v, receiver, throw); ok {
-		return res, true
-	}
-	return p.target.setStr(name, v, receiver, throw), true
+	return p.proxySetStr(name, v, receiver, throw), true
 }
 
 func (p *proxyObject) setForeignIdx(idx valueInt, v, receiver Value, throw bool) (bool, bool) {
-	if res, ok := p.proxySet(idx.toString(), v, receiver, throw); ok {
-		return res, true
-	}
-	return p.target.setIdx(idx, v, receiver, throw), true
+	return p.proxySetIdx(idx, v, receiver, throw), true
 }
 
 func (p *proxyObject) setForeignSym(s *Symbol, v, receiver Value, throw bool) (bool, bool) {
-	if res, ok := p.proxySet(s, v, receiver, throw); ok {
-		return res, true
-	}
-	return p.target.setSym(s, v, receiver, throw), true
+	return p.proxySetSym(s, v, receiver, throw), true
 }
 
-func (p *proxyObject) proxyDelete(n Value) (bool, bool) {
-	target := p.target
-	if v, ok := p.proxyCall(proxy_trap_deleteProperty, target, n); ok {
-		if v.ToBoolean() {
-			if targetDesc, ok := target.getOwnProp(n).(*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", n.String()))
-				}
+func (p *proxyObject) proxyDeleteCheck(trapResult bool, targetProp Value, name fmt.Stringer, target *Object) {
+	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()))
 			}
-			return true, true
 		}
-		return false, true
+		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()))
+		}
 	}
-	return false, false
 }
 
 func (p *proxyObject) deleteStr(name unistring.String, throw bool) bool {
-	if ret, ok := p.proxyDelete(stringValueFromRaw(name)); ok {
-		return ret
+	target := p.target
+	if v, ok := p.checkHandler().deleteStr(target, name); ok {
+		p.proxyDeleteCheck(v, target.self.getOwnPropStr(name), name, target)
+		return v
 	}
 
-	return p.target.self.deleteStr(name, throw)
+	return target.self.deleteStr(name, throw)
 }
 
 func (p *proxyObject) deleteIdx(idx valueInt, throw bool) bool {
-	if ret, ok := p.proxyDelete(idx.toString()); ok {
-		return ret
+	target := p.target
+	if v, ok := p.checkHandler().deleteIdx(target, idx); ok {
+		p.proxyDeleteCheck(v, target.self.getOwnPropIdx(idx), idx, target)
+		return v
 	}
 
-	return p.target.self.deleteIdx(idx, throw)
+	return target.self.deleteIdx(idx, throw)
 }
 
 func (p *proxyObject) deleteSym(s *Symbol, throw bool) bool {
-	if ret, ok := p.proxyDelete(s); ok {
-		return ret
+	target := p.target
+	if v, ok := p.checkHandler().deleteSym(target, s); ok {
+		p.proxyDeleteCheck(v, target.self.getOwnPropSym(s), s, target)
+		return v
 	}
 
-	return p.target.self.deleteSym(s, throw)
+	return target.self.deleteSym(s, throw)
 }
 
 func (p *proxyObject) ownPropertyKeys(all bool, _ []Value) []Value {
@@ -554,7 +781,7 @@ func (p *proxyObject) ownPropertyKeys(all bool, _ []Value) []Value {
 
 func (p *proxyObject) proxyOwnKeys() ([]Value, bool) {
 	target := p.target
-	if v, ok := p.proxyCall(proxy_trap_ownKeys, p.target); ok {
+	if v, ok := p.checkHandler().ownKeys(target); ok {
 		keys := p.val.runtime.toObject(v)
 		var keyList []Value
 		keySet := make(map[Value]struct{})
@@ -623,7 +850,7 @@ func (p *proxyObject) apply(call FunctionCall) Value {
 	if p.call == nil {
 		p.val.runtime.NewTypeError("proxy target is not a function")
 	}
-	if v, ok := p.proxyCall(proxy_trap_apply, p.target, nilSafe(call.This), p.val.runtime.newArrayValues(call.Arguments)); ok {
+	if v, ok := p.checkHandler().apply(p.target, nilSafe(call.This), call.Arguments); ok {
 		return v
 	}
 	return p.call(call)
@@ -636,7 +863,7 @@ func (p *proxyObject) construct(args []Value, newTarget *Object) *Object {
 	if newTarget == nil {
 		newTarget = p.val
 	}
-	if v, ok := p.proxyCall(proxy_trap_construct, p.target, p.val.runtime.newArrayValues(args), newTarget); ok {
+	if v, ok := p.checkHandler().construct(p.target, args, newTarget); ok {
 		return p.val.runtime.toObject(v)
 	}
 	return p.ctor(args, newTarget)

+ 1 - 1
tc39_test.go

@@ -330,7 +330,7 @@ var (
 		"sec-object.getownpropertydescriptors",
 		"sec-object.entries",
 		"sec-object.values",
-		"sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys",
+		"sec-proxy-*",
 	}
 )