[string] port String.p.startsWith to torque

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

Spec: https://tc39.github.io/ecma262/#sec-string.prototype.startswith

Bug: v8:8400
Change-Id: I51aff0b3a4126c17ab4f89763019fd7e4ba665d9
Reviewed-on: https://chromium-review.googlesource.com/c/1361340
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59355}
This commit is contained in:
Ujjwal Sharma 2019-02-04 18:34:16 +05:30 committed by Commit Bot
parent 1358917e51
commit 44ffcca488
10 changed files with 148 additions and 56 deletions

View File

@ -882,6 +882,7 @@ torque_files = [
"src/builtins/extras-utils.tq",
"src/builtins/object-fromentries.tq",
"src/builtins/iterator.tq",
"src/builtins/string-startswith.tq",
"src/builtins/typed-array.tq",
"src/builtins/typed-array-createtypedarray.tq",
"test/torque/test-torque.tq",
@ -889,7 +890,6 @@ torque_files = [
]
torque_namespaces = [
"base",
"arguments",
"array",
"array-copywithin",
@ -903,14 +903,16 @@ torque_namespaces = [
"array-splice",
"array-unshift",
"array-lastindexof",
"base",
"collections",
"iterator",
"object",
"typed-array",
"typed-array-createtypedarray",
"data-view",
"extras-utils",
"iterator",
"object",
"string",
"test",
"typed-array",
"typed-array-createtypedarray",
]
action("run_torque") {

View File

@ -61,6 +61,10 @@ type Number = Smi | HeapNumber;
type BigInt extends HeapObject generates 'TNode<BigInt>';
type Numeric = Number | BigInt;
// A direct string can be accessed directly through CSA without going into the
// C++ runtime. See also: ToDirectStringAssembler.
type DirectString extends String generates 'TNode<String>';
type RootIndex generates 'TNode<Int32T>' constexpr 'RootIndex';
type Map extends HeapObject generates 'TNode<Map>';
@ -321,6 +325,8 @@ const kIteratorValueNotAnObject: constexpr MessageTemplate
generates 'MessageTemplate::kIteratorValueNotAnObject';
const kNotIterable: constexpr MessageTemplate
generates 'MessageTemplate::kNotIterable';
const kFirstArgumentNotRegExp: constexpr MessageTemplate
generates 'MessageTemplate::kFirstArgumentNotRegExp';
const kMaxArrayIndex:
constexpr uint32 generates 'JSArray::kMaxArrayIndex';
@ -493,6 +499,8 @@ 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 macro StrictEqual(Object, Object): Boolean;
extern macro SmiLexicographicCompare(Smi, Smi): Smi;
@ -670,6 +678,7 @@ extern operator '.instanceType' macro LoadInstanceType(HeapObject):
InstanceType;
extern operator '.length' macro LoadStringLengthAsWord(String): intptr;
extern operator '.length_smi' macro LoadStringLengthAsSmi(String): Smi;
extern operator '.length' macro GetArgumentsLength(constexpr Arguments): intptr;
extern operator '[]' macro GetArgumentValue(
@ -692,6 +701,8 @@ extern macro TaggedToSmi(Object): Smi
labels CastError;
extern macro TaggedToPositiveSmi(Object): PositiveSmi
labels CastError;
extern macro TaggedToDirectString(Object): DirectString
labels CastError;
extern macro HeapObjectToJSArray(HeapObject): JSArray
labels CastError;
extern macro HeapObjectToCallable(HeapObject): Callable
@ -811,6 +822,11 @@ Cast<String>(o: HeapObject): String
return HeapObjectToString(o) otherwise CastError;
}
Cast<DirectString>(o: HeapObject): DirectString
labels CastError {
return TaggedToDirectString(o) otherwise CastError;
}
Cast<Constructor>(o: HeapObject): Constructor
labels CastError {
return HeapObjectToConstructor(o) otherwise CastError;

View File

@ -1118,8 +1118,6 @@ namespace internal {
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-string.prototype.sup */ \
TFJ(StringPrototypeSup, 0, kReceiver) \
/* ES6 #sec-string.prototype.startswith */ \
CPP(StringPrototypeStartsWith) \
/* ES6 #sec-string.prototype.tostring */ \
TFJ(StringPrototypeToString, 0, kReceiver) \
TFJ(StringPrototypeTrim, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \

View File

@ -30,6 +30,8 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
TNode<Object> RegExpCreate(TNode<Context> context, TNode<Map> initial_map,
TNode<Object> regexp_string, TNode<String> flags);
TNode<BoolT> IsRegExp(TNode<Context> context, TNode<Object> maybe_receiver);
protected:
TNode<Smi> SmiZero();
TNode<IntPtrT> IntPtrZero();
@ -112,8 +114,6 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
void FlagGetter(Node* context, Node* receiver, JSRegExp::Flag flag,
int counter, const char* method_name);
TNode<BoolT> IsRegExp(TNode<Context> context, TNode<Object> maybe_receiver);
Node* RegExpInitialize(Node* const context, Node* const regexp,
Node* const maybe_pattern, Node* const maybe_flags);

View File

@ -290,53 +290,6 @@ BUILTIN(StringPrototypeNormalize) {
}
#endif // !V8_INTL_SUPPORT
BUILTIN(StringPrototypeStartsWith) {
HandleScope handle_scope(isolate);
TO_THIS_STRING(str, "String.prototype.startsWith");
// 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.startsWith")));
}
Handle<String> search_string;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
Object::ToString(isolate, search));
Handle<Object> position = args.atOrUndefined(isolate, 2);
int start;
if (position->IsUndefined(isolate)) {
start = 0;
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
Object::ToInteger(isolate, position));
start = str->ToValidIndex(*position);
}
if (start + search_string->length() > str->length()) {
return ReadOnlyRoots(isolate).false_value();
}
FlatStringReader str_reader(isolate, String::Flatten(isolate, str));
FlatStringReader search_reader(isolate,
String::Flatten(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();
}
#ifndef V8_INTL_SUPPORT
namespace {

View File

@ -0,0 +1,90 @@
// Copyright 2018 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.
#include 'src/builtins/builtins-regexp-gen.h'
namespace string {
extern macro RegExpBuiltinsAssembler::IsRegExp(implicit context:
Context)(Object): bool;
// TODO(ryzokuken): Add RequireObjectCoercible to base.tq and update callsites
macro RequireObjectCoercible(implicit context: Context)(argument: Object):
Object {
if (IsNullOrUndefined(argument)) {
ThrowTypeError(
context, kCalledOnNullOrUndefined, 'String.prototype.startsWith');
}
return argument;
}
// https://tc39.github.io/ecma262/#sec-string.prototype.startswith
transitioning javascript builtin StringPrototypeStartsWith(
context: Context, receiver: Object, ...arguments): Boolean {
const searchString: Object = arguments[0];
const position: 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.startsWith');
}
// 5. Let searchStr be ? ToString(searchString).
const searchStr: String = ToString_Inline(context, searchString);
// 6. Let pos be ? ToInteger(position).
const pos: Number = ToInteger_Inline(context, position);
// 7. Assert: If position is undefined, then pos is 0.
// 8. Let len be the length of S.
const len: Number = string.length_smi;
// 9. Let start be min(max(pos, 0), len).
const start: Number = NumberMin(NumberMax(pos, 0), len);
// 10. Let searchLength be the length of searchStr.
const searchLength: Smi = searchStr.length_smi;
// 11. If searchLength + start is greater than len, return false.
if (searchLength + start > len) 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.
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;
}
label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringStartsWith(context, string, searchStr, start);
}
}
}

View File

@ -13848,5 +13848,13 @@ void CodeStubAssembler::GotoIfInitialPrototypePropertiesModified(
}
}
TNode<String> CodeStubAssembler::TaggedToDirectString(TNode<Object> value,
Label* fail) {
ToDirectStringAssembler to_direct(state(), value);
to_direct.TryToDirect(fail);
to_direct.PointerToData(fail);
return CAST(value);
}
} // namespace internal
} // namespace v8

View File

@ -322,6 +322,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
return UncheckedCast<Smi>(value);
}
TNode<String> TaggedToDirectString(TNode<Object> value, Label* fail);
TNode<Number> TaggedToNumber(TNode<Object> value, Label* fail) {
GotoIfNot(IsNumber(value), fail);
return UncheckedCast<Number>(value);

View File

@ -713,5 +713,27 @@ RUNTIME_FUNCTION(Runtime_StringMaxLength) {
return Smi::FromInt(String::kMaxLength);
}
RUNTIME_FUNCTION(Runtime_StringStartsWith) {
HandleScope handle_scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
CONVERT_ARG_HANDLE_CHECKED(String, search_string, 1);
CONVERT_NUMBER_CHECKED(int, start, Int32, args[2]);
// Check if start + searchLength is in bounds.
DCHECK_LE(start + search_string->length(), string->length());
FlatStringReader string_reader(isolate, String::Flatten(isolate, string));
FlatStringReader search_reader(isolate,
String::Flatten(isolate, search_string));
for (int i = 0; i < search_string->length(); i++) {
if (string_reader.Get(start + i) != search_reader.Get(i)) {
return ReadOnlyRoots(isolate).false_value();
}
}
return ReadOnlyRoots(isolate).true_value();
}
} // namespace internal
} // namespace v8

View File

@ -421,6 +421,7 @@ namespace internal {
F(StringLessThanOrEqual, 2, 1) \
F(StringMaxLength, 0, 1) \
F(StringReplaceOneCharWithString, 3, 1) \
F(StringStartsWith, 3, 1) \
F(StringSubstring, 3, 1) \
F(StringToArray, 2, 1) \
F(StringTrim, 2, 1)