OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
11 // with the distribution. | 11 // with the distribution. |
12 // * Neither the name of Google Inc. nor the names of its | 12 // * Neither the name of Google Inc. nor the names of its |
13 // contributors may be used to endorse or promote products derived | 13 // contributors may be used to endorse or promote products derived |
14 // from this software without specific prior written permission. | 14 // from this software without specific prior written permission. |
15 // | 15 // |
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 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. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 // Flags: --harmony-proxies | 28 // Flags: --harmony-proxies --harmony-reflect |
| 29 |
| 30 |
| 31 |
| 32 /////////////////////////////////////////////////////////////////////////////// |
| 33 // JSON.stringify |
| 34 |
29 | 35 |
30 function testStringify(expected, object) { | 36 function testStringify(expected, object) { |
31 // Test fast case that bails out to slow case. | 37 // Test fast case that bails out to slow case. |
32 assertEquals(expected, JSON.stringify(object)); | 38 assertEquals(expected, JSON.stringify(object)); |
33 // Test slow case. | 39 // Test slow case. |
34 assertEquals(expected, JSON.stringify(object, undefined, 0)); | 40 assertEquals(expected, JSON.stringify(object, undefined, 0)); |
35 } | 41 } |
36 | 42 |
37 // Test serializing a proxy, function proxy and objects that contain them. | 43 |
| 44 // Test serializing a proxy, a function proxy, and objects that contain them. |
| 45 |
38 var handler1 = { | 46 var handler1 = { |
39 get: function(target, name) { | 47 get: function(target, name) { |
40 return name.toUpperCase(); | 48 return name.toUpperCase(); |
41 }, | 49 }, |
42 enumerate: function(target) { | 50 ownKeys: function() { |
43 return ['a', 'b', 'c']; | 51 return ['a', 'b', 'c']; |
44 }, | 52 }, |
45 getOwnPropertyDescriptor: function(target, name) { | 53 getOwnPropertyDescriptor: function() { |
46 return { enumerable: true }; | 54 return { enumerable: true, configurable: true }; |
47 } | 55 } |
48 } | 56 } |
49 | 57 |
50 var proxy1 = new Proxy({}, handler1); | 58 var proxy1 = new Proxy({}, handler1); |
51 testStringify('{"a":"A","b":"B","c":"C"}', proxy1); | 59 testStringify('{"a":"A","b":"B","c":"C"}', proxy1); |
52 | 60 |
53 var proxy_fun = Proxy.createFunction(handler1, function() { return 1; }); | 61 var proxy_fun = new Proxy(() => {}, handler1); |
| 62 assertTrue(typeof(proxy_fun) === 'function'); |
54 testStringify(undefined, proxy_fun); | 63 testStringify(undefined, proxy_fun); |
55 testStringify('[1,null]', [1, proxy_fun]); | 64 testStringify('[1,null]', [1, proxy_fun]); |
56 | 65 |
| 66 handler1.apply = function() { return 666; }; |
| 67 testStringify(undefined, proxy_fun); |
| 68 testStringify('[1,null]', [1, proxy_fun]); |
| 69 |
57 var parent1a = { b: proxy1 }; | 70 var parent1a = { b: proxy1 }; |
58 testStringify('{"b":{"a":"A","b":"B","c":"C"}}', parent1a); | 71 testStringify('{"b":{"a":"A","b":"B","c":"C"}}', parent1a); |
59 | 72 |
60 var parent1b = { a: 123, b: proxy1, c: true }; | 73 var parent1b = { a: 123, b: proxy1, c: true }; |
61 testStringify('{"a":123,"b":{"a":"A","b":"B","c":"C"},"c":true}', parent1b); | 74 testStringify('{"a":123,"b":{"a":"A","b":"B","c":"C"},"c":true}', parent1b); |
62 | 75 |
63 var parent1c = [123, proxy1, true]; | 76 var parent1c = [123, proxy1, true]; |
64 testStringify('[123,{"a":"A","b":"B","c":"C"},true]', parent1c); | 77 testStringify('[123,{"a":"A","b":"B","c":"C"},true]', parent1c); |
65 | 78 |
| 79 |
66 // Proxy with side effect. | 80 // Proxy with side effect. |
| 81 |
67 var handler2 = { | 82 var handler2 = { |
68 get: function(target, name) { | 83 get: function(target, name) { |
69 delete parent2.c; | 84 delete parent2.c; |
70 return name.toUpperCase(); | 85 return name.toUpperCase(); |
71 }, | 86 }, |
72 enumerate: function(target) { | 87 ownKeys: function() { |
73 return ['a', 'b', 'c']; | 88 return ['a', 'b', 'c']; |
74 }, | 89 }, |
75 getOwnPropertyDescriptor: function(target, name) { | 90 getOwnPropertyDescriptor: function() { |
76 return { enumerable: true }; | 91 return { enumerable: true, configurable: true }; |
77 } | 92 } |
78 } | 93 } |
79 | 94 |
80 var proxy2 = new Proxy({}, handler2); | 95 var proxy2 = new Proxy({}, handler2); |
81 var parent2 = { a: "delete", b: proxy2, c: "remove" }; | 96 var parent2 = { a: "delete", b: proxy2, c: "remove" }; |
82 var expected2 = '{"a":"delete","b":{"a":"A","b":"B","c":"C"}}'; | 97 var expected2 = '{"a":"delete","b":{"a":"A","b":"B","c":"C"}}'; |
83 assertEquals(expected2, JSON.stringify(parent2)); | 98 assertEquals(expected2, JSON.stringify(parent2)); |
84 parent2.c = "remove"; // Revert side effect. | 99 parent2.c = "remove"; // Revert side effect. |
85 assertEquals(expected2, JSON.stringify(parent2, undefined, 0)); | 100 assertEquals(expected2, JSON.stringify(parent2, undefined, 0)); |
86 | 101 |
87 // Proxy with a get function that uses the first argument. | 102 |
| 103 // Proxy with a get function that uses the receiver argument. |
| 104 |
88 var handler3 = { | 105 var handler3 = { |
89 get: function(target, name) { | 106 get: function(target, name, receiver) { |
90 if (name == 'valueOf') return function() { return "proxy" }; | 107 if (name == 'valueOf' || name === Symbol.toPrimitive) { |
91 return name + "(" + target + ")"; | 108 return function() { return "proxy" }; |
| 109 }; |
| 110 if (typeof name !== 'symbol') return name + "(" + receiver + ")"; |
92 }, | 111 }, |
93 enumerate: function(target) { | 112 ownKeys: function() { |
94 return ['a', 'b', 'c']; | 113 return ['a', 'b', 'c']; |
95 }, | 114 }, |
96 getOwnPropertyDescriptor: function(target, name) { | 115 getOwnPropertyDescriptor: function() { |
97 return { enumerable: true }; | 116 return { enumerable: true, configurable: true }; |
98 } | 117 } |
99 } | 118 } |
100 | 119 |
101 var proxy3 = new Proxy({}, handler3); | 120 var proxy3 = new Proxy({}, handler3); |
102 var parent3 = { x: 123, y: proxy3 } | 121 var parent3 = { x: 123, y: proxy3 } |
103 testStringify('{"x":123,"y":{"a":"a(proxy)","b":"b(proxy)","c":"c(proxy)"}}', | 122 testStringify('{"x":123,"y":{"a":"a(proxy)","b":"b(proxy)","c":"c(proxy)"}}', |
104 parent3); | 123 parent3); |
105 | 124 |
| 125 |
106 // Empty proxy. | 126 // Empty proxy. |
| 127 |
107 var handler4 = { | 128 var handler4 = { |
108 get: function(target, name) { | 129 get: function(target, name) { |
109 return 0; | 130 return 0; |
110 }, | 131 }, |
111 enumerate: function(target) { | 132 enumerate: function(target) { |
112 return []; | 133 return [][Symbol.iterator](); |
| 134 }, |
| 135 has: function() { |
| 136 return true; |
113 }, | 137 }, |
114 getOwnPropertyDescriptor: function(target, name) { | 138 getOwnPropertyDescriptor: function(target, name) { |
115 return { enumerable: false }; | 139 return { enumerable: false }; |
116 } | 140 } |
117 } | 141 } |
118 | 142 |
119 var proxy4 = new Proxy({}, handler4); | 143 var proxy4 = new Proxy({}, handler4); |
120 testStringify('{}', proxy4); | 144 testStringify('{}', proxy4); |
121 testStringify('{"a":{}}', { a: proxy4 }); | 145 testStringify('{"a":{}}', { a: proxy4 }); |
122 | 146 |
| 147 |
123 // Proxy that provides a toJSON function that uses this. | 148 // Proxy that provides a toJSON function that uses this. |
| 149 |
124 var handler5 = { | 150 var handler5 = { |
125 get: function(target, name) { | 151 get: function(target, name) { |
126 if (name == 'z') return 97000; | 152 if (name == 'z') return 97000; |
127 return function(key) { return key.charCodeAt(0) + this.z; }; | 153 return function(key) { return key.charCodeAt(0) + this.z; }; |
128 }, | 154 }, |
129 enumerate: function(target) { | 155 enumerate: function(target) { |
130 return ['toJSON', 'z']; | 156 return ['toJSON', 'z'][Symbol.iterator](); |
| 157 }, |
| 158 has: function() { |
| 159 return true; |
131 }, | 160 }, |
132 getOwnPropertyDescriptor: function(target, name) { | 161 getOwnPropertyDescriptor: function(target, name) { |
133 return { enumerable: true }; | 162 return { enumerable: true }; |
134 } | 163 } |
135 } | 164 } |
136 | 165 |
137 var proxy5 = new Proxy({}, handler5); | 166 var proxy5 = new Proxy({}, handler5); |
138 testStringify('{"a":97097}', { a: proxy5 }); | 167 testStringify('{"a":97097}', { a: proxy5 }); |
139 | 168 |
| 169 |
140 // Proxy that provides a toJSON function that returns undefined. | 170 // Proxy that provides a toJSON function that returns undefined. |
| 171 |
141 var handler6 = { | 172 var handler6 = { |
142 get: function(target, name) { | 173 get: function(target, name) { |
143 return function(key) { return undefined; }; | 174 return function(key) { return undefined; }; |
144 }, | 175 }, |
145 enumerate: function(target) { | 176 enumerate: function(target) { |
146 return ['toJSON']; | 177 return ['toJSON'][Symbol.iterator](); |
| 178 }, |
| 179 has: function() { |
| 180 return true; |
147 }, | 181 }, |
148 getOwnPropertyDescriptor: function(target, name) { | 182 getOwnPropertyDescriptor: function(target, name) { |
149 return { enumerable: true }; | 183 return { enumerable: true }; |
150 } | 184 } |
151 } | 185 } |
152 | 186 |
153 var proxy6 = new Proxy({}, handler6); | 187 var proxy6 = new Proxy({}, handler6); |
154 testStringify('[1,null,true]', [1, proxy6, true]); | 188 testStringify('[1,null,true]', [1, proxy6, true]); |
155 testStringify('{"a":1,"c":true}', {a: 1, b: proxy6, c: true}); | 189 testStringify('{"a":1,"c":true}', {a: 1, b: proxy6, c: true}); |
156 | 190 |
| 191 |
157 // Object containing a proxy that changes the parent's properties. | 192 // Object containing a proxy that changes the parent's properties. |
| 193 |
158 var handler7 = { | 194 var handler7 = { |
159 get: function(target, name) { | 195 get: function(target, name) { |
160 delete parent7.a; | 196 delete parent7.a; |
161 delete parent7.c; | 197 delete parent7.c; |
162 parent7.e = "5"; | 198 parent7.e = "5"; |
163 return name.toUpperCase(); | 199 return name.toUpperCase(); |
164 }, | 200 }, |
165 enumerate: function(target) { | 201 ownKeys: function() { |
166 return ['a', 'b', 'c']; | 202 return ['a', 'b', 'c']; |
167 }, | 203 }, |
168 getOwnPropertyDescriptor: function(target, name) { | 204 getOwnPropertyDescriptor: function() { |
169 return { enumerable: true }; | 205 return { enumerable: true, configurable: true }; |
170 } | 206 } |
171 } | 207 } |
172 | 208 |
173 var proxy7 = new Proxy({}, handler7); | 209 var proxy7 = new Proxy({}, handler7); |
174 var parent7 = { a: "1", b: proxy7, c: "3", d: "4" }; | 210 var parent7 = { a: "1", b: proxy7, c: "3", d: "4" }; |
175 assertEquals('{"a":"1","b":{"a":"A","b":"B","c":"C"},"d":"4"}', | 211 assertEquals('{"a":"1","b":{"a":"A","b":"B","c":"C"},"d":"4"}', |
176 JSON.stringify(parent7)); | 212 JSON.stringify(parent7)); |
177 assertEquals('{"b":{"a":"A","b":"B","c":"C"},"d":"4","e":"5"}', | 213 assertEquals('{"b":{"a":"A","b":"B","c":"C"},"d":"4","e":"5"}', |
178 JSON.stringify(parent7)); | 214 JSON.stringify(parent7)); |
| 215 |
| 216 |
| 217 // (Proxy handler to log trap calls) |
| 218 |
| 219 var log = []; |
| 220 var logger = {}; |
| 221 var handler = new Proxy({}, logger); |
| 222 |
| 223 logger.get = function(t, trap, r) { |
| 224 return function() { |
| 225 log.push([trap, ...arguments]); |
| 226 return Reflect[trap](...arguments); |
| 227 } |
| 228 }; |
| 229 |
| 230 |
| 231 // Object is a callable proxy |
| 232 |
| 233 log.length = 0; |
| 234 var target = () => 42; |
| 235 var proxy = new Proxy(target, handler); |
| 236 assertTrue(typeof proxy === 'function'); |
| 237 |
| 238 assertEquals(undefined, JSON.stringify(proxy)); |
| 239 assertEquals(1, log.length) |
| 240 for (var i in log) assertSame(target, log[i][1]); |
| 241 |
| 242 assertEquals(["get", target, "toJSON", proxy], log[0]); |
| 243 |
| 244 |
| 245 // Object is a non-callable non-arraylike proxy |
| 246 |
| 247 log.length = 0; |
| 248 var target = {foo: 42} |
| 249 var proxy = new Proxy(target, handler); |
| 250 assertFalse(Array.isArray(proxy)); |
| 251 |
| 252 assertEquals('{"foo":42}', JSON.stringify(proxy)); |
| 253 assertEquals(4, log.length) |
| 254 for (var i in log) assertSame(target, log[i][1]); |
| 255 |
| 256 assertEquals( |
| 257 ["get", target, "toJSON", proxy], log[0]); |
| 258 assertEquals( |
| 259 ["ownKeys", target], log[1]); // EnumerableOwnNames |
| 260 assertEquals( |
| 261 ["getOwnPropertyDescriptor", target, "foo"], log[2]); // EnumerableOwnNames |
| 262 assertEquals( |
| 263 ["get", target, "foo", proxy], log[3]); |
| 264 |
| 265 |
| 266 // Object is an arraylike proxy |
| 267 |
| 268 log.length = 0; |
| 269 var target = [42]; |
| 270 var proxy = new Proxy(target, handler); |
| 271 assertTrue(Array.isArray(proxy)); |
| 272 |
| 273 assertEquals('[42]', JSON.stringify(proxy)); |
| 274 assertEquals(3, log.length) |
| 275 for (var i in log) assertSame(target, log[i][1]); |
| 276 |
| 277 assertEquals(["get", target, "toJSON", proxy], log[0]); |
| 278 assertEquals(["get", target, "length", proxy], log[1]); |
| 279 assertEquals(["get", target, "0", proxy], log[2]); |
| 280 |
| 281 |
| 282 // Replacer is a callable proxy |
| 283 |
| 284 log.length = 0; |
| 285 var object = {0: "foo", 1: 666}; |
| 286 var target = (key, val) => key == "1" ? val + 42 : val; |
| 287 var proxy = new Proxy(target, handler); |
| 288 assertTrue(typeof proxy === 'function'); |
| 289 |
| 290 assertEquals('{"0":"foo","1":708}', JSON.stringify(object, proxy)); |
| 291 assertEquals(3, log.length) |
| 292 for (var i in log) assertSame(target, log[i][1]); |
| 293 |
| 294 assertEquals(4, log[0].length) |
| 295 assertEquals("apply", log[0][0]); |
| 296 assertEquals("", log[0][3][0]); |
| 297 assertEquals({0: "foo", 1: 666}, log[0][3][1]); |
| 298 assertEquals(4, log[1].length) |
| 299 assertEquals("apply", log[1][0]); |
| 300 assertEquals(["0", "foo"], log[1][3]); |
| 301 assertEquals(4, log[2].length) |
| 302 assertEquals("apply", log[2][0]); |
| 303 assertEquals(["1", 666], log[2][3]); |
| 304 |
| 305 |
| 306 // Replacer is an arraylike proxy |
| 307 |
| 308 log.length = 0; |
| 309 var object = {0: "foo", 1: 666}; |
| 310 var target = [0]; |
| 311 var proxy = new Proxy(target, handler); |
| 312 assertTrue(Array.isArray(proxy)); |
| 313 |
| 314 assertEquals('{"0":"foo"}', JSON.stringify(object, proxy)); |
| 315 assertEquals(2, log.length) |
| 316 for (var i in log) assertSame(target, log[i][1]); |
| 317 |
| 318 assertEquals(["get", target, "length", proxy], log[0]); |
| 319 assertEquals(["get", target, "0", proxy], log[1]); |
| 320 |
| 321 |
| 322 // Replacer is an arraylike proxy and object is an array |
| 323 |
| 324 log.length = 0; |
| 325 var object = ["foo", 42]; |
| 326 var target = [0]; |
| 327 var proxy = new Proxy(target, handler); |
| 328 assertTrue(Array.isArray(proxy)); |
| 329 |
| 330 assertEquals('["foo",42]', JSON.stringify(object, proxy)); |
| 331 assertEquals(2, log.length); |
| 332 for (var i in log) assertSame(target, log[i][1]); |
| 333 |
| 334 assertEquals(["get", target, "length", proxy], log[0]); |
| 335 assertEquals(["get", target, "0", proxy], log[1]); |
| 336 |
| 337 |
| 338 // Replacer is an arraylike proxy with a non-trivial length |
| 339 |
| 340 var getTrap = function(t, key) { |
| 341 if (key === "length") return {[Symbol.toPrimitive]() {return 42}}; |
| 342 if (key === "41") return "foo"; |
| 343 if (key === "42") return "bar"; |
| 344 }; |
| 345 var target = []; |
| 346 var proxy = new Proxy(target, {get: getTrap}); |
| 347 assertTrue(Array.isArray(proxy)); |
| 348 var object = {foo: true, bar: 666}; |
| 349 assertEquals('{"foo":true}', JSON.stringify(object, proxy)); |
| 350 |
| 351 |
| 352 // Replacer is an arraylike proxy with a bogus length |
| 353 |
| 354 var getTrap = function(t, key) { |
| 355 if (key === "length") return Symbol(); |
| 356 if (key === "41") return "foo"; |
| 357 if (key === "42") return "bar"; |
| 358 }; |
| 359 var target = []; |
| 360 var proxy = new Proxy(target, {get: getTrap}); |
| 361 assertTrue(Array.isArray(proxy)); |
| 362 var object = {foo: true, bar: 666}; |
| 363 assertThrows(() => JSON.stringify(object, proxy), TypeError); |
| 364 |
| 365 |
| 366 // Replacer returns a non-callable non-arraylike proxy |
| 367 |
| 368 log.length = 0; |
| 369 var object = ["foo", 42]; |
| 370 var target = {baz: 5}; |
| 371 var proxy = new Proxy(target, handler); |
| 372 var replacer = (key, val) => key === "1" ? proxy : val; |
| 373 |
| 374 assertEquals('["foo",{"baz":5}]', JSON.stringify(object, replacer)); |
| 375 assertEquals(3, log.length); |
| 376 for (var i in log) assertSame(target, log[i][1]); |
| 377 |
| 378 assertEquals(["ownKeys", target], log[0]); |
| 379 assertEquals(["getOwnPropertyDescriptor", target, "baz"], log[1]); |
| 380 |
| 381 |
| 382 // Replacer returns an arraylike proxy |
| 383 |
| 384 log.length = 0; |
| 385 var object = ["foo", 42]; |
| 386 var target = ["bar"]; |
| 387 var proxy = new Proxy(target, handler); |
| 388 var replacer = (key, val) => key === "1" ? proxy : val; |
| 389 |
| 390 assertEquals('["foo",["bar"]]', JSON.stringify(object, replacer)); |
| 391 assertEquals(2, log.length); |
| 392 for (var i in log) assertSame(target, log[i][1]); |
| 393 |
| 394 assertEquals(["get", target, "length", proxy], log[0]); |
| 395 assertEquals(["get", target, "0", proxy], log[1]); |
| 396 |
| 397 |
| 398 // Replacer returns an arraylike proxy with a non-trivial length |
| 399 |
| 400 var getTrap = function(t, key) { |
| 401 if (key === "length") return {[Symbol.toPrimitive]() {return 3}}; |
| 402 if (key === "2") return "baz"; |
| 403 if (key === "3") return "bar"; |
| 404 }; |
| 405 var target = []; |
| 406 var proxy = new Proxy(target, {get: getTrap}); |
| 407 var replacer = (key, val) => key === "goo" ? proxy : val; |
| 408 var object = {foo: true, goo: false}; |
| 409 assertEquals('{"foo":true,"goo":[null,null,"baz"]}', |
| 410 JSON.stringify(object, replacer)); |
| 411 |
| 412 |
| 413 // Replacer returns an arraylike proxy with a bogus length |
| 414 |
| 415 var getTrap = function(t, key) { |
| 416 if (key === "length") return Symbol(); |
| 417 if (key === "2") return "baz"; |
| 418 if (key === "3") return "bar"; |
| 419 }; |
| 420 var target = []; |
| 421 var proxy = new Proxy(target, {get: getTrap}); |
| 422 var replacer = (key, val) => key === "goo" ? proxy : val; |
| 423 var object = {foo: true, goo: false}; |
| 424 assertThrows(() => JSON.stringify(object, replacer)); |
| 425 |
| 426 |
| 427 // Replacer returns a callable proxy |
| 428 |
| 429 log.length = 0; |
| 430 var target = () => 666; |
| 431 var proxy = new Proxy(target, handler); |
| 432 var replacer = (key, val) => key === "1" ? proxy : val; |
| 433 |
| 434 assertEquals('["foo",null]', JSON.stringify(["foo", 42], replacer)); |
| 435 assertEquals(0, log.length); |
| 436 |
| 437 assertEquals('{"0":"foo"}', JSON.stringify({0: "foo", 1: 42}, replacer)); |
| 438 assertEquals(0, log.length); |
| 439 |
| 440 |
| 441 |
| 442 /////////////////////////////////////////////////////////////////////////////// |
| 443 // JSON.parse |
| 444 |
| 445 |
| 446 // Reviver is a callable proxy |
| 447 |
| 448 log.length = 0; |
| 449 var target = () => 42; |
| 450 var proxy = new Proxy(target, handler); |
| 451 assertTrue(typeof proxy === "function"); |
| 452 |
| 453 assertEquals(42, JSON.parse("[true, false]", proxy)); |
| 454 assertEquals(3, log.length); |
| 455 for (var i in log) assertSame(target, log[i][1]); |
| 456 |
| 457 assertEquals(4, log[0].length); |
| 458 assertEquals("apply", log[0][0]); |
| 459 assertEquals(["0", true], log[0][3]); |
| 460 assertEquals(4, log[1].length); |
| 461 assertEquals("apply", log[1][0]); |
| 462 assertEquals(["1", false], log[1][3]); |
| 463 assertEquals(4, log[2].length); |
| 464 assertEquals("apply", log[2][0]); |
| 465 assertEquals(["", [42, 42]], log[2][3]); |
| 466 |
| 467 |
| 468 // Reviver plants a non-arraylike proxy into a yet-to-be-visited property |
| 469 |
| 470 log.length = 0; |
| 471 var target = {baz: 42}; |
| 472 var proxy = new Proxy(target, handler); |
| 473 var reviver = function(p, v) { |
| 474 if (p === "baz") return 5; |
| 475 if (p === "foo") this.bar = proxy; |
| 476 return v; |
| 477 } |
| 478 |
| 479 assertEquals({foo: 0, bar: proxy}, JSON.parse('{"foo":0,"bar":1}', reviver)); |
| 480 assertEquals(4, log.length); |
| 481 for (var i in log) assertSame(target, log[i][1]); |
| 482 |
| 483 assertEquals(["ownKeys", target], log[0]); |
| 484 assertEquals(["getOwnPropertyDescriptor", target, "baz"], log[1]); |
| 485 assertEquals(["get", target, "baz", proxy], log[2]); |
| 486 assertEquals(["defineProperty", target, "baz", |
| 487 {value: 5, configurable: true, writable: true, enumerable: true}], log[3]); |
| 488 |
| 489 |
| 490 // Reviver plants an arraylike proxy into a yet-to-be-visited property |
| 491 |
| 492 log.length = 0; |
| 493 var target = [42]; |
| 494 var proxy = new Proxy(target, handler); |
| 495 assertTrue(Array.isArray(proxy)); |
| 496 var reviver = function(p, v) { |
| 497 if (p === "0") return undefined; |
| 498 if (p === "foo") this.bar = proxy; |
| 499 return v; |
| 500 } |
| 501 |
| 502 var result = JSON.parse('{"foo":0,"bar":1}', reviver); |
| 503 assertEquals({foo: 0, bar: proxy}, result); |
| 504 assertSame(result.bar, proxy); |
| 505 assertEquals(3, log.length); |
| 506 for (var i in log) assertSame(target, log[i][1]); |
| 507 |
| 508 assertEquals(["get", target, "length", proxy], log[0]); |
| 509 assertEquals(["get", target, "0", proxy], log[1]); |
| 510 assertEquals(["deleteProperty", target, "0"], log[2]); |
OLD | NEW |