[regexp] Update lastIndex semantics in RegExpBuiltinExec
Updated according to the recent spec change at https://github.com/tc39/ecma262/pull/798. BUG=v8:5949 Review-Url: https://codereview.chromium.org/2681323002 Cr-Commit-Position: refs/heads/master@{#43062}
This commit is contained in:
parent
93c1e73d06
commit
b798b5212a
@ -241,6 +241,27 @@ Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
|
|||||||
Node* const native_context = LoadNativeContext(context);
|
Node* const native_context = LoadNativeContext(context);
|
||||||
Node* const string_length = LoadStringLength(string);
|
Node* const string_length = LoadStringLength(string);
|
||||||
|
|
||||||
|
// Load lastIndex.
|
||||||
|
Variable var_lastindex(this, MachineRepresentation::kTagged);
|
||||||
|
{
|
||||||
|
Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath);
|
||||||
|
var_lastindex.Bind(regexp_lastindex);
|
||||||
|
|
||||||
|
// Omit ToLength if lastindex is a non-negative smi.
|
||||||
|
Label call_tolength(this, Label::kDeferred), next(this);
|
||||||
|
Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
|
||||||
|
|
||||||
|
Bind(&call_tolength);
|
||||||
|
{
|
||||||
|
Callable tolength_callable = CodeFactory::ToLength(isolate);
|
||||||
|
var_lastindex.Bind(
|
||||||
|
CallStub(tolength_callable, context, regexp_lastindex));
|
||||||
|
Goto(&next);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bind(&next);
|
||||||
|
}
|
||||||
|
|
||||||
// Check whether the regexp is global or sticky, which determines whether we
|
// Check whether the regexp is global or sticky, which determines whether we
|
||||||
// update last index later on.
|
// update last index later on.
|
||||||
Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
|
Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
|
||||||
@ -251,33 +272,12 @@ Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
|
|||||||
|
|
||||||
// Grab and possibly update last index.
|
// Grab and possibly update last index.
|
||||||
Label run_exec(this);
|
Label run_exec(this);
|
||||||
Variable var_lastindex(this, MachineRepresentation::kTagged);
|
|
||||||
{
|
{
|
||||||
Label if_doupdate(this), if_dontupdate(this);
|
Label if_doupdate(this), if_dontupdate(this);
|
||||||
Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
|
Branch(should_update_last_index, &if_doupdate, &if_dontupdate);
|
||||||
|
|
||||||
Bind(&if_doupdate);
|
Bind(&if_doupdate);
|
||||||
{
|
{
|
||||||
Node* const regexp_lastindex =
|
|
||||||
LoadLastIndex(context, regexp, is_fastpath);
|
|
||||||
var_lastindex.Bind(regexp_lastindex);
|
|
||||||
|
|
||||||
// Omit ToLength if lastindex is a non-negative smi.
|
|
||||||
{
|
|
||||||
Label call_tolength(this, Label::kDeferred), next(this);
|
|
||||||
Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
|
|
||||||
|
|
||||||
Bind(&call_tolength);
|
|
||||||
{
|
|
||||||
Callable tolength_callable = CodeFactory::ToLength(isolate);
|
|
||||||
var_lastindex.Bind(
|
|
||||||
CallStub(tolength_callable, context, regexp_lastindex));
|
|
||||||
Goto(&next);
|
|
||||||
}
|
|
||||||
|
|
||||||
Bind(&next);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* const lastindex = var_lastindex.value();
|
Node* const lastindex = var_lastindex.value();
|
||||||
|
|
||||||
Label if_isoob(this, Label::kDeferred);
|
Label if_isoob(this, Label::kDeferred);
|
||||||
|
@ -564,21 +564,21 @@ log = [];
|
|||||||
re.lastIndex = fakeLastIndex;
|
re.lastIndex = fakeLastIndex;
|
||||||
var result = re.exec(fakeString);
|
var result = re.exec(fakeString);
|
||||||
assertEquals(["str"], result);
|
assertEquals(["str"], result);
|
||||||
assertEquals(["ts"], log);
|
assertEquals(["ts", "li"], log);
|
||||||
|
|
||||||
// Again, to check if caching interferes.
|
// Again, to check if caching interferes.
|
||||||
log = [];
|
log = [];
|
||||||
re.lastIndex = fakeLastIndex;
|
re.lastIndex = fakeLastIndex;
|
||||||
result = re.exec(fakeString);
|
result = re.exec(fakeString);
|
||||||
assertEquals(["str"], result);
|
assertEquals(["str"], result);
|
||||||
assertEquals(["ts"], log);
|
assertEquals(["ts", "li"], log);
|
||||||
|
|
||||||
// And one more time, just to be certain.
|
// And one more time, just to be certain.
|
||||||
log = [];
|
log = [];
|
||||||
re.lastIndex = fakeLastIndex;
|
re.lastIndex = fakeLastIndex;
|
||||||
result = re.exec(fakeString);
|
result = re.exec(fakeString);
|
||||||
assertEquals(["str"], result);
|
assertEquals(["str"], result);
|
||||||
assertEquals(["ts"], log);
|
assertEquals(["ts", "li"], log);
|
||||||
|
|
||||||
// Now with a global regexp, where lastIndex is actually used.
|
// Now with a global regexp, where lastIndex is actually used.
|
||||||
re = /str/g;
|
re = /str/g;
|
||||||
|
@ -27,20 +27,16 @@
|
|||||||
|
|
||||||
function testSideEffects(subject, re) {
|
function testSideEffects(subject, re) {
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
var expected_counter = 0;
|
|
||||||
const accesses_lastindex = (re.global || re.sticky);
|
|
||||||
var side_effect_object = { valueOf: function() { return counter++; } };
|
var side_effect_object = { valueOf: function() { return counter++; } };
|
||||||
re.lastIndex = side_effect_object;
|
re.lastIndex = side_effect_object;
|
||||||
re.exec(subject);
|
re.exec(subject);
|
||||||
|
|
||||||
if (accesses_lastindex) expected_counter++;
|
assertEquals(1, counter);
|
||||||
assertEquals(expected_counter, counter);
|
|
||||||
|
|
||||||
re.lastIndex = side_effect_object;
|
re.lastIndex = side_effect_object;
|
||||||
re.test(subject);
|
re.test(subject);
|
||||||
|
|
||||||
if (accesses_lastindex) expected_counter++;
|
assertEquals(2, counter);
|
||||||
assertEquals(expected_counter, counter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testSideEffects("zzzz", /a/);
|
testSideEffects("zzzz", /a/);
|
||||||
|
@ -91,6 +91,10 @@
|
|||||||
'built-ins/RegExp/prototype/Symbol.replace/y-init-lastindex': [FAIL],
|
'built-ins/RegExp/prototype/Symbol.replace/y-init-lastindex': [FAIL],
|
||||||
'built-ins/RegExp/prototype/Symbol.replace/y-set-lastindex': [FAIL],
|
'built-ins/RegExp/prototype/Symbol.replace/y-set-lastindex': [FAIL],
|
||||||
|
|
||||||
|
# https://bugs.chromium.org/p/v8/issues/detail?id=5949
|
||||||
|
'built-ins/RegExp/prototype/exec/failure-lastindex-no-access': [FAIL],
|
||||||
|
'built-ins/RegExp/prototype/exec/success-lastindex-no-access': [FAIL],
|
||||||
|
|
||||||
###### END REGEXP SUBCLASSING SECTION ######
|
###### END REGEXP SUBCLASSING SECTION ######
|
||||||
|
|
||||||
# https://bugs.chromium.org/p/v8/issues/detail?id=4895
|
# https://bugs.chromium.org/p/v8/issues/detail?id=4895
|
||||||
|
Loading…
Reference in New Issue
Block a user