blob: f0fb8f68578e5de69f82a8920b8fda5d4c10c6ed [file] [log] [blame]
//@ runNoFTL
function assert(testedValue, msg) {
if (!testedValue)
throw Error(msg);
}
// RegExp subclass should not be able to override lastIndex.
(function () {
let accesses = [];
class SubRegExp extends RegExp {
get lastIndex() {
accesses.push("getLastIndex");
return super.lastIndex;
}
set lastIndex(newIndex) {
accesses.push("setLastIndex");
super.lastIndex = newIndex;
}
}
let obj = new SubRegExp(/rch/);
assert(accesses == "", "Should not be able to override lastIndex");
let result = RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(accesses == "", "Should not be able to override lastIndex");
assert(result === 3, "Unexpected result");
})();
// RegExp subclass overriding exec.
(function () {
let accesses = [];
class SubRegExp extends RegExp {
exec(str) {
accesses.push("exec");
return super.exec(str);
}
}
let obj = new SubRegExp(/rch/);
assert(accesses == "", "unexpected call to overridden props");
let result = RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(accesses == "exec", "Property accesses do not match expectation");
assert(result === 3, "Unexpected result");
})();
// Any object with custom prototype overriding lastIndex.
(function () {
let accesses = [];
let TestRegExpProto = {
get lastIndex() {
accesses.push("getLastIndex");
return this._regex.lastIndex;
},
set lastIndex(newIndex) {
accesses.push("setLastIndex");
this._regex.lastIndex = newIndex;
},
}
TestRegExpProto.__proto__ = RegExp.prototype;
let TestRegExp = function(regex) {
this._regex = new RegExp(regex);
}
TestRegExp.prototype = TestRegExpProto;
TestRegExpProto.constructor = TestRegExp;
let obj = new TestRegExp(/rch/);
assert(accesses == "", "unexpected call to overridden props");
try {
RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(false, "Error not thrown");
} catch (e) {
assert(e.toString() == "TypeError: Builtin RegExp exec can only be called on a RegExp object",
"Unexpected error message");
}
assert(accesses == "getLastIndex", "Property accesses do not match expectation");
})();
// Any object with custom prototype overriding exec.
(function () {
let accesses = [];
let TestRegExpProto = {
exec(str) {
accesses.push("exec");
return this._regex.exec(str);
}
}
TestRegExpProto.__proto__ = RegExp.prototype;
let TestRegExp = function(regex) {
this._regex = new RegExp(regex);
}
TestRegExp.prototype = TestRegExpProto;
TestRegExpProto.constructor = TestRegExp;
let obj = new TestRegExp(/rch/);
assert(accesses == "", "unexpected call to overridden props");
let result = RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(accesses == "exec", "Property accesses do not match expectation");
assert(result === 3, "Unexpected result");
})();
// 2 levels of RegExp subclasses with the middle parent overriding exec.
(function () {
let accesses = [];
class RegExpB extends RegExp {
exec(str) {
accesses.push("exec");
return super.exec(str);
}
}
class RegExpC extends RegExpB { }
assert(RegExpB.__proto__ == RegExp);
assert(RegExpC.__proto__ == RegExpB);
let obj = new RegExpC(/rch/);
assert(accesses == "", "unexpected call to overridden props");
let result = RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(accesses == "exec", "Property accesses do not match expectation");
assert(result === 3, "Unexpected result");
})();
// 2 levels of RegExp subclasses with substituted prototype before instantiation.
(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, "exec", {
value: function(str) {
accesses.push("exec");
return /rch/.exec(str);
}
});
Object.defineProperty(X.prototype, "lastIndex", {
get: function() {
accesses.push("getLastIndex");
return 0;
},
set: function(value) {
accesses.push("setLastIndex");
}
});
// 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.search].call(obj, "searchme");
assert(accesses == "getLastIndex,exec,getLastIndex", "Property accesses do not match expectation");
assert(result === 3, "Unexpected result");
})();
// 2 levels of RegExp subclasses with substituted prototype after instantiation.
(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 obj = new C();
let X = function () {}
Object.defineProperty(X.prototype, "exec", {
value: function(str) {
accesses.push("exec");
return /rch/.exec(str);
}
});
Object.defineProperty(X.prototype, "lastIndex", {
get: function() {
accesses.push("getLastIndex");
return 0;
},
set: function(value) {
accesses.push("setLastIndex");
}
});
// Monkey with the prototype chain after 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);
assert(accesses == "", "unexpected call to overridden props");
let result = RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(accesses == "exec", "Property accesses do not match expectation");
assert(result === 3, "Unexpected result");
})();
// 2 levels of RegExp subclasses with proxied prototype.
(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("get_" + prop.toString());
function proxyExec(str) {
accesses.push("exec");
return /rch/.exec(str);
}
if (prop === "exec")
return proxyExec;
return obj[prop];
},
set: function(obj, prop, value) {
accesses.push("set_" + prop.toString());
}
});
B.prototype.__proto__ = proxy;
let obj = new B();
assert(accesses == "", "unexpected call to overridden props");
let result = RegExp.prototype[Symbol.search].call(obj, "searchme");
assert(accesses == "get_exec,exec", "Property accesses do not match expectation");
assert(result === 3, "Unexpected result");
})();
// Proxied RegExp observing every get.
(function () {
let accesses = [];
let regexp = new RegExp(/rch/);
let proxy = new Proxy(regexp, {
get(obj, prop) {
accesses.push(prop.toString());
if (prop == "exec") {
return function(str) {
return obj.exec(str);
}
}
return obj[prop];
}
});
assert(accesses == "", "unexpected call to overridden props");
let result = RegExp.prototype[Symbol.search].call(proxy, "searchme");
assert(accesses.toString() == "lastIndex,exec,lastIndex", "Proxy not able to observe some gets");
assert(result === 3, "Unexpected result");
})();