Commit 155ccadd authored by Ujjwal Sharma's avatar Ujjwal Sharma Committed by Commit Bot

[string] port String.p.endsWith to torque

Port String.prototype.endsWith from a CPP builtin to a Torque builtin.

Spec: https://tc39.github.io/ecma262/#sec-string.prototype.endswith
Bug: v8:8400
Change-Id: I4ac8cb92acb68389db844deaecc9ae1c6e7d6bd5
Reviewed-on: https://chromium-review.googlesource.com/c/1454677
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59441}
parent daf67d6d
......@@ -882,6 +882,7 @@ torque_files = [
"src/builtins/extras-utils.tq",
"src/builtins/object-fromentries.tq",
"src/builtins/iterator.tq",
"src/builtins/string-endswith.tq",
"src/builtins/string-startswith.tq",
"src/builtins/typed-array.tq",
"src/builtins/typed-array-createtypedarray.tq",
......
......@@ -506,7 +506,7 @@ extern macro LoadAndUntagToWord32Root(constexpr RootIndex): int32;
extern runtime StringEqual(Context, String, String): Oddball;
extern builtin StringLessThan(Context, String, String): Boolean;
extern macro StringCharCodeAt(String, intptr): int32;
extern runtime StringStartsWith(Context, String, String, Number): Boolean;
extern runtime StringCompareSequence(Context, String, String, Number): Boolean;
extern macro StrictEqual(Object, Object): Boolean;
extern macro SmiLexicographicCompare(Smi, Smi): Smi;
......
......@@ -1061,8 +1061,6 @@ namespace internal {
TFJ(StringPrototypeCodePointAt, 1, kReceiver, kPosition) \
/* ES6 #sec-string.prototype.concat */ \
TFJ(StringPrototypeConcat, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-string.prototype.endswith */ \
CPP(StringPrototypeEndsWith) \
/* ES6 #sec-string.prototype.fontcolor */ \
TFJ(StringPrototypeFontcolor, 1, kReceiver, kValue) \
/* ES6 #sec-string.prototype.fontsize */ \
......
......@@ -118,70 +118,6 @@ BUILTIN(StringFromCodePoint) {
return *result;
}
// ES6 section 21.1.3.6
// String.prototype.endsWith ( searchString [ , endPosition ] )
BUILTIN(StringPrototypeEndsWith) {
HandleScope handle_scope(isolate);
TO_THIS_STRING(str, "String.prototype.endsWith");
// Check if the search string is a regExp and fail if it is.
Handle<Object> search = args.atOrUndefined(isolate, 1);
Maybe<bool> is_reg_exp = RegExpUtils::IsRegExp(isolate, search);
if (is_reg_exp.IsNothing()) {
DCHECK(isolate->has_pending_exception());
return ReadOnlyRoots(isolate).exception();
}
if (is_reg_exp.FromJust()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp,
isolate->factory()->NewStringFromStaticChars(
"String.prototype.endsWith")));
}
Handle<String> search_string;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
Object::ToString(isolate, search));
Handle<Object> position = args.atOrUndefined(isolate, 2);
int end;
if (position->IsUndefined(isolate)) {
end = str->length();
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
Object::ToInteger(isolate, position));
end = str->ToValidIndex(*position);
}
int start = end - search_string->length();
if (start < 0) return ReadOnlyRoots(isolate).false_value();
str = String::Flatten(isolate, str);
search_string = String::Flatten(isolate, search_string);
DisallowHeapAllocation no_gc; // ensure vectors stay valid
String::FlatContent str_content = str->GetFlatContent(no_gc);
String::FlatContent search_content = search_string->GetFlatContent(no_gc);
if (str_content.IsOneByte() && search_content.IsOneByte()) {
Vector<const uint8_t> str_vector = str_content.ToOneByteVector();
Vector<const uint8_t> search_vector = search_content.ToOneByteVector();
return isolate->heap()->ToBoolean(memcmp(str_vector.start() + start,
search_vector.start(),
search_string->length()) == 0);
}
FlatStringReader str_reader(isolate, str);
FlatStringReader search_reader(isolate, search_string);
for (int i = 0; i < search_string->length(); i++) {
if (str_reader.Get(start + i) != search_reader.Get(i)) {
return ReadOnlyRoots(isolate).false_value();
}
}
return ReadOnlyRoots(isolate).true_value();
}
// ES6 section 21.1.3.9
// String.prototype.lastIndexOf ( searchString [ , position ] )
BUILTIN(StringPrototypeLastIndexOf) {
......
// Copyright 2019 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.
namespace string {
macro TryFastStringCompareSequence(
string: String, searchStr: String, start: Number,
searchLength: Smi): Boolean labels Slow {
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchStr) otherwise Slow;
const stringIndexSmi: Smi = Cast<Smi>(start) otherwise Slow;
let searchIndex: intptr = 0;
let stringIndex = Convert<intptr>(stringIndexSmi);
const searchLengthInteger = Convert<intptr>(searchLength);
while (searchIndex < searchLengthInteger) {
if (StringCharCodeAt(directSearchStr, searchIndex) !=
StringCharCodeAt(directString, stringIndex)) {
return False;
}
searchIndex++;
stringIndex++;
}
return True;
}
// https://tc39.github.io/ecma262/#sec-string.prototype.endswith
transitioning javascript builtin StringPrototypeEndsWith(
context: Context, receiver: Object, ...arguments): Boolean {
const searchString: Object = arguments[0];
const endPosition: Object = arguments[1];
// 1. Let O be ? RequireObjectCoercible(this value).
const object: Object = RequireObjectCoercible(receiver);
// 2. Let S be ? ToString(O).
const string: String = ToString_Inline(context, object);
// 3. Let isRegExp be ? IsRegExp(searchString).
// 4. If isRegExp is true, throw a TypeError exception.
if (IsRegExp(searchString)) {
ThrowTypeError(
context, kFirstArgumentNotRegExp, 'String.prototype.endsWith');
}
// 5. Let searchStr be ? ToString(searchString).
const searchStr: String = ToString_Inline(context, searchString);
// 6. Let len be the length of S.
const len: Number = string.length_smi;
// 7. If endPosition is undefined, let pos be len,
// else let pos be ? ToInteger(endPosition).
const pos: Number = (endPosition == Undefined) ?
len :
ToInteger_Inline(context, endPosition);
// 8. Let end be min(max(pos, 0), len).
const end: Number = NumberMin(NumberMax(pos, 0), len);
// 9. Let searchLength be the length of searchStr.
const searchLength: Smi = searchStr.length_smi;
// 10. Let start be end - searchLength.
let start = end - searchLength;
// 11. If start is less than 0, return false.
if (start < 0) return False;
// 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr,
// return true.
// 13. Otherwise, return false.
try {
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(
string, searchStr, start, searchLength) otherwise Slow;
}
label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(context, string, searchStr, start);
}
}
}
......@@ -59,32 +59,15 @@ namespace string {
// 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr,
// return true.
// 13. Otherwise, return false.
try {
// Fast Path: If both strings are direct and relevant indices are Smis.
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchStr) otherwise Slow;
const stringIndexSmi: Smi = Cast<Smi>(start) otherwise Slow;
let searchIndex: intptr = 0;
let stringIndex: intptr = Convert<intptr>(stringIndexSmi);
const searchLengthInteger: intptr = Convert<intptr>(searchLength);
while (searchIndex < searchLengthInteger) {
if (StringCharCodeAt(directSearchStr, searchIndex) !=
StringCharCodeAt(directString, stringIndex)) {
// 13. Otherwise, return false.
return False;
}
searchIndex++;
stringIndex++;
}
return True;
return TryFastStringCompareSequence(
string, searchStr, start, searchLength) otherwise Slow;
}
label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringStartsWith(context, string, searchStr, start);
return StringCompareSequence(context, string, searchStr, start);
}
}
}
......@@ -713,7 +713,7 @@ RUNTIME_FUNCTION(Runtime_StringMaxLength) {
return Smi::FromInt(String::kMaxLength);
}
RUNTIME_FUNCTION(Runtime_StringStartsWith) {
RUNTIME_FUNCTION(Runtime_StringCompareSequence) {
HandleScope handle_scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
......
......@@ -421,7 +421,7 @@ namespace internal {
F(StringLessThanOrEqual, 2, 1) \
F(StringMaxLength, 0, 1) \
F(StringReplaceOneCharWithString, 3, 1) \
F(StringStartsWith, 3, 1) \
F(StringCompareSequence, 3, 1) \
F(StringSubstring, 3, 1) \
F(StringToArray, 2, 1) \
F(StringTrim, 2, 1)
......
......@@ -417,3 +417,13 @@ assertThrows(function() {
}, TypeError);
re[Symbol.match] = false;
assertEquals(false, "".startsWith(re));
let didThrow = false;
try {
"".endsWith(/./);
} catch (err) {
didThrow = true;
assertEquals(err.name, "TypeError");
assertEquals(err.message, "First argument to String.prototype.endsWith must not be a regular expression");
}
assertTrue(didThrow);
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