OLD | NEW |
---|---|
(Empty) | |
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 | |
29 | |
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 | |
Michael Starzinger
2011/10/28 12:08:34
I am not sure about the policy for long URLs in co
| |
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 non-strict 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("w =", str(w)); | |
218 | |
219 var f = w.f; | |
220 var x = f(66); | |
221 var x = f({a: 1}); | |
222 var x = w.f({a: 1}); | |
223 var a = x.a; | |
224 assertEquals(6, w.a); | |
225 assertEquals(8, w.b.bb); | |
226 assertEquals(7, w[2]["c"]); | |
227 assertEquals(undefined, w.c); | |
228 assertEquals(1, w.f(1)); | |
229 assertEquals(1, w.f({a: 1}).a); | |
230 assertEquals(2, w.g({a: 2})); | |
231 assertEquals(3, (w.r = {a: 3}).a); | |
232 assertEquals(3, w.r.a); | |
233 assertEquals(3, o.r.a); | |
234 w.h(3); | |
235 assertEquals(3, w.q); | |
236 assertEquals(3, o.q); | |
237 assertEquals(4, (new w.h(4)).q); | |
238 | |
239 var wb = w.b; | |
240 var wr = w.r; | |
241 var wf = w.f; | |
242 var wf3 = w.f(3); | |
243 var wfx = w.f({a: 6}); | |
244 var wgx = w.g({a: {aa: 7}}); | |
245 var wh4 = new w.h(4); | |
246 m.gate.disable(); | |
247 assertEquals(3, wf3); | |
248 assertThrows(function() { w.a }, Error); | |
249 assertThrows(function() { w.r }, Error); | |
250 assertThrows(function() { w.r = {a: 4} }, Error); | |
251 assertThrows(function() { o.r.a }, Error); | |
252 assertEquals("object", typeof o.r); | |
253 assertEquals(5, (o.r = {a: 5}).a); | |
254 assertEquals(5, o.r.a); | |
255 assertThrows(function() { w[1] }, Error); | |
256 assertThrows(function() { w.c }, Error); | |
257 assertThrows(function() { wb.bb }, Error); | |
258 assertThrows(function() { wr.a }, Error); | |
259 assertThrows(function() { wf(4) }, Error); | |
260 assertThrows(function() { wfx.a }, Error); | |
261 assertThrows(function() { wgx.aa }, Error); | |
262 assertThrows(function() { wh4.q }, Error); | |
263 | |
264 m.gate.enable(); | |
265 assertEquals(6, w.a); | |
266 assertEquals(5, w.r.a); | |
267 assertEquals(5, o.r.a); | |
268 assertEquals(7, w.r = 7); | |
269 assertEquals(7, w.r); | |
270 assertEquals(7, o.r); | |
271 assertEquals(8, w.b.bb); | |
272 assertEquals(7, w[2]["c"]); | |
273 assertEquals(undefined, w.c); | |
274 assertEquals(8, wb.bb); | |
275 assertEquals(3, wr.a); | |
276 assertEquals(4, wf(4)); | |
277 assertEquals(3, wf3); | |
278 assertEquals(6, wfx.a); | |
279 assertEquals(7, wgx.aa); | |
280 assertEquals(4, wh4.q); | |
281 | |
282 | |
283 // An identity-preserving membrane. Adapted from: | |
284 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving _membrane | |
Michael Starzinger
2011/10/28 12:08:34
Likewise.
| |
285 | |
286 function createMembrane(wetTarget) { | |
287 var wet2dry = WeakMap(); | |
288 var dry2wet = WeakMap(); | |
289 | |
290 function asDry(obj) { | |
291 registerObject(obj) | |
292 print("asDry enter", str(obj)) | |
293 try { | |
294 var x = asDry2(obj); | |
295 registerObject(x, "dry"); | |
296 print("asDry exit", str(obj), "as", str(x)); | |
297 return x; | |
298 } catch(e) { | |
299 print("asDry exception", str(e)); | |
300 throw e; | |
301 } | |
302 } | |
303 function asDry2(wet) { | |
304 if (wet !== Object(wet)) { | |
305 // primitives provide only irrevocable knowledge, so don't | |
306 // bother wrapping it. | |
307 return wet; | |
308 } | |
309 var dryResult = wet2dry.get(wet); | |
310 if (dryResult) { return dryResult; } | |
311 | |
312 var wetHandler = createHandler(wet); | |
313 var dryRevokeHandler = Proxy.create(Object.freeze({ | |
314 get: function(receiver, name) { | |
315 return function() { | |
316 var arg = (name === "get" || name == "set") ? arguments[1] : ""; | |
317 print("dry handler enter", name, arg); | |
318 var optWetHandler = dry2wet.get(dryRevokeHandler); | |
319 try { | |
320 var x = asDry(optWetHandler[name].apply( | |
321 optWetHandler, Array.prototype.map.call(arguments, asWet))); | |
322 print("dry handler exit", name, arg, "returning", str(x)); | |
323 return x; | |
324 } catch (eWet) { | |
325 var x = asDry(eWet); | |
326 print("dry handler exception", name, arg, "throwing", str(x)); | |
327 throw x; | |
328 } | |
329 }; | |
330 } | |
331 })); | |
332 dry2wet.set(dryRevokeHandler, wetHandler); | |
333 | |
334 if (typeof wet === "function") { | |
335 function callTrap() { | |
336 print("dry call trap enter", str(this)); | |
337 var x = asDry(wet.apply( | |
338 asWet(this), Array.prototype.map.call(arguments, asWet))); | |
339 print("dry call trap exit", str(this), "returning", str(x)); | |
340 return x; | |
341 } | |
342 function constructTrap() { | |
343 function forward(args) { return wet.apply(this, args) } | |
344 return asDry(new forward(Array.prototype.map.call(arguments, asWet))); | |
345 } | |
346 dryResult = | |
347 Proxy.createFunction(dryRevokeHandler, callTrap, constructTrap); | |
348 } else { | |
349 dryResult = | |
350 Proxy.create(dryRevokeHandler, asDry(Object.getPrototypeOf(wet))); | |
351 } | |
352 wet2dry.set(wet, dryResult); | |
353 dry2wet.set(dryResult, wet); | |
354 return dryResult; | |
355 } | |
356 | |
357 function asWet(obj) { | |
358 registerObject(obj) | |
359 print("asWet enter", str(obj)) | |
360 try { | |
361 var x = asWet2(obj) | |
362 registerObject(x, "wet") | |
363 print("asWet exit", str(obj), "as", str(x)) | |
364 return x | |
365 } catch(e) { | |
366 print("asWet exception", str(e)) | |
367 throw e | |
368 } | |
369 } | |
370 function asWet2(dry) { | |
371 if (dry !== Object(dry)) { | |
372 // primitives provide only irrevocable knowledge, so don't | |
373 // bother wrapping it. | |
374 return dry; | |
375 } | |
376 var wetResult = dry2wet.get(dry); | |
377 if (wetResult) { return wetResult; } | |
378 | |
379 var dryHandler = createHandler(dry); | |
380 var wetRevokeHandler = Proxy.create(Object.freeze({ | |
381 get: function(receiver, name) { | |
382 return function() { | |
383 var arg = (name === "get" || name == "set") ? arguments[1] : ""; | |
384 print("wet handler enter", name, arg); | |
385 var optDryHandler = wet2dry.get(wetRevokeHandler); | |
386 try { | |
387 var x = asWet(optDryHandler[name].apply( | |
388 optDryHandler, Array.prototype.map.call(arguments, asDry))); | |
389 print("wet handler exit", name, arg, "returning", str(x)); | |
390 return x; | |
391 } catch (eDry) { | |
392 var x = asWet(eDry); | |
393 print("wet handler exception", name, arg, "throwing", str(x)); | |
394 throw x; | |
395 } | |
396 }; | |
397 } | |
398 })); | |
399 wet2dry.set(wetRevokeHandler, dryHandler); | |
400 | |
401 if (typeof dry === "function") { | |
402 function callTrap() { | |
403 print("wet call trap enter", str(this)); | |
404 var x = asWet(dry.apply( | |
405 asDry(this), Array.prototype.map.call(arguments, asDry))); | |
406 print("wet call trap exit", str(this), "returning", str(x)); | |
407 return x; | |
408 } | |
409 function constructTrap() { | |
410 function forward(args) { return dry.apply(this, args) } | |
411 return asWet(new forward(Array.prototype.map.call(arguments, asDry))); | |
412 } | |
413 wetResult = | |
414 Proxy.createFunction(wetRevokeHandler, callTrap, constructTrap); | |
415 } else { | |
416 wetResult = | |
417 Proxy.create(wetRevokeHandler, asWet(Object.getPrototypeOf(dry))); | |
418 } | |
419 dry2wet.set(dry, wetResult); | |
420 wet2dry.set(wetResult, dry); | |
421 return wetResult; | |
422 } | |
423 | |
424 var gate = Object.freeze({ | |
425 revoke: function() { | |
426 dry2wet = wet2dry = Object.freeze({ | |
427 get: function(key) { throw new Error("revoked"); }, | |
428 set: function(key, val) { throw new Error("revoked"); } | |
429 }); | |
430 } | |
431 }); | |
432 | |
433 return Object.freeze({ wrapper: asDry(wetTarget), gate: gate }); | |
434 } | |
435 | |
436 | |
437 var receiver | |
438 var argument | |
439 var o = { | |
440 a: 6, | |
441 b: {bb: 8}, | |
442 f: function(x) { receiver = this; argument = x; return x }, | |
443 g: function(x) { receiver = this; argument = x; return x.a }, | |
444 h: function(x) { receiver = this; argument = x; this.q = x }, | |
445 s: function(x) { receiver = this; argument = x; this.x = {y: x}; return this } | |
446 } | |
447 o[2] = {c: 7} | |
448 var m = createMembrane(o) | |
449 var w = m.wrapper | |
450 print("dry w =", str(w)) | |
451 | |
452 var f = w.f | |
453 var x = f(66) | |
454 var x = f({a: 1}) | |
455 var x = w.f({a: 1}) | |
456 var a = x.a | |
457 assertEquals(6, w.a) | |
458 assertEquals(8, w.b.bb) | |
459 assertEquals(7, w[2]["c"]) | |
460 assertEquals(undefined, w.c) | |
461 assertEquals(1, w.f(1)) | |
462 assertSame(o, receiver) | |
463 assertEquals(1, w.f({a: 1}).a) | |
464 assertSame(o, receiver) | |
465 assertEquals(2, w.g({a: 2})) | |
466 assertSame(o, receiver) | |
467 assertEquals(w, w.f(w)) | |
468 assertSame(o, receiver) | |
469 assertSame(o, argument) | |
470 assertEquals(o, w.f(o)) | |
471 assertSame(o, receiver) | |
472 assertSame(o, argument) | |
473 assertEquals(3, (w.r = {a: 3}).a) | |
474 assertEquals(3, w.r.a) | |
475 assertEquals(3, o.r.a) | |
476 w.h(3) | |
477 assertEquals(3, w.q) | |
478 assertEquals(3, o.q) | |
479 assertEquals(4, (new w.h(4)).q) | |
480 assertEquals(5, w.s(5).x.y) | |
481 assertSame(o, receiver) | |
482 | |
483 var wb = w.b | |
484 var wr = w.r | |
485 var wf = w.f | |
486 var wf3 = w.f(3) | |
487 var wfx = w.f({a: 6}) | |
488 var wgx = w.g({a: {aa: 7}}) | |
489 var wh4 = new w.h(4) | |
490 var ws5 = w.s(5) | |
491 var ws5x = ws5.x | |
492 m.gate.revoke() | |
493 assertEquals(3, wf3) | |
494 assertThrows(function() { w.a }, Error) | |
495 assertThrows(function() { w.r }, Error) | |
496 assertThrows(function() { w.r = {a: 4} }, Error) | |
497 assertThrows(function() { o.r.a }, Error) | |
498 assertEquals("object", typeof o.r) | |
499 assertEquals(5, (o.r = {a: 5}).a) | |
500 assertEquals(5, o.r.a) | |
501 assertThrows(function() { w[1] }, Error) | |
502 assertThrows(function() { w.c }, Error) | |
503 assertThrows(function() { wb.bb }, Error) | |
504 assertEquals(3, wr.a) | |
505 assertThrows(function() { wf(4) }, Error) | |
506 assertEquals(6, wfx.a) | |
507 assertEquals(7, wgx.aa) | |
508 assertThrows(function() { wh4.q }, Error) | |
509 assertThrows(function() { ws5.x }, Error) | |
510 assertThrows(function() { ws5x.y }, Error) | |
OLD | NEW |