[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: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/master@{#59441}
This commit is contained in:
parent
daf67d6de3
commit
155ccadda6
1
BUILD.gn
1
BUILD.gn
@ -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) {
|
||||
|
86
src/builtins/string-endswith.tq
Normal file
86
src/builtins/string-endswith.tq
Normal file
@ -0,0 +1,86 @@
|
||||
// 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);
|
||||
|
Loading…
Reference in New Issue
Block a user