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
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.
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.
31
// A simple no-op handler. Adapted from:
32
// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#examplea_no-op_forwarding_proxy
34
function createHandler(obj) {
36
getOwnPropertyDescriptor: function(name) {
37
var desc = Object.getOwnPropertyDescriptor(obj, name);
38
if (desc !== undefined) desc.configurable = true;
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;
47
getOwnPropertyNames: function() {
48
return Object.getOwnPropertyNames(obj);
50
getPropertyNames: function() {
51
return Object.getOwnPropertyNames(obj);
52
//return Object.getPropertyNames(obj); // not in ES5
54
defineProperty: function(name, desc) {
55
Object.defineProperty(obj, name, desc);
57
delete: function(name) {
58
return delete obj[name];
61
if (Object.isFrozen(obj)) {
63
Object.getOwnPropertyNames(obj).forEach(function(name) {
64
result[name] = Object.getOwnPropertyDescriptor(obj, name);
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
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
78
enumerate: function() {
80
for (var name in obj) { result.push(name); };
83
keys: function() { return Object.keys(obj); }
89
// Auxiliary definitions enabling tracking of object identity in output.
91
var objectMap = new WeakMap;
92
var objectCounter = 0;
94
function registerObject(x, s) {
95
if (x === Object(x) && !objectMap.has(x))
96
objectMap.set(x, ++objectCounter + (s == undefined ? "" : ":" + s));
99
registerObject(this, "global");
100
registerObject(Object.prototype, "Object.prototype");
103
if (x === Object(x)) return "[" + typeof x + " " + objectMap.get(x) + "]";
104
if (typeof x == "string") return "\"" + x + "\"";
110
// A simple membrane. Adapted from:
111
// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane
113
function createSimpleMembrane(target) {
118
print("wrap enter", str(obj));
121
registerObject(x, "wrapped");
122
print("wrap exit", str(obj), "as", str(x));
125
print("wrap exception", str(e));
130
function wrap2(obj) {
131
if (obj !== Object(obj)) {
135
function wrapCall(fun, that, args) {
136
registerObject(that);
137
print("wrapCall enter", fun, str(that));
139
var x = wrapCall2(fun, that, args);
140
print("wrapCall exit", fun, str(that), "returning", str(x));
143
print("wrapCall exception", fun, str(that), str(e));
148
function wrapCall2(fun, that, args) {
149
if (!enabled) { throw new Error("disabled"); }
151
return wrap(fun.apply(that, Array.prototype.map.call(args, wrap)));
157
var baseHandler = createHandler(obj);
158
var handler = Proxy.create(Object.freeze({
159
get: function(receiver, name) {
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));
169
registerObject(baseHandler, "basehandler");
170
registerObject(handler, "handler");
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));
179
function constructTrap() {
180
if (!enabled) { throw new Error("disabled"); }
182
function forward(args) { return obj.apply(this, args) }
183
return wrap(new forward(Array.prototype.map.call(arguments, wrap)));
188
return Proxy.createFunction(handler, callTrap, constructTrap);
190
var prototype = wrap(Object.getPrototypeOf(obj));
191
return Proxy.create(handler, prototype);
195
var gate = Object.freeze({
196
enable: function() { enabled = true; },
197
disable: function() { enabled = false; }
200
return Object.freeze({
201
wrapper: wrap(target),
210
f: function(x) { return x },
211
g: function(x) { return x.a },
212
h: function(x) { this.q = x }
215
var m = createSimpleMembrane(o);
218
print("w =", str(w));
225
assertEquals(6, w.a);
226
assertEquals(8, w.b.bb);
227
assertEquals(7, w[2]["c"]);
228
assertEquals(undefined, w.c);
229
assertEquals(1, w.f(1));
230
assertEquals(1, w.f({a: 1}).a);
231
assertEquals(2, w.g({a: 2}));
232
assertEquals(3, (w.r = {a: 3}).a);
233
assertEquals(3, w.r.a);
234
assertEquals(3, o.r.a);
236
assertEquals(3, w.q);
237
assertEquals(3, o.q);
238
assertEquals(4, (new w.h(4)).q);
244
var wfx = w.f({a: 6});
245
var wgx = w.g({a: {aa: 7}});
246
var wh4 = new w.h(4);
248
assertEquals(3, wf3);
249
assertThrows(function() { w.a }, Error);
250
assertThrows(function() { w.r }, Error);
251
assertThrows(function() { w.r = {a: 4} }, Error);
252
assertThrows(function() { o.r.a }, Error);
253
assertEquals("object", typeof o.r);
254
assertEquals(5, (o.r = {a: 5}).a);
255
assertEquals(5, o.r.a);
256
assertThrows(function() { w[1] }, Error);
257
assertThrows(function() { w.c }, Error);
258
assertThrows(function() { wb.bb }, Error);
259
assertThrows(function() { wr.a }, Error);
260
assertThrows(function() { wf(4) }, Error);
261
assertThrows(function() { wfx.a }, Error);
262
assertThrows(function() { wgx.aa }, Error);
263
assertThrows(function() { wh4.q }, Error);
266
assertEquals(6, w.a);
267
assertEquals(5, w.r.a);
268
assertEquals(5, o.r.a);
269
assertEquals(7, w.r = 7);
270
assertEquals(7, w.r);
271
assertEquals(7, o.r);
272
assertEquals(8, w.b.bb);
273
assertEquals(7, w[2]["c"]);
274
assertEquals(undefined, w.c);
275
assertEquals(8, wb.bb);
276
assertEquals(3, wr.a);
277
assertEquals(4, wf(4));
278
assertEquals(3, wf3);
279
assertEquals(6, wfx.a);
280
assertEquals(7, wgx.aa);
281
assertEquals(4, wh4.q);
284
// An identity-preserving membrane. Adapted from:
285
// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving_membrane
287
function createMembrane(wetTarget) {
288
var wet2dry = WeakMap();
289
var dry2wet = WeakMap();
291
function asDry(obj) {
293
print("asDry enter", str(obj))
296
registerObject(x, "dry");
297
print("asDry exit", str(obj), "as", str(x));
300
print("asDry exception", str(e));
304
function asDry2(wet) {
305
if (wet !== Object(wet)) {
306
// primitives provide only irrevocable knowledge, so don't
307
// bother wrapping it.
310
var dryResult = wet2dry.get(wet);
311
if (dryResult) { return dryResult; }
313
var wetHandler = createHandler(wet);
314
var dryRevokeHandler = Proxy.create(Object.freeze({
315
get: function(receiver, name) {
317
var arg = (name === "get" || name == "set") ? arguments[1] : "";
318
print("dry handler enter", name, arg);
319
var optWetHandler = dry2wet.get(dryRevokeHandler);
321
var x = asDry(optWetHandler[name].apply(
322
optWetHandler, Array.prototype.map.call(arguments, asWet)));
323
print("dry handler exit", name, arg, "returning", str(x));
327
print("dry handler exception", name, arg, "throwing", str(x));
333
dry2wet.set(dryRevokeHandler, wetHandler);
335
if (typeof wet === "function") {
336
function callTrap() {
337
print("dry call trap enter", str(this));
338
var x = asDry(wet.apply(
339
asWet(this), Array.prototype.map.call(arguments, asWet)));
340
print("dry call trap exit", str(this), "returning", str(x));
343
function constructTrap() {
344
function forward(args) { return wet.apply(this, args) }
345
return asDry(new forward(Array.prototype.map.call(arguments, asWet)));
348
Proxy.createFunction(dryRevokeHandler, callTrap, constructTrap);
351
Proxy.create(dryRevokeHandler, asDry(Object.getPrototypeOf(wet)));
353
wet2dry.set(wet, dryResult);
354
dry2wet.set(dryResult, wet);
358
function asWet(obj) {
360
print("asWet enter", str(obj))
363
registerObject(x, "wet")
364
print("asWet exit", str(obj), "as", str(x))
367
print("asWet exception", str(e))
371
function asWet2(dry) {
372
if (dry !== Object(dry)) {
373
// primitives provide only irrevocable knowledge, so don't
374
// bother wrapping it.
377
var wetResult = dry2wet.get(dry);
378
if (wetResult) { return wetResult; }
380
var dryHandler = createHandler(dry);
381
var wetRevokeHandler = Proxy.create(Object.freeze({
382
get: function(receiver, name) {
384
var arg = (name === "get" || name == "set") ? arguments[1] : "";
385
print("wet handler enter", name, arg);
386
var optDryHandler = wet2dry.get(wetRevokeHandler);
388
var x = asWet(optDryHandler[name].apply(
389
optDryHandler, Array.prototype.map.call(arguments, asDry)));
390
print("wet handler exit", name, arg, "returning", str(x));
394
print("wet handler exception", name, arg, "throwing", str(x));
400
wet2dry.set(wetRevokeHandler, dryHandler);
402
if (typeof dry === "function") {
403
function callTrap() {
404
print("wet call trap enter", str(this));
405
var x = asWet(dry.apply(
406
asDry(this), Array.prototype.map.call(arguments, asDry)));
407
print("wet call trap exit", str(this), "returning", str(x));
410
function constructTrap() {
411
function forward(args) { return dry.apply(this, args) }
412
return asWet(new forward(Array.prototype.map.call(arguments, asDry)));
415
Proxy.createFunction(wetRevokeHandler, callTrap, constructTrap);
418
Proxy.create(wetRevokeHandler, asWet(Object.getPrototypeOf(dry)));
420
dry2wet.set(dry, wetResult);
421
wet2dry.set(wetResult, dry);
425
var gate = Object.freeze({
427
dry2wet = wet2dry = Object.freeze({
428
get: function(key) { throw new Error("revoked"); },
429
set: function(key, val) { throw new Error("revoked"); }
434
return Object.freeze({ wrapper: asDry(wetTarget), gate: gate });
443
f: function(x) { receiver = this; argument = x; return x },
444
g: function(x) { receiver = this; argument = x; return x.a },
445
h: function(x) { receiver = this; argument = x; this.q = x },
446
s: function(x) { receiver = this; argument = x; this.x = {y: x}; return this }
449
var m = createMembrane(o)
460
assertEquals(8, w.b.bb)
461
assertEquals(7, w[2]["c"])
462
assertEquals(undefined, w.c)
463
assertEquals(1, w.f(1))
464
assertSame(o, receiver)
465
assertEquals(1, w.f({a: 1}).a)
466
assertSame(o, receiver)
467
assertEquals(2, w.g({a: 2}))
468
assertSame(o, receiver)
469
assertSame(w, w.f(w))
470
assertSame(o, receiver)
471
assertSame(o, argument)
472
assertSame(o, w.f(o))
473
assertSame(o, receiver)
474
// Note that argument !== o, since o isn't dry, so gets wrapped wet again.
475
assertEquals(3, (w.r = {a: 3}).a)
476
assertEquals(3, w.r.a)
477
assertEquals(3, o.r.a)
481
assertEquals(4, (new w.h(4)).q)
482
assertEquals(5, w.s(5).x.y)
483
assertSame(o, receiver)
489
var wfx = w.f({a: 6})
490
var wgx = w.g({a: {aa: 7}})
496
assertThrows(function() { w.a }, Error)
497
assertThrows(function() { w.r }, Error)
498
assertThrows(function() { w.r = {a: 4} }, Error)
499
assertThrows(function() { o.r.a }, Error)
500
assertEquals("object", typeof o.r)
501
assertEquals(5, (o.r = {a: 5}).a)
502
assertEquals(5, o.r.a)
503
assertThrows(function() { w[1] }, Error)
504
assertThrows(function() { w.c }, Error)
505
assertThrows(function() { wb.bb }, Error)
506
assertEquals(3, wr.a)
507
assertThrows(function() { wf(4) }, Error)
508
assertEquals(6, wfx.a)
509
assertEquals(7, wgx.aa)
510
assertThrows(function() { wh4.q }, Error)
511
assertThrows(function() { ws5.x }, Error)
512
assertThrows(function() { ws5x.y }, Error)