Commit ed5496f3 authored by jgruber's avatar jgruber Committed by Commit bot

[regexp] Properly handle HeapNumbers in AdvanceStringIndex

This fixes behavior for HeapNumber {index} arguments passed to
AdvanceStringIndex.

Previously, we'd blindly treat {index} as a Smi. Passing a HeapNumber instead
would result in a Smi addition on the tagged HeapNumber pointer.

BUG=chromium:709015

Review-Url: https://codereview.chromium.org/2798933003
Cr-Commit-Position: refs/heads/master@{#44458}
parent 586bf1d8
......@@ -1535,16 +1535,40 @@ TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
Node* const index,
Node* const is_unicode) {
Node* const is_unicode,
bool is_fastpath) {
CSA_ASSERT(this, IsString(string));
// TODO(jgruber): Handle HeapNumber index.
CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(index)));
if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
// TODO(jgruber): If index is a HeapNumber, assert that it is outside the
// Smi range.
// Default to last_index + 1.
Node* const index_plus_one = SmiAdd(index, SmiConstant(1));
Node* const index_plus_one = NumberInc(index);
Variable var_result(this, MachineRepresentation::kTagged, index_plus_one);
// Advancing the index has some subtle issues involving the distinction
// between Smis and HeapNumbers. There's three cases:
// * {index} is a Smi, {index_plus_one} is a Smi. The standard case.
// * {index} is a Smi, {index_plus_one} overflows into a HeapNumber.
// In this case we can return the result early, because
// {index_plus_one} > {string}.length.
// * {index} is a HeapNumber, {index_plus_one} is a HeapNumber. This can only
// occur when {index} is outside the Smi range since we normalize
// explicitly. Again we can return early.
if (is_fastpath) {
// Must be in Smi range on the fast path. We control the value of {index}
// on all call-sites and can never exceed the length of the string.
STATIC_ASSERT(String::kMaxLength + 2 < Smi::kMaxValue);
CSA_ASSERT(this, TaggedIsPositiveSmi(index_plus_one));
}
Label if_isunicode(this), out(this);
Branch(is_unicode, &if_isunicode, &out);
GotoIfNot(is_unicode, &out);
// Keep this unconditional (even on the fast path) just to be safe.
Branch(TaggedIsPositiveSmi(index_plus_one), &if_isunicode, &out);
BIND(&if_isunicode);
{
......@@ -1562,7 +1586,7 @@ Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
&out);
// At a surrogate pair, return index + 2.
Node* const index_plus_two = SmiAdd(index, SmiConstant(2));
Node* const index_plus_two = NumberInc(index_plus_one);
var_result.Bind(index_plus_two);
Goto(&out);
......@@ -1851,7 +1875,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(Node* const context,
}
Node* const new_last_index =
AdvanceStringIndex(string, last_index, is_unicode);
AdvanceStringIndex(string, last_index, is_unicode, is_fastpath);
if (is_fastpath) {
// On the fast path, we can be certain that lastIndex can never be
......@@ -2169,7 +2193,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode);
Node* const new_next_search_from =
AdvanceStringIndex(string, next_search_from, is_unicode);
AdvanceStringIndex(string, next_search_from, is_unicode, true);
var_next_search_from.Bind(new_next_search_from);
Goto(&loop);
......
......@@ -89,7 +89,7 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
Node* RegExpExec(Node* context, Node* regexp, Node* string);
Node* AdvanceStringIndex(Node* const string, Node* const index,
Node* const is_unicode);
Node* const is_unicode, bool is_fastpath);
void RegExpPrototypeMatchBody(Node* const context, Node* const regexp,
Node* const string, const bool is_fastpath);
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function testAdvanceStringIndex(lastIndex, expectedLastIndex) {
let exec_count = 0;
let last_last_index = -1;
let fake_re = {
exec: () => { return (exec_count++ == 0) ? [""] : null },
get lastIndex() { return lastIndex; },
set lastIndex(value) { last_last_index = value },
get global() { return true; },
get flags() { return "g"; }
};
assertEquals([""], RegExp.prototype[Symbol.match].call(fake_re, "abc"));
assertEquals(expectedLastIndex, last_last_index);
}
testAdvanceStringIndex(new Number(42), 43); // Value wrapper.
testAdvanceStringIndex(%AllocateHeapNumber(), 1); // HeapNumber.
testAdvanceStringIndex(4294967295, 4294967296); // HeapNumber.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment