 Chromium Code Reviews
 Chromium Code Reviews Issue 8392038:
  A more holistic test case for proxies.  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
    
  
    Issue 8392038:
  A more holistic test case for proxies.  (Closed) 
  Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge| 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 |