blob: 6b0d2bb184380ff84f58ea20c2fc5fa7b7011de1 [file] [log] [blame]
ossy@webkit.org8f73aef2016-07-21 18:08:38 +00001//@ runNoFTL
mark.lam@apple.comd158a092016-04-19 17:25:20 +00002
3function assert(testedValue, msg) {
4 if (!testedValue)
5 throw Error(msg);
6}
7
8//======================================================================================
9// Testing the string that we're calling split on.
10
11// Proxied String subclass.
12(function () {
13 let accesses = [];
14 class ExtString extends String { }
15 var obj = new ExtString("splitme");
16 var proxy = new Proxy(obj, {
17 get(obj, prop) {
18 accesses.push(prop.toString());
19 if (prop === "toString") {
20 return function() {
21 accesses.push("in_toString");
22 return obj.toString();
23 }
24 }
25 return obj[prop];
26 }
27 });
28
29 assert(accesses == "", "unexpected call to overridden props");
30 let result = String.prototype.split.call(proxy, "it");
31 assert(accesses == "Symbol(Symbol.toPrimitive),toString,in_toString", "Property accesses do not match expectation");
32 assert(result == "spl,me", "Unexpected result");
33})();
34
35// Object that looks like a string.
36(function () {
37 let accesses = [];
38 var obj = {
39 [Symbol.toPrimitive]() {
40 accesses.push(Symbol.toPrimitive.toString());
41 return "splitme";
42 }
43 }
44
45 assert(accesses == "", "unexpected call to overridden props");
46 let result = String.prototype.split.call(obj, "it");
47 assert(accesses == "Symbol(Symbol.toPrimitive)", "Property accesses do not match expectation");
48 assert(result == "spl,me", "Unexpected result");
49})();
50
51// Object that looks like a string.
52(function () {
53 let accesses = [];
54 var obj = {
55 toString() {
56 accesses.push("toString");
57 return "splitme";
58 }
59 }
60
61 assert(accesses == "", "unexpected call to overridden props");
62 let result = String.prototype.split.call(obj, "it");
63 assert(accesses == "toString", "Property accesses do not match expectation");
64 assert(result == "spl,me", "Unexpected result");
65})();
66
67// String subclass with overridden @@split.
68(function () {
69 let accesses = [];
70 class ExtString extends String {
71 [Symbol.split] (str) {
72 accesses.push("Symbol(Symbol.split)");
73 return RegExp.prototype[Symbol.split].call(/it/, str);
74 }
75 };
76
77 var obj = new ExtString;
78
79 assert(accesses == "", "unexpected call to overridden props");
80 let result = "splitme".split(obj);
81 assert(accesses == "Symbol(Symbol.split)", "Property accesses do not match expectation");
82 assert(result == "spl,me", "Unexpected result");
83})();
84
85
86// Object with overridden @@search.
87(function () {
88 let accesses = [];
89 var obj = {
90 [Symbol.split] (str) {
91 accesses.push("Symbol(Symbol.split)");
92 return RegExp.prototype[Symbol.split].call(/it/, str);
93 },
94 }
95
96 assert(accesses == "", "unexpected call to overridden props");
97 let result = "splitme".split(obj);
98 assert(accesses == "Symbol(Symbol.split)", "Property accesses do not match expectation");
99 assert(result == "spl,me", "Unexpected result");
100})();
101
102//======================================================================================
103// Testing the regexp object that we're calling split with.
104
105// Subclass with overridden [@@species]: Testing ES6 21.2.5.11: 4. Let C be ? SpeciesConstructor(rx, %RegExp%).
106(function () {
107 let accesses = [];
108 class TestRegExp extends RegExp { }
109 Object.defineProperty(TestRegExp, Symbol.species, {
110 value: function() {
111 accesses.push(Symbol.species.toString());
112 return /it/y;
113 }
114 });
115 let obj = new TestRegExp;
116 let errorStr;
117
118 assert(accesses == "", "unexpected call to overridden props");
119 let result = "splitme".split(obj);
120 assert(accesses == "Symbol(Symbol.species)", "Property accesses do not match expectation");
121 assert(result == "spl,me", "Unexpected result");
122})();
123
124// RegExp subclass with constructor: Testing ES6 21.2.5.11: 4. Let C be ? SpeciesConstructor(rx, %RegExp%).
125(function () {
126 let accesses = [];
127 class TestRegExp extends RegExp {
128 constructor(str, flags) {
129 super(str, flags);
130 accesses.push("constructor");
131 }
132 }
133 let obj = new TestRegExp("it");
134
135 assert(accesses == "constructor", "unexpected call to overridden props");
136 let result = "splitme".split(obj);
137 assert(accesses == "constructor,constructor", "Property accesses do not match expectation");
138 assert(result == "spl,me", "Unexpected result");
139})();
140
141// An object with species constructor: Testing ES6 21.2.5.11: 4. Let C be ? SpeciesConstructor(rx, %RegExp%).
142(function () {
143 let accesses = [];
144 let obj = { constructor: {} };
145 obj.constructor[Symbol.species] = function() {
146 accesses.push("constructor");
147 return /it/y;
148 };
149 obj[Symbol.split] = function(str, limit) {
150 accesses.push(Symbol.split.toString());
151 return RegExp.prototype[Symbol.split].call(this, str, limit);
152 };
153
154 assert(accesses == "", "unexpected call to overridden props");
155 let result = "splitme".split(obj);
156 assert(accesses == "Symbol(Symbol.split),constructor", "Property accesses do not match expectation");
157 assert(result == "spl,me", "Unexpected result");
158})();
159
160// RegExp object with overridden flags: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
161(function () {
162 let flags = [ "flags", "global", "ignoreCase", "multiline", "sticky", "unicode" ];
163 let flagValues = [ "", false, false, false, false, false ];
164 for (let index in flags) {
165 (function(flag, flagValue) {
166 let accesses = [];
167 let obj = /it/;
168 Object.defineProperty(obj, flag, {
169 get: function() {
170 accesses.push(flag);
171 passed = true;
172 return flagValue;
173 }
174 });
175
176 assert(accesses == "", "unexpected call to overridden props");
177 let result = "splitme".split(obj);
178 assert(accesses == flag, "Property accesses do not match expectation");
179 assert(result == "spl,me", "Unexpected result");
180 }) (flags[index], flagValues[index]);
181 }
182})();
183
184// RegExp subclass with overridden flags in subclass method: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
185(function () {
186 let flags = [ "flags", "global", "ignoreCase", "multiline", "sticky", "unicode" ];
187 let flagValues = [ "", false, false, false, false, false ];
188 for (let index in flags) {
189 (function(flag, flagValue) {
190 let accesses = [];
191 class TestRegExp extends RegExp {
192 get [flag]() {
193 accesses.push(flag);
194 return flagValue;
195 }
196 };
197 let obj = new TestRegExp(/it/);
198
199 assert(accesses == "", "unexpected call to overridden props");
200 let result = "splitme".split(obj);
201 assert(accesses == flag, "Property accesses do not match expectation");
202 assert(result == "spl,me", "Unexpected result");
203
204 }) (flags[index], flagValues[index]);
205 }
206})();
207
208// RegExp subclass with overridden flags using Object.defineProperty: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
209(function () {
210 let flags = [ "flags", "global", "ignoreCase", "multiline", "sticky", "unicode" ];
211 let flagValues = [ "", false, false, false, false, false ];
212 for (let index in flags) {
213 (function(flag, flagValue) {
214 let accesses = [];
215 class TestRegExp extends RegExp { };
216 let obj = new TestRegExp(/it/);
217
218 Object.defineProperty(obj, flag, {
219 get: function() {
220 accesses.push(flag);
221 return flagValue;
222 }
223 });
224
225 assert(accesses == "", "unexpected call to overridden props");
226 let result = "splitme".split(obj);
227 assert(accesses == flag, "Property accesses do not match expectation");
228 assert(result == "spl,me", "Unexpected result");
229
230 }) (flags[index], flagValues[index]);
231 }
232})();
233
234// Any object with species constructor: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
235(function () {
236 let accesses = [];
237 let obj = { constructor: {} };
238 obj.constructor[Symbol.species] = function() {
239 accesses.push("constructor");
240 return /it/y;
241 };
242 obj[Symbol.split] = function(str, limit) {
243 accesses.push(Symbol.split.toString());
244 return RegExp.prototype[Symbol.split].call(this, str, limit);
245 };
246
247 Object.defineProperty(obj, "flags", {
248 get: function() {
249 accesses.push("flags");
250 return "";
251 }
252 });
253
254 assert(accesses == "", "unexpected call to overridden props");
255 let result = "splitme".split(obj);
256 assert(accesses == "Symbol(Symbol.split),flags,constructor", "Property accesses do not match expectation");
257 assert(result == "spl,me", "Unexpected result");
258})();
259
260// Any object with custom prototype: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
261(function () {
262 let accesses = [];
263 let TestRegExpProto = {
264 get flags() {
265 accesses.push("flags");
266 return "";
267 },
268 toString() {
269 accesses.push("toString");
270 return this._regex.toString();
271 },
272 get source() {
273 accesses.push("source");
274 return this._regex.source;
275 }
276 }
277 TestRegExpProto.__proto__ = RegExp.prototype;
278
279 let TestRegExp = function(regex) {
280 accesses.push("constructor");
281 this._regex = new RegExp(regex);
282 }
283 TestRegExp.prototype = TestRegExpProto;
284 TestRegExpProto.constructor = TestRegExp;
285
286 let obj = new TestRegExp(/it/);
287
288 assert(accesses == "constructor", "unexpected call to overridden props");
289 let result = "splitme".split(obj);
290 assert(accesses == "constructor,flags,source", "Property accesses do not match expectation");
291 assert(result == "spl,me", "Unexpected result");
292})();
293
294// 2 levels of subclasses: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
295(function () {
296 let accesses = [];
297
298 class RegExpB extends RegExp {
299 get flags() {
300 accesses.push("flags");
301 return "";
302 }
303 }
304 class RegExpC extends RegExpB { }
305
306 assert(RegExpB.__proto__ == RegExp);
307 assert(RegExpC.__proto__ == RegExpB);
308
309 let obj = new RegExpC(/it/);
310
311 assert(accesses == "", "unexpected call to overridden props");
312 let result = "splitme".split(obj);
313 assert(accesses == "flags", "Property accesses do not match expectation");
314 assert(result == "spl,me", "Unexpected result");
315})();
316
317// 2 levels of subclasses with substituted prototype before instantiation: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
318(function () {
319 let accesses = [];
320
321 class B extends RegExp { }
322 class C extends B { }
323
324 assert(B.__proto__ === RegExp);
325 assert(C.__proto__ === B);
326 assert(B.prototype.__proto__ === RegExp.prototype);
327 assert(C.prototype.__proto__ === B.prototype);
328
329 let X = function () {}
330 Object.defineProperty(X.prototype, "flags", {
331 get: function() {
332 accesses.push("flags");
333 return "";
334 }
335 });
336 Object.defineProperty(X.prototype, "exec", {
337 value: function(str) {
338 accesses.push("exec");
339 var matchResult = /it/y.exec(str.substr(this.lastIndex));
340 if (matchResult)
341 this.lastIndex += 2; // length of "it".
342 return matchResult;
343 }
344 });
345
346 // Monkey with the prototype chain before instantiating C.
347 X.__proto__ = RegExp;
348 X.prototype.__proto__ = RegExp.prototype;
349 C.__proto__ = X;
350 C.prototype.__proto__ = X.prototype;
351
352 assert(X.__proto__ === RegExp);
353 assert(C.__proto__ === X);
354 assert(X.prototype.__proto__ === RegExp.prototype);
355 assert(C.prototype.__proto__ === X.prototype);
356
357 let obj = new C;
358
359 assert(accesses == "", "unexpected call to overridden props");
360 let result = "splitme".split(obj);
361 assert(accesses == "flags,exec,exec,exec,exec,exec,exec", "Property accesses do not match expectation");
362 assert(result == "spl,me", "Unexpected result");
363})();
364
365// 2 levels of subclasses with substituted prototype after instantiation: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
366(function () {
367 let accesses = [];
368
369 class B extends RegExp { }
370 class C extends B { }
371
372 assert(B.__proto__ === RegExp);
373 assert(C.__proto__ === B);
374 assert(B.prototype.__proto__ === RegExp.prototype);
375 assert(C.prototype.__proto__ === B.prototype);
376
377 let X = function () {}
378 Object.defineProperty(X.prototype, "flags", {
379 get: function() {
380 accesses.push("flags");
381 return "";
382 }
383 });
384 Object.defineProperty(X.prototype, "exec", {
385 value: function(str) {
386 accesses.push("exec");
387 var matchResult = /it/y.exec(str.substr(this.lastIndex));
388 if (matchResult)
389 this.lastIndex += 2; // length of "it".
390 return matchResult;
391 }
392 });
393
394 // Instantiate C before monkeying with the prototype chain.
395 let obj = new C();
396
397 X.__proto__ = RegExp;
398 X.prototype.__proto__ = RegExp.prototype;
399 C.__proto__ = X;
400 C.prototype.__proto__ = X.prototype;
401
402 assert(X.__proto__ === RegExp);
403 assert(C.__proto__ === X);
404 assert(X.prototype.__proto__ === RegExp.prototype);
405 assert(C.prototype.__proto__ === X.prototype);
406
407 assert(accesses == "", "unexpected call to overridden props");
408 let result = "splitme".split(obj);
409 assert(accesses == "flags,exec,exec,exec,exec,exec,exec", "Property accesses do not match expectation");
410 assert(result == "spl,me", "Unexpected result");
411})();
412
413// 2 levels of subclasses with proxied prototype: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")).
414(function () {
415 let accesses = [];
416
417 class B extends RegExp { };
418
419 assert(B.__proto__ === RegExp);
420 assert(B.prototype.__proto__ === RegExp.prototype);
421
422 let proxy = new Proxy(RegExp.prototype, {
423 get: function(obj, prop) {
424 accesses.push(prop.toString());
425 if (prop === "exec") {
426 return function(str) {
427 accesses.push("in_exec");
428 var matchResult = /it/y.exec(str.substr(this.lastIndex));
429 if (matchResult)
430 this.lastIndex += 2; // length of "it".
431 return matchResult;
432 }
433 }
434 return obj[prop];
435 }
436 });
437 B.prototype.__proto__ = proxy;
438
439 let obj = new B();
440
441 assert(accesses == "", "unexpected call to overridden props");
442 let result = "splitme".split(obj);
443 assert(accesses == "Symbol(Symbol.split),flags,Symbol(Symbol.match),exec,in_exec,exec,in_exec,exec,in_exec,exec,in_exec,exec,in_exec,exec,in_exec", "Property accesses do not match expectation");
444 assert(result == "spl,me", "Unexpected result");
445})();
446
447// RegExp subclass with overridden exec: Testing ES6 21.2.5.11: 19.b. Let z be ? RegExpExec(splitter, S).
448(function () {
449 let accesses = [];
450 class TestRegExp extends RegExp {
451 exec(str) {
452 accesses.push("exec");
453 return RegExp.prototype.exec.call(this, str);
454 }
455 };
456 let obj = new TestRegExp(/it/);
457
458 assert(accesses == "", "unexpected call to overridden props");
459 let result = "splitme".split(obj);
460 assert(accesses == "exec,exec,exec,exec,exec,exec", "Property accesses do not match expectation");
461 assert(result == "spl,me", "Unexpected result");
462})();
463
464// Proxied RegExp observing every get.
465(function () {
466 let accesses = [];
467 let regexp = new RegExp(/it/);
468 let proxy = new Proxy(regexp, {
469 get(obj, prop) {
470 accesses.push(prop.toString());
471 return obj[prop];
472 }
473 });
474
475 assert(accesses == "", "unexpected call to overridden props");
476 let result = "splitme".split(proxy);
477 // Note: @@split creates a new instance of the RegExp using its @@species, and performs
478 // the split operation with that new instance. Hence, the proxy is only able to observe
479 // gets up to the creation of the new instance.
480 assert(accesses == "Symbol(Symbol.split),constructor,flags,Symbol(Symbol.match),source",
481 "Proxy not able to observe some gets");
482 assert(result == "spl,me", "Unexpected result");
483})();
484
485// Proxied RegExp (without @@match) observing every get.
486// This is to force the RegExp @species constructor to access source.
487(function () {
488 let accesses = [];
489 let regexp = new RegExp(/it/);
490 let proxy = new Proxy(regexp, {
491 get(obj, prop) {
492 accesses.push(prop.toString());
493 if (prop == Symbol.match)
494 return undefined;
495 return obj[prop];
496 }
497 });
498
499 assert(accesses == "", "unexpected call to overridden props");
500 let result = "splitme".split(proxy);
501 // Note: @@split creates a new instance of the RegExp using its @@species, and performs
502 // the split operation with that new instance. Hence, the proxy is only able to observe
503 // gets up to the creation of the new instance.
504 assert(accesses == "Symbol(Symbol.split),constructor,flags,Symbol(Symbol.match),Symbol(Symbol.toPrimitive),toString,source,flags",
505 "Proxy not able to observe some gets");
506 // The new instance of the RegExp would have been constructed with the pattern from
507 // the proxy toString() i.e. "\/lt\/" instead of source, because the proxy is an
508 // object without a [@@match] property.
509 assert(result == "splitme", "Unexpected result");
510})();