blob: cb3a26c535451c027a7093f3b6ad8a1cffc79c54 [file] [log] [blame]
Ben Murdochda12d292016-06-02 14:46:10 +01001// Copyright 2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Flags: --allow-natives-syntax
29
30
31var handler = {
32 get : function(r, n) { return n == "length" ? 2 : undefined }
33}
34
35
36// Calling (call, Function.prototype.call, Function.prototype.apply,
37// Function.prototype.bind).
38
39var global_object = this
40var receiver
41
42function TestCall(isStrict, callTrap) {
43 assertEquals(42, callTrap(undefined, undefined, [5, 37]))
44
45 var handler = {
46 get: function(r, k) {
47 return k == "length" ? 2 : Function.prototype[k]
48 },
49 apply: callTrap
50 }
51 var f = new Proxy(()=>{}, handler)
52 var o = {f: f}
53 global_object.f = f
54
55 receiver = 333
56 assertEquals(42, f(11, 31))
57 receiver = 333
58 assertEquals(42, o.f(10, 32))
59 assertSame(o, receiver)
60 receiver = 333
61 assertEquals(42, o["f"](9, 33))
62 assertSame(o, receiver)
63 receiver = 333
64 assertEquals(42, (1, o).f(8, 34))
65 assertSame(o, receiver)
66 receiver = 333
67 assertEquals(42, (1, o)["f"](7, 35))
68 assertSame(o, receiver)
69 receiver = 333
70 assertEquals(42, f.call(o, 32, 10))
71 assertSame(o, receiver)
72 receiver = 333
73 assertEquals(42, f.call(undefined, 33, 9))
74 receiver = 333
75 assertEquals(42, f.call(null, 33, 9))
76 receiver = 333
77 assertEquals(44, f.call(2, 21, 23))
78 assertSame(2, receiver.valueOf())
79 receiver = 333
80 assertEquals(42, Function.prototype.call.call(f, o, 20, 22))
81 assertSame(o, receiver)
82 receiver = 333
83 assertEquals(43, Function.prototype.call.call(f, null, 20, 23))
84 assertEquals(44, Function.prototype.call.call(f, 2, 21, 23))
85 assertEquals(2, receiver.valueOf())
86 receiver = 333
87 assertEquals(32, f.apply(o, [16, 16]))
88 assertSame(o, receiver)
89 receiver = 333
90 assertEquals(32, Function.prototype.apply.call(f, o, [17, 15]))
91 assertSame(o, receiver)
92 receiver = 333
93 assertEquals(42, %Call(f, o, 11, 31));
94 assertSame(o, receiver)
95 receiver = 333
96 assertEquals(42, %Call(f, null, 11, 31));
97 receiver = 333
98 assertEquals(42, %_Call(f, o, 11, 31))
99 assertSame(o, receiver)
100 receiver = 333
101 assertEquals(42, %_Call(f, null, 11, 31))
102
103 var ff = Function.prototype.bind.call(f, o, 12)
104 assertTrue(ff.length <= 1) // TODO(rossberg): Not spec'ed yet, be lax.
105 receiver = 333
106 assertEquals(42, ff(30))
107 assertSame(o, receiver)
108 receiver = 333
109 assertEquals(33, Function.prototype.call.call(ff, {}, 21))
110 assertSame(o, receiver)
111 receiver = 333
112 assertEquals(32, Function.prototype.apply.call(ff, {}, [20]))
113 assertSame(o, receiver)
114 receiver = 333
115 assertEquals(23, %Call(ff, {}, 11));
116 assertSame(o, receiver)
117 receiver = 333
118 assertEquals(23, %Call(ff, {}, 11, 3));
119 assertSame(o, receiver)
120 receiver = 333
121 assertEquals(34, %_Call(ff, {}, 22))
122 assertSame(o, receiver)
123 receiver = 333
124 assertEquals(34, %_Call(ff, {}, 22, 3))
125 assertSame(o, receiver)
126
127 var fff = Function.prototype.bind.call(ff, o, 30)
128 assertEquals(0, fff.length)
129 receiver = 333
130 assertEquals(42, fff())
131 assertSame(o, receiver)
132 receiver = 333
133 assertEquals(42, Function.prototype.call.call(fff, {}))
134 assertSame(o, receiver)
135 receiver = 333
136 assertEquals(42, Function.prototype.apply.call(fff, {}))
137 assertSame(o, receiver)
138 receiver = 333
139 assertEquals(42, %Call(fff, {}));
140 assertSame(o, receiver)
141 receiver = 333
142 assertEquals(42, %Call(fff, {}, 11, 3))
143 assertSame(o, receiver)
144 receiver = 333
145 assertEquals(42, %_Call(fff, {}))
146 assertSame(o, receiver)
147 receiver = 333
148 assertEquals(42, %_Call(fff, {}, 3, 4, 5))
149 assertSame(o, receiver)
150
151 var f = new Proxy(()=>{}, {apply: callTrap})
152 receiver = 333
153 assertEquals(42, f(11, 31))
154 var o = {f: f}
155 receiver = 333
156 assertEquals(42, o.f(10, 32))
157 assertSame(o, receiver)
158 receiver = 333
159 assertEquals(42, o["f"](9, 33))
160 assertSame(o, receiver)
161 receiver = 333
162 assertEquals(42, (1, o).f(8, 34))
163 assertSame(o, receiver)
164 receiver = 333
165 assertEquals(42, (1, o)["f"](7, 35))
166 assertSame(o, receiver)
167 receiver = 333
168 assertEquals(42, Function.prototype.call.call(f, o, 20, 22))
169 assertSame(o, receiver)
170 receiver = 333
171 assertEquals(32, Function.prototype.apply.call(f, o, [17, 15]))
172 assertSame(o, receiver)
173 receiver = 333
174 assertEquals(23, %Call(f, o, 11, 12))
175 assertSame(o, receiver)
176 receiver = 333
177 assertEquals(42, %_Call(f, o, 18, 24))
178 assertSame(o, receiver)
179}
180
181TestCall(false, function(_, that, [x, y]) {
182 receiver = that
183 return x + y
184})
185
186TestCall(true, function(_, that, args) {
187 "use strict"
188 receiver = that
189 return args[0] + args[1]
190})
191
192TestCall(false, function() {
193 receiver = arguments[1]
194 return arguments[2][0] + arguments[2][1]
195})
196
197TestCall(false, new Proxy(function(_, that, [x, y]) {
198 receiver = that
199 return x + y
200 }, handler))
201
202TestCall(true, new Proxy(function(_, that, args) {
203 "use strict"
204 receiver = that
205 return args[0] + args[1]
206 }, handler))
207
208TestCall(false, Object.freeze(new Proxy(function(_, that, [x, y]) {
209 receiver = that
210 return x + y
211 }, handler)))
212
213
214
215// Using intrinsics as call traps.
216
217function TestCallIntrinsic(type, callTrap) {
218 var f = new Proxy(()=>{}, {apply: (_, that, args) => callTrap(...args)})
219 var x = f()
220 assertTrue(typeof x == type)
221}
222
223TestCallIntrinsic("boolean", Boolean)
224TestCallIntrinsic("number", Number)
225TestCallIntrinsic("string", String)
226TestCallIntrinsic("object", Object)
227TestCallIntrinsic("function", Function)
228
229
230
231// Throwing from call trap.
232
233function TestCallThrow(callTrap) {
234 var f = new Proxy(()=>{}, {apply: callTrap})
235 assertThrowsEquals(() => f(11), "myexn")
236 assertThrowsEquals(() => ({x: f}).x(11), "myexn")
237 assertThrowsEquals(() => ({x: f})["x"](11), "myexn")
238 assertThrowsEquals(() => Function.prototype.call.call(f, {}, 2), "myexn")
239 assertThrowsEquals(() => Function.prototype.apply.call(f, {}, [1]), "myexn")
240 assertThrowsEquals(() => %Call(f, {}), "myexn")
241 assertThrowsEquals(() => %Call(f, {}, 1, 2), "myexn")
242 assertThrowsEquals(() => %_Call(f, {}), "myexn")
243 assertThrowsEquals(() => %_Call(f, {}, 1, 2), "myexn")
244
245 var f = Object.freeze(new Proxy(()=>{}, {apply: callTrap}))
246 assertThrowsEquals(() => f(11), "myexn")
247 assertThrowsEquals(() => ({x: f}).x(11), "myexn")
248 assertThrowsEquals(() => ({x: f})["x"](11), "myexn")
249 assertThrowsEquals(() => Function.prototype.call.call(f, {}, 2), "myexn")
250 assertThrowsEquals(() => Function.prototype.apply.call(f, {}, [1]), "myexn")
251 assertThrowsEquals(() => %Call(f, {}), "myexn")
252 assertThrowsEquals(() => %Call(f, {}, 1, 2), "myexn")
253 assertThrowsEquals(() => %_Call(f, {}), "myexn")
254 assertThrowsEquals(() => %_Call(f, {}, 1, 2), "myexn")
255}
256
257TestCallThrow(function() { throw "myexn" })
258TestCallThrow(new Proxy(() => {throw "myexn"}, {}))
259TestCallThrow(Object.freeze(new Proxy(() => {throw "myexn"}, {})))
260
261
262
263// Construction (new).
264
265var prototype = {myprop: 0}
266var receiver
267
268var handlerWithPrototype = {
269 get: function(r, n) {
270 if (n == "length") return 2;
271 assertEquals("prototype", n);
272 return prototype;
273 }
274}
275
276var handlerSansPrototype = {
277 get: function(r, n) {
278 if (n == "length") return 2;
279 assertEquals("prototype", n);
280 return undefined;
281 }
282}
283
284function ReturnUndef(_, args, newt) {
285 "use strict";
286 newt.sum = args[0] + args[1];
287}
288
289function ReturnThis(x, y) {
290 "use strict";
291 receiver = this;
292 this.sum = x + y;
293 return this;
294}
295
296function ReturnNew(_, args, newt) {
297 "use strict";
298 return {sum: args[0] + args[1]};
299}
300
301function ReturnNewWithProto(_, args, newt) {
302 "use strict";
303 var result = Object.create(prototype);
304 result.sum = args[0] + args[1];
305 return result;
306}
307
308function TestConstruct(proto, constructTrap) {
309 TestConstruct2(proto, constructTrap, handlerWithPrototype)
310 TestConstruct2(proto, constructTrap, handlerSansPrototype)
311}
312
313function TestConstruct2(proto, constructTrap, handler) {
314 var f = new Proxy(function(){}, {construct: constructTrap})
315 var o = new f(11, 31)
316 assertEquals(42, o.sum)
317 assertSame(proto, Object.getPrototypeOf(o))
318
319 var f = Object.freeze(new Proxy(function(){}, {construct: constructTrap}))
320 var o = new f(11, 32)
321 assertEquals(43, o.sum)
322 assertSame(proto, Object.getPrototypeOf(o))
323}
324
325TestConstruct(Object.prototype, ReturnNew)
326TestConstruct(prototype, ReturnNewWithProto)
327
328TestConstruct(Object.prototype, new Proxy(ReturnNew, {}))
329TestConstruct(prototype, new Proxy(ReturnNewWithProto, {}))
330
331TestConstruct(Object.prototype, Object.freeze(new Proxy(ReturnNew, {})))
332TestConstruct(prototype, Object.freeze(new Proxy(ReturnNewWithProto, {})))
333
334
335
336// Throwing from the construct trap.
337
338function TestConstructThrow(trap) {
339 var f = new Proxy(function(){}, {construct: trap});
340 assertThrowsEquals(() => new f(11), "myexn")
341 Object.freeze(f)
342 assertThrowsEquals(() => new f(11), "myexn")
343}
344
345TestConstructThrow(function() { throw "myexn" })
346TestConstructThrow(new Proxy(function() { throw "myexn" }, {}))
347TestConstructThrow(Object.freeze(new Proxy(function() { throw "myexn" }, {})))
348
349
350
351// Using function proxies as getters and setters.
352
353var value
354var receiver
355
356function TestAccessorCall(getterCallTrap, setterCallTrap) {
357 var pgetter = new Proxy(()=>{}, {apply: getterCallTrap})
358 var psetter = new Proxy(()=>{}, {apply: setterCallTrap})
359
360 var o = {}
361 var oo = Object.create(o)
362 Object.defineProperty(o, "a", {get: pgetter, set: psetter})
363 Object.defineProperty(o, "b", {get: pgetter})
364 Object.defineProperty(o, "c", {set: psetter})
365 Object.defineProperty(o, "3", {get: pgetter, set: psetter})
366 Object.defineProperty(oo, "a", {value: 43})
367
368 receiver = ""
369 assertEquals(42, o.a)
370 assertSame(o, receiver)
371 receiver = ""
372 assertEquals(42, o.b)
373 assertSame(o, receiver)
374 receiver = ""
375 assertEquals(undefined, o.c)
376 assertEquals("", receiver)
377 receiver = ""
378 assertEquals(42, o["a"])
379 assertSame(o, receiver)
380 receiver = ""
381 assertEquals(42, o[3])
382 assertSame(o, receiver)
383
384 receiver = ""
385 assertEquals(43, oo.a)
386 assertEquals("", receiver)
387 receiver = ""
388 assertEquals(42, oo.b)
389 assertSame(oo, receiver)
390 receiver = ""
391 assertEquals(undefined, oo.c)
392 assertEquals("", receiver)
393 receiver = ""
394 assertEquals(43, oo["a"])
395 assertEquals("", receiver)
396 receiver = ""
397 assertEquals(42, oo[3])
398 assertSame(oo, receiver)
399
400 receiver = ""
401 assertEquals(50, o.a = 50)
402 assertSame(o, receiver)
403 assertEquals(50, value)
404 receiver = ""
405 assertEquals(51, o.b = 51)
406 assertEquals("", receiver)
407 assertEquals(50, value) // no setter
408 assertThrows(function() { "use strict"; o.b = 51 }, TypeError)
409 receiver = ""
410 assertEquals(52, o.c = 52)
411 assertSame(o, receiver)
412 assertEquals(52, value)
413 receiver = ""
414 assertEquals(53, o["a"] = 53)
415 assertSame(o, receiver)
416 assertEquals(53, value)
417 receiver = ""
418 assertEquals(54, o[3] = 54)
419 assertSame(o, receiver)
420 assertEquals(54, value)
421
422 value = 0
423 receiver = ""
424 assertEquals(60, oo.a = 60)
425 assertEquals("", receiver)
426 assertEquals(0, value) // oo has own 'a'
427 assertEquals(61, oo.b = 61)
428 assertSame("", receiver)
429 assertEquals(0, value) // no setter
430 assertThrows(function() { "use strict"; oo.b = 61 }, TypeError)
431 receiver = ""
432 assertEquals(62, oo.c = 62)
433 assertSame(oo, receiver)
434 assertEquals(62, value)
435 receiver = ""
436 assertEquals(63, oo["c"] = 63)
437 assertSame(oo, receiver)
438 assertEquals(63, value)
439 receiver = ""
440 assertEquals(64, oo[3] = 64)
441 assertSame(oo, receiver)
442 assertEquals(64, value)
443}
444
445TestAccessorCall(
446 function(_, that) { receiver = that; return 42 },
447 function(_, that, [x]) { receiver = that; value = x }
448)
449
450TestAccessorCall(
451 function(_, that) { "use strict"; receiver = that; return 42 },
452 function(_, that, args) { "use strict"; receiver = that; value = args[0] }
453)
454
455TestAccessorCall(
456 new Proxy(function(_, that) { receiver = that; return 42 }, {}),
457 new Proxy(function(_, that, [x]) { receiver = that; value = x }, {})
458)
459
460TestAccessorCall(
461 Object.freeze(
462 new Proxy(function(_, that) { receiver = that; return 42 }, {})),
463 Object.freeze(
464 new Proxy(function(_, that, [x]) { receiver = that; value = x }, {}))
465)
466
467
468// Passing a proxy function to higher-order library functions.
469
470function TestHigherOrder(f) {
471 assertEquals(6, [6, 2].map(f)[0])
472 assertEquals(4, [5, 2].reduce(f, 4))
473 assertTrue([1, 2].some(f))
474 assertEquals("a.b.c", "a.b.c".replace(".", f))
475}
476
477TestHigherOrder(function(x) { return x })
478TestHigherOrder(function(x) { "use strict"; return x })
479TestHigherOrder(new Proxy(function(x) { return x }, {}))
480TestHigherOrder(Object.freeze(new Proxy(function(x) { return x }, {})))
481
482
483
484// TODO(rossberg): Ultimately, I want to have the following test function
485// run through, but it currently fails on so many cases (some not even
486// involving proxies), that I leave that for later...
487/*
488function TestCalls() {
489 var handler = {
490 get: function(r, k) {
491 return k == "length" ? 2 : Function.prototype[k]
492 }
493 }
494 var bind = Function.prototype.bind
495 var o = {}
496
497 var traps = [
498 function(x, y) {
499 return {receiver: this, result: x + y, strict: false}
500 },
501 function(x, y) { "use strict";
502 return {receiver: this, result: x + y, strict: true}
503 },
504 function() {
505 var x = arguments[0], y = arguments[1]
506 return {receiver: this, result: x + y, strict: false}
507 },
508 Proxy.createFunction(handler, function(x, y) {
509 return {receiver: this, result: x + y, strict: false}
510 }),
511 Proxy.createFunction(handler, function() {
512 var x = arguments[0], y = arguments[1]
513 return {receiver: this, result: x + y, strict: false}
514 }),
515 Proxy.createFunction(handler, function(x, y) { "use strict"
516 return {receiver: this, result: x + y, strict: true}
517 }),
518 CreateFrozen(handler, function(x, y) {
519 return {receiver: this, result: x + y, strict: false}
520 }),
521 CreateFrozen(handler, function(x, y) { "use strict"
522 return {receiver: this, result: x + y, strict: true}
523 }),
524 ]
525 var creates = [
526 function(trap) { return trap },
527 function(trap) { return CreateFrozen({}, callTrap) },
528 function(trap) { return Proxy.createFunction(handler, callTrap) },
529 function(trap) {
530 return Proxy.createFunction(handler, CreateFrozen({}, callTrap))
531 },
532 function(trap) {
533 return Proxy.createFunction(handler, Proxy.createFunction(handler, callTrap))
534 },
535 ]
536 var binds = [
537 function(f, o, x, y) { return f },
538 function(f, o, x, y) { return bind.call(f, o) },
539 function(f, o, x, y) { return bind.call(f, o, x) },
540 function(f, o, x, y) { return bind.call(f, o, x, y) },
541 function(f, o, x, y) { return bind.call(f, o, x, y, 5) },
542 function(f, o, x, y) { return bind.call(bind.call(f, o), {}, x, y) },
543 function(f, o, x, y) { return bind.call(bind.call(f, o, x), {}, y) },
544 function(f, o, x, y) { return bind.call(bind.call(f, o, x, y), {}, 5) },
545 ]
546 var calls = [
547 function(f, x, y) { return f(x, y) },
548 function(f, x, y) { var g = f; return g(x, y) },
549 function(f, x, y) { with ({}) return f(x, y) },
550 function(f, x, y) { var g = f; with ({}) return g(x, y) },
551 function(f, x, y, o) { with (o) return f(x, y) },
552 function(f, x, y, o) { return f.call(o, x, y) },
553 function(f, x, y, o) { return f.apply(o, [x, y]) },
554 function(f, x, y, o) { return Function.prototype.call.call(f, o, x, y) },
555 function(f, x, y, o) { return Function.prototype.apply.call(f, o, [x, y]) },
556 function(f, x, y, o) { return %_Call(f, o, x, y) },
557 function(f, x, y, o) { return %Call(f, o, x, y) },
558 function(f, x, y, o) { return %Apply(f, o, [null, x, y, null], 1, 2) },
559 function(f, x, y, o) { return %Apply(f, o, arguments, 2, 2) },
560 function(f, x, y, o) { if (typeof o == "object") return o.f(x, y) },
561 function(f, x, y, o) { if (typeof o == "object") return o["f"](x, y) },
562 function(f, x, y, o) { if (typeof o == "object") return (1, o).f(x, y) },
563 function(f, x, y, o) { if (typeof o == "object") return (1, o)["f"](x, y) },
564 ]
565 var receivers = [o, global_object, undefined, null, 2, "bla", true]
566 var expectedSloppies = [o, global_object, global_object, global_object]
567
568 for (var t = 0; t < traps.length; ++t) {
569 for (var i = 0; i < creates.length; ++i) {
570 for (var j = 0; j < binds.length; ++j) {
571 for (var k = 0; k < calls.length; ++k) {
572 for (var m = 0; m < receivers.length; ++m) {
573 for (var n = 0; n < receivers.length; ++n) {
574 var bound = receivers[m]
575 var receiver = receivers[n]
576 var func = binds[j](creates[i](traps[t]), bound, 31, 11)
577 var expected = j > 0 ? bound : receiver
578 var expectedSloppy = expectedSloppies[j > 0 ? m : n]
579 o.f = func
580 global_object.f = func
581 var x = calls[k](func, 11, 31, receiver)
582 if (x !== undefined) {
583 assertEquals(42, x.result)
584 if (calls[k].length < 4)
585 assertSame(x.strict ? undefined : global_object, x.receiver)
586 else if (x.strict)
587 assertSame(expected, x.receiver)
588 else if (expectedSloppy === undefined)
589 assertSame(expected, x.receiver.valueOf())
590 else
591 assertSame(expectedSloppy, x.receiver)
592 }
593 }
594 }
595 }
596 }
597 }
598 }
599}
600
601TestCalls()
602*/
603
604var realms = [Realm.create(), Realm.create()];
605Realm.shared = {};
606
607Realm.eval(realms[0], "function f(_, that) { return that; };");
608Realm.eval(realms[0], "Realm.shared.f = f;");
609Realm.eval(realms[0], "Realm.shared.fg = this;");
610Realm.eval(realms[1], "function g(_, that) { return that; };");
611Realm.eval(realms[1], "Realm.shared.g = g;");
612Realm.eval(realms[1], "Realm.shared.gg = this;");
613
614var fp = new Proxy(()=>{}, {apply: Realm.shared.f});
615var gp = new Proxy(()=>{}, {apply: Realm.shared.g});
616
617for (var i = 0; i < 10; i++) {
618 assertEquals(undefined, fp());
619 assertEquals(undefined, gp());
620
621 with (this) {
622 assertEquals(this, fp());
623 assertEquals(this, gp());
624 }
625
626 with ({}) {
627 assertEquals(undefined, fp());
628 assertEquals(undefined, gp());
629 }
630}