blob: 84641d589e8e8f838ccc11d9157d74907c03b9b3 [file] [log] [blame]
Ben Murdoch3fb3ca82011-12-02 17:19:32 +00001// Flags: --harmony-proxies
2
3// Copyright 2008 the V8 project authors. All rights reserved.
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following
12// disclaimer in the documentation and/or other materials provided
13// with the distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived
16// from this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31// TODO(rossberg): test exception cases.
32
33
34// Getters.
35
36function TestGet(handler) {
37 var o = Proxy.create(handler)
38 assertEquals(42, o.a)
39 assertEquals(42, o["b"])
40}
41
42TestGet({
43 get: function(r, k) { return 42 }
44})
45TestGet({
46 get: function(r, k) { return this.get2(r, k) },
47 get2: function(r, k) { return 42 }
48})
49TestGet({
50 getPropertyDescriptor: function(k) { return {value: 42} }
51})
52TestGet({
53 getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
54 getPropertyDescriptor2: function(k) { return {value: 42} }
55})
56TestGet({
57 getPropertyDescriptor: function(k) {
58 return {get value() { return 42 }}
59 }
60})
61TestGet({
62 get: undefined,
63 getPropertyDescriptor: function(k) { return {value: 42} }
64})
65
66TestGet(Proxy.create({
67 get: function(pr, pk) {
68 return function(r, k) { return 42 }
69 }
70}))
71
72
73function TestGetCall(handler) {
74 var p = Proxy.create(handler)
75 assertEquals(55, p.f())
76 assertEquals(55, p.f("unused", "arguments"))
77 assertEquals(55, p.f.call(p))
78 assertEquals(55, p.withargs(45, 5))
79 assertEquals(55, p.withargs.call(p, 11, 22))
80 assertEquals("6655", "66" + p) // calls p.toString
81}
82
83TestGetCall({
84 get: function(r, k) { return function() { return 55 } }
85})
86TestGetCall({
87 get: function(r, k) { return this.get2(r, k) },
88 get2: function(r, k) { return function() { return 55 } }
89})
90TestGetCall({
91 getPropertyDescriptor: function(k) {
92 return {value: function() { return 55 }}
93 }
94})
95TestGetCall({
96 getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
97 getPropertyDescriptor2: function(k) {
98 return {value: function() { return 55 }}
99 }
100})
101TestGetCall({
102 getPropertyDescriptor: function(k) {
103 return {get value() { return function() { return 55 } }}
104 }
105})
106TestGetCall({
107 get: undefined,
108 getPropertyDescriptor: function(k) {
109 return {value: function() { return 55 }}
110 }
111})
112TestGetCall({
113 get: function(r, k) {
114 if (k == "gg") {
115 return function() { return 55 }
116 } else if (k == "withargs") {
117 return function(n, m) { return n + m * 2 }
118 } else {
119 return function() { return this.gg() }
120 }
121 }
122})
123
124TestGetCall(Proxy.create({
125 get: function(pr, pk) {
126 return function(r, k) { return function() { return 55 } }
127 }
128}))
129
130
131
132// Setters.
133
134var key
135var val
136function TestSet(handler) {
137 var o = Proxy.create(handler)
138 assertEquals(42, o.a = 42)
139 assertEquals("a", key)
140 assertEquals(42, val)
141 assertEquals(43, o["b"] = 43)
142 assertEquals("b", key)
143 assertEquals(43, val)
144}
145
146TestSet({
147 set: function(r, k, v) { key = k; val = v; return true }
148})
149TestSet({
150 set: function(r, k, v) { return this.set2(r, k, v) },
151 set2: function(r, k, v) { key = k; val = v; return true }
152})
153TestSet({
154 getOwnPropertyDescriptor: function(k) { return {writable: true} },
155 defineProperty: function(k, desc) { key = k; val = desc.value }
156})
157TestSet({
158 getOwnPropertyDescriptor: function(k) {
159 return this.getOwnPropertyDescriptor2(k)
160 },
161 getOwnPropertyDescriptor2: function(k) { return {writable: true} },
162 defineProperty: function(k, desc) { this.defineProperty2(k, desc) },
163 defineProperty2: function(k, desc) { key = k; val = desc.value }
164})
165TestSet({
166 getOwnPropertyDescriptor: function(k) {
167 return {get writable() { return true }}
168 },
169 defineProperty: function(k, desc) { key = k; val = desc.value }
170})
171TestSet({
172 getOwnPropertyDescriptor: function(k) {
173 return {set: function(v) { key = k; val = v }}
174 }
175})
176TestSet({
177 getOwnPropertyDescriptor: function(k) { return null },
178 getPropertyDescriptor: function(k) { return {writable: true} },
179 defineProperty: function(k, desc) { key = k; val = desc.value }
180})
181TestSet({
182 getOwnPropertyDescriptor: function(k) { return null },
183 getPropertyDescriptor: function(k) {
184 return {get writable() { return true }}
185 },
186 defineProperty: function(k, desc) { key = k; val = desc.value }
187})
188TestSet({
189 getOwnPropertyDescriptor: function(k) { return null },
190 getPropertyDescriptor: function(k) {
191 return {set: function(v) { key = k; val = v }}
192 }
193})
194TestSet({
195 getOwnPropertyDescriptor: function(k) { return null },
196 getPropertyDescriptor: function(k) { return null },
197 defineProperty: function(k, desc) { key = k, val = desc.value }
198})
199
200TestSet(Proxy.create({
201 get: function(pr, pk) {
202 return function(r, k, v) { key = k; val = v; return true }
203 }
204}))
205
206
207
208// Property definition (Object.defineProperty and Object.defineProperties).
209
210var key
211var desc
212function TestDefine(handler) {
213 var o = Proxy.create(handler)
214 assertEquals(o, Object.defineProperty(o, "a", {value: 44}))
215 assertEquals("a", key)
216 assertEquals(1, Object.getOwnPropertyNames(desc).length)
217 assertEquals(44, desc.value)
218
219 assertEquals(o, Object.defineProperty(o, "b", {value: 45, writable: false}))
220 assertEquals("b", key)
221 assertEquals(2, Object.getOwnPropertyNames(desc).length)
222 assertEquals(45, desc.value)
223 assertEquals(false, desc.writable)
224
225 assertEquals(o, Object.defineProperty(o, "c", {value: 46, enumerable: false}))
226 assertEquals("c", key)
227 assertEquals(2, Object.getOwnPropertyNames(desc).length)
228 assertEquals(46, desc.value)
229 assertEquals(false, desc.enumerable)
230
231 var attributes = {configurable: true, mine: 66, minetoo: 23}
232 assertEquals(o, Object.defineProperty(o, "d", attributes))
233 assertEquals("d", key)
234 // Modifying the attributes object after the fact should have no effect.
235 attributes.configurable = false
236 attributes.mine = 77
237 delete attributes.minetoo
238 assertEquals(3, Object.getOwnPropertyNames(desc).length)
239 assertEquals(true, desc.configurable)
240 assertEquals(66, desc.mine)
241 assertEquals(23, desc.minetoo)
242
243 assertEquals(o, Object.defineProperty(o, "e", {get: function(){ return 5 }}))
244 assertEquals("e", key)
245 assertEquals(1, Object.getOwnPropertyNames(desc).length)
246 assertEquals(5, desc.get())
247
248 assertEquals(o, Object.defineProperty(o, "zzz", {}))
249 assertEquals("zzz", key)
250 assertEquals(0, Object.getOwnPropertyNames(desc).length)
251
252// TODO(rossberg): This test requires [s in proxy] to be implemented first.
253// var d = Proxy.create({
254// get: function(r, k) { return (k === "value") ? 77 : void 0 },
255// getOwnPropertyNames: function() { return ["value"] }
256// })
257// assertEquals(1, Object.getOwnPropertyNames(d).length)
258// assertEquals(77, d.value)
259// assertEquals(o, Object.defineProperty(o, "p", d))
260// assertEquals("p", key)
261// assertEquals(1, Object.getOwnPropertyNames(desc).length)
262// assertEquals(77, desc.value)
263
264 var props = {
265 'bla': {},
266 blub: {get: function() { return true }},
267 '': {get value() { return 20 }},
268 last: {value: 21, configurable: true, mine: "eyes"}
269 }
270 Object.defineProperty(props, "hidden", {value: "hidden", enumerable: false})
271 assertEquals(o, Object.defineProperties(o, props))
272 assertEquals("last", key)
273 assertEquals(2, Object.getOwnPropertyNames(desc).length)
274 assertEquals(21, desc.value)
275 assertEquals(true, desc.configurable)
276 assertEquals(undefined, desc.mine) // Arguably a bug in the spec...
277}
278
279TestDefine({
280 defineProperty: function(k, d) { key = k; desc = d; return true }
281})
282TestDefine({
283 defineProperty: function(k, d) { return this.defineProperty2(k, d) },
284 defineProperty2: function(k, d) { key = k; desc = d; return true }
285})
286TestDefine(Proxy.create({
287 get: function(pr, pk) {
288 return function(k, d) { key = k; desc = d; return true }
289 }
290}))
291
292
293
294// Property deletion (delete).
295
296var key
297function TestDelete(handler) {
298 var o = Proxy.create(handler)
299 assertEquals(true, delete o.a)
300 assertEquals("a", key)
301 assertEquals(true, delete o["b"])
302 assertEquals("b", key)
303
304 assertEquals(false, delete o.z1)
305 assertEquals("z1", key)
306 assertEquals(false, delete o["z2"])
307 assertEquals("z2", key);
308
309 (function() {
310 "use strict"
311 assertEquals(true, delete o.c)
312 assertEquals("c", key)
313 assertEquals(true, delete o["d"])
314 assertEquals("d", key)
315
316 assertThrows(function() { delete o.z3 }, TypeError)
317 assertEquals("z3", key)
318 assertThrows(function() { delete o["z4"] }, TypeError)
319 assertEquals("z4", key)
320 })()
321}
322
323TestDelete({
324 'delete': function(k) { key = k; return k < "z" }
325})
326TestDelete({
327 'delete': function(k) { return this.delete2(k) },
328 delete2: function(k) { key = k; return k < "z" }
329})
330TestDelete(Proxy.create({
331 get: function(pr, pk) {
332 return function(k) { key = k; return k < "z" }
333 }
334}))
335
336
337
338// Property descriptors (Object.getOwnPropertyDescriptor).
339
340function TestDescriptor(handler) {
341 var o = Proxy.create(handler)
342 var descs = [
343 {configurable: true},
344 {value: 34, enumerable: true, configurable: true},
345 {value: 3, writable: false, mine: "eyes", configurable: true},
346 {get value() { return 20 }, get configurable() { return true }},
347 {get: function() { "get" }, set: function() { "set" }, configurable: true}
348 ]
349 for (var i = 0; i < descs.length; ++i) {
350 assertEquals(o, Object.defineProperty(o, i, descs[i]))
351 var desc = Object.getOwnPropertyDescriptor(o, i)
352 for (p in descs[i]) {
353 // TODO(rossberg): Ignore user attributes as long as the spec isn't
354 // fixed suitably.
355 if (p != "mine") assertEquals(descs[i][p], desc[p])
356 }
357 assertEquals(undefined, Object.getOwnPropertyDescriptor(o, "absent"))
358 }
359}
360
361
362TestDescriptor({
363 defineProperty: function(k, d) { this["__" + k] = d; return true },
364 getOwnPropertyDescriptor: function(k) { return this["__" + k] }
365})
366TestDescriptor({
367 defineProperty: function(k, d) { this["__" + k] = d; return true },
368 getOwnPropertyDescriptor: function(k) {
369 return this.getOwnPropertyDescriptor2(k)
370 },
371 getOwnPropertyDescriptor2: function(k) { return this["__" + k] }
372})
373
374
375
376// Comparison.
377
378function TestComparison(eq) {
379 var o1 = Proxy.create({})
380 var o2 = Proxy.create({})
381
382 assertTrue(eq(o1, o1))
383 assertTrue(eq(o2, o2))
384 assertTrue(!eq(o1, o2))
385 assertTrue(!eq(o1, {}))
386 assertTrue(!eq({}, o2))
387 assertTrue(!eq({}, {}))
388}
389
390TestComparison(function(o1, o2) { return o1 == o2 })
391TestComparison(function(o1, o2) { return o1 === o2 })
392TestComparison(function(o1, o2) { return !(o1 != o2) })
393TestComparison(function(o1, o2) { return !(o1 !== o2) })
394
395
396
397// Type.
398
399assertEquals("object", typeof Proxy.create({}))
400assertTrue(typeof Proxy.create({}) == "object")
401assertTrue("object" == typeof Proxy.create({}))
402
403// No function proxies yet.
404
405
406
407// Element (in).
408
409var key
410function TestIn(handler) {
411 var o = Proxy.create(handler)
412 assertTrue("a" in o)
413 assertEquals("a", key)
414 assertTrue(99 in o)
415 assertEquals("99", key)
416 assertFalse("z" in o)
417 assertEquals("z", key)
418
419 if ("b" in o) {
420 } else {
421 assertTrue(false)
422 }
423 assertEquals("b", key)
424
425 if ("zz" in o) {
426 assertTrue(false)
427 }
428 assertEquals("zz", key)
429
430 if (!("c" in o)) {
431 assertTrue(false)
432 }
433 assertEquals("c", key)
434
435 if (!("zzz" in o)) {
436 } else {
437 assertTrue(false)
438 }
439 assertEquals("zzz", key)
440}
441
442TestIn({
443 has: function(k) { key = k; return k < "z" }
444})
445TestIn({
446 has: function(k) { return this.has2(k) },
447 has2: function(k) { key = k; return k < "z" }
448})
449TestIn({
450 getPropertyDescriptor: function(k) {
451 key = k; return k < "z" ? {value: 42} : void 0
452 }
453})
454TestIn({
455 getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
456 getPropertyDescriptor2: function(k) {
457 key = k; return k < "z" ? {value: 42} : void 0
458 }
459})
460TestIn({
461 getPropertyDescriptor: function(k) {
462 key = k; return k < "z" ? {get value() { return 42 }} : void 0
463 }
464})
465TestIn({
466 get: undefined,
467 getPropertyDescriptor: function(k) {
468 key = k; return k < "z" ? {value: 42} : void 0
469 }
470})
471
472TestIn(Proxy.create({
473 get: function(pr, pk) {
474 return function(k) { key = k; return k < "z" }
475 }
476}))
477
478
479
480// Instanceof (instanceof).
481
482function TestInstanceof() {
483 var o = {}
484 var p1 = Proxy.create({})
485 var p2 = Proxy.create({}, o)
486 var p3 = Proxy.create({}, p2)
487
488 var f = function() {}
489 f.prototype = o
490 var f1 = function() {}
491 f1.prototype = p1
492 var f2 = function() {}
493 f2.prototype = p2
494
495 assertTrue(o instanceof Object)
496 assertFalse(o instanceof f)
497 assertFalse(o instanceof f1)
498 assertFalse(o instanceof f2)
499 assertFalse(p1 instanceof Object)
500 assertFalse(p1 instanceof f)
501 assertFalse(p1 instanceof f1)
502 assertFalse(p1 instanceof f2)
503 assertTrue(p2 instanceof Object)
504 assertTrue(p2 instanceof f)
505 assertFalse(p2 instanceof f1)
506 assertFalse(p2 instanceof f2)
507 assertTrue(p3 instanceof Object)
508 assertTrue(p3 instanceof f)
509 assertFalse(p3 instanceof f1)
510 assertTrue(p3 instanceof f2)
511}
512
513TestInstanceof()
514
515
516
517// Prototype (Object.getPrototypeOf).
518
519function TestPrototype() {
520 var o = {}
521 var p1 = Proxy.create({})
522 var p2 = Proxy.create({}, o)
523 var p3 = Proxy.create({}, p2)
524 var p4 = Proxy.create({}, 666)
525
526 assertSame(Object.getPrototypeOf(o), Object.prototype)
527 assertSame(Object.getPrototypeOf(p1), null)
528 assertSame(Object.getPrototypeOf(p2), o)
529 assertSame(Object.getPrototypeOf(p3), p2)
530 assertSame(Object.getPrototypeOf(p4), null)
531}
532
533TestPrototype()
534
535
536
537// Property names (Object.getOwnPropertyNames, Object.keys).
538
539function TestPropertyNames(names, handler) {
540 var p = Proxy.create(handler)
541 assertArrayEquals(names, Object.getOwnPropertyNames(p))
542}
543
544TestPropertyNames([], {
545 getOwnPropertyNames: function() { return [] }
546})
547TestPropertyNames(["a", "zz", " ", "0"], {
548 getOwnPropertyNames: function() { return ["a", "zz", " ", 0] }
549})
550TestPropertyNames(["throw", "function "], {
551 getOwnPropertyNames: function() { return this.getOwnPropertyNames2() },
552 getOwnPropertyNames2: function() { return ["throw", "function "] }
553})
554TestPropertyNames(["[object Object]"], {
555 get getOwnPropertyNames() {
556 return function() { return [{}] }
557 }
558})
559
560
561function TestKeys(names, handler) {
562 var p = Proxy.create(handler)
563 assertArrayEquals(names, Object.keys(p))
564}
565
566TestKeys([], {
567 keys: function() { return [] }
568})
569TestKeys(["a", "zz", " ", "0"], {
570 keys: function() { return ["a", "zz", " ", 0] }
571})
572TestKeys(["throw", "function "], {
573 keys: function() { return this.keys2() },
574 keys2: function() { return ["throw", "function "] }
575})
576TestKeys(["[object Object]"], {
577 get keys() {
578 return function() { return [{}] }
579 }
580})
581TestKeys(["a", "0"], {
582 getOwnPropertyNames: function() { return ["a", 23, "zz", "", 0] },
583 getOwnPropertyDescriptor: function(k) { return {enumerable: k.length == 1} }
584})
585TestKeys(["23", "zz", ""], {
586 getOwnPropertyNames: function() { return this.getOwnPropertyNames2() },
587 getOwnPropertyNames2: function() { return ["a", 23, "zz", "", 0] },
588 getOwnPropertyDescriptor: function(k) {
589 return this.getOwnPropertyDescriptor2(k)
590 },
591 getOwnPropertyDescriptor2: function(k) { return {enumerable: k.length != 1} }
592})
593TestKeys(["a", "b", "c", "5"], {
594 get getOwnPropertyNames() {
595 return function() { return ["0", 4, "a", "b", "c", 5] }
596 },
597 get getOwnPropertyDescriptor() {
598 return function(k) { return {enumerable: k >= "44"} }
599 }
600})
601TestKeys([], {
602 get getOwnPropertyNames() {
603 return function() { return ["a", "b", "c"] }
604 },
605 getOwnPropertyDescriptor: function(k) { return {} }
606})
607
608
609
610// Fixing (Object.freeze, Object.seal, Object.preventExtensions,
611// Object.isFrozen, Object.isSealed, Object.isExtensible)
612
613function TestFix(names, handler) {
614 var proto = {p: 77}
615 var assertFixing = function(o, s, f, e) {
616 assertEquals(s, Object.isSealed(o))
617 assertEquals(f, Object.isFrozen(o))
618 assertEquals(e, Object.isExtensible(o))
619 }
620
621 var o1 = Proxy.create(handler, proto)
622 assertFixing(o1, false, false, true)
623 Object.seal(o1)
624 assertFixing(o1, true, names.length === 0, false)
625 assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o1).sort())
626 assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
627 Object.keys(o1).sort())
628 assertEquals(proto, Object.getPrototypeOf(o1))
629 assertEquals(77, o1.p)
630 for (var n in o1) {
631 var desc = Object.getOwnPropertyDescriptor(o1, n)
632 if (desc !== undefined) assertFalse(desc.configurable)
633 }
634
635 var o2 = Proxy.create(handler, proto)
636 assertFixing(o2, false, false, true)
637 Object.freeze(o2)
638 assertFixing(o2, true, true, false)
639 assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o2).sort())
640 assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
641 Object.keys(o2).sort())
642 assertEquals(proto, Object.getPrototypeOf(o2))
643 assertEquals(77, o2.p)
644 for (var n in o2) {
645 var desc = Object.getOwnPropertyDescriptor(o2, n)
646 if (desc !== undefined) assertFalse(desc.writable)
647 if (desc !== undefined) assertFalse(desc.configurable)
648 }
649
650 var o3 = Proxy.create(handler, proto)
651 assertFixing(o3, false, false, true)
652 Object.preventExtensions(o3)
653 assertFixing(o3, names.length === 0, names.length === 0, false)
654 assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o3).sort())
655 assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
656 Object.keys(o3).sort())
657 assertEquals(proto, Object.getPrototypeOf(o3))
658 assertEquals(77, o3.p)
659}
660
661TestFix([], {
662 fix: function() { return {} }
663})
664TestFix(["a", "b", "c", "d", "zz"], {
665 fix: function() {
666 return {
667 a: {value: "a", writable: true, configurable: false, enumerable: true},
668 b: {value: 33, writable: false, configurable: false, enumerable: true},
669 c: {value: 0, writable: true, configurable: true, enumerable: true},
670 d: {value: true, writable: false, configurable: true, enumerable: true},
671 zz: {value: 0, enumerable: false}
672 }
673 }
674})
675TestFix(["a"], {
676 fix: function() { return this.fix2() },
677 fix2: function() {
678 return {a: {value: 4, writable: true, configurable: true, enumerable: true}}
679 }
680})
681TestFix(["b"], {
682 get fix() {
683 return function() {
684 return {b: {configurable: true, writable: true, enumerable: true}}
685 }
686 }
687})