Backed out changeset 537d40121b6d (bug 887016)

This commit is contained in:
Tooru Fujisawa 2016-03-28 06:49:54 +09:00
parent 0be2d6fd80
commit ab25ebfb49
18 changed files with 225 additions and 743 deletions

View File

@ -644,7 +644,6 @@ const JSFunctionSpec js::regexp_methods[] = {
JS_FN("compile", regexp_compile, 2,0),
JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0),
JS_SELF_HOSTED_FN("test", "RegExpTest" , 1,0),
JS_SELF_HOSTED_SYM_FN(match, "RegExpMatch", 1,0),
JS_FS_END
};

View File

@ -56,97 +56,6 @@ function RegExpToString()
}
_SetCanonicalName(RegExpToString, "toString");
// ES 2016 draft Mar 25, 2016 21.2.5.2.3.
function AdvanceStringIndex(S, index) {
// Step 1.
assert(typeof S === "string", "Expected string as 1st argument");
// Step 2.
assert(index >= 0 && index <= MAX_NUMERIC_INDEX, "Expected integer as 2nd argument");
// Step 3 (skipped).
// Step 4 (skipped).
// Step 5.
var length = S.length;
// Step 6.
if (index + 1 >= length)
return index + 1;
// Step 7.
var first = callFunction(std_String_charCodeAt, S, index);
// Step 8.
if (first < 0xD800 || first > 0xDBFF)
return index + 1;
// Step 9.
var second = callFunction(std_String_charCodeAt, S, index + 1);
// Step 10.
if (second < 0xDC00 || second > 0xDFFF)
return index + 1;
// Step 11.
return index + 2;
}
// ES 2016 draft Mar 25, 2016 21.2.5.6.
function RegExpMatch(string) {
// Step 1.
var rx = this;
// Step 2.
if (!IsObject(rx))
ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, rx === null ? "null" : typeof rx);
// Step 3.
var S = ToString(string);
// Steps 4-5.
if (!rx.global)
return RegExpExec(rx, S, false);
// Step 6.a.
var fullUnicode = !!rx.unicode;
// Step 6.b.
rx.lastIndex = 0;
// Step 6.c.
var A = [];
// Step 6.d.
var n = 0;
// Step 6.e.
while (true) {
// Step 6.e.i.
var result = RegExpExec(rx, S, false);
// Step 6.e.ii.
if (result === null)
return (n === 0) ? null : A;
// Step 6.e.iii.1.
var matchStr = ToString(result[0]);
// Step 6.e.iii.2.
_DefineDataProperty(A, n, matchStr);
// Step 6.e.iii.4.
if (matchStr === "") {
var lastIndex = ToLength(rx.lastIndex);
rx.lastIndex = fullUnicode ? AdvanceStringIndex(S, lastIndex) : lastIndex + 1;
}
// Step 6.e.iii.5.
n++;
}
}
// ES6 21.2.5.2.
// NOTE: This is not RegExpExec (21.2.5.2.1).
function RegExp_prototype_Exec(string) {

View File

@ -4,73 +4,6 @@
/*global intl_Collator: false, */
function StringProtoHasNoMatch() {
var ObjectProto = GetBuiltinPrototype("Object");
var StringProto = GetBuiltinPrototype("String");
if (!ObjectHasPrototype(StringProto, ObjectProto))
return false;
return !(std_match in StringProto);
}
function IsStringMatchOptimizable() {
var RegExpProto = GetBuiltinPrototype("RegExp");
// If RegExpPrototypeOptimizable succeeds, `exec` and `@@match` are
// guaranteed to be data properties.
return RegExpPrototypeOptimizable(RegExpProto) &&
RegExpProto.exec === RegExp_prototype_Exec &&
RegExpProto[std_match] === RegExpMatch;
}
// ES 2016 draft Mar 25, 2016 21.1.3.11.
function String_match(regexp) {
// Step 1.
RequireObjectCoercible(this);
// Step 2.
var isPatternString = (typeof regexp === "string");
if (!(isPatternString && StringProtoHasNoMatch()) && regexp !== undefined && regexp !== null) {
// Step 2.a.
var matcher = GetMethod(regexp, std_match);
// Step 2.b.
if (matcher !== undefined)
return callContentFunction(matcher, regexp, this);
}
// Step 3.
var S = ToString(this);
// FIXME: Non-standard flags argument (bug 1108382).
var flags = undefined;
if (arguments.length > 1) {
if (IsMatchFlagsArgumentEnabled())
flags = arguments[1];
WarnOnceAboutFlagsArgument();
} else {
if (isPatternString && IsStringMatchOptimizable()) {
var flatResult = FlatStringMatch(S, regexp);
if (flatResult !== undefined)
return flatResult;
}
}
// Step 4.
var rx = RegExpCreate(regexp, flags);
// Step 5 (optimized case).
if (IsStringMatchOptimizable() && !flags)
return RegExpMatcher(rx, S, 0, false);
// Step 5.
return callContentFunction(GetMethod(rx, std_match), rx, S);
}
function String_generic_match(thisValue, regexp) {
if (thisValue === undefined)
ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.match');
return callFunction(String_match, thisValue, regexp);
}
/* ES6 Draft Oct 14, 2014 21.1.3.19 */
function String_substring(start, end) {
// Steps 1-3.

View File

@ -1,119 +0,0 @@
setJitCompilerOption("ion.warmup.trigger", 4);
function testBasic() {
var f = function() {
var result = "abc".match("b");
assertEq(result.length, 1);
assertEq(result.index, 1);
assertEq(result[0], "b");
};
for (var i = 0; i < 40; i++) {
f();
}
}
testBasic();
function testMod(apply, unapply) {
var f = function(applied) {
var result = "abc".match("b");
assertEq(result.length, 1);
if (applied) {
assertEq(result[0], "mod");
} else {
assertEq(result.index, 1);
assertEq(result[0], "b");
}
};
var applied = false;
for (var i = 0; i < 120; i++) {
f(applied);
if (i == 40) {
apply();
applied = true;
}
if (i == 80) {
unapply();
applied = false;
}
}
}
testMod(() => {
String.prototype[Symbol.match] = () => ["mod"];
}, () => {
delete String.prototype[Symbol.match];
});
testMod(() => {
Object.prototype[Symbol.match] = () => ["mod"];
}, () => {
delete Object.prototype[Symbol.match];
});
testMod(() => {
Object.setPrototypeOf(String.prototype, {
[Symbol.match]: () => ["mod"]
});
}, () => {
Object.setPrototypeOf(String.prototype, Object.prototype);
});
var orig_exec = RegExp.prototype.exec;
testMod(() => {
RegExp.prototype.exec = () => ["mod"];
}, () => {
RegExp.prototype.exec = orig_exec;
});
var orig_match = RegExp.prototype[Symbol.match];
testMod(() => {
RegExp.prototype[Symbol.match] = () => ["mod"];
}, () => {
RegExp.prototype[Symbol.match] = orig_match;
});
var observed = false;
function testObserved(apply, unapply) {
var f = function(applied) {
observed = false;
var result = "abc".match("b."); // Use meta char to avoid flat match.
assertEq(result.length, 1);
assertEq(result.index, 1);
assertEq(result[0], "bc");
assertEq(observed, applied);
};
var applied = false;
for (var i = 0; i < 120; i++) {
f(applied);
if (i == 40) {
apply();
applied = true;
}
if (i == 80) {
unapply();
applied = false;
}
}
}
var orig_global = Object.getOwnPropertyDescriptor(RegExp.prototype, "global");
testObserved(() => {
Object.defineProperty(RegExp.prototype, "global", {
get: function() {
observed = true;
return false;
}
});
}, () => {
Object.defineProperty(RegExp.prototype, "global", orig_global);
});
var orig_sticky = Object.getOwnPropertyDescriptor(RegExp.prototype, "sticky");
testObserved(() => {
Object.defineProperty(RegExp.prototype, "sticky", {
get: function() {
observed = true;
return false;
}
});
}, () => {
Object.defineProperty(RegExp.prototype, "sticky", orig_sticky);
});

View File

@ -2190,6 +2190,7 @@ MustCloneRegExpForCall(MCall* call, uint32_t useIndex)
if (useIndex == MCall::IndexOfArgument(0) &&
(target->native() == str_split ||
target->native() == str_replace ||
target->native() == str_match ||
target->native() == str_search))
{
return false;

View File

@ -444,7 +444,6 @@ MSG_DEF(JSMSG_UNDEFINED_CURRENCY, 0, JSEXN_TYPEERR, "undefined currency in
MSG_DEF(JSMSG_BACK_REF_OUT_OF_RANGE, 0, JSEXN_SYNTAXERR, "back reference out of range in regular expression")
MSG_DEF(JSMSG_BAD_CLASS_RANGE, 0, JSEXN_SYNTAXERR, "invalid range in character class")
MSG_DEF(JSMSG_ESCAPE_AT_END_OF_REGEXP, 0, JSEXN_SYNTAXERR, "\\ at end of pattern")
MSG_DEF(JSMSG_EXEC_NOT_OBJORNULL, 0, JSEXN_TYPEERR, "RegExp exec method should return object or null")
MSG_DEF(JSMSG_INVALID_DECIMAL_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid decimal escape in regular expression")
MSG_DEF(JSMSG_INVALID_GROUP, 0, JSEXN_SYNTAXERR, "invalid regexp group")
MSG_DEF(JSMSG_INVALID_IDENTITY_ESCAPE, 0, JSEXN_SYNTAXERR, "invalid identity escape in regular expression")

View File

@ -2260,6 +2260,32 @@ class MOZ_STACK_CLASS StringRegExpGuard
} /* anonymous namespace */
static bool
DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
RegExpShared& re)
{
ScopedMatchPairs matches(&cx->tempLifoAlloc());
bool sticky = re.sticky();
RegExpRunStatus status = re.execute(cx, input, 0, sticky, &matches, nullptr);
if (status == RegExpRunStatus_Error)
return false;
if (status == RegExpRunStatus_Success_NotFound) {
args.rval().setNull();
return true;
}
if (!res->updateFromMatchPairs(cx, input, matches))
return false;
RootedValue rval(cx);
if (!CreateRegExpMatchResult(cx, input, matches, &rval))
return false;
args.rval().set(rval);
return true;
}
/* ES6 21.2.5.2.3. */
static size_t
AdvanceStringIndex(HandleLinearString input, size_t length, size_t index, bool unicode)
@ -2295,6 +2321,199 @@ AdvanceStringIndex(HandleLinearString input, size_t length, size_t index, bool u
return index + 2;
}
/* ES5 15.5.4.10 step 8. */
static bool
DoMatchGlobal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
StringRegExpGuard& g)
{
// Step 8a.
//
// This single zeroing of "lastIndex" covers all "lastIndex" changes in the
// rest of String.prototype.match, particularly in steps 8f(i) and
// 8f(iii)(2)(a). Here's why.
//
// The inputs to the calls to RegExp.prototype.exec are a RegExp object
// whose .global is true and a string. The only side effect of a call in
// these circumstances is that the RegExp's .lastIndex will be modified to
// the next starting index after the discovered match (or to 0 if there's
// no remaining match). Because .lastIndex is a non-configurable data
// property and no script-controllable code executes after step 8a, passing
// step 8a implies *every* .lastIndex set succeeds. String.prototype.match
// calls RegExp.prototype.exec repeatedly, and the last call doesn't match,
// so the final value of .lastIndex is 0: exactly the state after step 8a
// succeeds. No spec step lets script observe intermediate .lastIndex
// values.
//
// The arrays returned by RegExp.prototype.exec always have a string at
// index 0, for which [[Get]]s have no side effects.
//
// Filling in a new array using [[DefineOwnProperty]] is unobservable.
//
// This is a tricky point, because after this set, our implementation *can*
// fail. The key is that script can't distinguish these failure modes from
// one where, in spec terms, we fail immediately after step 8a. That *in
// reality* we might have done extra matching work, or created a partial
// results array to return, or hit an interrupt, is irrelevant. The
// script can't tell we did any of those things but didn't update
// .lastIndex. Thus we can optimize steps 8b onward however we want,
// including eliminating intermediate .lastIndex sets, as long as we don't
// add ways for script to observe the intermediate states.
//
// In short: it's okay to cheat (by setting .lastIndex to 0, once) because
// we can't get caught.
if (!g.zeroLastIndex(cx))
return false;
// Step 8b.
AutoValueVector elements(cx);
size_t lastSuccessfulStart = 0;
// The loop variables from steps 8c-e aren't needed, as we use different
// techniques from the spec to implement step 8f's loop.
// Step 8f.
ScopedMatchPairs matches(&cx->tempLifoAlloc());
size_t charsLen = input->length();
RegExpShared& re = g.regExp();
bool unicode = re.unicode();
bool sticky = re.sticky();
for (size_t searchIndex = 0; searchIndex <= charsLen; ) {
if (!CheckForInterrupt(cx))
return false;
// Steps 8f(i-ii), minus "lastIndex" updates (see above).
RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, &matches, nullptr);
if (status == RegExpRunStatus_Error)
return false;
// Step 8f(ii).
if (status == RegExpRunStatus_Success_NotFound)
break;
lastSuccessfulStart = searchIndex;
MatchPair& match = matches[0];
// Steps 8f(iii)(1-3).
searchIndex = match.isEmpty()
? AdvanceStringIndex(input, charsLen, match.limit, unicode)
: match.limit;
// Step 8f(iii)(4-5).
JSLinearString* str = NewDependentString(cx, input, match.start, match.length());
if (!str)
return false;
if (!elements.append(StringValue(str)))
return false;
}
// Step 8g.
if (elements.empty()) {
args.rval().setNull();
return true;
}
// The last *successful* match updates the RegExpStatics. (Interestingly,
// this implies that String.prototype.match's semantics aren't those
// implied by the RegExp.prototype.exec calls in the ES5 algorithm.)
res->updateLazily(cx, input, &re, lastSuccessfulStart, sticky);
// Steps 8b, 8f(iii)(5-6), 8h.
JSObject* array = NewDenseCopiedArray(cx, elements.length(), elements.begin());
if (!array)
return false;
args.rval().setObject(*array);
return true;
}
static bool
BuildFlatMatchArray(JSContext* cx, HandleString textstr, const FlatMatch& fm, CallArgs* args)
{
if (fm.match() < 0) {
args->rval().setNull();
return true;
}
/* Get the templateObject that defines the shape and type of the output object */
JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
if (!templateObject)
return false;
RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, 1, templateObject));
if (!arr)
return false;
/* Store a Value for each pair. */
arr->setDenseInitializedLength(1);
arr->initDenseElement(0, StringValue(fm.pattern()));
/* Set the |index| property. (TemplateObject positions it in slot 0) */
arr->setSlot(0, Int32Value(fm.match()));
/* Set the |input| property. (TemplateObject positions it in slot 1) */
arr->setSlot(1, StringValue(textstr));
#ifdef DEBUG
RootedValue test(cx);
RootedId id(cx, NameToId(cx->names().index));
if (!NativeGetProperty(cx, arr, id, &test))
return false;
MOZ_ASSERT(test == arr->getSlot(0));
id = NameToId(cx->names().input);
if (!NativeGetProperty(cx, arr, id, &test))
return false;
MOZ_ASSERT(test == arr->getSlot(1));
#endif
args->rval().setObject(*arr);
return true;
}
/* ES5 15.5.4.10. */
bool
js::str_match(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Steps 1-2. */
RootedString str(cx, ThisToStringForStringProto(cx, args));
if (!str)
return false;
/* Steps 3-4, plus the trailing-argument "flags" extension. */
StringRegExpGuard g(cx);
if (!g.init(cx, args, true))
return false;
/* Fast path when the search pattern can be searched for as a string. */
if (const FlatMatch* fm = g.tryFlatMatch(cx, str, 1, args.length()))
return BuildFlatMatchArray(cx, str, *fm, &args);
/* Return if there was an error in tryFlatMatch. */
if (cx->isExceptionPending())
return false;
/* Create regular-expression internals as needed to perform the match. */
if (!g.normalizeRegExp(cx, false, 1, args))
return false;
RegExpStatics* res = cx->global()->getRegExpStatics(cx);
if (!res)
return false;
RootedLinearString linearStr(cx, str->ensureLinear(cx));
if (!linearStr)
return false;
/* Steps 5-6, 7. */
if (!g.regExp().global())
return DoMatchLocal(cx, args, res, linearStr, g.regExp());
/* Steps 6, 8. */
return DoMatchGlobal(cx, args, res, linearStr, g);
}
bool
js::str_search(JSContext* cx, unsigned argc, Value* vp)
{
@ -3992,7 +4211,7 @@ static const JSFunctionSpec string_methods[] = {
#endif
/* Perl-ish methods (search is actually Python-esque). */
JS_SELF_HOSTED_FN("match", "String_match", 1,0),
JS_FN("match", str_match, 1,JSFUN_GENERIC_NATIVE),
JS_FN("search", str_search, 1,JSFUN_GENERIC_NATIVE),
JS_INLINABLE_FN("replace", str_replace, 2,JSFUN_GENERIC_NATIVE, StringReplace),
JS_INLINABLE_FN("split", str_split, 2,JSFUN_GENERIC_NATIVE, StringSplit),
@ -4147,8 +4366,6 @@ static const JSFunctionSpec string_static_methods[] = {
JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0),
JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0),
JS_SELF_HOSTED_FN("match", "String_generic_match", 2,0),
// This must be at the end because of bug 853075: functions listed after
// self-hosted methods aren't available in self-hosted code.
#if EXPOSE_INTL_API
@ -5180,127 +5397,3 @@ js::PutEscapedString(char* buffer, size_t bufferSize, const Latin1Char* chars, s
template size_t
js::PutEscapedString(char* buffer, size_t bufferSize, const char16_t* chars, size_t length,
uint32_t quote);
static bool
FlatStringMatchHelper(JSContext* cx, HandleString str, HandleString pattern, bool* isFlat, int32_t* match)
{
RootedLinearString linearPattern(cx, pattern->ensureLinear(cx));
if (!linearPattern)
return false;
static const size_t MAX_FLAT_PAT_LEN = 256;
if (linearPattern->length() > MAX_FLAT_PAT_LEN || StringHasRegExpMetaChars(linearPattern)) {
*isFlat = false;
return true;
}
*isFlat = true;
if (str->isRope()) {
if (!RopeMatch(cx, &str->asRope(), linearPattern, match))
return false;
} else {
*match = StringMatch(&str->asLinear(), linearPattern);
}
return true;
}
static bool
BuildFlatMatchArray(JSContext* cx, HandleString str, HandleString pattern, int32_t match,
MutableHandleValue rval)
{
if (match < 0) {
rval.setNull();
return true;
}
/* Get the templateObject that defines the shape and type of the output object */
JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
if (!templateObject)
return false;
RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, 1, templateObject));
if (!arr)
return false;
/* Store a Value for each pair. */
arr->setDenseInitializedLength(1);
arr->initDenseElement(0, StringValue(pattern));
/* Set the |index| property. (TemplateObject positions it in slot 0) */
arr->setSlot(0, Int32Value(match));
/* Set the |input| property. (TemplateObject positions it in slot 1) */
arr->setSlot(1, StringValue(str));
#ifdef DEBUG
RootedValue test(cx);
RootedId id(cx, NameToId(cx->names().index));
if (!NativeGetProperty(cx, arr, id, &test))
return false;
MOZ_ASSERT(test == arr->getSlot(0));
id = NameToId(cx->names().input);
if (!NativeGetProperty(cx, arr, id, &test))
return false;
MOZ_ASSERT(test == arr->getSlot(1));
#endif
rval.setObject(*arr);
return true;
}
#ifdef DEBUG
static bool
CallIsStringOptimizable(JSContext* cx, const char* name, bool* result)
{
JSAtom* atom = Atomize(cx, name, strlen(name));
if (!atom)
return false;
RootedPropertyName propName(cx, atom->asPropertyName());
RootedValue funcVal(cx);
if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), propName, propName, 0, &funcVal))
return false;
InvokeArgs args(cx);
if (!args.init(0))
return false;
args.setCallee(funcVal);
args.setThis(UndefinedValue());
if (!Invoke(cx, args))
return false;
*result = args.rval().toBoolean();
return true;
}
#endif
bool
js::FlatStringMatch(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].isString());
MOZ_ASSERT(args[1].isString());
#ifdef DEBUG
bool isOptimizable = false;
if (!CallIsStringOptimizable(cx, "IsStringMatchOptimizable", &isOptimizable))
return false;
MOZ_ASSERT(isOptimizable);
#endif
RootedString str(cx,args[0].toString());
RootedString pattern(cx, args[1].toString());
bool isFlat = false;
int32_t match = 0;
if (!FlatStringMatchHelper(cx, str, pattern, &isFlat, &match))
return false;
if (!isFlat) {
args.rval().setUndefined();
return true;
}
return BuildFlatMatchArray(cx, str, pattern, match, args.rval());
}

View File

@ -430,6 +430,9 @@ FileEscapedString(FILE* fp, const char* chars, size_t length, uint32_t quote)
return res;
}
bool
str_match(JSContext* cx, unsigned argc, Value* vp);
bool
str_search(JSContext* cx, unsigned argc, Value* vp);
@ -450,9 +453,6 @@ str_replace_string_raw(JSContext* cx, HandleString string, HandleString pattern,
extern bool
StringConstructor(JSContext* cx, unsigned argc, Value* vp);
extern bool
FlatStringMatch(JSContext* cx, unsigned argc, Value* vp);
} /* namespace js */
#endif /* jsstr_h */

View File

@ -1,18 +0,0 @@
var BUGNUMBER = 887016;
var summary = "RegExpExec should throw if exec property of non-RegExp is not callable";
print(BUGNUMBER + ": " + summary);
for (var exec of [null, 0, false, undefined, ""]) {
// RegExp with non-callable exec
var re = /a/;
re.exec = exec;
RegExp.prototype[Symbol.match].call(re, "foo");
// non-RegExp with non-callable exec
assertThrowsInstanceOf(() => RegExp.prototype[Symbol.match].call({ exec }, "foo"),
TypeError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -1,31 +0,0 @@
var BUGNUMBER = 887016;
var summary = "RegExpExec should throw if returned value is not an object nor null.";
print(BUGNUMBER + ": " + summary);
for (var ret of [null, {}, [], /a/]) {
assertEq(RegExp.prototype[Symbol.match].call({
get global() {
return false;
},
exec(S) {
return ret;
}
}, "foo"), ret);
}
for (ret of [undefined, 1, true, false, Symbol.iterator]) {
assertThrowsInstanceOf(() => {
RegExp.prototype[Symbol.match].call({
get global() {
return false;
},
exec(S) {
return ret;
}
}, "foo");
}, TypeError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -25,24 +25,6 @@ re = new Proxy(/a/, {
return that[name];
}
});
assertEq(RegExp(re), re);
re = new Proxy(/a/, {
get(that, name) {
if (name == "constructor") {
return function() {};
}
return that[name];
}
});
assertEq(RegExp(re) === re, false);
re = new Proxy(/a/, {
get(that, name) {
if (name == Symbol.match) {
return undefined;
}
return that[name];
}
});
assertEq(RegExp(re) === re, false);
re = new Proxy(g.eval(`/a/`), {

View File

@ -1,12 +0,0 @@
var BUGNUMBER = 887016;
var summary = "RegExp.prototype[@@match] should check this value.";
print(BUGNUMBER + ": " + summary);
for (var v of [null, 1, true, undefined, "", Symbol.iterator]) {
assertThrowsInstanceOf(() => RegExp.prototype[Symbol.match].call(v),
TypeError);
}
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -1,147 +0,0 @@
var BUGNUMBER = 887016;
var summary = "Trace RegExp.prototype[@@match] behavior.";
print(BUGNUMBER + ": " + summary);
var n;
var log;
var target;
var global;
var unicode;
var logProxy;
var execResult;
var lastIndexResult;
var lastIndexExpected;
function P(A) {
return new Proxy(A, {
get(that, name) {
if (logProxy)
log += "get:result[" + name + "],";
return that[name];
}
});
}
var myRegExp = {
get global() {
log += "get:global,";
return global;
},
get lastIndex() {
log += "get:lastIndex,";
return lastIndexResult[n];
},
set lastIndex(v) {
log += "set:lastIndex,";
assertEq(v, lastIndexExpected[n]);
},
get unicode() {
log += "get:unicode,";
return unicode;
},
get exec() {
log += "get:exec,";
return function(S) {
log += "call:exec,";
assertEq(S, target);
return execResult[n++];
};
},
};
function reset() {
n = 0;
log = "";
target = "abcAbcABC";
global = true;
unicode = false;
logProxy = true;
}
// Trace global with non-empty match.
reset();
execResult = [ P(["abc"]), P(["ABC"]), null ];
lastIndexResult = [ , , , ];
lastIndexExpected = [ 0, , , ];
var ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
assertEq(JSON.stringify(ret), `["abc","ABC"]`);
assertEq(log,
"get:global," +
"get:unicode," +
"set:lastIndex," +
"get:exec,call:exec,get:result[0]," +
"get:exec,call:exec,get:result[0]," +
"get:exec,call:exec,");
// Trace global with empty match.
reset();
execResult = [ P([""]), P([""]), null ];
lastIndexResult = [ , 4, 20, ];
lastIndexExpected = [ 0, 5, 21, ];
ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
assertEq(JSON.stringify(ret), `["",""]`);
assertEq(log,
"get:global," +
"get:unicode," +
"set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,");
// Trace global and unicode with empty match.
// 1. not surrogate pair
// 2. lead surrogate pair
// 3. trail surrogate pair
// 4. lead surrogate pair without trail surrogate pair
// 5. index overflow
reset();
unicode = true;
// 0123 4 5678
target = "___\uD83D\uDC38___\uD83D";
execResult = [ P([""]), P([""]), P([""]), P([""]), P([""]), null ];
lastIndexResult = [ , 2, 3, 4, 8, 9, ];
lastIndexExpected = [ 0, 3, 5, 5, 9, 10, ];
ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
assertEq(JSON.stringify(ret), `["","","","",""]`);
assertEq(log,
"get:global," +
"get:unicode," +
"set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,get:result[0],get:lastIndex,set:lastIndex," +
"get:exec,call:exec,");
// Trace global with no match.
reset();
execResult = [ null ];
lastIndexResult = [ , ];
lastIndexExpected = [ 0, ];
ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
assertEq(ret, null);
assertEq(log,
"get:global," +
"get:unicode," +
"set:lastIndex," +
"get:exec,call:exec,");
// Trace non-global.
reset();
global = false;
execResult = [ P(["abc"]) ];
lastIndexResult = [];
lastIndexExpected = [];
ret = RegExp.prototype[Symbol.match].call(myRegExp, target);
// ret is the Proxy on non-global case, disable logging.
logProxy = false;
assertEq(JSON.stringify(ret), `["abc"]`);
assertEq(log,
"get:global," +
"get:exec,call:exec,");
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -1,36 +0,0 @@
var BUGNUMBER = 887016;
var summary = "Implement RegExp.prototype[@@match].";
print(BUGNUMBER + ": " + summary);
assertEq(RegExp.prototype[Symbol.match].name, "[Symbol.match]");
assertEq(RegExp.prototype[Symbol.match].length, 1);
var desc = Object.getOwnPropertyDescriptor(RegExp.prototype, Symbol.match);
assertEq(desc.configurable, true);
assertEq(desc.enumerable, false);
assertEq(desc.writable, true);
var re = /a/;
var v = re[Symbol.match]("abcAbcABC");
assertEq(Array.isArray(v), true);
assertEq(v.length, 1);
assertEq(v[0], "a");
re = /d/;
v = re[Symbol.match]("abcAbcABC");
assertEq(v, null);
re = /a/ig;
v = re[Symbol.match]("abcAbcABC");
assertEq(Array.isArray(v), true);
assertEq(v.length, 3);
assertEq(v[0], "a");
assertEq(v[1], "A");
assertEq(v[2], "A");
re = /d/g;
v = re[Symbol.match]("abcAbcABC");
assertEq(v, null);
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -1,31 +0,0 @@
var BUGNUMBER = 887016;
var summary = "Call RegExp.prototype[@@match] from String.prototype.match.";
print(BUGNUMBER + ": " + summary);
var called = 0;
var myRegExp = {
[Symbol.match](S) {
assertEq(S, "abcAbcABC");
called++;
return 42;
}
};
assertEq("abcAbcABC".match(myRegExp), 42);
assertEq(called, 1);
var origMatch = RegExp.prototype[Symbol.match];
called = 0;
RegExp.prototype[Symbol.match] = function(S) {
assertEq(S, "abcAbcABC");
called++;
return 43;
};
assertEq("abcAbcABC".match("abc"), 43);
assertEq(called, 1);
RegExp.prototype[Symbol.match] = origMatch;
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -453,14 +453,6 @@ GlobalObject::initSelfHostingBuiltins(JSContext* cx, Handle<GlobalObject*> globa
return false;
}
RootedValue std_match(cx);
std_match.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::match));
if (!JS_DefineProperty(cx, global, "std_match", std_match,
JSPROP_PERMANENT | JSPROP_READONLY))
{
return false;
}
RootedValue std_species(cx);
std_species.setSymbol(cx->wellKnownSymbols().get(JS::SymbolCode::species));
if (!JS_DefineProperty(cx, global, "std_species", std_species,

View File

@ -18,7 +18,6 @@
#include "jsfriendapi.h"
#include "jsfun.h"
#include "jshashutil.h"
#include "jsstr.h"
#include "jsweakmap.h"
#include "jswrapper.h"
#include "selfhosted.out.h"
@ -2075,32 +2074,6 @@ intrinsic_captureCurrentStack(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
IsMatchFlagsArgumentEnabled(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
args.rval().setBoolean(cx->runtime()->options().matchFlagArgument());
return true;
}
static bool
WarnOnceAboutFlagsArgument(JSContext* cx, unsigned argc, Value* vp)
{
if (!cx->compartment()->warnedAboutFlagsArgument) {
if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
cx->runtime()->options().matchFlagArgument()
? JSMSG_DEPRECATED_FLAGS_ARG
: JSMSG_OBSOLETE_FLAGS_ARG))
{
return false;
}
cx->compartment()->warnedAboutFlagsArgument = true;
}
return true;
}
// The self-hosting global isn't initialized with the normal set of builtins.
// Instead, individual C++-implemented functions that're required by
// self-hosted code are defined as global functions. Accessing these
@ -2158,6 +2131,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("std_String_charCodeAt", str_charCodeAt, 1,0, StringCharCodeAt),
JS_FN("std_String_indexOf", str_indexOf, 1,0),
JS_FN("std_String_lastIndexOf", str_lastIndexOf, 1,0),
JS_FN("std_String_match", str_match, 1,0),
JS_INLINABLE_FN("std_String_replace", str_replace, 2,0, StringReplace),
JS_INLINABLE_FN("std_String_split", str_split, 2,0, StringSplit),
JS_FN("std_String_startsWith", str_startsWith, 1,0),
@ -2420,16 +2394,11 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("RegExpInstanceOptimizable", RegExpInstanceOptimizable, 1,0,
RegExpInstanceOptimizable),
JS_FN("FlatStringMatch", FlatStringMatch, 2,0),
// See builtin/RegExp.h for descriptions of the regexp_* functions.
JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
JS_FN("regexp_construct", regexp_construct_self_hosting, 2,0),
JS_FN("IsMatchFlagsArgumentEnabled", IsMatchFlagsArgumentEnabled, 0,0),
JS_FN("WarnOnceAboutFlagsArgument", WarnOnceAboutFlagsArgument, 0,0),
JS_FN("IsModule", intrinsic_IsInstanceOfBuiltin<ModuleObject>, 1, 0),
JS_FN("CallModuleMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),

View File

@ -227,7 +227,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=933681
gPrototypeProperties['RegExp'] =
["constructor", "toSource", "toString", "compile", "exec", "test",
Symbol.match,
"flags", "global", "ignoreCase", "multiline", "source", "sticky", "unicode",
"lastIndex"];
gConstructorProperties['RegExp'] =