| //@ runNoFTL |
| |
| function assert(testedValue, msg) { |
| if (!testedValue) |
| throw Error(msg); |
| } |
| |
| // Subclass with overridden [@@species]: Testing ES6 21.2.5.11: 4. Let C be ? SpeciesConstructor(rx, %RegExp%). |
| (function () { |
| let accesses = []; |
| class TestRegExp extends RegExp { } |
| Object.defineProperty(TestRegExp, Symbol.species, { |
| value: function() { |
| accesses.push(Symbol.species.toString()); |
| return /it/y; |
| } |
| }); |
| let obj = new TestRegExp(/it/); |
| let errorStr; |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "Symbol(Symbol.species)", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // RegExp subclass with constructor: Testing ES6 21.2.5.11: 4. Let C be ? SpeciesConstructor(rx, %RegExp%). |
| (function () { |
| let accesses = []; |
| class TestRegExp extends RegExp { |
| constructor(str, flags) { |
| super(str, flags); |
| accesses.push("constructor"); |
| } |
| } |
| let obj = new TestRegExp("it"); |
| |
| assert(accesses == "constructor", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "constructor,constructor", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // An object with species constructor: Testing ES6 21.2.5.11: 4. Let C be ? SpeciesConstructor(rx, %RegExp%). |
| (function () { |
| let accesses = []; |
| let obj = { constructor: {} }; |
| obj.constructor[Symbol.species] = function() { |
| accesses.push("constructor"); |
| return /it/y; |
| }; |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "constructor", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // RegExp object with overridden flags: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let flags = [ "flags", "global", "ignoreCase", "multiline", "sticky", "unicode" ]; |
| let flagValues = [ "", false, false, false, false, false ]; |
| for (let index in flags) { |
| (function(flag, flagValue) { |
| let accesses = []; |
| let obj = /it/; |
| Object.defineProperty(obj, flag, { |
| get: function() { |
| accesses.push(flag); |
| passed = true; |
| return flagValue; |
| } |
| }); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == flag, "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| }) (flags[index], flagValues[index]); |
| } |
| })(); |
| |
| // RegExp subclass with overridden flags in subclass method: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let flags = [ "flags", "global", "ignoreCase", "multiline", "sticky", "unicode" ]; |
| let flagValues = [ "", false, false, false, false, false ]; |
| for (let index in flags) { |
| (function(flag, flagValue) { |
| let accesses = []; |
| class TestRegExp extends RegExp { |
| get [flag]() { |
| accesses.push(flag); |
| return flagValue; |
| } |
| }; |
| let obj = new TestRegExp(/it/); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == flag, "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| |
| }) (flags[index], flagValues[index]); |
| } |
| })(); |
| |
| // RegExp subclass with overridden flags using Object.defineProperty: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let flags = [ "flags", "global", "ignoreCase", "multiline", "sticky", "unicode" ]; |
| let flagValues = [ "", false, false, false, false, false ]; |
| for (let index in flags) { |
| (function(flag, flagValue) { |
| let accesses = []; |
| class TestRegExp extends RegExp { }; |
| let obj = new TestRegExp(/it/); |
| |
| Object.defineProperty(obj, flag, { |
| get: function() { |
| accesses.push(flag); |
| return flagValue; |
| } |
| }); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == flag, "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| |
| }) (flags[index], flagValues[index]); |
| } |
| })(); |
| |
| // Any object with species constructor: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let accesses = []; |
| let obj = { constructor: {} }; |
| obj.constructor[Symbol.species] = function() { |
| accesses.push("constructor"); |
| return /it/y; |
| }; |
| |
| Object.defineProperty(obj, "flags", { |
| get: function() { |
| accesses.push("flags"); |
| return ""; |
| } |
| }); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "flags,constructor", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // Any object with custom prototype: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let accesses = []; |
| let TestRegExpProto = { |
| get flags() { |
| accesses.push("flags"); |
| return ""; |
| }, |
| toString() { |
| accesses.push("toString"); |
| return this._regex.toString(); |
| }, |
| get source() { |
| accesses.push("source"); |
| return this._regex.source; |
| } |
| } |
| TestRegExpProto.__proto__ = RegExp.prototype; |
| |
| let TestRegExp = function(regex) { |
| accesses.push("constructor"); |
| this._regex = new RegExp(regex); |
| } |
| TestRegExp.prototype = TestRegExpProto; |
| TestRegExpProto.constructor = TestRegExp; |
| |
| let obj = new TestRegExp(/it/); |
| |
| assert(accesses == "constructor", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "constructor,flags,source", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // 2 levels of subclasses: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let accesses = []; |
| |
| class RegExpB extends RegExp { |
| get flags() { |
| accesses.push("flags"); |
| return ""; |
| } |
| } |
| class RegExpC extends RegExpB { } |
| |
| assert(RegExpB.__proto__ == RegExp); |
| assert(RegExpC.__proto__ == RegExpB); |
| |
| let obj = new RegExpC(/it/); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "flags", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // 2 levels of subclasses with substituted prototype before instantiation: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let accesses = []; |
| |
| class B extends RegExp { } |
| class C extends B { } |
| |
| assert(B.__proto__ === RegExp); |
| assert(C.__proto__ === B); |
| assert(B.prototype.__proto__ === RegExp.prototype); |
| assert(C.prototype.__proto__ === B.prototype); |
| |
| let X = function () {} |
| Object.defineProperty(X.prototype, "flags", { |
| get: function() { |
| accesses.push("flags"); |
| return ""; |
| } |
| }); |
| Object.defineProperty(X.prototype, "exec", { |
| value: function(str) { |
| accesses.push("exec"); |
| var matchResult = /it/y.exec(str.substr(this.lastIndex)); |
| if (matchResult) |
| this.lastIndex += 2; // length of "it". |
| return matchResult; |
| } |
| }); |
| |
| // Monkey with the prototype chain before instantiating C. |
| X.__proto__ = RegExp; |
| X.prototype.__proto__ = RegExp.prototype; |
| C.__proto__ = X; |
| C.prototype.__proto__ = X.prototype; |
| |
| assert(X.__proto__ === RegExp); |
| assert(C.__proto__ === X); |
| assert(X.prototype.__proto__ === RegExp.prototype); |
| assert(C.prototype.__proto__ === X.prototype); |
| |
| let obj = new C; |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "flags,exec,exec,exec,exec,exec,exec", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // 2 levels of subclasses with substituted prototype after instantiation: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let accesses = []; |
| |
| class B extends RegExp { } |
| class C extends B { } |
| |
| assert(B.__proto__ === RegExp); |
| assert(C.__proto__ === B); |
| assert(B.prototype.__proto__ === RegExp.prototype); |
| assert(C.prototype.__proto__ === B.prototype); |
| |
| let X = function () {} |
| Object.defineProperty(X.prototype, "flags", { |
| get: function() { |
| accesses.push("flags"); |
| return ""; |
| } |
| }); |
| Object.defineProperty(X.prototype, "exec", { |
| value: function(str) { |
| accesses.push("exec"); |
| var matchResult = /it/y.exec(str.substr(this.lastIndex)); |
| if (matchResult) |
| this.lastIndex += 2; // length of "it". |
| return matchResult; |
| } |
| }); |
| |
| // Instantiate C before monkeying with the prototype chain. |
| let obj = new C(); |
| |
| X.__proto__ = RegExp; |
| X.prototype.__proto__ = RegExp.prototype; |
| C.__proto__ = X; |
| C.prototype.__proto__ = X.prototype; |
| |
| assert(X.__proto__ === RegExp); |
| assert(C.__proto__ === X); |
| assert(X.prototype.__proto__ === RegExp.prototype); |
| assert(C.prototype.__proto__ === X.prototype); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "flags,exec,exec,exec,exec,exec,exec", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // 2 levels of subclasses with proxied prototype: Testing ES6 21.2.5.11: 5. Let flags be ? ToString(? Get(rx, "flags")). |
| (function () { |
| let accesses = []; |
| |
| class B extends RegExp { }; |
| |
| assert(B.__proto__ === RegExp); |
| assert(B.prototype.__proto__ === RegExp.prototype); |
| |
| let proxy = new Proxy(RegExp.prototype, { |
| get: function(obj, prop) { |
| accesses.push(prop.toString()); |
| if (prop === "exec") { |
| return function(str) { |
| accesses.push("in_exec"); |
| var matchResult = /it/y.exec(str.substr(this.lastIndex)); |
| if (matchResult) |
| this.lastIndex += 2; // length of "it". |
| return matchResult; |
| } |
| } |
| return obj[prop]; |
| } |
| }); |
| B.prototype.__proto__ = proxy; |
| |
| let obj = new B(); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "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"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // RegExp subclass with overridden exec: Testing ES6 21.2.5.11: 19.b. Let z be ? RegExpExec(splitter, S). |
| (function () { |
| let accesses = []; |
| class TestRegExp extends RegExp { |
| exec(str) { |
| accesses.push("exec"); |
| return RegExp.prototype.exec.call(this, str); |
| } |
| }; |
| let obj = new TestRegExp(/it/); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(obj, "splitme"); |
| assert(accesses == "exec,exec,exec,exec,exec,exec", "Property accesses do not match expectation"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // Proxied RegExp observing every get. |
| (function () { |
| let accesses = []; |
| let regexp = new RegExp(/it/); |
| let proxy = new Proxy(regexp, { |
| get(obj, prop) { |
| accesses.push(prop.toString()); |
| return obj[prop]; |
| } |
| }); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(proxy, "splitme"); |
| // Note: @@split creates a new instance of the RegExp using its @@species, and performs |
| // the split operation with that new instance. Hence, the proxy is only able to observe |
| // gets up to the creation of the new instance. |
| assert(accesses == "constructor,flags,Symbol(Symbol.match),source", |
| "Proxy not able to observe some gets"); |
| assert(result == "spl,me", "Unexpected result"); |
| })(); |
| |
| // Proxied RegExp (without @@match) observing every get. |
| // This is to force the RegExp @species constructor to access source. |
| (function () { |
| let accesses = []; |
| let regexp = new RegExp(/it/); |
| let proxy = new Proxy(regexp, { |
| get(obj, prop) { |
| accesses.push(prop.toString()); |
| if (prop == Symbol.match) |
| return undefined; |
| return obj[prop]; |
| } |
| }); |
| |
| assert(accesses == "", "unexpected call to overridden props"); |
| let result = RegExp.prototype[Symbol.split].call(proxy, "splitme"); |
| // Note: @@split creates a new instance of the RegExp using its @@species, and performs |
| // the split operation with that new instance. Hence, the proxy is only able to observe |
| // gets up to the creation of the new instance. |
| assert(accesses == "constructor,flags,Symbol(Symbol.match),Symbol(Symbol.toPrimitive),toString,source,flags", |
| "Proxy not able to observe some gets"); |
| // The new instance of the RegExp would have been constructed with the pattern from |
| // the proxy toString() i.e. "\/lt\/" instead of source, because the proxy is an |
| // object without a [@@match] property. |
| assert(result == "splitme", "Unexpected result"); |
| })(); |
| |