mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
671 lines
13 KiB
JavaScript
671 lines
13 KiB
JavaScript
// Copyright JS Foundation and other contributors, http://js.foundation
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
var replace = RegExp.prototype[Symbol.replace];
|
|
|
|
try {
|
|
replace.call (0, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
try {
|
|
replace.call (new RegExp(), {
|
|
toString: () => {
|
|
throw "abrupt string"
|
|
}
|
|
}, "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt string");
|
|
}
|
|
|
|
try {
|
|
replace.call (new RegExp(), "string", {
|
|
toString: () => {
|
|
throw "abrupt replace"
|
|
}
|
|
});
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt replace");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
get global() {
|
|
throw "abrupt global"
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt global");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
global: true,
|
|
set lastIndex(idx) {
|
|
throw "abrupt lastIndex"
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt lastIndex");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
get exec() {
|
|
throw "abrupt exec"
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt exec");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: RegExp.prototype.exec
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: 42
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
throw "abrupt exec result"
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt exec result");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return 1
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
get length() {
|
|
throw "abrupt result length"
|
|
}
|
|
}
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt result length");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
global: true,
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
throw "abrupt match"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt match");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
global: true,
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
return {
|
|
toString: () => {
|
|
throw "abrupt match toString"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt match toString");
|
|
}
|
|
|
|
var result_obj = {
|
|
toString: () => {
|
|
Object.defineProperty (result_obj, 'toString', {
|
|
value: () => {
|
|
throw "abrupt match toString delayed";
|
|
}
|
|
});
|
|
return "str";
|
|
}
|
|
}
|
|
|
|
var first = true;
|
|
try {
|
|
replace.call ({
|
|
global: true,
|
|
exec: () => {
|
|
if (!first) {
|
|
return null;
|
|
}
|
|
|
|
first = false;
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
return result_obj;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt match toString delayed");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
global: true,
|
|
get lastIndex() {
|
|
throw "abrupt lastIndex get"
|
|
},
|
|
set lastIndex(i) {},
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
return {
|
|
toString: () => {
|
|
return ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt lastIndex get");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
global: true,
|
|
get lastIndex() {
|
|
return {
|
|
valueOf: () => {
|
|
throw "abrupt lastIndex toNumber"
|
|
}
|
|
}
|
|
},
|
|
set lastIndex(i) {},
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
return {
|
|
toString: () => {
|
|
return ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt lastIndex toNumber");
|
|
}
|
|
|
|
var o = {
|
|
global: true,
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
return {
|
|
toString: () => {
|
|
return ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Object.defineProperty (o, 'lastIndex', {
|
|
configurable: true,
|
|
get: () => {
|
|
Object.defineProperty (o, 'lastIndex', {
|
|
get: () => {
|
|
return {
|
|
valueOf: () => {
|
|
return 42
|
|
}
|
|
};
|
|
},
|
|
set: (i) => {
|
|
throw "abrupt lastIndex put";
|
|
},
|
|
configurable: true
|
|
});
|
|
return {
|
|
valueOf: () => {
|
|
return 24
|
|
}
|
|
};
|
|
},
|
|
set: (i) => {}
|
|
});
|
|
|
|
try {
|
|
replace.call (o,
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt lastIndex put");
|
|
}
|
|
|
|
o = {
|
|
global: true,
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
get 0() {
|
|
return {
|
|
toString: () => {
|
|
return ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
};
|
|
Object.defineProperty (o, 'lastIndex', {
|
|
get: () => {
|
|
Object.defineProperty (o, 'lastIndex', {
|
|
value: 0,
|
|
writable: false
|
|
});
|
|
return 0;
|
|
},
|
|
set: () => {}
|
|
});
|
|
|
|
try {
|
|
replace.call (o,
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
o = {
|
|
global: true
|
|
};
|
|
Object.defineProperty (o, 'exec', {
|
|
configurable: true,
|
|
value: () => {
|
|
Object.defineProperty (o, 'exec', {
|
|
get: () => {
|
|
throw "abrupt exec"
|
|
},
|
|
set: (v) => {}
|
|
});
|
|
return {
|
|
length: 1,
|
|
0: "thisisastring"
|
|
}
|
|
}
|
|
});
|
|
|
|
try {
|
|
replace.call (o,
|
|
"string",
|
|
"replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt exec");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
0: "str",
|
|
get index() {
|
|
throw "abrupt index"
|
|
}
|
|
}
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt index");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
length: 1,
|
|
0: "str",
|
|
get index() {
|
|
return {
|
|
valueOf: () => {
|
|
throw "abrupt index toNumber"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt index toNumber");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
length: 2,
|
|
0: "str",
|
|
index: 0,
|
|
get 1() {
|
|
throw "abrupt capture"
|
|
}
|
|
}
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt capture");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
length: 2,
|
|
0: "str",
|
|
index: 0,
|
|
1: {
|
|
toString: () => {
|
|
throw "abrupt capture toString"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt capture toString");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
length: 2,
|
|
0: "str",
|
|
index: 0,
|
|
1: "st"
|
|
}
|
|
}
|
|
}, "string", () => {
|
|
throw "abrupt replace"
|
|
});
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt replace");
|
|
}
|
|
|
|
try {
|
|
replace.call ({
|
|
exec: () => {
|
|
return {
|
|
length: 2,
|
|
0: "str",
|
|
index: 0,
|
|
1: "st"
|
|
}
|
|
}
|
|
}, "string", () => {
|
|
return {
|
|
toString: () => {
|
|
throw "abrupt replace toString"
|
|
}
|
|
}
|
|
});
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt replace toString");
|
|
}
|
|
|
|
try {
|
|
replace.call (/abc/, "abc", () => {
|
|
throw "fastpath abrupt replace"
|
|
});
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "fastpath abrupt replace");
|
|
}
|
|
|
|
try {
|
|
replace.call (/abc/, "abc", () => {
|
|
return {
|
|
toString: () => {
|
|
throw "fastpath abrupt replace"
|
|
}
|
|
}
|
|
});
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "fastpath abrupt replace");
|
|
}
|
|
|
|
assert (replace.call (/abc/, "abc", "xyz") === "xyz");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "xyz") === "abxyzfg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$$-") === "ab-$-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$&-") === "ab-cde-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$`-") === "ab-ab-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$'-") === "ab-fg-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$0-") === "ab-$0-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$1-") === "ab-c-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$2-") === "ab-d-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$3-") === "ab-d-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$4-") === "ab--fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$5-") === "ab-e-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$6-") === "ab-$6-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$00-") === "ab-$00-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$01-") === "ab-c-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$10-") === "ab-c0-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$99-") === "ab-$99-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "-$$1-") === "ab-$1-fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "$") === "ab$fg");
|
|
assert (replace.call (/(c)((d)|(x))(e)/, "abcdefg", "$@") === "ab$@fg");
|
|
|
|
replace.call (/(c)((d)|(x))(e)/, "abcdefg", function () {
|
|
assert (arguments[0] === "cde");
|
|
assert (arguments[1] === "c");
|
|
assert (arguments[2] === "d");
|
|
assert (arguments[3] === "d");
|
|
assert (arguments[4] === undefined);
|
|
assert (arguments[5] === "e");
|
|
assert (arguments[6] === 2);
|
|
assert (arguments[7] === "abcdefg");
|
|
});
|
|
|
|
var re = /ab/g;
|
|
assert (replace.call (re, "-ab-ab-ab-ab-", "cd") === "-cd-cd-cd-cd-");
|
|
assert (re.lastIndex === 0);
|
|
|
|
re.lastIndex = 5;
|
|
assert (replace.call (re, "-ab-ab-ab-ab-", "cd") === "-cd-cd-cd-cd-");
|
|
assert (re.lastIndex === 0);
|
|
|
|
assert (replace.call (/(?:)/g, "string", "Duck") === "DucksDucktDuckrDuckiDucknDuckgDuck");
|
|
|
|
class Regexplike {
|
|
constructor() {
|
|
this.index = 0;
|
|
this.global = true;
|
|
}
|
|
|
|
exec() {
|
|
if (this.index > 0) {
|
|
return null;
|
|
}
|
|
|
|
this.index = 39;
|
|
var result = {
|
|
length: 1,
|
|
0: "Duck",
|
|
index: this.index
|
|
};
|
|
return result;
|
|
}
|
|
}
|
|
|
|
re = new Regexplike();
|
|
|
|
/* Well-behaved RegExp-like object. */
|
|
assert (replace.call (re, "What have you brought upon this cursed land", "$&") === "What have you brought upon this cursed Duck");
|
|
|
|
var replace_count = 0;
|
|
|
|
function replacer() {
|
|
replace_count++;
|
|
return arguments[0];
|
|
}
|
|
|
|
re.index = 0;
|
|
re.exec = function () {
|
|
if (this.index > 3) {
|
|
return null;
|
|
}
|
|
|
|
var result = {
|
|
length: 1,
|
|
0: "Duck",
|
|
index: this.index++
|
|
};
|
|
return result;
|
|
}
|
|
|
|
/* Mis-behaving RegExp-like object, replace function is called on each match, but the result is ignored for inconsistent matches. */
|
|
assert (replace.call (re, "Badger", replacer) === "Ducker");
|
|
assert (replace_count === 4);
|
|
|
|
re.index = 0;
|
|
assert (replace.call (re, "Badger", "Ord") === "Order");
|
|
|
|
try {
|
|
replace.call (RegExp.prototype, "string", "replace");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
/* Object with custom @@replace method */
|
|
var o = {}
|
|
o[Symbol.replace] = function () {
|
|
return "Duck"
|
|
};
|
|
assert ("string".replace (o, "Mallard") === "Duck");
|
|
|
|
o[Symbol.replace] = 42;
|
|
try {
|
|
"string".replace (o, "Duck");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e instanceof TypeError);
|
|
}
|
|
|
|
Object.defineProperty (o, Symbol.replace, {
|
|
get: () => {
|
|
throw "abrupt @@replace get"
|
|
},
|
|
set: (v) => {}
|
|
});
|
|
|
|
try {
|
|
"string".replace (o, "Duck");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt @@replace get");
|
|
}
|
|
|
|
o = {};
|
|
o[Symbol.replace] = function () {
|
|
throw "abrupt @@replace"
|
|
};
|
|
try {
|
|
"string".replace (o, "str");
|
|
assert (false);
|
|
} catch (e) {
|
|
assert (e === "abrupt @@replace")
|
|
}
|
|
|
|
class Regexplike2 {
|
|
exec() {
|
|
return {}
|
|
}
|
|
}
|
|
re = new Regexplike2();
|
|
assert (replace.call (re, "1") === "undefined");
|