[torque] Port String.prototype.includes/indexOf and StringIndexOf
- Removed no longer used StringBuiltinAssembler methods (DispatchOnStringEncodings, PointerToStringDataAtIndex) - Removed no longer used Runtime functions (StringIncludes, StringIndexOf, StringIndexOfUnchecked). - Overall builtin code size is reduced (652 bytes on Mac x64.release build), builtin size breakdown: BEFORE ====== TFS Builtin, StringIndexOf, 1092 TFJ Builtin, StringPrototypeIncludes, 1784 TFJ Builtin, StringPrototypeIndexOf, 1536 Total = 4412 AFTER ===== TFC Builtin, StringIndexOf, 2036 (+944) TFJ Builtin, StringPrototypeIncludes, 1072 (-712) TFJ Builtin, StringPrototypeIndexOf, 652 (-884) Total = 3760 (-652) Bug: v8:8996 Change-Id: I9a88c095e2097f7d570e58e744d6692dc524ddf4 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2660995 Commit-Queue: Peter Wong <peter.wm.wong@gmail.com> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#72456}
This commit is contained in:
parent
0461065ca9
commit
5a2c53f948
2
BUILD.gn
2
BUILD.gn
@ -1315,6 +1315,8 @@ torque_files = [
|
|||||||
"src/builtins/string-at.tq",
|
"src/builtins/string-at.tq",
|
||||||
"src/builtins/string-endswith.tq",
|
"src/builtins/string-endswith.tq",
|
||||||
"src/builtins/string-html.tq",
|
"src/builtins/string-html.tq",
|
||||||
|
"src/builtins/string-includes.tq",
|
||||||
|
"src/builtins/string-indexof.tq",
|
||||||
"src/builtins/string-iterator.tq",
|
"src/builtins/string-iterator.tq",
|
||||||
"src/builtins/string-pad.tq",
|
"src/builtins/string-pad.tq",
|
||||||
"src/builtins/string-repeat.tq",
|
"src/builtins/string-repeat.tq",
|
||||||
|
@ -110,7 +110,6 @@ namespace internal {
|
|||||||
TFC(StringEqual, Compare) \
|
TFC(StringEqual, Compare) \
|
||||||
TFC(StringGreaterThan, Compare) \
|
TFC(StringGreaterThan, Compare) \
|
||||||
TFC(StringGreaterThanOrEqual, Compare) \
|
TFC(StringGreaterThanOrEqual, Compare) \
|
||||||
TFS(StringIndexOf, kReceiver, kSearchString, kPosition) \
|
|
||||||
TFC(StringLessThan, Compare) \
|
TFC(StringLessThan, Compare) \
|
||||||
TFC(StringLessThanOrEqual, Compare) \
|
TFC(StringLessThanOrEqual, Compare) \
|
||||||
TFC(StringSubstring, StringSubstring) \
|
TFC(StringSubstring, StringSubstring) \
|
||||||
@ -750,10 +749,6 @@ namespace internal {
|
|||||||
CPP(StringFromCodePoint) \
|
CPP(StringFromCodePoint) \
|
||||||
/* ES6 #sec-string.fromcharcode */ \
|
/* ES6 #sec-string.fromcharcode */ \
|
||||||
TFJ(StringFromCharCode, kDontAdaptArgumentsSentinel) \
|
TFJ(StringFromCharCode, kDontAdaptArgumentsSentinel) \
|
||||||
/* ES6 #sec-string.prototype.includes */ \
|
|
||||||
TFJ(StringPrototypeIncludes, kDontAdaptArgumentsSentinel) \
|
|
||||||
/* ES6 #sec-string.prototype.indexof */ \
|
|
||||||
TFJ(StringPrototypeIndexOf, kDontAdaptArgumentsSentinel) \
|
|
||||||
/* ES6 #sec-string.prototype.lastindexof */ \
|
/* ES6 #sec-string.prototype.lastindexof */ \
|
||||||
CPP(StringPrototypeLastIndexOf) \
|
CPP(StringPrototypeLastIndexOf) \
|
||||||
/* ES6 #sec-string.prototype.match */ \
|
/* ES6 #sec-string.prototype.match */ \
|
||||||
|
@ -48,46 +48,6 @@ TNode<RawPtrT> StringBuiltinsAssembler::DirectStringData(
|
|||||||
return var_data.value();
|
return var_data.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringBuiltinsAssembler::DispatchOnStringEncodings(
|
|
||||||
TNode<Word32T> const lhs_instance_type,
|
|
||||||
TNode<Word32T> const rhs_instance_type, Label* if_one_one,
|
|
||||||
Label* if_one_two, Label* if_two_one, Label* if_two_two) {
|
|
||||||
STATIC_ASSERT(kStringEncodingMask == 0x8);
|
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0x0);
|
|
||||||
STATIC_ASSERT(kOneByteStringTag == 0x8);
|
|
||||||
|
|
||||||
// First combine the encodings.
|
|
||||||
|
|
||||||
const TNode<Int32T> encoding_mask = Int32Constant(kStringEncodingMask);
|
|
||||||
const TNode<Word32T> lhs_encoding =
|
|
||||||
Word32And(lhs_instance_type, encoding_mask);
|
|
||||||
const TNode<Word32T> rhs_encoding =
|
|
||||||
Word32And(rhs_instance_type, encoding_mask);
|
|
||||||
|
|
||||||
const TNode<Word32T> combined_encodings =
|
|
||||||
Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
|
|
||||||
|
|
||||||
// Then dispatch on the combined encoding.
|
|
||||||
|
|
||||||
Label unreachable(this, Label::kDeferred);
|
|
||||||
|
|
||||||
int32_t values[] = {
|
|
||||||
kOneByteStringTag | (kOneByteStringTag >> 1),
|
|
||||||
kOneByteStringTag | (kTwoByteStringTag >> 1),
|
|
||||||
kTwoByteStringTag | (kOneByteStringTag >> 1),
|
|
||||||
kTwoByteStringTag | (kTwoByteStringTag >> 1),
|
|
||||||
};
|
|
||||||
Label* labels[] = {
|
|
||||||
if_one_one, if_one_two, if_two_one, if_two_two,
|
|
||||||
};
|
|
||||||
|
|
||||||
STATIC_ASSERT(arraysize(values) == arraysize(labels));
|
|
||||||
Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
|
|
||||||
|
|
||||||
BIND(&unreachable);
|
|
||||||
Unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename SubjectChar, typename PatternChar>
|
template <typename SubjectChar, typename PatternChar>
|
||||||
TNode<IntPtrT> StringBuiltinsAssembler::CallSearchStringRaw(
|
TNode<IntPtrT> StringBuiltinsAssembler::CallSearchStringRaw(
|
||||||
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
@ -111,15 +71,57 @@ TNode<IntPtrT> StringBuiltinsAssembler::CallSearchStringRaw(
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
TNode<IntPtrT> StringBuiltinsAssembler::SearchOneByteStringInTwoByteString(
|
||||||
TNode<RawPtrT> StringBuiltinsAssembler::PointerToStringDataAtIndex(
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
TNode<RawPtrT> string_data, TNode<IntPtrT> index,
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
String::Encoding encoding) {
|
const TNode<IntPtrT> start_position) {
|
||||||
const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
|
return CallSearchStringRaw<const uc16, const uint8_t>(
|
||||||
? UINT8_ELEMENTS
|
subject_ptr, subject_length, search_ptr, search_length, start_position);
|
||||||
: UINT16_ELEMENTS;
|
}
|
||||||
TNode<IntPtrT> offset_in_bytes = ElementOffsetFromIndex(index, kind);
|
TNode<IntPtrT> StringBuiltinsAssembler::SearchOneByteStringInOneByteString(
|
||||||
return RawPtrAdd(string_data, offset_in_bytes);
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position) {
|
||||||
|
return CallSearchStringRaw<const uint8_t, const uint8_t>(
|
||||||
|
subject_ptr, subject_length, search_ptr, search_length, start_position);
|
||||||
|
}
|
||||||
|
TNode<IntPtrT> StringBuiltinsAssembler::SearchTwoByteStringInTwoByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position) {
|
||||||
|
return CallSearchStringRaw<const uc16, const uc16>(
|
||||||
|
subject_ptr, subject_length, search_ptr, search_length, start_position);
|
||||||
|
}
|
||||||
|
TNode<IntPtrT> StringBuiltinsAssembler::SearchTwoByteStringInOneByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position) {
|
||||||
|
return CallSearchStringRaw<const uint8_t, const uc16>(
|
||||||
|
subject_ptr, subject_length, search_ptr, search_length, start_position);
|
||||||
|
}
|
||||||
|
TNode<IntPtrT> StringBuiltinsAssembler::SearchOneByteInOneByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> start_position) {
|
||||||
|
const TNode<RawPtrT> subject_start_ptr =
|
||||||
|
RawPtrAdd(subject_ptr, start_position);
|
||||||
|
const TNode<IntPtrT> search_byte =
|
||||||
|
ChangeInt32ToIntPtr(Load<Uint8T>(search_ptr));
|
||||||
|
const TNode<UintPtrT> search_length =
|
||||||
|
Unsigned(IntPtrSub(subject_length, start_position));
|
||||||
|
const TNode<ExternalReference> memchr =
|
||||||
|
ExternalConstant(ExternalReference::libc_memchr_function());
|
||||||
|
const TNode<RawPtrT> result_address = UncheckedCast<RawPtrT>(
|
||||||
|
CallCFunction(memchr, MachineType::Pointer(),
|
||||||
|
std::make_pair(MachineType::Pointer(), subject_start_ptr),
|
||||||
|
std::make_pair(MachineType::IntPtr(), search_byte),
|
||||||
|
std::make_pair(MachineType::UintPtr(), search_length)));
|
||||||
|
return Select<IntPtrT>(
|
||||||
|
WordEqual(result_address, IntPtrConstant(0)),
|
||||||
|
[=] { return IntPtrConstant(-1); },
|
||||||
|
[=] {
|
||||||
|
return IntPtrAdd(RawPtrSub(result_address, subject_start_ptr),
|
||||||
|
start_position);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringBuiltinsAssembler::GenerateStringEqual(TNode<String> left,
|
void StringBuiltinsAssembler::GenerateStringEqual(TNode<String> left,
|
||||||
@ -887,273 +889,6 @@ TF_BUILTIN(StringFromCharCode, StringBuiltinsAssembler) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringBuiltinsAssembler::StringIndexOf(
|
|
||||||
const TNode<String> subject_string, const TNode<String> search_string,
|
|
||||||
const TNode<Smi> position,
|
|
||||||
const std::function<void(TNode<Smi>)>& f_return) {
|
|
||||||
const TNode<IntPtrT> int_zero = IntPtrConstant(0);
|
|
||||||
const TNode<IntPtrT> search_length = LoadStringLengthAsWord(search_string);
|
|
||||||
const TNode<IntPtrT> subject_length = LoadStringLengthAsWord(subject_string);
|
|
||||||
const TNode<IntPtrT> start_position = IntPtrMax(SmiUntag(position), int_zero);
|
|
||||||
|
|
||||||
Label zero_length_needle(this), return_minus_1(this);
|
|
||||||
{
|
|
||||||
GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
|
|
||||||
|
|
||||||
// Check that the needle fits in the start position.
|
|
||||||
GotoIfNot(IntPtrLessThanOrEqual(search_length,
|
|
||||||
IntPtrSub(subject_length, start_position)),
|
|
||||||
&return_minus_1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the string pointers are identical, we can just return 0. Note that this
|
|
||||||
// implies {start_position} == 0 since we've passed the check above.
|
|
||||||
Label return_zero(this);
|
|
||||||
GotoIf(TaggedEqual(subject_string, search_string), &return_zero);
|
|
||||||
|
|
||||||
// Try to unpack subject and search strings. Bail to runtime if either needs
|
|
||||||
// to be flattened.
|
|
||||||
ToDirectStringAssembler subject_to_direct(state(), subject_string);
|
|
||||||
ToDirectStringAssembler search_to_direct(state(), search_string);
|
|
||||||
|
|
||||||
Label call_runtime_unchecked(this, Label::kDeferred);
|
|
||||||
|
|
||||||
subject_to_direct.TryToDirect(&call_runtime_unchecked);
|
|
||||||
search_to_direct.TryToDirect(&call_runtime_unchecked);
|
|
||||||
|
|
||||||
// Load pointers to string data.
|
|
||||||
const TNode<RawPtrT> subject_ptr =
|
|
||||||
subject_to_direct.PointerToData(&call_runtime_unchecked);
|
|
||||||
const TNode<RawPtrT> search_ptr =
|
|
||||||
search_to_direct.PointerToData(&call_runtime_unchecked);
|
|
||||||
|
|
||||||
const TNode<IntPtrT> subject_offset = subject_to_direct.offset();
|
|
||||||
const TNode<IntPtrT> search_offset = search_to_direct.offset();
|
|
||||||
|
|
||||||
// Like String::IndexOf, the actual matching is done by the optimized
|
|
||||||
// SearchString method in string-search.h. Dispatch based on string instance
|
|
||||||
// types, then call straight into C++ for matching.
|
|
||||||
|
|
||||||
CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero));
|
|
||||||
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero));
|
|
||||||
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position));
|
|
||||||
CSA_ASSERT(this,
|
|
||||||
IntPtrLessThanOrEqual(search_length,
|
|
||||||
IntPtrSub(subject_length, start_position)));
|
|
||||||
|
|
||||||
Label one_one(this), one_two(this), two_one(this), two_two(this);
|
|
||||||
DispatchOnStringEncodings(subject_to_direct.instance_type(),
|
|
||||||
search_to_direct.instance_type(), &one_one,
|
|
||||||
&one_two, &two_one, &two_two);
|
|
||||||
|
|
||||||
using onebyte_t = const uint8_t;
|
|
||||||
using twobyte_t = const uc16;
|
|
||||||
|
|
||||||
BIND(&one_one);
|
|
||||||
{
|
|
||||||
const TNode<RawPtrT> adjusted_subject_ptr = PointerToStringDataAtIndex(
|
|
||||||
subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
|
|
||||||
const TNode<RawPtrT> adjusted_search_ptr = PointerToStringDataAtIndex(
|
|
||||||
search_ptr, search_offset, String::ONE_BYTE_ENCODING);
|
|
||||||
|
|
||||||
Label direct_memchr_call(this), generic_fast_path(this);
|
|
||||||
Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
|
|
||||||
&generic_fast_path);
|
|
||||||
|
|
||||||
// An additional fast path that calls directly into memchr for 1-length
|
|
||||||
// search strings.
|
|
||||||
BIND(&direct_memchr_call);
|
|
||||||
{
|
|
||||||
const TNode<RawPtrT> string_addr =
|
|
||||||
RawPtrAdd(adjusted_subject_ptr, start_position);
|
|
||||||
const TNode<IntPtrT> search_length =
|
|
||||||
IntPtrSub(subject_length, start_position);
|
|
||||||
const TNode<IntPtrT> search_byte =
|
|
||||||
ChangeInt32ToIntPtr(Load<Uint8T>(adjusted_search_ptr));
|
|
||||||
|
|
||||||
const TNode<ExternalReference> memchr =
|
|
||||||
ExternalConstant(ExternalReference::libc_memchr_function());
|
|
||||||
const TNode<RawPtrT> result_address = UncheckedCast<RawPtrT>(
|
|
||||||
CallCFunction(memchr, MachineType::Pointer(),
|
|
||||||
std::make_pair(MachineType::Pointer(), string_addr),
|
|
||||||
std::make_pair(MachineType::IntPtr(), search_byte),
|
|
||||||
std::make_pair(MachineType::UintPtr(), search_length)));
|
|
||||||
GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
|
|
||||||
const TNode<IntPtrT> result_index =
|
|
||||||
IntPtrAdd(RawPtrSub(result_address, string_addr), start_position);
|
|
||||||
f_return(SmiTag(result_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&generic_fast_path);
|
|
||||||
{
|
|
||||||
const TNode<IntPtrT> result = CallSearchStringRaw<onebyte_t, onebyte_t>(
|
|
||||||
adjusted_subject_ptr, subject_length, adjusted_search_ptr,
|
|
||||||
search_length, start_position);
|
|
||||||
f_return(SmiTag(result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&one_two);
|
|
||||||
{
|
|
||||||
const TNode<RawPtrT> adjusted_subject_ptr = PointerToStringDataAtIndex(
|
|
||||||
subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
|
|
||||||
const TNode<RawPtrT> adjusted_search_ptr = PointerToStringDataAtIndex(
|
|
||||||
search_ptr, search_offset, String::TWO_BYTE_ENCODING);
|
|
||||||
|
|
||||||
const TNode<IntPtrT> result = CallSearchStringRaw<onebyte_t, twobyte_t>(
|
|
||||||
adjusted_subject_ptr, subject_length, adjusted_search_ptr,
|
|
||||||
search_length, start_position);
|
|
||||||
f_return(SmiTag(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&two_one);
|
|
||||||
{
|
|
||||||
const TNode<RawPtrT> adjusted_subject_ptr = PointerToStringDataAtIndex(
|
|
||||||
subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
|
|
||||||
const TNode<RawPtrT> adjusted_search_ptr = PointerToStringDataAtIndex(
|
|
||||||
search_ptr, search_offset, String::ONE_BYTE_ENCODING);
|
|
||||||
|
|
||||||
const TNode<IntPtrT> result = CallSearchStringRaw<twobyte_t, onebyte_t>(
|
|
||||||
adjusted_subject_ptr, subject_length, adjusted_search_ptr,
|
|
||||||
search_length, start_position);
|
|
||||||
f_return(SmiTag(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&two_two);
|
|
||||||
{
|
|
||||||
const TNode<RawPtrT> adjusted_subject_ptr = PointerToStringDataAtIndex(
|
|
||||||
subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
|
|
||||||
const TNode<RawPtrT> adjusted_search_ptr = PointerToStringDataAtIndex(
|
|
||||||
search_ptr, search_offset, String::TWO_BYTE_ENCODING);
|
|
||||||
|
|
||||||
const TNode<IntPtrT> result = CallSearchStringRaw<twobyte_t, twobyte_t>(
|
|
||||||
adjusted_subject_ptr, subject_length, adjusted_search_ptr,
|
|
||||||
search_length, start_position);
|
|
||||||
f_return(SmiTag(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&return_minus_1);
|
|
||||||
f_return(SmiConstant(-1));
|
|
||||||
|
|
||||||
BIND(&return_zero);
|
|
||||||
f_return(SmiConstant(0));
|
|
||||||
|
|
||||||
BIND(&zero_length_needle);
|
|
||||||
{
|
|
||||||
Comment("0-length search_string");
|
|
||||||
f_return(SmiTag(IntPtrMin(subject_length, start_position)));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&call_runtime_unchecked);
|
|
||||||
{
|
|
||||||
// Simplified version of the runtime call where the types of the arguments
|
|
||||||
// are already known due to type checks in this stub.
|
|
||||||
Comment("Call Runtime Unchecked");
|
|
||||||
TNode<Smi> result =
|
|
||||||
CAST(CallRuntime(Runtime::kStringIndexOfUnchecked, NoContextConstant(),
|
|
||||||
subject_string, search_string, position));
|
|
||||||
f_return(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 String.prototype.indexOf(searchString [, position])
|
|
||||||
// #sec-string.prototype.indexof
|
|
||||||
// Unchecked helper for builtins lowering.
|
|
||||||
TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
|
|
||||||
auto receiver = Parameter<String>(Descriptor::kReceiver);
|
|
||||||
auto search_string = Parameter<String>(Descriptor::kSearchString);
|
|
||||||
auto position = Parameter<Smi>(Descriptor::kPosition);
|
|
||||||
StringIndexOf(receiver, search_string, position,
|
|
||||||
[this](TNode<Smi> result) { this->Return(result); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 String.prototype.includes(searchString [, position])
|
|
||||||
// #sec-string.prototype.includes
|
|
||||||
TF_BUILTIN(StringPrototypeIncludes, StringIncludesIndexOfAssembler) {
|
|
||||||
TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
|
|
||||||
UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
|
|
||||||
auto context = Parameter<Context>(Descriptor::kContext);
|
|
||||||
Generate(kIncludes, argc, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 String.prototype.indexOf(searchString [, position])
|
|
||||||
// #sec-string.prototype.indexof
|
|
||||||
TF_BUILTIN(StringPrototypeIndexOf, StringIncludesIndexOfAssembler) {
|
|
||||||
TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
|
|
||||||
UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));
|
|
||||||
auto context = Parameter<Context>(Descriptor::kContext);
|
|
||||||
Generate(kIndexOf, argc, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StringIncludesIndexOfAssembler::Generate(SearchVariant variant,
|
|
||||||
TNode<IntPtrT> argc,
|
|
||||||
TNode<Context> context) {
|
|
||||||
CodeStubArguments arguments(this, argc);
|
|
||||||
const TNode<Object> receiver = arguments.GetReceiver();
|
|
||||||
|
|
||||||
TVARIABLE(Object, var_search_string);
|
|
||||||
TVARIABLE(Object, var_position);
|
|
||||||
Label argc_1(this), argc_2(this), call_runtime(this, Label::kDeferred),
|
|
||||||
fast_path(this);
|
|
||||||
|
|
||||||
GotoIf(IntPtrEqual(arguments.GetLength(), IntPtrConstant(1)), &argc_1);
|
|
||||||
GotoIf(IntPtrGreaterThan(arguments.GetLength(), IntPtrConstant(1)), &argc_2);
|
|
||||||
{
|
|
||||||
Comment("0 Argument case");
|
|
||||||
CSA_ASSERT(this, IntPtrEqual(arguments.GetLength(), IntPtrConstant(0)));
|
|
||||||
TNode<Oddball> undefined = UndefinedConstant();
|
|
||||||
var_search_string = undefined;
|
|
||||||
var_position = undefined;
|
|
||||||
Goto(&call_runtime);
|
|
||||||
}
|
|
||||||
BIND(&argc_1);
|
|
||||||
{
|
|
||||||
Comment("1 Argument case");
|
|
||||||
var_search_string = arguments.AtIndex(0);
|
|
||||||
var_position = SmiConstant(0);
|
|
||||||
Goto(&fast_path);
|
|
||||||
}
|
|
||||||
BIND(&argc_2);
|
|
||||||
{
|
|
||||||
Comment("2 Argument case");
|
|
||||||
var_search_string = arguments.AtIndex(0);
|
|
||||||
var_position = arguments.AtIndex(1);
|
|
||||||
GotoIfNot(TaggedIsSmi(var_position.value()), &call_runtime);
|
|
||||||
Goto(&fast_path);
|
|
||||||
}
|
|
||||||
BIND(&fast_path);
|
|
||||||
{
|
|
||||||
Comment("Fast Path");
|
|
||||||
const TNode<Object> search = var_search_string.value();
|
|
||||||
const TNode<Smi> position = CAST(var_position.value());
|
|
||||||
GotoIf(TaggedIsSmi(receiver), &call_runtime);
|
|
||||||
GotoIf(TaggedIsSmi(search), &call_runtime);
|
|
||||||
GotoIfNot(IsString(CAST(receiver)), &call_runtime);
|
|
||||||
GotoIfNot(IsString(CAST(search)), &call_runtime);
|
|
||||||
|
|
||||||
StringIndexOf(CAST(receiver), CAST(search), position,
|
|
||||||
[&](TNode<Smi> result) {
|
|
||||||
if (variant == kIndexOf) {
|
|
||||||
arguments.PopAndReturn(result);
|
|
||||||
} else {
|
|
||||||
arguments.PopAndReturn(SelectBooleanConstant(
|
|
||||||
SmiGreaterThanOrEqual(result, SmiConstant(0))));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
BIND(&call_runtime);
|
|
||||||
{
|
|
||||||
Comment("Call Runtime");
|
|
||||||
Runtime::FunctionId runtime = variant == kIndexOf
|
|
||||||
? Runtime::kStringIndexOf
|
|
||||||
: Runtime::kStringIncludes;
|
|
||||||
const TNode<Object> result =
|
|
||||||
CallRuntime(runtime, context, receiver, var_search_string.value(),
|
|
||||||
var_position.value());
|
|
||||||
arguments.PopAndReturn(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
|
void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
|
||||||
const TNode<Context> context, const TNode<Object> object,
|
const TNode<Context> context, const TNode<Object> object,
|
||||||
const TNode<Object> maybe_string, Handle<Symbol> symbol,
|
const TNode<Object> maybe_string, Handle<Symbol> symbol,
|
||||||
|
@ -61,6 +61,29 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
String::Encoding from_encoding,
|
String::Encoding from_encoding,
|
||||||
String::Encoding to_encoding);
|
String::Encoding to_encoding);
|
||||||
|
|
||||||
|
// Torque wrapper methods for CallSearchStringRaw for each combination of
|
||||||
|
// search and subject character widths (char8/char16). This is a workaround
|
||||||
|
// for Torque's current lack of support for extern macros with generics.
|
||||||
|
TNode<IntPtrT> SearchOneByteStringInTwoByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position);
|
||||||
|
TNode<IntPtrT> SearchOneByteStringInOneByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position);
|
||||||
|
TNode<IntPtrT> SearchTwoByteStringInTwoByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position);
|
||||||
|
TNode<IntPtrT> SearchTwoByteStringInOneByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> search_length,
|
||||||
|
const TNode<IntPtrT> start_position);
|
||||||
|
TNode<IntPtrT> SearchOneByteInOneByteString(
|
||||||
|
const TNode<RawPtrT> subject_ptr, const TNode<IntPtrT> subject_length,
|
||||||
|
const TNode<RawPtrT> search_ptr, const TNode<IntPtrT> start_position);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void StringEqual_Loop(TNode<String> lhs, TNode<Word32T> lhs_instance_type,
|
void StringEqual_Loop(TNode<String> lhs, TNode<Word32T> lhs_instance_type,
|
||||||
MachineType lhs_type, TNode<String> rhs,
|
MachineType lhs_type, TNode<String> rhs,
|
||||||
@ -70,11 +93,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
TNode<RawPtrT> DirectStringData(TNode<String> string,
|
TNode<RawPtrT> DirectStringData(TNode<String> string,
|
||||||
TNode<Word32T> string_instance_type);
|
TNode<Word32T> string_instance_type);
|
||||||
|
|
||||||
void DispatchOnStringEncodings(const TNode<Word32T> lhs_instance_type,
|
|
||||||
const TNode<Word32T> rhs_instance_type,
|
|
||||||
Label* if_one_one, Label* if_one_two,
|
|
||||||
Label* if_two_one, Label* if_two_two);
|
|
||||||
|
|
||||||
template <typename SubjectChar, typename PatternChar>
|
template <typename SubjectChar, typename PatternChar>
|
||||||
TNode<IntPtrT> CallSearchStringRaw(const TNode<RawPtrT> subject_ptr,
|
TNode<IntPtrT> CallSearchStringRaw(const TNode<RawPtrT> subject_ptr,
|
||||||
const TNode<IntPtrT> subject_length,
|
const TNode<IntPtrT> subject_length,
|
||||||
@ -82,10 +100,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
const TNode<IntPtrT> search_length,
|
const TNode<IntPtrT> search_length,
|
||||||
const TNode<IntPtrT> start_position);
|
const TNode<IntPtrT> start_position);
|
||||||
|
|
||||||
TNode<RawPtrT> PointerToStringDataAtIndex(TNode<RawPtrT> string_data,
|
|
||||||
TNode<IntPtrT> index,
|
|
||||||
String::Encoding encoding);
|
|
||||||
|
|
||||||
void GenerateStringEqual(TNode<String> left, TNode<String> right);
|
void GenerateStringEqual(TNode<String> left, TNode<String> right);
|
||||||
void GenerateStringRelationalComparison(TNode<String> left,
|
void GenerateStringRelationalComparison(TNode<String> left,
|
||||||
TNode<String> right, Operation op);
|
TNode<String> right, Operation op);
|
||||||
@ -93,11 +107,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
using StringAtAccessor = std::function<TNode<Object>(
|
using StringAtAccessor = std::function<TNode<Object>(
|
||||||
TNode<String> receiver, TNode<IntPtrT> length, TNode<IntPtrT> index)>;
|
TNode<String> receiver, TNode<IntPtrT> length, TNode<IntPtrT> index)>;
|
||||||
|
|
||||||
void StringIndexOf(const TNode<String> subject_string,
|
|
||||||
const TNode<String> search_string,
|
|
||||||
const TNode<Smi> position,
|
|
||||||
const std::function<void(TNode<Smi>)>& f_return);
|
|
||||||
|
|
||||||
const TNode<Smi> IndexOfDollarChar(const TNode<Context> context,
|
const TNode<Smi> IndexOfDollarChar(const TNode<Context> context,
|
||||||
const TNode<String> string);
|
const TNode<String> string);
|
||||||
|
|
||||||
@ -172,18 +181,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
TNode<IntPtrT> character_count);
|
TNode<IntPtrT> character_count);
|
||||||
};
|
};
|
||||||
|
|
||||||
class StringIncludesIndexOfAssembler : public StringBuiltinsAssembler {
|
|
||||||
public:
|
|
||||||
explicit StringIncludesIndexOfAssembler(compiler::CodeAssemblerState* state)
|
|
||||||
: StringBuiltinsAssembler(state) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
enum SearchVariant { kIncludes, kIndexOf };
|
|
||||||
|
|
||||||
void Generate(SearchVariant variant, TNode<IntPtrT> argc,
|
|
||||||
TNode<Context> context);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
|
||||||
|
@ -116,9 +116,7 @@ IfInBounds(String, uintptr, uintptr), IfOutOfBounds {
|
|||||||
const index: uintptr = Unsigned(Convert<intptr>(indexSmi));
|
const index: uintptr = Unsigned(Convert<intptr>(indexSmi));
|
||||||
// Max string length fits Smi range, so we can do an unsigned bounds
|
// Max string length fits Smi range, so we can do an unsigned bounds
|
||||||
// check.
|
// check.
|
||||||
const kMaxStringLengthFitsSmi: constexpr bool =
|
StaticAssertStringLengthFitsSmi();
|
||||||
kStringMaxLengthUintptr < kSmiMaxValue;
|
|
||||||
static_assert(kMaxStringLengthFitsSmi);
|
|
||||||
if (index >= length) goto IfOutOfBounds;
|
if (index >= length) goto IfOutOfBounds;
|
||||||
goto IfInBounds(string, index, length);
|
goto IfInBounds(string, index, length);
|
||||||
}
|
}
|
||||||
|
@ -108,9 +108,7 @@ transitioning macro RegExpPrototypeMatchBody(implicit context: Context)(
|
|||||||
// On the fast path, we can be certain that lastIndex can never be
|
// On the fast path, we can be certain that lastIndex can never be
|
||||||
// incremented to overflow the Smi range since the maximal string
|
// incremented to overflow the Smi range since the maximal string
|
||||||
// length is less than the maximal Smi value.
|
// length is less than the maximal Smi value.
|
||||||
const kMaxStringLengthFitsSmi: constexpr bool =
|
StaticAssertStringLengthFitsSmi();
|
||||||
kStringMaxLengthUintptr < kSmiMaxValue;
|
|
||||||
static_assert(kMaxStringLengthFitsSmi);
|
|
||||||
assert(TaggedIsPositiveSmi(newLastIndex));
|
assert(TaggedIsPositiveSmi(newLastIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
49
src/builtins/string-includes.tq
Normal file
49
src/builtins/string-includes.tq
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2021 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-string-gen.h'
|
||||||
|
|
||||||
|
namespace string {
|
||||||
|
|
||||||
|
// https://tc39.es/ecma262/#sec-string.prototype.includes
|
||||||
|
transitioning javascript builtin
|
||||||
|
StringPrototypeIncludes(js-implicit context: NativeContext, receiver: JSAny)(
|
||||||
|
...arguments): Boolean {
|
||||||
|
const methodName: constexpr string = 'String.prototype.includes';
|
||||||
|
const searchString: JSAny = arguments[0];
|
||||||
|
const position: JSAny = arguments[1];
|
||||||
|
|
||||||
|
// 1. Let O be ? RequireObjectCoercible(this value).
|
||||||
|
// 2. Let S be ? ToString(O).
|
||||||
|
const s = ToThisString(receiver, methodName);
|
||||||
|
|
||||||
|
// 3. Let isRegExp be ? IsRegExp(searchString).
|
||||||
|
// 4. If isRegExp is true, throw a TypeError exception.
|
||||||
|
if (regexp::IsRegExp(searchString)) {
|
||||||
|
ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Let searchStr be ? ToString(searchString).
|
||||||
|
const searchStr = ToString_Inline(searchString);
|
||||||
|
|
||||||
|
// 6. Let pos be ? ToIntegerOrInfinity(position).
|
||||||
|
// 7. Assert: If position is undefined, then pos is 0.
|
||||||
|
let start: Smi = 0;
|
||||||
|
if (position != Undefined) {
|
||||||
|
// 8. Let len be the length of S.
|
||||||
|
const len = s.length_uintptr;
|
||||||
|
|
||||||
|
// 9. Let start be the result of clamping pos between 0 and len.
|
||||||
|
StaticAssertStringLengthFitsSmi();
|
||||||
|
start = Convert<Smi>(Signed(ClampToIndexRange(position, len)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. Let index be ! StringIndexOf(S, searchStr, start).
|
||||||
|
const index = StringIndexOf(s, searchStr, start);
|
||||||
|
|
||||||
|
// 11. If index is not -1, return true.
|
||||||
|
// 12. Return false.
|
||||||
|
return index != -1 ? True : False;
|
||||||
|
}
|
||||||
|
}
|
39
src/builtins/string-indexof.tq
Normal file
39
src/builtins/string-indexof.tq
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2021 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-string-gen.h'
|
||||||
|
|
||||||
|
namespace string {
|
||||||
|
|
||||||
|
// https://tc39.es/ecma262/#sec-string.prototype.indexof
|
||||||
|
transitioning javascript builtin
|
||||||
|
StringPrototypeIndexOf(
|
||||||
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Smi {
|
||||||
|
const methodName: constexpr string = 'String.prototype.indexOf';
|
||||||
|
const searchString: JSAny = arguments[0];
|
||||||
|
const position: JSAny = arguments[1];
|
||||||
|
|
||||||
|
// 1. Let O be ? RequireObjectCoercible(this value).
|
||||||
|
// 2. Let S be ? ToString(O).
|
||||||
|
const s = ToThisString(receiver, methodName);
|
||||||
|
|
||||||
|
// 3. Let searchStr be ? ToString(searchString).
|
||||||
|
const searchStr = ToString_Inline(searchString);
|
||||||
|
|
||||||
|
// 4. Let pos be ? ToIntegerOrInfinity(position).
|
||||||
|
// 5. Assert: If position is undefined, then pos is 0.
|
||||||
|
let start: Smi = 0;
|
||||||
|
if (position != Undefined) {
|
||||||
|
// 6. Let len be the length of S.
|
||||||
|
const len = s.length_uintptr;
|
||||||
|
|
||||||
|
// 7. Let start be the result of clamping pos between 0 and len.
|
||||||
|
StaticAssertStringLengthFitsSmi();
|
||||||
|
start = Convert<Smi>(Signed(ClampToIndexRange(position, len)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Let index be ! StringIndexOf(S, searchStr, start).
|
||||||
|
return StringIndexOf(s, searchStr, start);
|
||||||
|
}
|
||||||
|
}
|
@ -9,52 +9,6 @@ extern macro ReplaceSymbolConstant(): Symbol;
|
|||||||
extern macro StringBuiltinsAssembler::GetSubstitution(
|
extern macro StringBuiltinsAssembler::GetSubstitution(
|
||||||
implicit context: Context)(String, Smi, Smi, String): String;
|
implicit context: Context)(String, Smi, Smi, String): String;
|
||||||
|
|
||||||
extern builtin
|
|
||||||
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
|
|
||||||
|
|
||||||
// TODO(turbofan): This could be replaced with a fast C-call to StringSearchRaw.
|
|
||||||
macro AbstractStringIndexOf<A: type, B: type>(
|
|
||||||
string: ConstSlice<A>, searchString: ConstSlice<B>, fromIndex: Smi): Smi {
|
|
||||||
for (let i: intptr = SmiUntag(fromIndex);
|
|
||||||
i <= string.length - searchString.length; i++) {
|
|
||||||
if (IsSubstringAt(string, searchString, i)) {
|
|
||||||
return SmiTag(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AbstractStringIndexOfFunctor {
|
|
||||||
fromIndex: Smi;
|
|
||||||
}
|
|
||||||
// Ideally, this would be a method of AbstractStringIndexOfFunctor, but
|
|
||||||
// currently methods don't support templates.
|
|
||||||
macro Call<A: type, B: type>(
|
|
||||||
self: AbstractStringIndexOfFunctor, string: ConstSlice<A>,
|
|
||||||
searchStr: ConstSlice<B>): Smi {
|
|
||||||
return AbstractStringIndexOf(string, searchStr, self.fromIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro AbstractStringIndexOf(implicit context: Context)(
|
|
||||||
string: String, searchString: String, fromIndex: Smi): Smi {
|
|
||||||
// Special case the empty string.
|
|
||||||
const searchStringLength = searchString.length_intptr;
|
|
||||||
const stringLength = string.length_intptr;
|
|
||||||
if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) {
|
|
||||||
return fromIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't bother to search if the searchString would go past the end
|
|
||||||
// of the string. This is actually necessary because of runtime
|
|
||||||
// checks.
|
|
||||||
if (SmiUntag(fromIndex) + searchStringLength > stringLength) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TwoStringsToSlices<Smi>(
|
|
||||||
string, searchString, AbstractStringIndexOfFunctor{fromIndex: fromIndex});
|
|
||||||
}
|
|
||||||
|
|
||||||
transitioning macro
|
transitioning macro
|
||||||
ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void {
|
ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void {
|
||||||
let shouldThrow: bool;
|
let shouldThrow: bool;
|
||||||
|
@ -308,8 +308,6 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
|
|||||||
V(ThrowReferenceError) \
|
V(ThrowReferenceError) \
|
||||||
V(ThrowSymbolIteratorInvalid) \
|
V(ThrowSymbolIteratorInvalid) \
|
||||||
/* Strings */ \
|
/* Strings */ \
|
||||||
V(StringIncludes) \
|
|
||||||
V(StringIndexOf) \
|
|
||||||
V(StringReplaceOneCharWithString) \
|
V(StringReplaceOneCharWithString) \
|
||||||
V(StringSubstring) \
|
V(StringSubstring) \
|
||||||
V(StringToNumber) \
|
V(StringToNumber) \
|
||||||
@ -351,7 +349,6 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
|
|||||||
V(StringAdd) \
|
V(StringAdd) \
|
||||||
V(StringCharCodeAt) \
|
V(StringCharCodeAt) \
|
||||||
V(StringEqual) \
|
V(StringEqual) \
|
||||||
V(StringIndexOfUnchecked) \
|
|
||||||
V(StringParseFloat) \
|
V(StringParseFloat) \
|
||||||
V(StringParseInt) \
|
V(StringParseInt) \
|
||||||
V(SymbolDescriptiveString) \
|
V(SymbolDescriptiveString) \
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include 'src/builtins/builtins-string-gen.h'
|
||||||
|
|
||||||
@abstract
|
@abstract
|
||||||
@generateCppClass
|
@generateCppClass
|
||||||
@reserveBitsInInstanceType(6)
|
@reserveBitsInInstanceType(6)
|
||||||
@ -306,3 +308,87 @@ macro TwoStringsToSlices<Result: type, Functor: type>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro StaticAssertStringLengthFitsSmi(): void {
|
||||||
|
const kMaxStringLengthFitsSmi: constexpr bool =
|
||||||
|
kStringMaxLengthUintptr < kSmiMaxValue;
|
||||||
|
static_assert(kMaxStringLengthFitsSmi);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern macro StringBuiltinsAssembler::SearchOneByteStringInTwoByteString(
|
||||||
|
RawPtr<char16>, intptr, RawPtr<char8>, intptr, intptr): intptr;
|
||||||
|
extern macro StringBuiltinsAssembler::SearchOneByteStringInOneByteString(
|
||||||
|
RawPtr<char8>, intptr, RawPtr<char8>, intptr, intptr): intptr;
|
||||||
|
extern macro StringBuiltinsAssembler::SearchTwoByteStringInTwoByteString(
|
||||||
|
RawPtr<char16>, intptr, RawPtr<char16>, intptr, intptr): intptr;
|
||||||
|
extern macro StringBuiltinsAssembler::SearchTwoByteStringInOneByteString(
|
||||||
|
RawPtr<char8>, intptr, RawPtr<char16>, intptr, intptr): intptr;
|
||||||
|
extern macro StringBuiltinsAssembler::SearchOneByteInOneByteString(
|
||||||
|
RawPtr<char8>, intptr, RawPtr<char8>, intptr): intptr;
|
||||||
|
|
||||||
|
macro AbstractStringIndexOf(
|
||||||
|
subject: RawPtr<char16>, subjectLen: intptr, search: RawPtr<char8>,
|
||||||
|
searchLen: intptr, fromIndex: intptr): intptr {
|
||||||
|
return SearchOneByteStringInTwoByteString(
|
||||||
|
subject, subjectLen, search, searchLen, fromIndex);
|
||||||
|
}
|
||||||
|
macro AbstractStringIndexOf(
|
||||||
|
subject: RawPtr<char8>, subjectLen: intptr, search: RawPtr<char8>,
|
||||||
|
searchLen: intptr, fromIndex: intptr): intptr {
|
||||||
|
if (searchLen == 1) {
|
||||||
|
return SearchOneByteInOneByteString(subject, subjectLen, search, fromIndex);
|
||||||
|
}
|
||||||
|
return SearchOneByteStringInOneByteString(
|
||||||
|
subject, subjectLen, search, searchLen, fromIndex);
|
||||||
|
}
|
||||||
|
macro AbstractStringIndexOf(
|
||||||
|
subject: RawPtr<char16>, subjectLen: intptr, search: RawPtr<char16>,
|
||||||
|
searchLen: intptr, fromIndex: intptr): intptr {
|
||||||
|
return SearchTwoByteStringInTwoByteString(
|
||||||
|
subject, subjectLen, search, searchLen, fromIndex);
|
||||||
|
}
|
||||||
|
macro AbstractStringIndexOf(
|
||||||
|
subject: RawPtr<char8>, subjectLen: intptr, search: RawPtr<char16>,
|
||||||
|
searchLen: intptr, fromIndex: intptr): intptr {
|
||||||
|
return SearchTwoByteStringInOneByteString(
|
||||||
|
subject, subjectLen, search, searchLen, fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AbstractStringIndexOfFunctor {
|
||||||
|
fromIndex: Smi;
|
||||||
|
}
|
||||||
|
// Ideally, this would be a method of AbstractStringIndexOfFunctor, but
|
||||||
|
// currently methods don't support templates.
|
||||||
|
macro Call<A: type, B: type>(
|
||||||
|
self: AbstractStringIndexOfFunctor, string: ConstSlice<A>,
|
||||||
|
searchStr: ConstSlice<B>): Smi {
|
||||||
|
return Convert<Smi>(AbstractStringIndexOf(
|
||||||
|
string.GCUnsafeStartPointer(), string.length,
|
||||||
|
searchStr.GCUnsafeStartPointer(), searchStr.length,
|
||||||
|
Convert<intptr>(self.fromIndex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
macro AbstractStringIndexOf(implicit context: Context)(
|
||||||
|
string: String, searchString: String, fromIndex: Smi): Smi {
|
||||||
|
// Special case the empty string.
|
||||||
|
const searchStringLength = searchString.length_intptr;
|
||||||
|
const stringLength = string.length_intptr;
|
||||||
|
if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) {
|
||||||
|
return fromIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't bother to search if the searchString would go past the end
|
||||||
|
// of the string. This is actually necessary because of runtime
|
||||||
|
// checks.
|
||||||
|
if (SmiUntag(fromIndex) + searchStringLength > stringLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TwoStringsToSlices<Smi>(
|
||||||
|
string, searchString, AbstractStringIndexOfFunctor{fromIndex: fromIndex});
|
||||||
|
}
|
||||||
|
|
||||||
|
builtin StringIndexOf(implicit context: Context)(
|
||||||
|
s: String, searchString: String, start: Smi): Smi {
|
||||||
|
return AbstractStringIndexOf(s, searchString, SmiMax(start, 0));
|
||||||
|
}
|
||||||
|
@ -139,72 +139,6 @@ RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
|
|||||||
return isolate->StackOverflow();
|
return isolate->StackOverflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ES6 #sec-string.prototype.includes
|
|
||||||
// String.prototype.includes(searchString [, position])
|
|
||||||
RUNTIME_FUNCTION(Runtime_StringIncludes) {
|
|
||||||
HandleScope scope(isolate);
|
|
||||||
DCHECK_EQ(3, args.length());
|
|
||||||
|
|
||||||
Handle<Object> receiver = args.at(0);
|
|
||||||
if (receiver->IsNullOrUndefined(isolate)) {
|
|
||||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
|
||||||
isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
|
|
||||||
isolate->factory()->NewStringFromAsciiChecked(
|
|
||||||
"String.prototype.includes")));
|
|
||||||
}
|
|
||||||
Handle<String> receiver_string;
|
|
||||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver_string,
|
|
||||||
Object::ToString(isolate, receiver));
|
|
||||||
|
|
||||||
// Check if the search string is a regExp and fail if it is.
|
|
||||||
Handle<Object> search = args.at(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.includes")));
|
|
||||||
}
|
|
||||||
Handle<String> search_string;
|
|
||||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string,
|
|
||||||
Object::ToString(isolate, args.at(1)));
|
|
||||||
Handle<Object> position;
|
|
||||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position,
|
|
||||||
Object::ToInteger(isolate, args.at(2)));
|
|
||||||
|
|
||||||
uint32_t index = receiver_string->ToValidIndex(*position);
|
|
||||||
int index_in_str =
|
|
||||||
String::IndexOf(isolate, receiver_string, search_string, index);
|
|
||||||
return *isolate->factory()->ToBoolean(index_in_str != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 #sec-string.prototype.indexof
|
|
||||||
// String.prototype.indexOf(searchString [, position])
|
|
||||||
RUNTIME_FUNCTION(Runtime_StringIndexOf) {
|
|
||||||
HandleScope scope(isolate);
|
|
||||||
DCHECK_EQ(3, args.length());
|
|
||||||
return String::IndexOf(isolate, args.at(0), args.at(1), args.at(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ES6 #sec-string.prototype.indexof
|
|
||||||
// String.prototype.indexOf(searchString, position)
|
|
||||||
// Fast version that assumes that does not perform conversions of the incoming
|
|
||||||
// arguments.
|
|
||||||
RUNTIME_FUNCTION(Runtime_StringIndexOfUnchecked) {
|
|
||||||
HandleScope scope(isolate);
|
|
||||||
DCHECK_EQ(3, args.length());
|
|
||||||
Handle<String> receiver_string = args.at<String>(0);
|
|
||||||
Handle<String> search_string = args.at<String>(1);
|
|
||||||
int index = std::min(std::max(args.smi_at(2), 0), receiver_string->length());
|
|
||||||
|
|
||||||
return Smi::FromInt(String::IndexOf(isolate, receiver_string, search_string,
|
|
||||||
static_cast<uint32_t>(index)));
|
|
||||||
}
|
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
|
RUNTIME_FUNCTION(Runtime_StringLastIndexOf) {
|
||||||
HandleScope handle_scope(isolate);
|
HandleScope handle_scope(isolate);
|
||||||
return String::LastIndexOf(isolate, args.at(0), args.at(1),
|
return String::LastIndexOf(isolate, args.at(0), args.at(1),
|
||||||
|
@ -431,9 +431,6 @@ namespace internal {
|
|||||||
F(StringEscapeQuotes, 1, 1) \
|
F(StringEscapeQuotes, 1, 1) \
|
||||||
F(StringGreaterThan, 2, 1) \
|
F(StringGreaterThan, 2, 1) \
|
||||||
F(StringGreaterThanOrEqual, 2, 1) \
|
F(StringGreaterThanOrEqual, 2, 1) \
|
||||||
F(StringIncludes, 3, 1) \
|
|
||||||
F(StringIndexOf, 3, 1) \
|
|
||||||
F(StringIndexOfUnchecked, 3, 1) \
|
|
||||||
F(StringLastIndexOf, 2, 1) \
|
F(StringLastIndexOf, 2, 1) \
|
||||||
F(StringLessThan, 2, 1) \
|
F(StringLessThan, 2, 1) \
|
||||||
F(StringLessThanOrEqual, 2, 1) \
|
F(StringLessThanOrEqual, 2, 1) \
|
||||||
|
@ -465,27 +465,27 @@ KNOWN_OBJECTS = {
|
|||||||
("old_space", 0x029a1): "StringSplitCache",
|
("old_space", 0x029a1): "StringSplitCache",
|
||||||
("old_space", 0x02da9): "RegExpMultipleCache",
|
("old_space", 0x02da9): "RegExpMultipleCache",
|
||||||
("old_space", 0x031b1): "BuiltinsConstantsTable",
|
("old_space", 0x031b1): "BuiltinsConstantsTable",
|
||||||
("old_space", 0x035a9): "AsyncFunctionAwaitRejectSharedFun",
|
("old_space", 0x035b1): "AsyncFunctionAwaitRejectSharedFun",
|
||||||
("old_space", 0x035cd): "AsyncFunctionAwaitResolveSharedFun",
|
("old_space", 0x035d5): "AsyncFunctionAwaitResolveSharedFun",
|
||||||
("old_space", 0x035f1): "AsyncGeneratorAwaitRejectSharedFun",
|
("old_space", 0x035f9): "AsyncGeneratorAwaitRejectSharedFun",
|
||||||
("old_space", 0x03615): "AsyncGeneratorAwaitResolveSharedFun",
|
("old_space", 0x0361d): "AsyncGeneratorAwaitResolveSharedFun",
|
||||||
("old_space", 0x03639): "AsyncGeneratorYieldResolveSharedFun",
|
("old_space", 0x03641): "AsyncGeneratorYieldResolveSharedFun",
|
||||||
("old_space", 0x0365d): "AsyncGeneratorReturnResolveSharedFun",
|
("old_space", 0x03665): "AsyncGeneratorReturnResolveSharedFun",
|
||||||
("old_space", 0x03681): "AsyncGeneratorReturnClosedRejectSharedFun",
|
("old_space", 0x03689): "AsyncGeneratorReturnClosedRejectSharedFun",
|
||||||
("old_space", 0x036a5): "AsyncGeneratorReturnClosedResolveSharedFun",
|
("old_space", 0x036ad): "AsyncGeneratorReturnClosedResolveSharedFun",
|
||||||
("old_space", 0x036c9): "AsyncIteratorValueUnwrapSharedFun",
|
("old_space", 0x036d1): "AsyncIteratorValueUnwrapSharedFun",
|
||||||
("old_space", 0x036ed): "PromiseAllResolveElementSharedFun",
|
("old_space", 0x036f5): "PromiseAllResolveElementSharedFun",
|
||||||
("old_space", 0x03711): "PromiseAllSettledResolveElementSharedFun",
|
("old_space", 0x03719): "PromiseAllSettledResolveElementSharedFun",
|
||||||
("old_space", 0x03735): "PromiseAllSettledRejectElementSharedFun",
|
("old_space", 0x0373d): "PromiseAllSettledRejectElementSharedFun",
|
||||||
("old_space", 0x03759): "PromiseAnyRejectElementSharedFun",
|
("old_space", 0x03761): "PromiseAnyRejectElementSharedFun",
|
||||||
("old_space", 0x0377d): "PromiseCapabilityDefaultRejectSharedFun",
|
("old_space", 0x03785): "PromiseCapabilityDefaultRejectSharedFun",
|
||||||
("old_space", 0x037a1): "PromiseCapabilityDefaultResolveSharedFun",
|
("old_space", 0x037a9): "PromiseCapabilityDefaultResolveSharedFun",
|
||||||
("old_space", 0x037c5): "PromiseCatchFinallySharedFun",
|
("old_space", 0x037cd): "PromiseCatchFinallySharedFun",
|
||||||
("old_space", 0x037e9): "PromiseGetCapabilitiesExecutorSharedFun",
|
("old_space", 0x037f1): "PromiseGetCapabilitiesExecutorSharedFun",
|
||||||
("old_space", 0x0380d): "PromiseThenFinallySharedFun",
|
("old_space", 0x03815): "PromiseThenFinallySharedFun",
|
||||||
("old_space", 0x03831): "PromiseThrowerFinallySharedFun",
|
("old_space", 0x03839): "PromiseThrowerFinallySharedFun",
|
||||||
("old_space", 0x03855): "PromiseValueThunkFinallySharedFun",
|
("old_space", 0x0385d): "PromiseValueThunkFinallySharedFun",
|
||||||
("old_space", 0x03879): "ProxyRevokeSharedFun",
|
("old_space", 0x03881): "ProxyRevokeSharedFun",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Lower 32 bits of first page addresses for various heap spaces.
|
# Lower 32 bits of first page addresses for various heap spaces.
|
||||||
|
Loading…
Reference in New Issue
Block a user