github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_proxy_test.go (about)

     1  package goja
     2  
     3  import (
     4  	"strconv"
     5  	"testing"
     6  )
     7  
     8  func TestProxy_Object_target_getPrototypeOf(t *testing.T) {
     9  	const SCRIPT = `
    10      var proto = {};
    11  	var obj = Object.create(proto);
    12  	var proxy = new Proxy(obj, {});
    13  	var p = Object.getPrototypeOf(proxy);
    14  	assert.sameValue(proto, p);
    15  	`
    16  
    17  	testScriptWithTestLib(SCRIPT, _undefined, t)
    18  }
    19  
    20  func TestProxy_Object_proxy_getPrototypeOf(t *testing.T) {
    21  	const SCRIPT = `
    22      var proto = {};
    23  	var proto2 = {};
    24  	var obj = Object.create(proto);
    25  	var proxy = new Proxy(obj, {
    26  		getPrototypeOf: function(target) {
    27  			return proto2;
    28  		}
    29  	});
    30  	var p = Object.getPrototypeOf(proxy);
    31  	assert.sameValue(proto2, p);
    32  	`
    33  
    34  	testScriptWithTestLib(SCRIPT, _undefined, t)
    35  }
    36  
    37  func TestProxy_Object_native_proxy_getPrototypeOf(t *testing.T) {
    38  	const SCRIPT = `
    39  	var p = Object.getPrototypeOf(proxy);
    40  	assert.sameValue(proto, p);
    41  	`
    42  
    43  	runtime := New()
    44  
    45  	prototype := runtime.NewObject()
    46  	runtime.Set("proto", prototype)
    47  
    48  	target := runtime.NewObject()
    49  	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
    50  		GetPrototypeOf: func(target *Object) *Object {
    51  			return prototype
    52  		},
    53  	})
    54  	runtime.Set("proxy", proxy)
    55  
    56  	runtime.testScriptWithTestLib(SCRIPT, _undefined, t)
    57  }
    58  
    59  func TestProxy_Object_target_setPrototypeOf(t *testing.T) {
    60  	const SCRIPT = `
    61      var proto = {};
    62  	var obj = {};
    63  	Object.setPrototypeOf(obj, proto);
    64  	var proxy = new Proxy(obj, {});
    65  	var p = Object.getPrototypeOf(proxy);
    66  	assert.sameValue(proto, p);
    67  	`
    68  
    69  	testScriptWithTestLib(SCRIPT, _undefined, t)
    70  }
    71  
    72  func TestProxy_Object_proxy_setPrototypeOf(t *testing.T) {
    73  	const SCRIPT = `
    74      var proto = {};
    75  	var proto2 = {};
    76  	var obj = {};
    77  	Object.setPrototypeOf(obj, proto);
    78  	var proxy = new Proxy(obj, {
    79  		setPrototypeOf: function(target, prototype) {
    80  			return Object.setPrototypeOf(target, proto2);
    81  		}
    82  	});
    83  	Object.setPrototypeOf(proxy, null);
    84  	var p = Object.getPrototypeOf(proxy);
    85  	assert.sameValue(proto2, p);
    86  	`
    87  
    88  	testScriptWithTestLib(SCRIPT, _undefined, t)
    89  }
    90  
    91  func TestProxy_Object_target_isExtensible(t *testing.T) {
    92  	const SCRIPT = `
    93  	var obj = {};
    94  	Object.seal(obj);
    95  	var proxy = new Proxy(obj, {});
    96  	Object.isExtensible(proxy);
    97  	`
    98  
    99  	testScript(SCRIPT, valueFalse, t)
   100  }
   101  
   102  func TestProxy_proxy_isExtensible(t *testing.T) {
   103  	const SCRIPT = `
   104  	var obj = {};
   105  	Object.seal(obj);
   106  	var proxy = new Proxy(obj, {
   107  		isExtensible: function(target) {
   108  			return false;
   109  		}
   110  	});
   111  	Object.isExtensible(proxy);
   112  	`
   113  
   114  	testScript(SCRIPT, valueFalse, t)
   115  }
   116  
   117  func TestProxy_native_proxy_isExtensible(t *testing.T) {
   118  	const SCRIPT = `
   119  	(function() {
   120  		Object.preventExtensions(target);
   121  		return Object.isExtensible(proxy);
   122  	})();
   123  	`
   124  
   125  	runtime := New()
   126  
   127  	target := runtime.NewObject()
   128  	runtime.Set("target", target)
   129  
   130  	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
   131  		IsExtensible: func(target *Object) (success bool) {
   132  			return false
   133  		},
   134  	})
   135  	runtime.Set("proxy", proxy)
   136  
   137  	val, err := runtime.RunString(SCRIPT)
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  	if val.ToBoolean() {
   142  		t.Fatal()
   143  	}
   144  }
   145  
   146  func TestProxy_Object_target_preventExtensions(t *testing.T) {
   147  	const SCRIPT = `
   148  	var obj = {
   149  		canEvolve: true
   150  	};
   151  	var proxy = new Proxy(obj, {});
   152  	Object.preventExtensions(proxy);
   153  	proxy.canEvolve
   154  	`
   155  
   156  	testScript(SCRIPT, valueTrue, t)
   157  }
   158  
   159  func TestProxy_proxy_preventExtensions(t *testing.T) {
   160  	const SCRIPT = `
   161  	var obj = {
   162  		canEvolve: true
   163  	};
   164  	var proxy = new Proxy(obj, {
   165  		preventExtensions: function(target) {
   166  			target.canEvolve = false;
   167  			Object.preventExtensions(obj);
   168  			return true;
   169  		}
   170  	});
   171  	Object.preventExtensions(proxy);
   172  	proxy.canEvolve;
   173  	`
   174  
   175  	testScript(SCRIPT, valueFalse, t)
   176  }
   177  
   178  func TestProxy_native_proxy_preventExtensions(t *testing.T) {
   179  	const SCRIPT = `
   180  	(function() {
   181  		Object.preventExtensions(proxy);
   182  		return proxy.canEvolve;
   183  	})();
   184  	`
   185  
   186  	runtime := New()
   187  
   188  	target := runtime.NewObject()
   189  	target.Set("canEvolve", true)
   190  	runtime.Set("target", target)
   191  
   192  	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
   193  		PreventExtensions: func(target *Object) (success bool) {
   194  			target.Set("canEvolve", false)
   195  			_, err := runtime.RunString("Object.preventExtensions(target)")
   196  			if err != nil {
   197  				panic(err)
   198  			}
   199  			return true
   200  		},
   201  	})
   202  	runtime.Set("proxy", proxy)
   203  
   204  	val, err := runtime.RunString(SCRIPT)
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	if val.ToBoolean() {
   209  		t.Fatal()
   210  	}
   211  }
   212  
   213  func TestProxy_Object_target_getOwnPropertyDescriptor(t *testing.T) {
   214  	const SCRIPT = `
   215  	var desc = {
   216  		configurable: false,
   217  		enumerable: false,
   218  		value: 42,
   219  		writable: false 
   220  	};
   221  
   222  	var obj = {};
   223  	Object.defineProperty(obj, "foo", desc);
   224  
   225  	var proxy = new Proxy(obj, {});
   226  
   227  	var desc2 = Object.getOwnPropertyDescriptor(proxy, "foo");
   228  	desc2.value
   229  	`
   230  
   231  	testScript(SCRIPT, valueInt(42), t)
   232  }
   233  
   234  func TestProxy_proxy_getOwnPropertyDescriptor(t *testing.T) {
   235  	const SCRIPT = `
   236  	var desc = {
   237  		configurable: false,
   238  		enumerable: false,
   239  		value: 42,
   240  		writable: false 
   241  	};
   242  	var proxy_desc = {
   243  		configurable: false,
   244  		enumerable: false,
   245  		value: 24,
   246  		writable: false 
   247  	};
   248  
   249  	var obj = {};
   250  	Object.defineProperty(obj, "foo", desc);
   251  
   252  	var proxy = new Proxy(obj, {
   253  		getOwnPropertyDescriptor: function(target, property) {
   254  			return proxy_desc;
   255  		}
   256  	});
   257  
   258  	assert.throws(TypeError, function() {
   259  		Object.getOwnPropertyDescriptor(proxy, "foo");
   260  	});
   261  	undefined;
   262  	`
   263  
   264  	testScriptWithTestLib(SCRIPT, _undefined, t)
   265  }
   266  
   267  func TestProxy_native_proxy_getOwnPropertyDescriptor(t *testing.T) {
   268  	const SCRIPT = `
   269  	(function() {
   270  		var desc = {
   271  			configurable: true,
   272  			enumerable: false,
   273  			value: 42,
   274  			writable: false 
   275  		};
   276  		var proxy_desc = {
   277  			configurable: true,
   278  			enumerable: false,
   279  			value: 24,
   280  			writable: false 
   281  		};
   282  		
   283  		var obj = {};
   284  		Object.defineProperty(obj, "foo", desc);
   285  
   286  		return function(constructor) {
   287  			var proxy = constructor(obj, proxy_desc);
   288  
   289  			var desc2 = Object.getOwnPropertyDescriptor(proxy, "foo");
   290  			return desc2.value
   291  		}
   292  	})();
   293  	`
   294  
   295  	runtime := New()
   296  
   297  	constructor := func(call FunctionCall) Value {
   298  		target := call.Argument(0).(*Object)
   299  		proxyDesc := call.Argument(1).(*Object)
   300  
   301  		return runtime.NewProxy(target, &ProxyTrapConfig{
   302  			GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
   303  				return runtime.toPropertyDescriptor(proxyDesc)
   304  			},
   305  		}).proxy.val
   306  	}
   307  
   308  	val, err := runtime.RunString(SCRIPT)
   309  	if err != nil {
   310  		t.Fatal(err)
   311  	}
   312  
   313  	if c, ok := val.(*Object).self.assertCallable(); ok {
   314  		val := c(FunctionCall{
   315  			This:      val,
   316  			Arguments: []Value{runtime.ToValue(constructor)},
   317  		})
   318  		if i := val.ToInteger(); i != 24 {
   319  			t.Fatalf("val: %d", i)
   320  		}
   321  	} else {
   322  		t.Fatal("not a function")
   323  	}
   324  }
   325  
   326  func TestProxy_native_proxy_getOwnPropertyDescriptorIdx(t *testing.T) {
   327  	vm := New()
   328  	a := vm.NewArray()
   329  	proxy1 := vm.NewProxy(a, &ProxyTrapConfig{
   330  		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
   331  			panic(vm.NewTypeError("GetOwnPropertyDescriptor was called for %q", prop))
   332  		},
   333  		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
   334  			if prop >= -1 && prop <= 1 {
   335  				return PropertyDescriptor{
   336  					Value:        vm.ToValue(prop),
   337  					Configurable: FLAG_TRUE,
   338  				}
   339  			}
   340  			return PropertyDescriptor{}
   341  		},
   342  	})
   343  
   344  	proxy2 := vm.NewProxy(a, &ProxyTrapConfig{
   345  		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
   346  			switch prop {
   347  			case "-1", "0", "1":
   348  				return PropertyDescriptor{
   349  					Value:        vm.ToValue(prop),
   350  					Configurable: FLAG_TRUE,
   351  				}
   352  			}
   353  			return PropertyDescriptor{}
   354  		},
   355  	})
   356  
   357  	proxy3 := vm.NewProxy(a, &ProxyTrapConfig{
   358  		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
   359  			return PropertyDescriptor{
   360  				Value:        vm.ToValue(prop),
   361  				Configurable: FLAG_TRUE,
   362  			}
   363  		},
   364  		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) PropertyDescriptor {
   365  			panic(vm.NewTypeError("GetOwnPropertyDescriptorIdx was called for %d", prop))
   366  		},
   367  	})
   368  
   369  	vm.Set("proxy1", proxy1)
   370  	vm.Set("proxy2", proxy2)
   371  	vm.Set("proxy3", proxy3)
   372  	vm.testScriptWithTestLibX(`
   373  	var desc;
   374  	for (var i = -1; i <= 1; i++) {
   375  		desc = Object.getOwnPropertyDescriptor(proxy1, i);
   376  		assert(deepEqual(desc, {value: i, writable: false, enumerable: false, configurable: true}), "1. int "+i);
   377  
   378  		desc = Object.getOwnPropertyDescriptor(proxy1, ""+i);
   379  		assert(deepEqual(desc, {value: i, writable: false, enumerable: false, configurable: true}), "1. str "+i);
   380  
   381  		desc = Object.getOwnPropertyDescriptor(proxy2, i);
   382  		assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. int "+i);
   383  
   384  		desc = Object.getOwnPropertyDescriptor(proxy2, ""+i);
   385  		assert(deepEqual(desc, {value: ""+i, writable: false, enumerable: false, configurable: true}), "2. str "+i);
   386  	}
   387  
   388  	for (const prop of ["00", " 0", "-0", "01"]) {
   389  		desc = Object.getOwnPropertyDescriptor(proxy3, prop);
   390  		assert(deepEqual(desc, {value: prop, writable: false, enumerable: false, configurable: true}), "3. "+prop);
   391  	}
   392  	`, _undefined, t)
   393  }
   394  
   395  func TestProxy_native_proxy_getOwnPropertyDescriptorSym(t *testing.T) {
   396  	vm := New()
   397  	o := vm.NewObject()
   398  	sym := NewSymbol("42")
   399  	vm.Set("sym", sym)
   400  	proxy := vm.NewProxy(o, &ProxyTrapConfig{
   401  		GetOwnPropertyDescriptorSym: func(target *Object, s *Symbol) PropertyDescriptor {
   402  			if target != o {
   403  				panic(vm.NewTypeError("Invalid target"))
   404  			}
   405  			if s == sym {
   406  				return PropertyDescriptor{
   407  					Value:        vm.ToValue("passed"),
   408  					Writable:     FLAG_TRUE,
   409  					Configurable: FLAG_TRUE,
   410  				}
   411  			}
   412  			return PropertyDescriptor{}
   413  		},
   414  	})
   415  
   416  	vm.Set("proxy", proxy)
   417  	vm.testScriptWithTestLibX(`
   418  	var desc = Object.getOwnPropertyDescriptor(proxy, sym);
   419  	assert(deepEqual(desc, {value: "passed", writable: true, enumerable: false, configurable: true}));
   420  	assert.sameValue(Object.getOwnPropertyDescriptor(proxy, Symbol.iterator), undefined);
   421  	`, _undefined, t)
   422  }
   423  
   424  func TestProxy_native_proxy_getOwnPropertyDescriptor_non_existing(t *testing.T) {
   425  	vm := New()
   426  	proxy := vm.NewProxy(vm.NewObject(), &ProxyTrapConfig{
   427  		GetOwnPropertyDescriptor: func(target *Object, prop string) (propertyDescriptor PropertyDescriptor) {
   428  			return // empty PropertyDescriptor
   429  		},
   430  	})
   431  	vm.Set("proxy", proxy)
   432  	res, err := vm.RunString(`Object.getOwnPropertyDescriptor(proxy, "foo") === undefined`)
   433  	if err != nil {
   434  		t.Fatal(err)
   435  	}
   436  	if res != valueTrue {
   437  		t.Fatal(res)
   438  	}
   439  }
   440  
   441  func TestProxy_Object_target_defineProperty(t *testing.T) {
   442  	const SCRIPT = `
   443  	var obj = {};
   444  	var proxy = new Proxy(obj, {});
   445  	Object.defineProperty(proxy, "foo", {
   446  		value: "test123"
   447  	});
   448  	proxy.foo;
   449  	`
   450  
   451  	testScript(SCRIPT, asciiString("test123"), t)
   452  }
   453  
   454  func TestProxy_proxy_defineProperty(t *testing.T) {
   455  	const SCRIPT = `
   456  	var obj = {};
   457  	var proxy = new Proxy(obj, {
   458  		defineProperty: function(target, prop, descriptor) {
   459  			target.foo = "321tset";
   460  			return true;
   461  		}
   462  	});
   463  	Object.defineProperty(proxy, "foo", {
   464  		value: "test123"
   465  	});
   466  	proxy.foo;
   467  	`
   468  
   469  	testScript(SCRIPT, asciiString("321tset"), t)
   470  }
   471  
   472  func TestProxy_native_proxy_defineProperty(t *testing.T) {
   473  	const SCRIPT = `
   474  	Object.defineProperty(proxy, "foo", {
   475  		value: "teststr"
   476  	});
   477  	Object.defineProperty(proxy, "0", {
   478  		value: "testidx"
   479  	});
   480  	Object.defineProperty(proxy, Symbol.toStringTag, {
   481  		value: "testsym"
   482  	});
   483  	assert.sameValue(proxy.foo, "teststr-passed-str");
   484  	assert.sameValue(proxy[0], "testidx-passed-idx");
   485  	assert.sameValue(proxy[Symbol.toStringTag], "testsym-passed-sym");
   486  	`
   487  
   488  	runtime := New()
   489  
   490  	target := runtime.NewObject()
   491  
   492  	proxy := runtime.NewProxy(target, &ProxyTrapConfig{
   493  		DefineProperty: func(target *Object, key string, propertyDescriptor PropertyDescriptor) (success bool) {
   494  			target.Set(key, propertyDescriptor.Value.String()+"-passed-str")
   495  			return true
   496  		},
   497  		DefinePropertyIdx: func(target *Object, key int, propertyDescriptor PropertyDescriptor) (success bool) {
   498  			target.Set(strconv.Itoa(key), propertyDescriptor.Value.String()+"-passed-idx")
   499  			return true
   500  		},
   501  		DefinePropertySym: func(target *Object, key *Symbol, propertyDescriptor PropertyDescriptor) (success bool) {
   502  			target.SetSymbol(key, propertyDescriptor.Value.String()+"-passed-sym")
   503  			return true
   504  		},
   505  	})
   506  	runtime.Set("proxy", proxy)
   507  
   508  	runtime.testScriptWithTestLib(SCRIPT, _undefined, t)
   509  }
   510  
   511  func TestProxy_target_has_in(t *testing.T) {
   512  	const SCRIPT = `
   513  	var obj = {
   514  		secret: true
   515  	};
   516  	var proxy = new Proxy(obj, {});
   517  	
   518  	"secret" in proxy
   519  	`
   520  
   521  	testScript(SCRIPT, valueTrue, t)
   522  }
   523  
   524  func TestProxy_proxy_has_in(t *testing.T) {
   525  	const SCRIPT = `
   526  	var obj = {
   527  		secret: true
   528  	};
   529  	var proxy = new Proxy(obj, {
   530  		has: function(target, key) {
   531  			return key !== "secret";
   532  		}
   533  	});
   534  	
   535  	"secret" in proxy
   536  	`
   537  
   538  	testScript(SCRIPT, valueFalse, t)
   539  }
   540  
   541  func TestProxy_target_has_with(t *testing.T) {
   542  	const SCRIPT = `
   543  	var obj = {
   544  		secret: true
   545  	};
   546  	var proxy = new Proxy(obj, {});
   547  	
   548  	with(proxy) {
   549  		(secret);
   550  	}
   551  	`
   552  
   553  	testScript(SCRIPT, valueTrue, t)
   554  }
   555  
   556  func TestProxy_proxy_has_with(t *testing.T) {
   557  	const SCRIPT = `
   558  	var obj = {
   559  		secret: true
   560  	};
   561  	var proxy = new Proxy(obj, {
   562  		has: function(target, key) {
   563  			return key !== "secret";
   564  		}
   565  	});
   566  	
   567  	var thrown = false;
   568  	try {
   569  		with(proxy) {
   570  			(secret);
   571  		}
   572  	} catch (e) {
   573  		if (e instanceof ReferenceError) {
   574  			thrown = true;
   575  		} else {
   576  			throw e;
   577  		}
   578  	}
   579  	thrown;
   580  	`
   581  
   582  	testScript(SCRIPT, valueTrue, t)
   583  }
   584  
   585  func TestProxy_target_get(t *testing.T) {
   586  	const SCRIPT = `
   587  	var obj = {};
   588  	var proxy = new Proxy(obj, {});
   589  	Object.defineProperty(proxy, "foo", {
   590  		value: "test123"
   591  	});
   592  	proxy.foo;
   593  	`
   594  
   595  	testScript(SCRIPT, asciiString("test123"), t)
   596  }
   597  
   598  func TestProxy_proxy_get(t *testing.T) {
   599  	const SCRIPT = `
   600  	var obj = {};
   601  	var proxy = new Proxy(obj, {
   602  		get: function(target, prop, receiver) {
   603  			return "321tset"
   604  		}
   605  	});
   606  	Object.defineProperty(proxy, "foo", {
   607  		value: "test123",
   608  		configurable: true,
   609  	});
   610  	proxy.foo;
   611  	`
   612  
   613  	testScript(SCRIPT, asciiString("321tset"), t)
   614  }
   615  
   616  func TestProxy_proxy_get_json_stringify(t *testing.T) {
   617  	const SCRIPT = `
   618  	var obj = {};
   619  	var propValue = "321tset";
   620  	var _handler, _target, _prop, _receiver;
   621  	var proxy = new Proxy(obj, {
   622  		ownKeys: function() {
   623  			return ["foo"];
   624  		},
   625  		getOwnPropertyDescriptor: function(target, prop) {
   626  			if (prop === "foo") {
   627  				return {
   628  					value: propValue,
   629  					enumerable: true,
   630  					configurable: true
   631  				}
   632  			}
   633  		},
   634  		get: function(target, prop, receiver) {
   635  			if (prop === "foo") {
   636  				_prop = prop;
   637  				_receiver = receiver;
   638  				return propValue;
   639  			}
   640  			return obj[prop];
   641  		}
   642  	});
   643  	var res = JSON.stringify(proxy);
   644  	assert.sameValue(res, '{"foo":"321tset"}');
   645  	assert.sameValue(_prop, "foo");
   646  	assert.sameValue(_receiver, proxy);
   647  	`
   648  
   649  	testScriptWithTestLib(SCRIPT, _undefined, t)
   650  }
   651  
   652  func TestProxy_native_proxy_get(t *testing.T) {
   653  	vm := New()
   654  	propValueStr := vm.ToValue("321tset")
   655  	propValueIdx := vm.ToValue("idx")
   656  	propValueSym := vm.ToValue("sym")
   657  	sym := NewSymbol("test")
   658  	obj := vm.NewObject()
   659  	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
   660  		OwnKeys: func(*Object) *Object {
   661  			return vm.NewArray("0", "foo")
   662  		},
   663  		GetOwnPropertyDescriptor: func(target *Object, prop string) (propertyDescriptor PropertyDescriptor) {
   664  			if prop == "foo" {
   665  				return PropertyDescriptor{
   666  					Value:        propValueStr,
   667  					Enumerable:   FLAG_TRUE,
   668  					Configurable: FLAG_TRUE,
   669  				}
   670  			}
   671  			if prop == "0" {
   672  				panic(vm.NewTypeError("GetOwnPropertyDescriptor(0) was called"))
   673  			}
   674  			return
   675  		},
   676  		GetOwnPropertyDescriptorIdx: func(target *Object, prop int) (propertyDescriptor PropertyDescriptor) {
   677  			if prop == 0 {
   678  				return PropertyDescriptor{
   679  					Value:        propValueIdx,
   680  					Enumerable:   FLAG_TRUE,
   681  					Configurable: FLAG_TRUE,
   682  				}
   683  			}
   684  			return
   685  		},
   686  		Get: func(target *Object, property string, receiver Value) (value Value) {
   687  			if property == "foo" {
   688  				return propValueStr
   689  			}
   690  			if property == "0" {
   691  				panic(vm.NewTypeError("Get(0) was called"))
   692  			}
   693  			return obj.Get(property)
   694  		},
   695  		GetIdx: func(target *Object, property int, receiver Value) (value Value) {
   696  			if property == 0 {
   697  				return propValueIdx
   698  			}
   699  			return obj.Get(strconv.Itoa(property))
   700  		},
   701  		GetSym: func(target *Object, property *Symbol, receiver Value) (value Value) {
   702  			if property == sym {
   703  				return propValueSym
   704  			}
   705  			return obj.GetSymbol(property)
   706  		},
   707  	})
   708  	vm.Set("proxy", proxy)
   709  	res, err := vm.RunString(`JSON.stringify(proxy)`)
   710  	if err != nil {
   711  		t.Fatal(err)
   712  	}
   713  	if !res.SameAs(asciiString(`{"0":"idx","foo":"321tset"}`)) {
   714  		t.Fatalf("res: %v", res)
   715  	}
   716  	res, err = vm.RunString(`proxy[Symbol.toPrimitive]`)
   717  	if err != nil {
   718  		t.Fatal(err)
   719  	}
   720  	if !IsUndefined(res) {
   721  		t.Fatalf("res: %v", res)
   722  	}
   723  
   724  	res, err = vm.RunString(`proxy.hasOwnProperty(Symbol.toPrimitive)`)
   725  	if err != nil {
   726  		t.Fatal(err)
   727  	}
   728  	if !res.SameAs(valueFalse) {
   729  		t.Fatalf("res: %v", res)
   730  	}
   731  
   732  	if val := vm.ToValue(proxy).(*Object).GetSymbol(sym); val == nil || !val.SameAs(propValueSym) {
   733  		t.Fatalf("Get(symbol): %v", val)
   734  	}
   735  
   736  	res, err = vm.RunString(`proxy.toString()`)
   737  	if err != nil {
   738  		t.Fatal(err)
   739  	}
   740  	if !res.SameAs(asciiString(`[object Object]`)) {
   741  		t.Fatalf("res: %v", res)
   742  	}
   743  }
   744  
   745  func TestProxy_native_proxy_set(t *testing.T) {
   746  	vm := New()
   747  	propValueStr := vm.ToValue("321tset")
   748  	propValueIdx := vm.ToValue("idx")
   749  	propValueSym := vm.ToValue("sym")
   750  	sym := NewSymbol("test")
   751  	obj := vm.NewObject()
   752  	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
   753  		Set: func(target *Object, property string, value Value, receiver Value) (success bool) {
   754  			if property == "str" {
   755  				obj.Set(property, propValueStr)
   756  				return true
   757  			}
   758  			panic(vm.NewTypeError("Setter for unexpected property: %q", property))
   759  		},
   760  		SetIdx: func(target *Object, property int, value Value, receiver Value) (success bool) {
   761  			if property == 0 {
   762  				obj.Set(strconv.Itoa(property), propValueIdx)
   763  				return true
   764  			}
   765  			panic(vm.NewTypeError("Setter for unexpected idx property: %d", property))
   766  		},
   767  		SetSym: func(target *Object, property *Symbol, value Value, receiver Value) (success bool) {
   768  			if property == sym {
   769  				obj.SetSymbol(property, propValueSym)
   770  				return true
   771  			}
   772  			panic(vm.NewTypeError("Setter for unexpected sym property: %q", property.String()))
   773  		},
   774  	})
   775  	proxyObj := vm.ToValue(proxy).ToObject(vm)
   776  	err := proxyObj.Set("str", "")
   777  	if err != nil {
   778  		t.Fatal(err)
   779  	}
   780  	err = proxyObj.Set("0", "")
   781  	if err != nil {
   782  		t.Fatal(err)
   783  	}
   784  	err = proxyObj.SetSymbol(sym, "")
   785  	if err != nil {
   786  		t.Fatal(err)
   787  	}
   788  	if v := obj.Get("str"); !propValueStr.SameAs(v) {
   789  		t.Fatal(v)
   790  	}
   791  	if v := obj.Get("0"); !propValueIdx.SameAs(v) {
   792  		t.Fatal(v)
   793  	}
   794  	if v := obj.GetSymbol(sym); !propValueSym.SameAs(v) {
   795  		t.Fatal(v)
   796  	}
   797  }
   798  
   799  func TestProxy_target_set_prop(t *testing.T) {
   800  	const SCRIPT = `
   801  	var obj = {};
   802  	var proxy = new Proxy(obj, {});
   803  	proxy.foo = "test123";
   804  	proxy.foo;
   805  	`
   806  
   807  	testScript(SCRIPT, asciiString("test123"), t)
   808  }
   809  
   810  func TestProxy_proxy_set_prop(t *testing.T) {
   811  	const SCRIPT = `
   812  	var obj = {};
   813  	var proxy = new Proxy(obj, {
   814  		set: function(target, prop, receiver) {
   815  			target.foo = "321tset";
   816  			return true;
   817  		}
   818  	});
   819  	proxy.foo = "test123";
   820  	proxy.foo;
   821  	`
   822  
   823  	testScript(SCRIPT, asciiString("321tset"), t)
   824  }
   825  func TestProxy_target_set_associative(t *testing.T) {
   826  	const SCRIPT = `
   827  	var obj = {};
   828  	var proxy = new Proxy(obj, {});
   829  	proxy["foo"] = "test123";
   830  	proxy.foo;
   831  	`
   832  
   833  	testScript(SCRIPT, asciiString("test123"), t)
   834  }
   835  
   836  func TestProxy_proxy_set_associative(t *testing.T) {
   837  	const SCRIPT = `
   838  	var obj = {};
   839  	var proxy = new Proxy(obj, {
   840  		set: function(target, property, value, receiver) {
   841  			target["foo"] = "321tset";
   842  			return true;
   843  		}
   844  	});
   845  	proxy["foo"] = "test123";
   846  	proxy.foo;
   847  	`
   848  
   849  	testScript(SCRIPT, asciiString("321tset"), t)
   850  }
   851  
   852  func TestProxy_target_delete(t *testing.T) {
   853  	const SCRIPT = `
   854  	var obj = {
   855  		foo: "test"
   856  	};
   857  	var proxy = new Proxy(obj, {});
   858  	delete proxy.foo;
   859  
   860  	proxy.foo;
   861  	`
   862  
   863  	testScript(SCRIPT, _undefined, t)
   864  }
   865  
   866  func TestProxy_proxy_delete(t *testing.T) {
   867  	const SCRIPT = `
   868  	var obj = {
   869  		foo: "test"
   870  	};
   871  	var proxy = new Proxy(obj, {
   872  		deleteProperty: function(target, prop) {
   873  			return true;
   874  		}
   875  	});
   876  	delete proxy.foo;
   877  
   878  	proxy.foo;
   879  	`
   880  
   881  	testScript(SCRIPT, asciiString("test"), t)
   882  }
   883  
   884  func TestProxy_native_delete(t *testing.T) {
   885  	vm := New()
   886  	sym := NewSymbol("test")
   887  	obj := vm.NewObject()
   888  	var strCalled, idxCalled, symCalled, strNegCalled, idxNegCalled, symNegCalled bool
   889  	proxy := vm.NewProxy(obj, &ProxyTrapConfig{
   890  		DeleteProperty: func(target *Object, property string) (success bool) {
   891  			if property == "str" {
   892  				strCalled = true
   893  				return true
   894  			}
   895  			if property == "strNeg" {
   896  				strNegCalled = true
   897  				return false
   898  			}
   899  			panic(vm.NewTypeError("DeleteProperty for unexpected property: %q", property))
   900  		},
   901  		DeletePropertyIdx: func(target *Object, property int) (success bool) {
   902  			if property == 0 {
   903  				idxCalled = true
   904  				return true
   905  			}
   906  			if property == 1 {
   907  				idxNegCalled = true
   908  				return false
   909  			}
   910  			panic(vm.NewTypeError("DeletePropertyIdx for unexpected idx property: %d", property))
   911  		},
   912  		DeletePropertySym: func(target *Object, property *Symbol) (success bool) {
   913  			if property == sym {
   914  				symCalled = true
   915  				return true
   916  			}
   917  			if property == SymIterator {
   918  				symNegCalled = true
   919  				return false
   920  			}
   921  			panic(vm.NewTypeError("DeletePropertySym for unexpected sym property: %q", property.String()))
   922  		},
   923  	})
   924  	proxyObj := vm.ToValue(proxy).ToObject(vm)
   925  	err := proxyObj.Delete("str")
   926  	if err != nil {
   927  		t.Fatal(err)
   928  	}
   929  	err = proxyObj.Delete("0")
   930  	if err != nil {
   931  		t.Fatal(err)
   932  	}
   933  	err = proxyObj.DeleteSymbol(sym)
   934  	if err != nil {
   935  		t.Fatal(err)
   936  	}
   937  	if !strCalled {
   938  		t.Fatal("str")
   939  	}
   940  	if !idxCalled {
   941  		t.Fatal("idx")
   942  	}
   943  	if !symCalled {
   944  		t.Fatal("sym")
   945  	}
   946  	vm.Set("proxy", proxy)
   947  	_, err = vm.RunString(`
   948  	if (delete proxy.strNeg) {
   949  		throw new Error("strNeg");
   950  	}
   951  	if (delete proxy[1]) {
   952  		throw new Error("idxNeg");
   953  	}
   954  	if (delete proxy[Symbol.iterator]) {
   955  		throw new Error("symNeg");
   956  	}
   957  	`)
   958  	if err != nil {
   959  		t.Fatal(err)
   960  	}
   961  	if !strNegCalled {
   962  		t.Fatal("strNeg")
   963  	}
   964  	if !idxNegCalled {
   965  		t.Fatal("idxNeg")
   966  	}
   967  	if !symNegCalled {
   968  		t.Fatal("symNeg")
   969  	}
   970  }
   971  
   972  func TestProxy_target_keys(t *testing.T) {
   973  	const SCRIPT = `
   974  	var obj = {
   975  		foo: "test"
   976  	};
   977  	var proxy = new Proxy(obj, {});
   978  
   979  	var keys = Object.keys(proxy);
   980  	if (keys.length != 1) {
   981  		throw new Error("assertion error");
   982  	}
   983  	`
   984  
   985  	testScript(SCRIPT, _undefined, t)
   986  }
   987  
   988  func TestProxy_proxy_keys(t *testing.T) {
   989  	const SCRIPT = `
   990  	var obj = {
   991  		foo: "test"
   992  	};
   993  	var proxy = new Proxy(obj, {
   994  		ownKeys: function(target) {
   995  			return ["foo", "bar"];
   996  		}
   997  	});
   998  
   999  	var keys = Object.keys(proxy);
  1000  	if (keys.length !== 1) {
  1001  		throw new Error("length is "+keys.length);
  1002  	}
  1003  	if (keys[0] !== "foo") {
  1004  		throw new Error("keys[0] is "+keys[0]);
  1005  	}
  1006  	`
  1007  
  1008  	testScript(SCRIPT, _undefined, t)
  1009  }
  1010  
  1011  func TestProxy_target_call(t *testing.T) {
  1012  	const SCRIPT = `
  1013  	var obj = function() {
  1014  		return "test"
  1015  	}
  1016  	
  1017  	var proxy = new Proxy(obj, {});
  1018  
  1019  	proxy();
  1020  	`
  1021  
  1022  	testScript(SCRIPT, asciiString("test"), t)
  1023  }
  1024  
  1025  func TestProxy_proxy_call(t *testing.T) {
  1026  	const SCRIPT = `
  1027  	var obj = function() {
  1028  		return "test"
  1029  	}
  1030  	
  1031  	var proxy = new Proxy(obj, {
  1032  		apply: function(target, thisArg, args) {
  1033  			return "tset"
  1034  		}
  1035  	});
  1036  
  1037  	proxy();
  1038  	`
  1039  
  1040  	testScript(SCRIPT, asciiString("tset"), t)
  1041  }
  1042  
  1043  func TestProxy_target_func_apply(t *testing.T) {
  1044  	const SCRIPT = `
  1045  	var obj = function() {
  1046  		return "test"
  1047  	}
  1048  	
  1049  	var proxy = new Proxy(obj, {});
  1050  
  1051  	proxy.apply();
  1052  	`
  1053  
  1054  	testScript(SCRIPT, asciiString("test"), t)
  1055  }
  1056  
  1057  func TestProxy_proxy_func_apply(t *testing.T) {
  1058  	const SCRIPT = `
  1059  	var obj = function() {
  1060  		return "test"
  1061  	}
  1062  	
  1063  	var proxy = new Proxy(obj, {
  1064  		apply: function(target, thisArg, args) {
  1065  			return "tset"
  1066  		}
  1067  	});
  1068  
  1069  	proxy.apply();
  1070  	`
  1071  
  1072  	testScript(SCRIPT, asciiString("tset"), t)
  1073  }
  1074  
  1075  func TestProxy_target_func_call(t *testing.T) {
  1076  	const SCRIPT = `
  1077  	var obj = function() {
  1078  		return "test"
  1079  	}
  1080  	
  1081  	var proxy = new Proxy(obj, {});
  1082  
  1083  	proxy.call();
  1084  	`
  1085  
  1086  	testScript(SCRIPT, asciiString("test"), t)
  1087  }
  1088  
  1089  func TestProxy_proxy_func_call(t *testing.T) {
  1090  	const SCRIPT = `
  1091  	var obj = function() {
  1092  		return "test"
  1093  	}
  1094  	
  1095  	var proxy = new Proxy(obj, {
  1096  		apply: function(target, thisArg, args) {
  1097  			return "tset"
  1098  		}
  1099  	});
  1100  
  1101  	proxy.call();
  1102  	`
  1103  
  1104  	testScript(SCRIPT, asciiString("tset"), t)
  1105  }
  1106  
  1107  func TestProxy_target_new(t *testing.T) {
  1108  	const SCRIPT = `
  1109  	var obj = function(word) {
  1110  		this.foo = function() {
  1111  			return word;
  1112  		}
  1113  	}
  1114  	
  1115  	var proxy = new Proxy(obj, {});
  1116  
  1117  	var instance = new proxy("test");
  1118  	instance.foo();
  1119  	`
  1120  
  1121  	testScript(SCRIPT, asciiString("test"), t)
  1122  }
  1123  
  1124  func TestProxy_proxy_new(t *testing.T) {
  1125  	const SCRIPT = `
  1126  	var obj = function(word) {
  1127  		this.foo = function() {
  1128  			return word;
  1129  		}
  1130  	}
  1131  	
  1132  	var proxy = new Proxy(obj, {
  1133  		construct: function(target, args, newTarget) {
  1134  			var word = args[0]; 
  1135  			return {
  1136  				foo: function() {
  1137  					return "caught-" + word
  1138  				}
  1139  			}
  1140  		}
  1141  	});
  1142  
  1143  	var instance = new proxy("test");
  1144  	instance.foo();
  1145  	`
  1146  
  1147  	testScript(SCRIPT, asciiString("caught-test"), t)
  1148  }
  1149  
  1150  func TestProxy_Object_native_proxy_ownKeys(t *testing.T) {
  1151  	headers := map[string][]string{
  1152  		"k0": {},
  1153  	}
  1154  	vm := New()
  1155  	proxy := vm.NewProxy(vm.NewObject(), &ProxyTrapConfig{
  1156  		OwnKeys: func(target *Object) (object *Object) {
  1157  			keys := make([]interface{}, 0, len(headers))
  1158  			for k := range headers {
  1159  				keys = append(keys, k)
  1160  			}
  1161  			return vm.ToValue(keys).ToObject(vm)
  1162  		},
  1163  		GetOwnPropertyDescriptor: func(target *Object, prop string) PropertyDescriptor {
  1164  			v, exists := headers[prop]
  1165  			if exists {
  1166  				return PropertyDescriptor{
  1167  					Value:        vm.ToValue(v),
  1168  					Enumerable:   FLAG_TRUE,
  1169  					Configurable: FLAG_TRUE,
  1170  				}
  1171  			}
  1172  			return PropertyDescriptor{}
  1173  		},
  1174  	})
  1175  	vm.Set("headers", proxy)
  1176  	v, err := vm.RunString(`
  1177  		var keys = Object.keys(headers);
  1178  		keys.length === 1 && keys[0] === "k0";
  1179  		`)
  1180  	if err != nil {
  1181  		t.Fatal(err)
  1182  	}
  1183  	if v != valueTrue {
  1184  		t.Fatal("not true", v)
  1185  	}
  1186  }
  1187  
  1188  func TestProxy_proxy_forIn(t *testing.T) {
  1189  	const SCRIPT = `
  1190  	var proto = {
  1191  		a: 2,
  1192  		protoProp: 1
  1193  	}
  1194  	Object.defineProperty(proto, "protoNonEnum", {
  1195  		value: 2,
  1196  		writable: true,
  1197  		configurable: true
  1198  	});
  1199  	var target = Object.create(proto);
  1200  	var proxy = new Proxy(target, {
  1201  		ownKeys: function() {
  1202  			return ["a", "b"];
  1203  		},
  1204  		getOwnPropertyDescriptor: function(target, p) {
  1205  			switch (p) {
  1206  			case "a":
  1207  			case "b":
  1208  				return {
  1209  					value: 42,
  1210  					enumerable: true,
  1211  					configurable: true
  1212  				}
  1213  			}
  1214  		},
  1215  	});
  1216  
  1217  	var forInResult = [];
  1218  	for (var key in proxy) {
  1219  		if (forInResult.indexOf(key) !== -1) {
  1220  			throw new Error("Duplicate property "+key);
  1221  		}
  1222  		forInResult.push(key);
  1223  	}
  1224  	forInResult.length === 3 && forInResult[0] === "a" && forInResult[1] === "b" && forInResult[2] === "protoProp";
  1225  	`
  1226  
  1227  	testScript(SCRIPT, valueTrue, t)
  1228  }
  1229  
  1230  func TestProxyExport(t *testing.T) {
  1231  	vm := New()
  1232  	v, err := vm.RunString(`
  1233  	new Proxy({}, {});
  1234  	`)
  1235  	if err != nil {
  1236  		t.Fatal(err)
  1237  	}
  1238  	v1 := v.Export()
  1239  	if _, ok := v1.(Proxy); !ok {
  1240  		t.Fatalf("Export returned unexpected type: %T", v1)
  1241  	}
  1242  }
  1243  
  1244  func TestProxy_proxy_createTargetNotCallable(t *testing.T) {
  1245  	// from https://github.com/tc39/test262/blob/main/test/built-ins/Proxy/create-target-is-not-callable.js
  1246  	const SCRIPT = `
  1247  	var p = new Proxy({}, {});
  1248  
  1249  	assert.throws(TypeError, function() {
  1250  		  p();
  1251  	});
  1252  	`
  1253  
  1254  	testScriptWithTestLib(SCRIPT, _undefined, t)
  1255  }
  1256  
  1257  func TestProxyEnumerableSymbols(t *testing.T) {
  1258  	const SCRIPT = `
  1259  	var getOwnKeys = [];
  1260  	var ownKeysResult = [Symbol(), "foo", "0"];
  1261  	var proxy = new Proxy({}, {
  1262  	  getOwnPropertyDescriptor: function(_target, key) {
  1263  		getOwnKeys.push(key);
  1264  	  },
  1265  	  ownKeys: function() {
  1266  		return ownKeysResult;
  1267  	  },
  1268  	});
  1269  
  1270  	let {...$} = proxy;
  1271  	compareArray(getOwnKeys, ownKeysResult);
  1272  	`
  1273  
  1274  	testScriptWithTestLib(SCRIPT, valueTrue, t)
  1275  }