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 --harmony-proxies | |
29 | |
30 | |
31 | |
32 // A simple membrane. Adapted from: | |
33 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#a_simple_membrane | |
34 | |
35 function createSimpleMembrane(target) { | |
36 let enabled = true; | |
37 | |
38 function wrap(obj) { | |
39 if (obj !== Object(obj)) return obj; | |
40 | |
41 let handler = new Proxy({}, {get: function(_, key) { | |
42 if (!enabled) throw new Error("disabled"); | |
43 switch (key) { | |
44 case "apply": | |
45 return (_, that, args) => { | |
46 try { | |
47 return wrap(Reflect.apply( | |
48 obj, wrap(that), args.map((x) => wrap(x)))); | |
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); | |
74 } | |
75 | |
76 const gate = Object.freeze({ | |
77 enable: () => enabled = true, | |
78 disable: () => enabled = false | |
79 }); | |
80 | |
81 return Object.freeze({ | |
82 wrapper: wrap(target), | |
83 gate: gate | |
84 }); | |
85 } | |
86 | |
87 | |
88 // Test the simple membrane. | |
89 { | |
90 var o = { | |
91 a: 6, | |
92 b: {bb: 8}, | |
93 f: function(x) { return x }, | |
94 g: function(x) { return x.a }, | |
95 h: function(x) { this.q = x } | |
96 }; | |
97 o[2] = {c: 7}; | |
98 var m = createSimpleMembrane(o); | |
99 var w = m.wrapper; | |
100 var f = w.f; | |
101 var x = f(66); | |
102 var x = f({a: 1}); | |
103 var x = w.f({a: 1}); | |
104 var a = x.a; | |
105 assertEquals(6, w.a); | |
106 assertEquals(8, w.b.bb); | |
107 assertEquals(7, w[2]["c"]); | |
108 assertEquals(undefined, w.c); | |
109 assertEquals(1, w.f(1)); | |
110 assertEquals(1, w.f({a: 1}).a); | |
111 assertEquals(2, w.g({a: 2})); | |
112 assertEquals(3, (w.r = {a: 3}).a); | |
113 assertEquals(3, w.r.a); | |
114 assertEquals(3, o.r.a); | |
115 w.h(3); | |
116 assertEquals(3, w.q); | |
117 assertEquals(3, o.q); | |
118 assertEquals(4, (new w.h(4)).q); | |
119 | |
120 var wb = w.b; | |
121 var wr = w.r; | |
122 var wf = w.f; | |
123 var wf3 = w.f(3); | |
124 var wfx = w.f({a: 6}); | |
125 var wgx = w.g({a: {aa: 7}}); | |
126 var wh4 = new w.h(4); | |
127 m.gate.disable(); | |
128 assertEquals(3, wf3); | |
129 assertThrows(function() { w.a }, Error); | |
130 assertThrows(function() { w.r }, Error); | |
131 assertThrows(function() { w.r = {a: 4} }, Error); | |
132 assertThrows(function() { o.r.a }, Error); | |
133 assertEquals("object", typeof o.r); | |
134 assertEquals(5, (o.r = {a: 5}).a); | |
135 assertEquals(5, o.r.a); | |
136 assertThrows(function() { w[1] }, Error); | |
137 assertThrows(function() { w.c }, Error); | |
138 assertThrows(function() { wb.bb }, Error); | |
139 assertThrows(function() { wr.a }, Error); | |
140 assertThrows(function() { wf(4) }, Error); | |
141 assertThrows(function() { wfx.a }, Error); | |
142 assertThrows(function() { wgx.aa }, Error); | |
143 assertThrows(function() { wh4.q }, Error); | |
144 | |
145 m.gate.enable(); | |
146 assertEquals(6, w.a); | |
147 assertEquals(5, w.r.a); | |
148 assertEquals(5, o.r.a); | |
149 assertEquals(7, w.r = 7); | |
150 assertEquals(7, w.r); | |
151 assertEquals(7, o.r); | |
152 assertEquals(8, w.b.bb); | |
153 assertEquals(7, w[2]["c"]); | |
154 assertEquals(undefined, w.c); | |
155 assertEquals(8, wb.bb); | |
156 assertEquals(3, wr.a); | |
157 assertEquals(4, wf(4)); | |
158 assertEquals(3, wf3); | |
159 assertEquals(6, wfx.a); | |
160 assertEquals(7, wgx.aa); | |
161 assertEquals(4, wh4.q); | |
162 } | |
163 | |
164 | |
165 | |
166 // An identity-preserving membrane. Adapted from: | |
167 // http://wiki.ecmascript.org/doku.php?id=harmony:proxies#an_identity-preserving
_membrane | |
168 | |
169 function createMembrane(target) { | |
170 const wet2dry = 0; | |
171 const dry2wet = 1; | |
172 | |
173 function flip(dir) { return (dir + 1) % 2 } | |
174 | |
175 let maps = [new WeakMap(), new WeakMap()]; | |
176 | |
177 let revoked = false; | |
178 | |
179 function wrap(dir, obj) { | |
180 if (obj !== Object(obj)) return obj; | |
181 | |
182 let wrapper = maps[dir].get(obj); | |
183 if (wrapper) return wrapper; | |
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; | |
224 } | |
225 | |
226 const gate = Object.freeze({ | |
227 revoke: () => revoked = true | |
228 }); | |
229 | |
230 return Object.freeze({ | |
231 wrapper: wrap(wet2dry, target), | |
232 gate: gate | |
233 }); | |
234 } | |
235 | |
236 | |
237 // Test the identity-preserving membrane. | |
238 { | |
239 var receiver | |
240 var argument | |
241 var o = { | |
242 a: 6, | |
243 b: {bb: 8}, | |
244 f: function(x) {receiver = this; argument = x; return x}, | |
245 g: function(x) {receiver = this; argument = x; return x.a}, | |
246 h: function(x) {receiver = this; argument = x; this.q = x}, | |
247 s: function(x) {receiver = this; argument = x; this.x = {y: x}; return this} | |
248 } | |
249 o[2] = {c: 7} | |
250 var m = createMembrane(o) | |
251 var w = m.wrapper | |
252 var f = w.f | |
253 var x = f(66) | |
254 var x = f({a: 1}) | |
255 var x = w.f({a: 1}) | |
256 var a = x.a | |
257 assertEquals(6, w.a) | |
258 assertEquals(8, w.b.bb) | |
259 assertEquals(7, w[2]["c"]) | |
260 assertEquals(undefined, w.c) | |
261 assertEquals(1, w.f(1)) | |
262 assertSame(o, receiver) | |
263 assertEquals(1, w.f({a: 1}).a) | |
264 assertSame(o, receiver) | |
265 assertEquals(2, w.g({a: 2})) | |
266 assertSame(o, receiver) | |
267 assertSame(w, w.f(w)) | |
268 assertSame(o, receiver) | |
269 assertSame(o, argument) | |
270 assertSame(o, w.f(o)) | |
271 assertSame(o, receiver) | |
272 // Note that argument !== o, since o isn't dry, so gets wrapped wet again. | |
273 assertEquals(3, (w.r = {a: 3}).a) | |
274 assertEquals(3, w.r.a) | |
275 assertEquals(3, o.r.a) | |
276 w.h(3) | |
277 assertEquals(3, w.q) | |
278 assertEquals(3, o.q) | |
279 assertEquals(4, (new w.h(4)).q) | |
280 assertEquals(5, w.s(5).x.y) | |
281 assertSame(o, receiver) | |
282 | |
283 var wb = w.b | |
284 var wr = w.r | |
285 var wf = w.f | |
286 var wf3 = w.f(3) | |
287 var wfx = w.f({a: 6}) | |
288 var wgx = w.g({a: {aa: 7}}) | |
289 var wh4 = new w.h(4) | |
290 var ws5 = w.s(5) | |
291 var ws5x = ws5.x | |
292 m.gate.revoke() | |
293 assertEquals(3, wf3) | |
294 assertThrows(function() { w.a }, Error) | |
295 assertThrows(function() { w.r }, Error) | |
296 assertThrows(function() { w.r = {a: 4} }, Error) | |
297 assertThrows(function() { o.r.a }, Error) | |
298 assertEquals("object", typeof o.r) | |
299 assertEquals(5, (o.r = {a: 5}).a) | |
300 assertEquals(5, o.r.a) | |
301 assertThrows(function() { w[1] }, Error) | |
302 assertThrows(function() { w.c }, Error) | |
303 assertThrows(function() { wb.bb }, Error) | |
304 assertEquals(3, wr.a) | |
305 assertThrows(function() { wf(4) }, Error) | |
306 assertEquals(6, wfx.a) | |
307 assertEquals(7, wgx.aa) | |
308 assertThrows(function() { wh4.q }, Error) | |
309 assertThrows(function() { ws5.x }, Error) | |
310 assertThrows(function() { ws5x.y }, Error) | |
311 } | |
OLD | NEW |