[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:
jgruber 2017-02-09 06:54:05 -08:00 committed by Commit bot
parent 93c1e73d06
commit b798b5212a
4 changed files with 30 additions and 30 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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/);

View File

@ -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