| OLD | NEW |
| 1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | 27 |
| 28 // Flags: --harmony --harmony-proxies | 28 // Flags: --harmony --harmony-proxies |
| 29 | 29 |
| 30 | 30 |
| 31 // A simple no-op handler. Adapted from: | |
| 32 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#examplea_no-op_forward
ing_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 keys: function() { return Object.keys(obj); } | |
| 79 }; | |
| 80 } | |
| 81 | |
| 82 | |
| 83 | |
| 84 // Auxiliary definitions enabling tracking of object identity in output. | |
| 85 | |
| 86 var objectMap = new WeakMap; | |
| 87 var objectCounter = 0; | |
| 88 | |
| 89 function registerObject(x, s) { | |
| 90 if (x === Object(x) && !objectMap.has(x)) | |
| 91 objectMap.set(x, ++objectCounter + (s == undefined ? "" : ":" + s)); | |
| 92 } | |
| 93 | |
| 94 registerObject(this, "global"); | |
| 95 registerObject(Object.prototype, "Object.prototype"); | |
| 96 | |
| 97 function str(x) { | |
| 98 if (x === Object(x)) return "[" + typeof x + " " + objectMap.get(x) + "]"; | |
| 99 if (typeof x == "string") return "\"" + x + "\""; | |
| 100 return "" + x; | |
| 101 } | |
| 102 | |
| 103 | |
| 104 | 31 |
| 105 // A simple membrane. Adapted from: | 32 // A simple membrane. Adapted from: |
| 106 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane | 33 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane |
| 107 | 34 |
| 108 function createSimpleMembrane(target) { | 35 function createSimpleMembrane(target) { |
| 109 var enabled = true; | 36 let enabled = true; |
| 110 | 37 |
| 111 function wrap(obj) { | 38 function wrap(obj) { |
| 112 registerObject(obj); | 39 if (obj !== Object(obj)) return obj; |
| 113 print("wrap enter", str(obj)); | 40 |
| 114 try { | 41 let handler = new Proxy({}, {get: function(_, key) { |
| 115 var x = wrap2(obj); | 42 if (!enabled) throw new Error("disabled"); |
| 116 registerObject(x, "wrapped"); | 43 switch (key) { |
| 117 print("wrap exit", str(obj), "as", str(x)); | 44 case "apply": |
| 118 return x; | 45 return (_, that, args) => { |
| 119 } catch(e) { | 46 try { |
| 120 print("wrap exception", str(e)); | 47 return wrap(Reflect.apply( |
| 121 throw e; | 48 obj, wrap(that), args.map((x) => wrap(x)))); |
| 122 } | 49 } catch(e) { |
| 50 throw wrap(e); |
| 51 } |
| 52 } |
| 53 case "construct": |
| 54 return (_, args, newt) => { |
| 55 try { |
| 56 return wrap(Reflect.construct( |
| 57 obj, args.map((x) => wrap(x)), wrap(newt))); |
| 58 } catch(e) { |
| 59 throw wrap(e); |
| 60 } |
| 61 } |
| 62 default: |
| 63 return (_, ...args) => { |
| 64 try { |
| 65 return wrap(Reflect[key](obj, ...(args.map(wrap)))); |
| 66 } catch(e) { |
| 67 throw wrap(e); |
| 68 } |
| 69 } |
| 70 } |
| 71 }}); |
| 72 |
| 73 return new Proxy(obj, handler); |
| 123 } | 74 } |
| 124 | 75 |
| 125 function wrap2(obj) { | 76 const gate = Object.freeze({ |
| 126 if (obj !== Object(obj)) { | 77 enable: () => enabled = true, |
| 127 return obj; | 78 disable: () => enabled = false |
| 128 } | |
| 129 | |
| 130 function wrapCall(fun, that, args) { | |
| 131 registerObject(that); | |
| 132 print("wrapCall enter", fun, str(that)); | |
| 133 try { | |
| 134 var x = wrapCall2(fun, that, args); | |
| 135 print("wrapCall exit", fun, str(that), "returning", str(x)); | |
| 136 return x; | |
| 137 } catch(e) { | |
| 138 print("wrapCall exception", fun, str(that), str(e)); | |
| 139 throw e; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 function wrapCall2(fun, that, args) { | |
| 144 if (!enabled) { throw new Error("disabled"); } | |
| 145 try { | |
| 146 return wrap(fun.apply(that, Array.prototype.map.call(args, wrap))); | |
| 147 } catch (e) { | |
| 148 throw wrap(e); | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 var baseHandler = createHandler(obj); | |
| 153 var handler = new Proxy({}, Object.freeze({ | |
| 154 get: function(receiver, name) { | |
| 155 return function() { | |
| 156 var arg = (name === "get" || name == "set") ? arguments[1] : ""; | |
| 157 print("handler enter", name, arg); | |
| 158 var x = wrapCall(baseHandler[name], baseHandler, arguments); | |
| 159 print("handler exit", name, arg, "returning", str(x)); | |
| 160 return x; | |
| 161 } | |
| 162 } | |
| 163 })); | |
| 164 registerObject(baseHandler, "basehandler"); | |
| 165 registerObject(handler, "handler"); | |
| 166 | |
| 167 if (typeof obj === "function") { | |
| 168 function callTrap() { | |
| 169 print("call trap enter", str(obj), str(this)); | |
| 170 var x = wrapCall(obj, wrap(this), arguments); | |
| 171 print("call trap exit", str(obj), str(this), "returning", str(x)); | |
| 172 return x; | |
| 173 } | |
| 174 function constructTrap() { | |
| 175 if (!enabled) { throw new Error("disabled"); } | |
| 176 try { | |
| 177 function forward(args) { return obj.apply(this, args) } | |
| 178 return wrap(new forward(Array.prototype.map.call(arguments, wrap))); | |
| 179 } catch (e) { | |
| 180 throw wrap(e); | |
| 181 } | |
| 182 } | |
| 183 return Proxy.createFunction(handler, callTrap, constructTrap); | |
| 184 } else { | |
| 185 var prototype = wrap(Object.getPrototypeOf(obj)); | |
| 186 return new Proxy(prototype, handler); | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 var gate = Object.freeze({ | |
| 191 enable: function() { enabled = true; }, | |
| 192 disable: function() { enabled = false; } | |
| 193 }); | 79 }); |
| 194 | 80 |
| 195 return Object.freeze({ | 81 return Object.freeze({ |
| 196 wrapper: wrap(target), | 82 wrapper: wrap(target), |
| 197 gate: gate | 83 gate: gate |
| 198 }); | 84 }); |
| 199 } | 85 } |
| 200 | 86 |
| 201 | 87 |
| 202 var o = { | 88 // Test the simple membrane. |
| 203 a: 6, | 89 { |
| 204 b: {bb: 8}, | 90 var o = { |
| 205 f: function(x) { return x }, | 91 a: 6, |
| 206 g: function(x) { return x.a }, | 92 b: {bb: 8}, |
| 207 h: function(x) { this.q = x } | 93 f: function(x) { return x }, |
| 208 }; | 94 g: function(x) { return x.a }, |
| 209 o[2] = {c: 7}; | 95 h: function(x) { this.q = x } |
| 210 var m = createSimpleMembrane(o); | 96 }; |
| 211 var w = m.wrapper; | 97 o[2] = {c: 7}; |
| 212 print("o =", str(o)) | 98 var m = createSimpleMembrane(o); |
| 213 print("w =", str(w)); | 99 var w = m.wrapper; |
| 214 | 100 var f = w.f; |
| 215 var f = w.f; | 101 var x = f(66); |
| 216 var x = f(66); | 102 var x = f({a: 1}); |
| 217 var x = f({a: 1}); | 103 var x = w.f({a: 1}); |
| 218 var x = w.f({a: 1}); | 104 var a = x.a; |
| 219 var a = x.a; | 105 assertEquals(6, w.a); |
| 220 assertEquals(6, w.a); | 106 assertEquals(8, w.b.bb); |
| 221 assertEquals(8, w.b.bb); | 107 assertEquals(7, w[2]["c"]); |
| 222 assertEquals(7, w[2]["c"]); | 108 assertEquals(undefined, w.c); |
| 223 assertEquals(undefined, w.c); | 109 assertEquals(1, w.f(1)); |
| 224 assertEquals(1, w.f(1)); | 110 assertEquals(1, w.f({a: 1}).a); |
| 225 assertEquals(1, w.f({a: 1}).a); | 111 assertEquals(2, w.g({a: 2})); |
| 226 assertEquals(2, w.g({a: 2})); | 112 assertEquals(3, (w.r = {a: 3}).a); |
| 227 assertEquals(3, (w.r = {a: 3}).a); | 113 assertEquals(3, w.r.a); |
| 228 assertEquals(3, w.r.a); | 114 assertEquals(3, o.r.a); |
| 229 assertEquals(3, o.r.a); | 115 w.h(3); |
| 230 w.h(3); | 116 assertEquals(3, w.q); |
| 231 assertEquals(3, w.q); | 117 assertEquals(3, o.q); |
| 232 assertEquals(3, o.q); | 118 assertEquals(4, (new w.h(4)).q); |
| 233 assertEquals(4, (new w.h(4)).q); | 119 |
| 234 | 120 var wb = w.b; |
| 235 var wb = w.b; | 121 var wr = w.r; |
| 236 var wr = w.r; | 122 var wf = w.f; |
| 237 var wf = w.f; | 123 var wf3 = w.f(3); |
| 238 var wf3 = w.f(3); | 124 var wfx = w.f({a: 6}); |
| 239 var wfx = w.f({a: 6}); | 125 var wgx = w.g({a: {aa: 7}}); |
| 240 var wgx = w.g({a: {aa: 7}}); | 126 var wh4 = new w.h(4); |
| 241 var wh4 = new w.h(4); | 127 m.gate.disable(); |
| 242 m.gate.disable(); | 128 assertEquals(3, wf3); |
| 243 assertEquals(3, wf3); | 129 assertThrows(function() { w.a }, Error); |
| 244 assertThrows(function() { w.a }, Error); | 130 assertThrows(function() { w.r }, Error); |
| 245 assertThrows(function() { w.r }, Error); | 131 assertThrows(function() { w.r = {a: 4} }, Error); |
| 246 assertThrows(function() { w.r = {a: 4} }, Error); | 132 assertThrows(function() { o.r.a }, Error); |
| 247 assertThrows(function() { o.r.a }, Error); | 133 assertEquals("object", typeof o.r); |
| 248 assertEquals("object", typeof o.r); | 134 assertEquals(5, (o.r = {a: 5}).a); |
| 249 assertEquals(5, (o.r = {a: 5}).a); | 135 assertEquals(5, o.r.a); |
| 250 assertEquals(5, o.r.a); | 136 assertThrows(function() { w[1] }, Error); |
| 251 assertThrows(function() { w[1] }, Error); | 137 assertThrows(function() { w.c }, Error); |
| 252 assertThrows(function() { w.c }, Error); | 138 assertThrows(function() { wb.bb }, Error); |
| 253 assertThrows(function() { wb.bb }, Error); | 139 assertThrows(function() { wr.a }, Error); |
| 254 assertThrows(function() { wr.a }, Error); | 140 assertThrows(function() { wf(4) }, Error); |
| 255 assertThrows(function() { wf(4) }, Error); | 141 assertThrows(function() { wfx.a }, Error); |
| 256 assertThrows(function() { wfx.a }, Error); | 142 assertThrows(function() { wgx.aa }, Error); |
| 257 assertThrows(function() { wgx.aa }, Error); | 143 assertThrows(function() { wh4.q }, Error); |
| 258 assertThrows(function() { wh4.q }, Error); | 144 |
| 259 | 145 m.gate.enable(); |
| 260 m.gate.enable(); | 146 assertEquals(6, w.a); |
| 261 assertEquals(6, w.a); | 147 assertEquals(5, w.r.a); |
| 262 assertEquals(5, w.r.a); | 148 assertEquals(5, o.r.a); |
| 263 assertEquals(5, o.r.a); | 149 assertEquals(7, w.r = 7); |
| 264 assertEquals(7, w.r = 7); | 150 assertEquals(7, w.r); |
| 265 assertEquals(7, w.r); | 151 assertEquals(7, o.r); |
| 266 assertEquals(7, o.r); | 152 assertEquals(8, w.b.bb); |
| 267 assertEquals(8, w.b.bb); | 153 assertEquals(7, w[2]["c"]); |
| 268 assertEquals(7, w[2]["c"]); | 154 assertEquals(undefined, w.c); |
| 269 assertEquals(undefined, w.c); | 155 assertEquals(8, wb.bb); |
| 270 assertEquals(8, wb.bb); | 156 assertEquals(3, wr.a); |
| 271 assertEquals(3, wr.a); | 157 assertEquals(4, wf(4)); |
| 272 assertEquals(4, wf(4)); | 158 assertEquals(3, wf3); |
| 273 assertEquals(3, wf3); | 159 assertEquals(6, wfx.a); |
| 274 assertEquals(6, wfx.a); | 160 assertEquals(7, wgx.aa); |
| 275 assertEquals(7, wgx.aa); | 161 assertEquals(4, wh4.q); |
| 276 assertEquals(4, wh4.q); | 162 } |
| 163 |
| 277 | 164 |
| 278 | 165 |
| 279 // An identity-preserving membrane. Adapted from: | 166 // An identity-preserving membrane. Adapted from: |
| 280 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving
_membrane | 167 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving
_membrane |
| 281 | 168 |
| 282 function createMembrane(wetTarget) { | 169 function createMembrane(target) { |
| 283 var wet2dry = new WeakMap(); | 170 const wet2dry = 0; |
| 284 var dry2wet = new WeakMap(); | 171 const dry2wet = 1; |
| 285 | 172 |
| 286 function asDry(obj) { | 173 function flip(dir) { return (dir + 1) % 2 } |
| 287 registerObject(obj) | 174 |
| 288 print("asDry enter", str(obj)) | 175 let maps = [new WeakMap(), new WeakMap()]; |
| 289 try { | 176 |
| 290 var x = asDry2(obj); | 177 let revoked = false; |
| 291 registerObject(x, "dry"); | 178 |
| 292 print("asDry exit", str(obj), "as", str(x)); | 179 function wrap(dir, obj) { |
| 293 return x; | 180 if (obj !== Object(obj)) return obj; |
| 294 } catch(e) { | 181 |
| 295 print("asDry exception", str(e)); | 182 let wrapper = maps[dir].get(obj); |
| 296 throw e; | 183 if (wrapper) return wrapper; |
| 297 } | 184 |
| 185 let handler = new Proxy({}, {get: function(_, key) { |
| 186 if (revoked) throw new Error("revoked"); |
| 187 switch (key) { |
| 188 case "apply": |
| 189 return (_, that, args) => { |
| 190 try { |
| 191 return wrap(dir, Reflect.apply( |
| 192 obj, wrap(flip(dir), that), |
| 193 args.map((x) => wrap(flip(dir), x)))); |
| 194 } catch(e) { |
| 195 throw wrap(dir, e); |
| 196 } |
| 197 } |
| 198 case "construct": |
| 199 return (_, args, newt) => { |
| 200 try { |
| 201 return wrap(dir, Reflect.construct( |
| 202 obj, args.map((x) => wrap(flip(dir), x)), |
| 203 wrap(flip(dir), newt))); |
| 204 } catch(e) { |
| 205 throw wrap(dir, e); |
| 206 } |
| 207 } |
| 208 default: |
| 209 return (_, ...args) => { |
| 210 try { |
| 211 return wrap(dir, Reflect[key]( |
| 212 obj, ...(args.map((x) => wrap(flip(dir), x))))) |
| 213 } catch(e) { |
| 214 throw wrap(dir, e); |
| 215 } |
| 216 } |
| 217 } |
| 218 }}); |
| 219 |
| 220 wrapper = new Proxy(obj, handler); |
| 221 maps[dir].set(obj, wrapper); |
| 222 maps[flip(dir)].set(wrapper, obj); |
| 223 return wrapper; |
| 298 } | 224 } |
| 299 function asDry2(wet) { | 225 |
| 300 if (wet !== Object(wet)) { | 226 const gate = Object.freeze({ |
| 301 // primitives provide only irrevocable knowledge, so don't | 227 revoke: () => revoked = true |
| 302 // bother wrapping it. | 228 }); |
| 303 return wet; | 229 |
| 304 } | 230 return Object.freeze({ |
| 305 var dryResult = wet2dry.get(wet); | 231 wrapper: wrap(wet2dry, target), |
| 306 if (dryResult) { return dryResult; } | 232 gate: gate |
| 307 | 233 }); |
| 308 var wetHandler = createHandler(wet); | 234 } |
| 309 var dryRevokeHandler = new Proxy({}, Object.freeze({ | 235 |
| 310 get: function(receiver, name) { | 236 |
| 311 return function() { | 237 // Test the identity-preserving membrane. |
| 312 var arg = (name === "get" || name == "set") ? arguments[1] : ""; | 238 { |
| 313 print("dry handler enter", name, arg); | 239 var receiver |
| 314 var optWetHandler = dry2wet.get(dryRevokeHandler); | 240 var argument |
| 315 try { | 241 var o = { |
| 316 var x = asDry(optWetHandler[name].apply( | 242 a: 6, |
| 317 optWetHandler, Array.prototype.map.call(arguments, asWet))); | 243 b: {bb: 8}, |
| 318 print("dry handler exit", name, arg, "returning", str(x)); | 244 f: function(x) {receiver = this; argument = x; return x}, |
| 319 return x; | 245 g: function(x) {receiver = this; argument = x; return x.a}, |
| 320 } catch (eWet) { | 246 h: function(x) {receiver = this; argument = x; this.q = x}, |
| 321 var x = asDry(eWet); | 247 s: function(x) {receiver = this; argument = x; this.x = {y: x}; return this} |
| 322 print("dry handler exception", name, arg, "throwing", str(x)); | |
| 323 throw x; | |
| 324 } | |
| 325 }; | |
| 326 } | |
| 327 })); | |
| 328 dry2wet.set(dryRevokeHandler, wetHandler); | |
| 329 | |
| 330 if (typeof wet === "function") { | |
| 331 function callTrap() { | |
| 332 print("dry call trap enter", str(this)); | |
| 333 var x = asDry(wet.apply( | |
| 334 asWet(this), Array.prototype.map.call(arguments, asWet))); | |
| 335 print("dry call trap exit", str(this), "returning", str(x)); | |
| 336 return x; | |
| 337 } | |
| 338 function constructTrap() { | |
| 339 function forward(args) { return wet.apply(this, args) } | |
| 340 return asDry(new forward(Array.prototype.map.call(arguments, asWet))); | |
| 341 } | |
| 342 dryResult = | |
| 343 Proxy.createFunction(dryRevokeHandler, callTrap, constructTrap); | |
| 344 } else { | |
| 345 dryResult = | |
| 346 new Proxy(asDry(Object.getPrototypeOf(wet)), dryRevokeHandler); | |
| 347 } | |
| 348 wet2dry.set(wet, dryResult); | |
| 349 dry2wet.set(dryResult, wet); | |
| 350 return dryResult; | |
| 351 } | 248 } |
| 352 | 249 o[2] = {c: 7} |
| 353 function asWet(obj) { | 250 var m = createMembrane(o) |
| 354 registerObject(obj) | 251 var w = m.wrapper |
| 355 print("asWet enter", str(obj)) | 252 var f = w.f |
| 356 try { | 253 var x = f(66) |
| 357 var x = asWet2(obj) | 254 var x = f({a: 1}) |
| 358 registerObject(x, "wet") | 255 var x = w.f({a: 1}) |
| 359 print("asWet exit", str(obj), "as", str(x)) | 256 var a = x.a |
| 360 return x | 257 assertEquals(6, w.a) |
| 361 } catch(e) { | 258 assertEquals(8, w.b.bb) |
| 362 print("asWet exception", str(e)) | 259 assertEquals(7, w[2]["c"]) |
| 363 throw e | 260 assertEquals(undefined, w.c) |
| 364 } | 261 assertEquals(1, w.f(1)) |
| 365 } | 262 assertSame(o, receiver) |
| 366 function asWet2(dry) { | 263 assertEquals(1, w.f({a: 1}).a) |
| 367 if (dry !== Object(dry)) { | 264 assertSame(o, receiver) |
| 368 // primitives provide only irrevocable knowledge, so don't | 265 assertEquals(2, w.g({a: 2})) |
| 369 // bother wrapping it. | 266 assertSame(o, receiver) |
| 370 return dry; | 267 assertSame(w, w.f(w)) |
| 371 } | 268 assertSame(o, receiver) |
| 372 var wetResult = dry2wet.get(dry); | 269 assertSame(o, argument) |
| 373 if (wetResult) { return wetResult; } | 270 assertSame(o, w.f(o)) |
| 374 | 271 assertSame(o, receiver) |
| 375 var dryHandler = createHandler(dry); | 272 // Note that argument !== o, since o isn't dry, so gets wrapped wet again. |
| 376 var wetRevokeHandler = new Proxy({}, Object.freeze({ | 273 assertEquals(3, (w.r = {a: 3}).a) |
| 377 get: function(receiver, name) { | 274 assertEquals(3, w.r.a) |
| 378 return function() { | 275 assertEquals(3, o.r.a) |
| 379 var arg = (name === "get" || name == "set") ? arguments[1] : ""; | 276 w.h(3) |
| 380 print("wet handler enter", name, arg); | 277 assertEquals(3, w.q) |
| 381 var optDryHandler = wet2dry.get(wetRevokeHandler); | 278 assertEquals(3, o.q) |
| 382 try { | 279 assertEquals(4, (new w.h(4)).q) |
| 383 var x = asWet(optDryHandler[name].apply( | 280 assertEquals(5, w.s(5).x.y) |
| 384 optDryHandler, Array.prototype.map.call(arguments, asDry))); | 281 assertSame(o, receiver) |
| 385 print("wet handler exit", name, arg, "returning", str(x)); | 282 |
| 386 return x; | 283 var wb = w.b |
| 387 } catch (eDry) { | 284 var wr = w.r |
| 388 var x = asWet(eDry); | 285 var wf = w.f |
| 389 print("wet handler exception", name, arg, "throwing", str(x)); | 286 var wf3 = w.f(3) |
| 390 throw x; | 287 var wfx = w.f({a: 6}) |
| 391 } | 288 var wgx = w.g({a: {aa: 7}}) |
| 392 }; | 289 var wh4 = new w.h(4) |
| 393 } | 290 var ws5 = w.s(5) |
| 394 })); | 291 var ws5x = ws5.x |
| 395 wet2dry.set(wetRevokeHandler, dryHandler); | 292 m.gate.revoke() |
| 396 | 293 assertEquals(3, wf3) |
| 397 if (typeof dry === "function") { | 294 assertThrows(function() { w.a }, Error) |
| 398 function callTrap() { | 295 assertThrows(function() { w.r }, Error) |
| 399 print("wet call trap enter", str(this)); | 296 assertThrows(function() { w.r = {a: 4} }, Error) |
| 400 var x = asWet(dry.apply( | 297 assertThrows(function() { o.r.a }, Error) |
| 401 asDry(this), Array.prototype.map.call(arguments, asDry))); | 298 assertEquals("object", typeof o.r) |
| 402 print("wet call trap exit", str(this), "returning", str(x)); | 299 assertEquals(5, (o.r = {a: 5}).a) |
| 403 return x; | 300 assertEquals(5, o.r.a) |
| 404 } | 301 assertThrows(function() { w[1] }, Error) |
| 405 function constructTrap() { | 302 assertThrows(function() { w.c }, Error) |
| 406 function forward(args) { return dry.apply(this, args) } | 303 assertThrows(function() { wb.bb }, Error) |
| 407 return asWet(new forward(Array.prototype.map.call(arguments, asDry))); | 304 assertEquals(3, wr.a) |
| 408 } | 305 assertThrows(function() { wf(4) }, Error) |
| 409 wetResult = | 306 assertEquals(6, wfx.a) |
| 410 Proxy.createFunction(wetRevokeHandler, callTrap, constructTrap); | 307 assertEquals(7, wgx.aa) |
| 411 } else { | 308 assertThrows(function() { wh4.q }, Error) |
| 412 wetResult = | 309 assertThrows(function() { ws5.x }, Error) |
| 413 new Proxy(asWet(Object.getPrototypeOf(dry)), wetRevokeHandler); | 310 assertThrows(function() { ws5x.y }, Error) |
| 414 } | 311 } |
| 415 dry2wet.set(dry, wetResult); | |
| 416 wet2dry.set(wetResult, dry); | |
| 417 return wetResult; | |
| 418 } | |
| 419 | |
| 420 var gate = Object.freeze({ | |
| 421 revoke: function() { | |
| 422 dry2wet = wet2dry = Object.freeze({ | |
| 423 get: function(key) { throw new Error("revoked"); }, | |
| 424 set: function(key, val) { throw new Error("revoked"); } | |
| 425 }); | |
| 426 } | |
| 427 }); | |
| 428 | |
| 429 return Object.freeze({ wrapper: asDry(wetTarget), gate: gate }); | |
| 430 } | |
| 431 | |
| 432 | |
| 433 var receiver | |
| 434 var argument | |
| 435 var o = { | |
| 436 a: 6, | |
| 437 b: {bb: 8}, | |
| 438 f: function(x) { receiver = this; argument = x; return x }, | |
| 439 g: function(x) { receiver = this; argument = x; return x.a }, | |
| 440 h: function(x) { receiver = this; argument = x; this.q = x }, | |
| 441 s: function(x) { receiver = this; argument = x; this.x = {y: x}; return this } | |
| 442 } | |
| 443 o[2] = {c: 7} | |
| 444 var m = createMembrane(o) | |
| 445 var w = m.wrapper | |
| 446 print("o =", str(o)) | |
| 447 print("w =", str(w)) | |
| 448 | |
| 449 var f = w.f | |
| 450 var x = f(66) | |
| 451 var x = f({a: 1}) | |
| 452 var x = w.f({a: 1}) | |
| 453 var a = x.a | |
| 454 assertEquals(6, w.a) | |
| 455 assertEquals(8, w.b.bb) | |
| 456 assertEquals(7, w[2]["c"]) | |
| 457 assertEquals(undefined, w.c) | |
| 458 assertEquals(1, w.f(1)) | |
| 459 assertSame(o, receiver) | |
| 460 assertEquals(1, w.f({a: 1}).a) | |
| 461 assertSame(o, receiver) | |
| 462 assertEquals(2, w.g({a: 2})) | |
| 463 assertSame(o, receiver) | |
| 464 assertSame(w, w.f(w)) | |
| 465 assertSame(o, receiver) | |
| 466 assertSame(o, argument) | |
| 467 assertSame(o, w.f(o)) | |
| 468 assertSame(o, receiver) | |
| 469 // Note that argument !== o, since o isn't dry, so gets wrapped wet again. | |
| 470 assertEquals(3, (w.r = {a: 3}).a) | |
| 471 assertEquals(3, w.r.a) | |
| 472 assertEquals(3, o.r.a) | |
| 473 w.h(3) | |
| 474 assertEquals(3, w.q) | |
| 475 assertEquals(3, o.q) | |
| 476 assertEquals(4, (new w.h(4)).q) | |
| 477 assertEquals(5, w.s(5).x.y) | |
| 478 assertSame(o, receiver) | |
| 479 | |
| 480 var wb = w.b | |
| 481 var wr = w.r | |
| 482 var wf = w.f | |
| 483 var wf3 = w.f(3) | |
| 484 var wfx = w.f({a: 6}) | |
| 485 var wgx = w.g({a: {aa: 7}}) | |
| 486 var wh4 = new w.h(4) | |
| 487 var ws5 = w.s(5) | |
| 488 var ws5x = ws5.x | |
| 489 m.gate.revoke() | |
| 490 assertEquals(3, wf3) | |
| 491 assertThrows(function() { w.a }, Error) | |
| 492 assertThrows(function() { w.r }, Error) | |
| 493 assertThrows(function() { w.r = {a: 4} }, Error) | |
| 494 assertThrows(function() { o.r.a }, Error) | |
| 495 assertEquals("object", typeof o.r) | |
| 496 assertEquals(5, (o.r = {a: 5}).a) | |
| 497 assertEquals(5, o.r.a) | |
| 498 assertThrows(function() { w[1] }, Error) | |
| 499 assertThrows(function() { w.c }, Error) | |
| 500 assertThrows(function() { wb.bb }, Error) | |
| 501 assertEquals(3, wr.a) | |
| 502 assertThrows(function() { wf(4) }, Error) | |
| 503 assertEquals(6, wfx.a) | |
| 504 assertEquals(7, wgx.aa) | |
| 505 assertThrows(function() { wh4.q }, Error) | |
| 506 assertThrows(function() { ws5.x }, Error) | |
| 507 assertThrows(function() { ws5x.y }, Error) | |
| OLD | NEW |