249bf75e0c
Both of them shipped in Chrome 49 without incident. Also move relevant tests from harmony/ to es6/. Review URL: https://codereview.chromium.org/1815773002 Cr-Commit-Position: refs/heads/master@{#34964}
309 lines
8.6 KiB
JavaScript
309 lines
8.6 KiB
JavaScript
// Copyright 2011 the V8 project authors. All rights reserved.
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following
|
|
// disclaimer in the documentation and/or other materials provided
|
|
// with the distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
// A simple membrane. Adapted from:
|
|
// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane
|
|
|
|
function createSimpleMembrane(target) {
|
|
let enabled = true;
|
|
|
|
function wrap(obj) {
|
|
if (obj !== Object(obj)) return obj;
|
|
|
|
let handler = new Proxy({}, {get: function(_, key) {
|
|
if (!enabled) throw new Error("disabled");
|
|
switch (key) {
|
|
case "apply":
|
|
return (_, that, args) => {
|
|
try {
|
|
return wrap(Reflect.apply(
|
|
obj, wrap(that), args.map((x) => wrap(x))));
|
|
} catch(e) {
|
|
throw wrap(e);
|
|
}
|
|
}
|
|
case "construct":
|
|
return (_, args, newt) => {
|
|
try {
|
|
return wrap(Reflect.construct(
|
|
obj, args.map((x) => wrap(x)), wrap(newt)));
|
|
} catch(e) {
|
|
throw wrap(e);
|
|
}
|
|
}
|
|
default:
|
|
return (_, ...args) => {
|
|
try {
|
|
return wrap(Reflect[key](obj, ...(args.map(wrap))));
|
|
} catch(e) {
|
|
throw wrap(e);
|
|
}
|
|
}
|
|
}
|
|
}});
|
|
|
|
return new Proxy(obj, handler);
|
|
}
|
|
|
|
const gate = Object.freeze({
|
|
enable: () => enabled = true,
|
|
disable: () => enabled = false
|
|
});
|
|
|
|
return Object.freeze({
|
|
wrapper: wrap(target),
|
|
gate: gate
|
|
});
|
|
}
|
|
|
|
|
|
// Test the simple membrane.
|
|
{
|
|
var o = {
|
|
a: 6,
|
|
b: {bb: 8},
|
|
f: function(x) { return x },
|
|
g: function(x) { return x.a },
|
|
h: function(x) { this.q = x }
|
|
};
|
|
o[2] = {c: 7};
|
|
var m = createSimpleMembrane(o);
|
|
var w = m.wrapper;
|
|
var f = w.f;
|
|
var x = f(66);
|
|
var x = f({a: 1});
|
|
var x = w.f({a: 1});
|
|
var a = x.a;
|
|
assertEquals(6, w.a);
|
|
assertEquals(8, w.b.bb);
|
|
assertEquals(7, w[2]["c"]);
|
|
assertEquals(undefined, w.c);
|
|
assertEquals(1, w.f(1));
|
|
assertEquals(1, w.f({a: 1}).a);
|
|
assertEquals(2, w.g({a: 2}));
|
|
assertEquals(3, (w.r = {a: 3}).a);
|
|
assertEquals(3, w.r.a);
|
|
assertEquals(3, o.r.a);
|
|
w.h(3);
|
|
assertEquals(3, w.q);
|
|
assertEquals(3, o.q);
|
|
assertEquals(4, (new w.h(4)).q);
|
|
|
|
var wb = w.b;
|
|
var wr = w.r;
|
|
var wf = w.f;
|
|
var wf3 = w.f(3);
|
|
var wfx = w.f({a: 6});
|
|
var wgx = w.g({a: {aa: 7}});
|
|
var wh4 = new w.h(4);
|
|
m.gate.disable();
|
|
assertEquals(3, wf3);
|
|
assertThrows(function() { w.a }, Error);
|
|
assertThrows(function() { w.r }, Error);
|
|
assertThrows(function() { w.r = {a: 4} }, Error);
|
|
assertThrows(function() { o.r.a }, Error);
|
|
assertEquals("object", typeof o.r);
|
|
assertEquals(5, (o.r = {a: 5}).a);
|
|
assertEquals(5, o.r.a);
|
|
assertThrows(function() { w[1] }, Error);
|
|
assertThrows(function() { w.c }, Error);
|
|
assertThrows(function() { wb.bb }, Error);
|
|
assertThrows(function() { wr.a }, Error);
|
|
assertThrows(function() { wf(4) }, Error);
|
|
assertThrows(function() { wfx.a }, Error);
|
|
assertThrows(function() { wgx.aa }, Error);
|
|
assertThrows(function() { wh4.q }, Error);
|
|
|
|
m.gate.enable();
|
|
assertEquals(6, w.a);
|
|
assertEquals(5, w.r.a);
|
|
assertEquals(5, o.r.a);
|
|
assertEquals(7, w.r = 7);
|
|
assertEquals(7, w.r);
|
|
assertEquals(7, o.r);
|
|
assertEquals(8, w.b.bb);
|
|
assertEquals(7, w[2]["c"]);
|
|
assertEquals(undefined, w.c);
|
|
assertEquals(8, wb.bb);
|
|
assertEquals(3, wr.a);
|
|
assertEquals(4, wf(4));
|
|
assertEquals(3, wf3);
|
|
assertEquals(6, wfx.a);
|
|
assertEquals(7, wgx.aa);
|
|
assertEquals(4, wh4.q);
|
|
}
|
|
|
|
|
|
|
|
// An identity-preserving membrane. Adapted from:
|
|
// http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving_membrane
|
|
|
|
function createMembrane(target) {
|
|
const wet2dry = 0;
|
|
const dry2wet = 1;
|
|
|
|
function flip(dir) { return (dir + 1) % 2 }
|
|
|
|
let maps = [new WeakMap(), new WeakMap()];
|
|
|
|
let revoked = false;
|
|
|
|
function wrap(dir, obj) {
|
|
if (obj !== Object(obj)) return obj;
|
|
|
|
let wrapper = maps[dir].get(obj);
|
|
if (wrapper) return wrapper;
|
|
|
|
let handler = new Proxy({}, {get: function(_, key) {
|
|
if (revoked) throw new Error("revoked");
|
|
switch (key) {
|
|
case "apply":
|
|
return (_, that, args) => {
|
|
try {
|
|
return wrap(dir, Reflect.apply(
|
|
obj, wrap(flip(dir), that),
|
|
args.map((x) => wrap(flip(dir), x))));
|
|
} catch(e) {
|
|
throw wrap(dir, e);
|
|
}
|
|
}
|
|
case "construct":
|
|
return (_, args, newt) => {
|
|
try {
|
|
return wrap(dir, Reflect.construct(
|
|
obj, args.map((x) => wrap(flip(dir), x)),
|
|
wrap(flip(dir), newt)));
|
|
} catch(e) {
|
|
throw wrap(dir, e);
|
|
}
|
|
}
|
|
default:
|
|
return (_, ...args) => {
|
|
try {
|
|
return wrap(dir, Reflect[key](
|
|
obj, ...(args.map((x) => wrap(flip(dir), x)))))
|
|
} catch(e) {
|
|
throw wrap(dir, e);
|
|
}
|
|
}
|
|
}
|
|
}});
|
|
|
|
wrapper = new Proxy(obj, handler);
|
|
maps[dir].set(obj, wrapper);
|
|
maps[flip(dir)].set(wrapper, obj);
|
|
return wrapper;
|
|
}
|
|
|
|
const gate = Object.freeze({
|
|
revoke: () => revoked = true
|
|
});
|
|
|
|
return Object.freeze({
|
|
wrapper: wrap(wet2dry, target),
|
|
gate: gate
|
|
});
|
|
}
|
|
|
|
|
|
// Test the identity-preserving membrane.
|
|
{
|
|
var receiver
|
|
var argument
|
|
var o = {
|
|
a: 6,
|
|
b: {bb: 8},
|
|
f: function(x) {receiver = this; argument = x; return x},
|
|
g: function(x) {receiver = this; argument = x; return x.a},
|
|
h: function(x) {receiver = this; argument = x; this.q = x},
|
|
s: function(x) {receiver = this; argument = x; this.x = {y: x}; return this}
|
|
}
|
|
o[2] = {c: 7}
|
|
var m = createMembrane(o)
|
|
var w = m.wrapper
|
|
var f = w.f
|
|
var x = f(66)
|
|
var x = f({a: 1})
|
|
var x = w.f({a: 1})
|
|
var a = x.a
|
|
assertEquals(6, w.a)
|
|
assertEquals(8, w.b.bb)
|
|
assertEquals(7, w[2]["c"])
|
|
assertEquals(undefined, w.c)
|
|
assertEquals(1, w.f(1))
|
|
assertSame(o, receiver)
|
|
assertEquals(1, w.f({a: 1}).a)
|
|
assertSame(o, receiver)
|
|
assertEquals(2, w.g({a: 2}))
|
|
assertSame(o, receiver)
|
|
assertSame(w, w.f(w))
|
|
assertSame(o, receiver)
|
|
assertSame(o, argument)
|
|
assertSame(o, w.f(o))
|
|
assertSame(o, receiver)
|
|
// Note that argument !== o, since o isn't dry, so gets wrapped wet again.
|
|
assertEquals(3, (w.r = {a: 3}).a)
|
|
assertEquals(3, w.r.a)
|
|
assertEquals(3, o.r.a)
|
|
w.h(3)
|
|
assertEquals(3, w.q)
|
|
assertEquals(3, o.q)
|
|
assertEquals(4, (new w.h(4)).q)
|
|
assertEquals(5, w.s(5).x.y)
|
|
assertSame(o, receiver)
|
|
|
|
var wb = w.b
|
|
var wr = w.r
|
|
var wf = w.f
|
|
var wf3 = w.f(3)
|
|
var wfx = w.f({a: 6})
|
|
var wgx = w.g({a: {aa: 7}})
|
|
var wh4 = new w.h(4)
|
|
var ws5 = w.s(5)
|
|
var ws5x = ws5.x
|
|
m.gate.revoke()
|
|
assertEquals(3, wf3)
|
|
assertThrows(function() { w.a }, Error)
|
|
assertThrows(function() { w.r }, Error)
|
|
assertThrows(function() { w.r = {a: 4} }, Error)
|
|
assertThrows(function() { o.r.a }, Error)
|
|
assertEquals("object", typeof o.r)
|
|
assertEquals(5, (o.r = {a: 5}).a)
|
|
assertEquals(5, o.r.a)
|
|
assertThrows(function() { w[1] }, Error)
|
|
assertThrows(function() { w.c }, Error)
|
|
assertThrows(function() { wb.bb }, Error)
|
|
assertEquals(3, wr.a)
|
|
assertThrows(function() { wf(4) }, Error)
|
|
assertEquals(6, wfx.a)
|
|
assertEquals(7, wgx.aa)
|
|
assertThrows(function() { wh4.q }, Error)
|
|
assertThrows(function() { ws5.x }, Error)
|
|
assertThrows(function() { ws5x.y }, Error)
|
|
}
|