Index: test/mjsunit/harmony/proxies-example-membrane.js |
diff --git a/test/mjsunit/harmony/proxies-example-membrane.js b/test/mjsunit/harmony/proxies-example-membrane.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..817b6359de03ecb16aa3979bc6d604dcacdf2349 |
--- /dev/null |
+++ b/test/mjsunit/harmony/proxies-example-membrane.js |
@@ -0,0 +1,510 @@ |
+// Copyright 2011 the V8 project authors. All rights reserved. |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following |
+// disclaimer in the documentation and/or other materials provided |
+// with the distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived |
+// from this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+// Flags: --harmony |
+ |
+ |
+// A simple no-op handler. Adapted from: |
+// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#examplea_no-op_forwarding_proxy |
Michael Starzinger
2011/10/28 12:08:34
I am not sure about the policy for long URLs in co
|
+ |
+function createHandler(obj) { |
+ return { |
+ getOwnPropertyDescriptor: function(name) { |
+ var desc = Object.getOwnPropertyDescriptor(obj, name); |
+ if (desc !== undefined) desc.configurable = true; |
+ return desc; |
+ }, |
+ getPropertyDescriptor: function(name) { |
+ var desc = Object.getOwnPropertyDescriptor(obj, name); |
+ //var desc = Object.getPropertyDescriptor(obj, name); // not in ES5 |
+ if (desc !== undefined) desc.configurable = true; |
+ return desc; |
+ }, |
+ getOwnPropertyNames: function() { |
+ return Object.getOwnPropertyNames(obj); |
+ }, |
+ getPropertyNames: function() { |
+ return Object.getOwnPropertyNames(obj); |
+ //return Object.getPropertyNames(obj); // not in ES5 |
+ }, |
+ defineProperty: function(name, desc) { |
+ Object.defineProperty(obj, name, desc); |
+ }, |
+ delete: function(name) { |
+ return delete obj[name]; |
+ }, |
+ fix: function() { |
+ if (Object.isFrozen(obj)) { |
+ var result = {}; |
+ Object.getOwnPropertyNames(obj).forEach(function(name) { |
+ result[name] = Object.getOwnPropertyDescriptor(obj, name); |
+ }); |
+ return result; |
+ } |
+ // As long as obj is not frozen, the proxy won't allow itself to be fixed |
+ return undefined; // will cause a TypeError to be thrown |
+ }, |
+ has: function(name) { return name in obj; }, |
+ hasOwn: function(name) { return ({}).hasOwnProperty.call(obj, name); }, |
+ get: function(receiver, name) { return obj[name]; }, |
+ set: function(receiver, name, val) { |
+ obj[name] = val; // bad behavior when set fails in non-strict mode |
+ return true; |
+ }, |
+ enumerate: function() { |
+ var result = []; |
+ for (var name in obj) { result.push(name); }; |
+ return result; |
+ }, |
+ keys: function() { return Object.keys(obj); } |
+ }; |
+} |
+ |
+ |
+ |
+// Auxiliary definitions enabling tracking of object identity in output. |
+ |
+var objectMap = new WeakMap; |
+var objectCounter = 0; |
+ |
+function registerObject(x, s) { |
+ if (x === Object(x) && !objectMap.has(x)) |
+ objectMap.set(x, ++objectCounter + (s == undefined ? "" : ":" + s)); |
+} |
+ |
+registerObject(this, "global"); |
+registerObject(Object.prototype, "Object.prototype"); |
+ |
+function str(x) { |
+ if (x === Object(x)) return "[" + typeof x + " " + objectMap.get(x) + "]"; |
+ if (typeof x == "string") return "\"" + x + "\""; |
+ return "" + x; |
+} |
+ |
+ |
+ |
+// A simple membrane. Adapted from: |
+// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane |
+ |
+function createSimpleMembrane(target) { |
+ var enabled = true; |
+ |
+ function wrap(obj) { |
+ registerObject(obj); |
+ print("wrap enter", str(obj)); |
+ try { |
+ var x = wrap2(obj); |
+ registerObject(x, "wrapped"); |
+ print("wrap exit", str(obj), "as", str(x)); |
+ return x; |
+ } catch(e) { |
+ print("wrap exception", str(e)); |
+ throw e; |
+ } |
+ } |
+ |
+ function wrap2(obj) { |
+ if (obj !== Object(obj)) { |
+ return obj; |
+ } |
+ |
+ function wrapCall(fun, that, args) { |
+ registerObject(that); |
+ print("wrapCall enter", fun, str(that)); |
+ try { |
+ var x = wrapCall2(fun, that, args); |
+ print("wrapCall exit", fun, str(that), "returning", str(x)); |
+ return x; |
+ } catch(e) { |
+ print("wrapCall exception", fun, str(that), str(e)); |
+ throw e; |
+ } |
+ } |
+ |
+ function wrapCall2(fun, that, args) { |
+ if (!enabled) { throw new Error("disabled"); } |
+ try { |
+ return wrap(fun.apply(that, Array.prototype.map.call(args, wrap))); |
+ } catch (e) { |
+ throw wrap(e); |
+ } |
+ } |
+ |
+ var baseHandler = createHandler(obj); |
+ var handler = Proxy.create(Object.freeze({ |
+ get: function(receiver, name) { |
+ return function() { |
+ var arg = (name === "get" || name == "set") ? arguments[1] : ""; |
+ print("handler enter", name, arg); |
+ var x = wrapCall(baseHandler[name], baseHandler, arguments); |
+ print("handler exit", name, arg, "returning", str(x)); |
+ return x; |
+ } |
+ } |
+ })); |
+ registerObject(baseHandler, "basehandler"); |
+ registerObject(handler, "handler"); |
+ |
+ if (typeof obj === "function") { |
+ function callTrap() { |
+ print("call trap enter", str(obj), str(this)); |
+ var x = wrapCall(obj, wrap(this), arguments); |
+ print("call trap exit", str(obj), str(this), "returning", str(x)); |
+ return x; |
+ } |
+ function constructTrap() { |
+ if (!enabled) { throw new Error("disabled"); } |
+ try { |
+ function forward(args) { return obj.apply(this, args) } |
+ return wrap(new forward(Array.prototype.map.call(arguments, wrap))); |
+ } catch (e) { |
+ throw wrap(e); |
+ } |
+ } |
+ return Proxy.createFunction(handler, callTrap, constructTrap); |
+ } else { |
+ var prototype = wrap(Object.getPrototypeOf(obj)); |
+ return Proxy.create(handler, prototype); |
+ } |
+ } |
+ |
+ var gate = Object.freeze({ |
+ enable: function() { enabled = true; }, |
+ disable: function() { enabled = false; } |
+ }); |
+ |
+ return Object.freeze({ |
+ wrapper: wrap(target), |
+ gate: gate |
+ }); |
+} |
+ |
+ |
+var o = { |
+ a: 6, |
+ b: {bb: 8}, |
+ f: function(x) { return x }, |
+ g: function(x) { return x.a }, |
+ h: function(x) { this.q = x } |
+}; |
+o[2] = {c: 7}; |
+var m = createSimpleMembrane(o); |
+var w = m.wrapper; |
+print("w =", str(w)); |
+ |
+var f = w.f; |
+var x = f(66); |
+var x = f({a: 1}); |
+var x = w.f({a: 1}); |
+var a = x.a; |
+assertEquals(6, w.a); |
+assertEquals(8, w.b.bb); |
+assertEquals(7, w[2]["c"]); |
+assertEquals(undefined, w.c); |
+assertEquals(1, w.f(1)); |
+assertEquals(1, w.f({a: 1}).a); |
+assertEquals(2, w.g({a: 2})); |
+assertEquals(3, (w.r = {a: 3}).a); |
+assertEquals(3, w.r.a); |
+assertEquals(3, o.r.a); |
+w.h(3); |
+assertEquals(3, w.q); |
+assertEquals(3, o.q); |
+assertEquals(4, (new w.h(4)).q); |
+ |
+var wb = w.b; |
+var wr = w.r; |
+var wf = w.f; |
+var wf3 = w.f(3); |
+var wfx = w.f({a: 6}); |
+var wgx = w.g({a: {aa: 7}}); |
+var wh4 = new w.h(4); |
+m.gate.disable(); |
+assertEquals(3, wf3); |
+assertThrows(function() { w.a }, Error); |
+assertThrows(function() { w.r }, Error); |
+assertThrows(function() { w.r = {a: 4} }, Error); |
+assertThrows(function() { o.r.a }, Error); |
+assertEquals("object", typeof o.r); |
+assertEquals(5, (o.r = {a: 5}).a); |
+assertEquals(5, o.r.a); |
+assertThrows(function() { w[1] }, Error); |
+assertThrows(function() { w.c }, Error); |
+assertThrows(function() { wb.bb }, Error); |
+assertThrows(function() { wr.a }, Error); |
+assertThrows(function() { wf(4) }, Error); |
+assertThrows(function() { wfx.a }, Error); |
+assertThrows(function() { wgx.aa }, Error); |
+assertThrows(function() { wh4.q }, Error); |
+ |
+m.gate.enable(); |
+assertEquals(6, w.a); |
+assertEquals(5, w.r.a); |
+assertEquals(5, o.r.a); |
+assertEquals(7, w.r = 7); |
+assertEquals(7, w.r); |
+assertEquals(7, o.r); |
+assertEquals(8, w.b.bb); |
+assertEquals(7, w[2]["c"]); |
+assertEquals(undefined, w.c); |
+assertEquals(8, wb.bb); |
+assertEquals(3, wr.a); |
+assertEquals(4, wf(4)); |
+assertEquals(3, wf3); |
+assertEquals(6, wfx.a); |
+assertEquals(7, wgx.aa); |
+assertEquals(4, wh4.q); |
+ |
+ |
+// An identity-preserving membrane. Adapted from: |
+// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving_membrane |
Michael Starzinger
2011/10/28 12:08:34
Likewise.
|
+ |
+function createMembrane(wetTarget) { |
+ var wet2dry = WeakMap(); |
+ var dry2wet = WeakMap(); |
+ |
+ function asDry(obj) { |
+ registerObject(obj) |
+ print("asDry enter", str(obj)) |
+ try { |
+ var x = asDry2(obj); |
+ registerObject(x, "dry"); |
+ print("asDry exit", str(obj), "as", str(x)); |
+ return x; |
+ } catch(e) { |
+ print("asDry exception", str(e)); |
+ throw e; |
+ } |
+ } |
+ function asDry2(wet) { |
+ if (wet !== Object(wet)) { |
+ // primitives provide only irrevocable knowledge, so don't |
+ // bother wrapping it. |
+ return wet; |
+ } |
+ var dryResult = wet2dry.get(wet); |
+ if (dryResult) { return dryResult; } |
+ |
+ var wetHandler = createHandler(wet); |
+ var dryRevokeHandler = Proxy.create(Object.freeze({ |
+ get: function(receiver, name) { |
+ return function() { |
+ var arg = (name === "get" || name == "set") ? arguments[1] : ""; |
+ print("dry handler enter", name, arg); |
+ var optWetHandler = dry2wet.get(dryRevokeHandler); |
+ try { |
+ var x = asDry(optWetHandler[name].apply( |
+ optWetHandler, Array.prototype.map.call(arguments, asWet))); |
+ print("dry handler exit", name, arg, "returning", str(x)); |
+ return x; |
+ } catch (eWet) { |
+ var x = asDry(eWet); |
+ print("dry handler exception", name, arg, "throwing", str(x)); |
+ throw x; |
+ } |
+ }; |
+ } |
+ })); |
+ dry2wet.set(dryRevokeHandler, wetHandler); |
+ |
+ if (typeof wet === "function") { |
+ function callTrap() { |
+ print("dry call trap enter", str(this)); |
+ var x = asDry(wet.apply( |
+ asWet(this), Array.prototype.map.call(arguments, asWet))); |
+ print("dry call trap exit", str(this), "returning", str(x)); |
+ return x; |
+ } |
+ function constructTrap() { |
+ function forward(args) { return wet.apply(this, args) } |
+ return asDry(new forward(Array.prototype.map.call(arguments, asWet))); |
+ } |
+ dryResult = |
+ Proxy.createFunction(dryRevokeHandler, callTrap, constructTrap); |
+ } else { |
+ dryResult = |
+ Proxy.create(dryRevokeHandler, asDry(Object.getPrototypeOf(wet))); |
+ } |
+ wet2dry.set(wet, dryResult); |
+ dry2wet.set(dryResult, wet); |
+ return dryResult; |
+ } |
+ |
+ function asWet(obj) { |
+ registerObject(obj) |
+ print("asWet enter", str(obj)) |
+ try { |
+ var x = asWet2(obj) |
+ registerObject(x, "wet") |
+ print("asWet exit", str(obj), "as", str(x)) |
+ return x |
+ } catch(e) { |
+ print("asWet exception", str(e)) |
+ throw e |
+ } |
+ } |
+ function asWet2(dry) { |
+ if (dry !== Object(dry)) { |
+ // primitives provide only irrevocable knowledge, so don't |
+ // bother wrapping it. |
+ return dry; |
+ } |
+ var wetResult = dry2wet.get(dry); |
+ if (wetResult) { return wetResult; } |
+ |
+ var dryHandler = createHandler(dry); |
+ var wetRevokeHandler = Proxy.create(Object.freeze({ |
+ get: function(receiver, name) { |
+ return function() { |
+ var arg = (name === "get" || name == "set") ? arguments[1] : ""; |
+ print("wet handler enter", name, arg); |
+ var optDryHandler = wet2dry.get(wetRevokeHandler); |
+ try { |
+ var x = asWet(optDryHandler[name].apply( |
+ optDryHandler, Array.prototype.map.call(arguments, asDry))); |
+ print("wet handler exit", name, arg, "returning", str(x)); |
+ return x; |
+ } catch (eDry) { |
+ var x = asWet(eDry); |
+ print("wet handler exception", name, arg, "throwing", str(x)); |
+ throw x; |
+ } |
+ }; |
+ } |
+ })); |
+ wet2dry.set(wetRevokeHandler, dryHandler); |
+ |
+ if (typeof dry === "function") { |
+ function callTrap() { |
+ print("wet call trap enter", str(this)); |
+ var x = asWet(dry.apply( |
+ asDry(this), Array.prototype.map.call(arguments, asDry))); |
+ print("wet call trap exit", str(this), "returning", str(x)); |
+ return x; |
+ } |
+ function constructTrap() { |
+ function forward(args) { return dry.apply(this, args) } |
+ return asWet(new forward(Array.prototype.map.call(arguments, asDry))); |
+ } |
+ wetResult = |
+ Proxy.createFunction(wetRevokeHandler, callTrap, constructTrap); |
+ } else { |
+ wetResult = |
+ Proxy.create(wetRevokeHandler, asWet(Object.getPrototypeOf(dry))); |
+ } |
+ dry2wet.set(dry, wetResult); |
+ wet2dry.set(wetResult, dry); |
+ return wetResult; |
+ } |
+ |
+ var gate = Object.freeze({ |
+ revoke: function() { |
+ dry2wet = wet2dry = Object.freeze({ |
+ get: function(key) { throw new Error("revoked"); }, |
+ set: function(key, val) { throw new Error("revoked"); } |
+ }); |
+ } |
+ }); |
+ |
+ return Object.freeze({ wrapper: asDry(wetTarget), gate: gate }); |
+} |
+ |
+ |
+var receiver |
+var argument |
+var o = { |
+ a: 6, |
+ b: {bb: 8}, |
+ f: function(x) { receiver = this; argument = x; return x }, |
+ g: function(x) { receiver = this; argument = x; return x.a }, |
+ h: function(x) { receiver = this; argument = x; this.q = x }, |
+ s: function(x) { receiver = this; argument = x; this.x = {y: x}; return this } |
+} |
+o[2] = {c: 7} |
+var m = createMembrane(o) |
+var w = m.wrapper |
+print("dry w =", str(w)) |
+ |
+var f = w.f |
+var x = f(66) |
+var x = f({a: 1}) |
+var x = w.f({a: 1}) |
+var a = x.a |
+assertEquals(6, w.a) |
+assertEquals(8, w.b.bb) |
+assertEquals(7, w[2]["c"]) |
+assertEquals(undefined, w.c) |
+assertEquals(1, w.f(1)) |
+assertSame(o, receiver) |
+assertEquals(1, w.f({a: 1}).a) |
+assertSame(o, receiver) |
+assertEquals(2, w.g({a: 2})) |
+assertSame(o, receiver) |
+assertEquals(w, w.f(w)) |
+assertSame(o, receiver) |
+assertSame(o, argument) |
+assertEquals(o, w.f(o)) |
+assertSame(o, receiver) |
+assertSame(o, argument) |
+assertEquals(3, (w.r = {a: 3}).a) |
+assertEquals(3, w.r.a) |
+assertEquals(3, o.r.a) |
+w.h(3) |
+assertEquals(3, w.q) |
+assertEquals(3, o.q) |
+assertEquals(4, (new w.h(4)).q) |
+assertEquals(5, w.s(5).x.y) |
+assertSame(o, receiver) |
+ |
+var wb = w.b |
+var wr = w.r |
+var wf = w.f |
+var wf3 = w.f(3) |
+var wfx = w.f({a: 6}) |
+var wgx = w.g({a: {aa: 7}}) |
+var wh4 = new w.h(4) |
+var ws5 = w.s(5) |
+var ws5x = ws5.x |
+m.gate.revoke() |
+assertEquals(3, wf3) |
+assertThrows(function() { w.a }, Error) |
+assertThrows(function() { w.r }, Error) |
+assertThrows(function() { w.r = {a: 4} }, Error) |
+assertThrows(function() { o.r.a }, Error) |
+assertEquals("object", typeof o.r) |
+assertEquals(5, (o.r = {a: 5}).a) |
+assertEquals(5, o.r.a) |
+assertThrows(function() { w[1] }, Error) |
+assertThrows(function() { w.c }, Error) |
+assertThrows(function() { wb.bb }, Error) |
+assertEquals(3, wr.a) |
+assertThrows(function() { wf(4) }, Error) |
+assertEquals(6, wfx.a) |
+assertEquals(7, wgx.aa) |
+assertThrows(function() { wh4.q }, Error) |
+assertThrows(function() { ws5.x }, Error) |
+assertThrows(function() { ws5x.y }, Error) |