Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / v8 / test / mjsunit / harmony / proxies-example-membrane.js
1 // 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: --harmony --harmony-proxies
29
30
31 // A simple no-op handler. Adapted from:
32 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#examplea_no-op_forwarding_proxy
33
34 function createHandler(obj) {
35   return {
36     getOwnPropertyDescriptor: function(name) {
37       var desc = Object.getOwnPropertyDescriptor(obj, name);
38       if (desc !== undefined) desc.configurable = true;
39       return desc;
40     },
41     getPropertyDescriptor: function(name) {
42       var desc = Object.getOwnPropertyDescriptor(obj, name);
43       //var desc = Object.getPropertyDescriptor(obj, name);  // not in ES5
44       if (desc !== undefined) desc.configurable = true;
45       return desc;
46     },
47     getOwnPropertyNames: function() {
48       return Object.getOwnPropertyNames(obj);
49     },
50     getPropertyNames: function() {
51       return Object.getOwnPropertyNames(obj);
52       //return Object.getPropertyNames(obj);  // not in ES5
53     },
54     defineProperty: function(name, desc) {
55       Object.defineProperty(obj, name, desc);
56     },
57     delete: function(name) {
58       return delete obj[name];
59     },
60     fix: function() {
61       if (Object.isFrozen(obj)) {
62         var result = {};
63         Object.getOwnPropertyNames(obj).forEach(function(name) {
64           result[name] = Object.getOwnPropertyDescriptor(obj, name);
65         });
66         return result;
67       }
68       // As long as obj is not frozen, the proxy won't allow itself to be fixed
69       return undefined; // will cause a TypeError to be thrown
70     },
71     has: function(name) { return name in obj; },
72     hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); },
73     get: function(receiver, name) { return obj[name]; },
74     set: function(receiver, name, val) {
75       obj[name] = val;  // bad behavior when set fails in sloppy mode
76       return true;
77     },
78     enumerate: function() {
79       var result = [];
80       for (var name in obj) { result.push(name); };
81       return result;
82     },
83     keys: function() { return Object.keys(obj); }
84   };
85 }
86
87
88
89 // Auxiliary definitions enabling tracking of object identity in output.
90
91 var objectMap = new WeakMap;
92 var objectCounter = 0;
93
94 function registerObject(x, s) {
95   if (x === Object(x) && !objectMap.has(x))
96     objectMap.set(x, ++objectCounter + (s == undefined ? "" : ":" + s));
97 }
98
99 registerObject(this, "global");
100 registerObject(Object.prototype, "Object.prototype");
101
102 function str(x) {
103   if (x === Object(x)) return "[" + typeof x + " " + objectMap.get(x) + "]";
104   if (typeof x == "string") return "\"" + x + "\"";
105   return "" + x;
106 }
107
108
109
110 // A simple membrane. Adapted from:
111 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane
112
113 function createSimpleMembrane(target) {
114   var enabled = true;
115
116   function wrap(obj) {
117     registerObject(obj);
118     print("wrap enter", str(obj));
119     try {
120       var x = wrap2(obj);
121       registerObject(x, "wrapped");
122       print("wrap exit", str(obj), "as", str(x));
123       return x;
124     } catch(e) {
125       print("wrap exception", str(e));
126       throw e;
127     }
128   }
129
130   function wrap2(obj) {
131     if (obj !== Object(obj)) {
132       return obj;
133     }
134
135     function wrapCall(fun, that, args) {
136       registerObject(that);
137       print("wrapCall enter", fun, str(that));
138       try {
139         var x = wrapCall2(fun, that, args);
140         print("wrapCall exit", fun, str(that), "returning", str(x));
141         return x;
142       } catch(e) {
143         print("wrapCall exception", fun, str(that), str(e));
144         throw e;
145       }
146     }
147
148     function wrapCall2(fun, that, args) {
149       if (!enabled) { throw new Error("disabled"); }
150       try {
151         return wrap(fun.apply(that, Array.prototype.map.call(args, wrap)));
152       } catch (e) {
153         throw wrap(e);
154       }
155     }
156
157     var baseHandler = createHandler(obj);
158     var handler = Proxy.create(Object.freeze({
159       get: function(receiver, name) {
160         return function() {
161           var arg = (name === "get" || name == "set") ? arguments[1] : "";
162           print("handler enter", name, arg);
163           var x = wrapCall(baseHandler[name], baseHandler, arguments);
164           print("handler exit", name, arg, "returning", str(x));
165           return x;
166         }
167       }
168     }));
169     registerObject(baseHandler, "basehandler");
170     registerObject(handler, "handler");
171
172     if (typeof obj === "function") {
173       function callTrap() {
174         print("call trap enter", str(obj), str(this));
175         var x = wrapCall(obj, wrap(this), arguments);
176         print("call trap exit", str(obj), str(this), "returning", str(x));
177         return x;
178       }
179       function constructTrap() {
180         if (!enabled) { throw new Error("disabled"); }
181         try {
182           function forward(args) { return obj.apply(this, args) }
183           return wrap(new forward(Array.prototype.map.call(arguments, wrap)));
184         } catch (e) {
185           throw wrap(e);
186         }
187       }
188       return Proxy.createFunction(handler, callTrap, constructTrap);
189     } else {
190       var prototype = wrap(Object.getPrototypeOf(obj));
191       return Proxy.create(handler, prototype);
192     }
193   }
194
195   var gate = Object.freeze({
196     enable: function() { enabled = true; },
197     disable: function() { enabled = false; }
198   });
199
200   return Object.freeze({
201     wrapper: wrap(target),
202     gate: gate
203   });
204 }
205
206
207 var o = {
208   a: 6,
209   b: {bb: 8},
210   f: function(x) { return x },
211   g: function(x) { return x.a },
212   h: function(x) { this.q = x }
213 };
214 o[2] = {c: 7};
215 var m = createSimpleMembrane(o);
216 var w = m.wrapper;
217 print("o =", str(o))
218 print("w =", str(w));
219
220 var f = w.f;
221 var x = f(66);
222 var x = f({a: 1});
223 var x = w.f({a: 1});
224 var a = x.a;
225 assertEquals(6, w.a);
226 assertEquals(8, w.b.bb);
227 assertEquals(7, w[2]["c"]);
228 assertEquals(undefined, w.c);
229 assertEquals(1, w.f(1));
230 assertEquals(1, w.f({a: 1}).a);
231 assertEquals(2, w.g({a: 2}));
232 assertEquals(3, (w.r = {a: 3}).a);
233 assertEquals(3, w.r.a);
234 assertEquals(3, o.r.a);
235 w.h(3);
236 assertEquals(3, w.q);
237 assertEquals(3, o.q);
238 assertEquals(4, (new w.h(4)).q);
239
240 var wb = w.b;
241 var wr = w.r;
242 var wf = w.f;
243 var wf3 = w.f(3);
244 var wfx = w.f({a: 6});
245 var wgx = w.g({a: {aa: 7}});
246 var wh4 = new w.h(4);
247 m.gate.disable();
248 assertEquals(3, wf3);
249 assertThrows(function() { w.a }, Error);
250 assertThrows(function() { w.r }, Error);
251 assertThrows(function() { w.r = {a: 4} }, Error);
252 assertThrows(function() { o.r.a }, Error);
253 assertEquals("object", typeof o.r);
254 assertEquals(5, (o.r = {a: 5}).a);
255 assertEquals(5, o.r.a);
256 assertThrows(function() { w[1] }, Error);
257 assertThrows(function() { w.c }, Error);
258 assertThrows(function() { wb.bb }, Error);
259 assertThrows(function() { wr.a }, Error);
260 assertThrows(function() { wf(4) }, Error);
261 assertThrows(function() { wfx.a }, Error);
262 assertThrows(function() { wgx.aa }, Error);
263 assertThrows(function() { wh4.q }, Error);
264
265 m.gate.enable();
266 assertEquals(6, w.a);
267 assertEquals(5, w.r.a);
268 assertEquals(5, o.r.a);
269 assertEquals(7, w.r = 7);
270 assertEquals(7, w.r);
271 assertEquals(7, o.r);
272 assertEquals(8, w.b.bb);
273 assertEquals(7, w[2]["c"]);
274 assertEquals(undefined, w.c);
275 assertEquals(8, wb.bb);
276 assertEquals(3, wr.a);
277 assertEquals(4, wf(4));
278 assertEquals(3, wf3);
279 assertEquals(6, wfx.a);
280 assertEquals(7, wgx.aa);
281 assertEquals(4, wh4.q);
282
283
284 // An identity-preserving membrane. Adapted from:
285 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving_membrane
286
287 function createMembrane(wetTarget) {
288   var wet2dry = new WeakMap();
289   var dry2wet = new WeakMap();
290
291   function asDry(obj) {
292     registerObject(obj)
293     print("asDry enter", str(obj))
294     try {
295       var x = asDry2(obj);
296       registerObject(x, "dry");
297       print("asDry exit", str(obj), "as", str(x));
298       return x;
299     } catch(e) {
300       print("asDry exception", str(e));
301       throw e;
302     }
303   }
304   function asDry2(wet) {
305     if (wet !== Object(wet)) {
306       // primitives provide only irrevocable knowledge, so don't
307       // bother wrapping it.
308       return wet;
309     }
310     var dryResult = wet2dry.get(wet);
311     if (dryResult) { return dryResult; }
312
313     var wetHandler = createHandler(wet);
314     var dryRevokeHandler = Proxy.create(Object.freeze({
315       get: function(receiver, name) {
316         return function() {
317           var arg = (name === "get" || name == "set") ? arguments[1] : "";
318           print("dry handler enter", name, arg);
319           var optWetHandler = dry2wet.get(dryRevokeHandler);
320           try {
321             var x = asDry(optWetHandler[name].apply(
322               optWetHandler, Array.prototype.map.call(arguments, asWet)));
323             print("dry handler exit", name, arg, "returning", str(x));
324             return x;
325           } catch (eWet) {
326             var x = asDry(eWet);
327             print("dry handler exception", name, arg, "throwing", str(x));
328             throw x;
329           }
330         };
331       }
332     }));
333     dry2wet.set(dryRevokeHandler, wetHandler);
334
335     if (typeof wet === "function") {
336       function callTrap() {
337         print("dry call trap enter", str(this));
338         var x = asDry(wet.apply(
339           asWet(this), Array.prototype.map.call(arguments, asWet)));
340         print("dry call trap exit", str(this), "returning", str(x));
341         return x;
342       }
343       function constructTrap() {
344         function forward(args) { return wet.apply(this, args) }
345         return asDry(new forward(Array.prototype.map.call(arguments, asWet)));
346       }
347       dryResult =
348         Proxy.createFunction(dryRevokeHandler, callTrap, constructTrap);
349     } else {
350       dryResult =
351         Proxy.create(dryRevokeHandler, asDry(Object.getPrototypeOf(wet)));
352     }
353     wet2dry.set(wet, dryResult);
354     dry2wet.set(dryResult, wet);
355     return dryResult;
356   }
357
358   function asWet(obj) {
359     registerObject(obj)
360     print("asWet enter", str(obj))
361     try {
362       var x = asWet2(obj)
363       registerObject(x, "wet")
364       print("asWet exit", str(obj), "as", str(x))
365       return x
366     } catch(e) {
367       print("asWet exception", str(e))
368       throw e
369     }
370   }
371   function asWet2(dry) {
372     if (dry !== Object(dry)) {
373       // primitives provide only irrevocable knowledge, so don't
374       // bother wrapping it.
375       return dry;
376     }
377     var wetResult = dry2wet.get(dry);
378     if (wetResult) { return wetResult; }
379
380     var dryHandler = createHandler(dry);
381     var wetRevokeHandler = Proxy.create(Object.freeze({
382       get: function(receiver, name) {
383         return function() {
384           var arg = (name === "get" || name == "set") ? arguments[1] : "";
385           print("wet handler enter", name, arg);
386           var optDryHandler = wet2dry.get(wetRevokeHandler);
387           try {
388             var x = asWet(optDryHandler[name].apply(
389               optDryHandler, Array.prototype.map.call(arguments, asDry)));
390             print("wet handler exit", name, arg, "returning", str(x));
391             return x;
392           } catch (eDry) {
393             var x = asWet(eDry);
394             print("wet handler exception", name, arg, "throwing", str(x));
395             throw x;
396           }
397         };
398       }
399     }));
400     wet2dry.set(wetRevokeHandler, dryHandler);
401
402     if (typeof dry === "function") {
403       function callTrap() {
404         print("wet call trap enter", str(this));
405         var x = asWet(dry.apply(
406           asDry(this), Array.prototype.map.call(arguments, asDry)));
407         print("wet call trap exit", str(this), "returning", str(x));
408         return x;
409       }
410       function constructTrap() {
411         function forward(args) { return dry.apply(this, args) }
412         return asWet(new forward(Array.prototype.map.call(arguments, asDry)));
413       }
414       wetResult =
415         Proxy.createFunction(wetRevokeHandler, callTrap, constructTrap);
416     } else {
417       wetResult =
418         Proxy.create(wetRevokeHandler, asWet(Object.getPrototypeOf(dry)));
419     }
420     dry2wet.set(dry, wetResult);
421     wet2dry.set(wetResult, dry);
422     return wetResult;
423   }
424
425   var gate = Object.freeze({
426     revoke: function() {
427       dry2wet = wet2dry = Object.freeze({
428         get: function(key) { throw new Error("revoked"); },
429         set: function(key, val) { throw new Error("revoked"); }
430       });
431     }
432   });
433
434   return Object.freeze({ wrapper: asDry(wetTarget), gate: gate });
435 }
436
437
438 var receiver
439 var argument
440 var o = {
441   a: 6,
442   b: {bb: 8},
443   f: function(x) { receiver = this; argument = x; return x },
444   g: function(x) { receiver = this; argument = x; return x.a },
445   h: function(x) { receiver = this; argument = x; this.q = x },
446   s: function(x) { receiver = this; argument = x; this.x = {y: x}; return this }
447 }
448 o[2] = {c: 7}
449 var m = createMembrane(o)
450 var w = m.wrapper
451 print("o =", str(o))
452 print("w =", str(w))
453
454 var f = w.f
455 var x = f(66)
456 var x = f({a: 1})
457 var x = w.f({a: 1})
458 var a = x.a
459 assertEquals(6, w.a)
460 assertEquals(8, w.b.bb)
461 assertEquals(7, w[2]["c"])
462 assertEquals(undefined, w.c)
463 assertEquals(1, w.f(1))
464 assertSame(o, receiver)
465 assertEquals(1, w.f({a: 1}).a)
466 assertSame(o, receiver)
467 assertEquals(2, w.g({a: 2}))
468 assertSame(o, receiver)
469 assertSame(w, w.f(w))
470 assertSame(o, receiver)
471 assertSame(o, argument)
472 assertSame(o, w.f(o))
473 assertSame(o, receiver)
474 // Note that argument !== o, since o isn't dry, so gets wrapped wet again.
475 assertEquals(3, (w.r = {a: 3}).a)
476 assertEquals(3, w.r.a)
477 assertEquals(3, o.r.a)
478 w.h(3)
479 assertEquals(3, w.q)
480 assertEquals(3, o.q)
481 assertEquals(4, (new w.h(4)).q)
482 assertEquals(5, w.s(5).x.y)
483 assertSame(o, receiver)
484
485 var wb = w.b
486 var wr = w.r
487 var wf = w.f
488 var wf3 = w.f(3)
489 var wfx = w.f({a: 6})
490 var wgx = w.g({a: {aa: 7}})
491 var wh4 = new w.h(4)
492 var ws5 = w.s(5)
493 var ws5x = ws5.x
494 m.gate.revoke()
495 assertEquals(3, wf3)
496 assertThrows(function() { w.a }, Error)
497 assertThrows(function() { w.r }, Error)
498 assertThrows(function() { w.r = {a: 4} }, Error)
499 assertThrows(function() { o.r.a }, Error)
500 assertEquals("object", typeof o.r)
501 assertEquals(5, (o.r = {a: 5}).a)
502 assertEquals(5, o.r.a)
503 assertThrows(function() { w[1] }, Error)
504 assertThrows(function() { w.c }, Error)
505 assertThrows(function() { wb.bb }, Error)
506 assertEquals(3, wr.a)
507 assertThrows(function() { wf(4) }, Error)
508 assertEquals(6, wfx.a)
509 assertEquals(7, wgx.aa)
510 assertThrows(function() { wh4.q }, Error)
511 assertThrows(function() { ws5.x }, Error)
512 assertThrows(function() { ws5x.y }, Error)