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 |