[JSC] AI should not propagate AbstractValue relying on constant folding phase
[WebKit-https.git] / JSTests / microbenchmarks / string-prototype-split-observable-side-effects.js
1 //@ runNoFTL
2
3 function 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 })();