Commit 65d2c4b4 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] uniform flattening and string access in Torque

Port String::Flatten to Torque (using a fast C call for the
non-allocating part) and provide fast and easy access to sequential
string data in Torque: GetStringData() flattens if necessary and
computes slices that allow direct access.

Applications: String.prototype.replaceAll, String.prototype.endsWith,
  and String.prototype.beginsWith now use GetStringData() and direct
  slice access instead of the slow StringCharCodeAt and they no
  longer bail out to the runtime for flattening.

Drive-by changes:
  - Expose String instance type bits as bitfields and enums in Torque.
  - Fix method lookup in Torque to include superclass methods.
  - Use char8 and char16 types in more places.
  - Allow fast C calls with void return type.
  - Add Torque macros to create subslices.
  - Add no-GC scopes to runtime functions loading external string data.


Bug: v8:7793
Change-Id: I763b9b24212770307c9b2fe9f070f21f65d68d58
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2565515
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71611}
parent fb37749a
......@@ -101,7 +101,7 @@ type uint16 extends uint31
type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
type uint8 extends uint16
generates 'TNode<Uint8T>' constexpr 'uint8_t';
type char8 extends int8 constexpr 'char';
type char8 extends uint8 constexpr 'char';
type char16 extends uint16 constexpr 'char16_t';
type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
......@@ -626,7 +626,6 @@ extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsUndefined(Object): bool;
extern macro IsNullOrUndefined(Object): bool;
extern macro IsString(HeapObject): bool;
extern macro IsSeqOneByteString(HeapObject): bool;
extern transitioning builtin NonPrimitiveToPrimitive_String(
Context, JSAny): JSPrimitive;
extern transitioning builtin NonPrimitiveToPrimitive_Default(
......@@ -655,9 +654,9 @@ extern macro LoadBufferIntptr(RawPtr, constexpr int32): intptr;
extern runtime StringEqual(Context, String, String): Oddball;
extern builtin StringLessThan(Context, String, String): Boolean;
extern macro StringCharCodeAt(String, uintptr): int32;
extern runtime StringCompareSequence(Context, String, String, Number): Boolean;
extern macro StringFromSingleCharCode(int32): String;
extern macro StringCharCodeAt(String, uintptr): char16;
extern macro StringFromSingleCharCode(char8): String;
extern macro StringFromSingleCharCode(char16): String;
extern macro NumberToString(Number): String;
extern macro StringToNumber(String): Number;
......
......@@ -40,12 +40,6 @@ TNode<RawPtrT> StringBuiltinsAssembler::DirectStringData(
BIND(&if_external);
{
// This is only valid for ExternalStrings where the resource data
// pointer is cached (i.e. no uncached external strings).
CSA_ASSERT(this, Word32NotEqual(
Word32And(string_instance_type,
Int32Constant(kUncachedExternalStringMask)),
Int32Constant(kUncachedExternalStringTag)));
var_data = LoadExternalStringResourceDataPtr(CAST(string));
Goto(&if_join);
}
......
......@@ -139,7 +139,7 @@ transitioning javascript builtin StringPrototypeCharAt(
GenerateStringAt(receiver, position, 'String.prototype.charAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index);
const code: char16 = StringCharCodeAt(string, index);
return StringFromSingleCharCode(code);
} label IfOutOfBounds {
return kEmptyString;
......@@ -154,7 +154,7 @@ transitioning javascript builtin StringPrototypeCharCodeAt(
GenerateStringAt(receiver, position, 'String.prototype.charCodeAt')
otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index);
const code: uint32 = StringCharCodeAt(string, index);
return Convert<Smi>(code);
} label IfOutOfBounds {
return kNaN;
......@@ -250,7 +250,7 @@ transitioning builtin StringAddConvertRight(implicit context: Context)(
builtin StringCharAt(implicit context: Context)(
receiver: String, position: uintptr): String {
// Load the character code at the {position} from the {receiver}.
const code: int32 = StringCharCodeAt(receiver, position);
const code: char16 = StringCharCodeAt(receiver, position);
// And return the single character string with only that {code}
return StringFromSingleCharCode(code);
}
......
......@@ -404,7 +404,7 @@ Cast<PrivateSymbol>(o: HeapObject): PrivateSymbol labels CastError {
return Cast<PrivateSymbol>(s) otherwise CastError;
}
Cast<DirectString>(o: HeapObject): DirectString
Cast<DirectString>(o: String): DirectString
labels CastError {
return TaggedToDirectString(o) otherwise CastError;
}
......@@ -538,11 +538,112 @@ Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)(
return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(a);
}
macro Cast<T: type>(o: String): T labels CastError;
Cast<SeqOneByteString>(o: HeapObject): SeqOneByteString labels CastError {
if (!IsSeqOneByteString(o)) goto CastError;
return Cast<SeqOneByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<SeqOneByteString>(o: String): SeqOneByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation == StringRepresentationTag::kSeqStringTag &
instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<SeqOneByteString>(o);
}
Cast<SeqTwoByteString>(o: HeapObject): SeqTwoByteString labels CastError {
return Cast<SeqTwoByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<SeqTwoByteString>(o: String): SeqTwoByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation == StringRepresentationTag::kSeqStringTag &
!instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<SeqTwoByteString>(o);
}
Cast<ThinString>(o: HeapObject): ThinString labels CastError {
return Cast<ThinString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ThinString>(o: String): ThinString labels CastError {
const instanceType = o.StringInstanceType();
if (instanceType.representation != StringRepresentationTag::kThinStringTag) {
goto CastError;
}
return %RawDownCast<ThinString>(o);
}
Cast<ConsString>(o: HeapObject): ConsString labels CastError {
return Cast<ConsString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ConsString>(o: String): ConsString labels CastError {
const instanceType = o.StringInstanceType();
if (instanceType.representation != StringRepresentationTag::kConsStringTag) {
goto CastError;
}
return %RawDownCast<ConsString>(o);
}
Cast<SlicedString>(o: HeapObject): SlicedString labels CastError {
return Cast<SlicedString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<SlicedString>(o: String): SlicedString labels CastError {
const instanceType = o.StringInstanceType();
if (instanceType.representation !=
StringRepresentationTag::kSlicedStringTag) {
goto CastError;
}
return %RawDownCast<SlicedString>(o);
}
Cast<ExternalOneByteString>(o: HeapObject):
ExternalOneByteString labels CastError {
return Cast<ExternalOneByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ExternalOneByteString>(o: String): ExternalOneByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation ==
StringRepresentationTag::kExternalStringTag &
instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<ExternalOneByteString>(o);
}
Cast<ExternalTwoByteString>(o: HeapObject):
ExternalTwoByteString labels CastError {
return Cast<ExternalTwoByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ExternalTwoByteString>(o: String): ExternalTwoByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation ==
StringRepresentationTag::kExternalStringTag &
!instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<ExternalTwoByteString>(o);
}
Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
labels CastError {
typeswitch (o) {
......
......@@ -58,10 +58,7 @@ FromConstexpr<int8, constexpr int31>(i: constexpr int31): int8 {
return %RawDownCast<int8>(i);
}
FromConstexpr<char8, constexpr int31>(i: constexpr int31): char8 {
const i: int32 = i;
static_assert(i <= 255);
static_assert(0 <= i);
return %RawDownCast<char8>(i);
return %RawDownCast<char8>(FromConstexpr<uint8>(i));
}
FromConstexpr<Number, constexpr Smi>(s: constexpr Smi): Number {
return SmiConstant(s);
......
......@@ -63,8 +63,9 @@ transitioning macro ThisNumberValue(implicit context: Context)(
macro ToCharCode(input: int32): char8 {
assert(0 <= input && input < 36);
return input < 10 ? %RawDownCast<char8>(input + kAsciiZero) :
%RawDownCast<char8>(input - 10 + kAsciiLowerCaseA);
return input < 10 ?
%RawDownCast<char8>(Unsigned(input + kAsciiZero)) :
%RawDownCast<char8>(Unsigned(input - 10 + kAsciiLowerCaseA));
}
@export
......@@ -92,7 +93,7 @@ macro NumberToStringSmi(x: int32, radix: int32): String labels Slow {
length = length + 1;
}
assert(length > 0);
const strSeq: SeqOneByteString = AllocateSeqOneByteString(Unsigned(length));
const strSeq = AllocateNonEmptySeqOneByteString(Unsigned(length));
let cursor: intptr = Convert<intptr>(length) - 1;
while (n > 0) {
const digit: int32 = n % radix;
......
......@@ -3,25 +3,47 @@
// found in the LICENSE file.
namespace string {
macro TryFastStringCompareSequence(
string: String, searchStr: String, start: uintptr,
searchLength: uintptr): Boolean labels Slow {
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchStr) otherwise Slow;
let searchIndex: uintptr = 0;
let stringIndex: uintptr = start;
// TODO(tebbi): This could be replaced with a fast C-call to
// CompareCharsUnsigned.
macro IsSubstringAt<A: type, B: type>(
string: ConstSlice<A>, searchStr: ConstSlice<B>, start: intptr): bool {
const subslice =
Subslice(string, start, searchStr.length) otherwise return false;
let stringIterator = subslice.Iterator();
let searchIterator = searchStr.Iterator();
while (searchIndex < searchLength) {
if (StringCharCodeAt(directSearchStr, searchIndex) !=
StringCharCodeAt(directString, stringIndex)) {
return False;
while (true) {
const searchChar = searchIterator.Next() otherwise return true;
const stringChar = stringIterator.Next() otherwise unreachable;
if (searchChar != stringChar) {
return false;
}
}
VerifiedUnreachable();
}
searchIndex++;
stringIndex++;
macro IsSubstringAt(string: String, searchStr: String, start: intptr): bool {
// TODO(tebbi): Avoid repeating this verbose pattern.
try {
GetStringData(string) otherwise FirstOneByte, FirstTwoByte;
} label FirstOneByte(string: ConstSlice<char8>) {
try {
GetStringData(searchStr) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStr: ConstSlice<char8>) {
return IsSubstringAt(string, searchStr, start);
} label SecondTwoByte(searchStr: ConstSlice<char16>) {
return IsSubstringAt(string, searchStr, start);
}
} label FirstTwoByte(string: ConstSlice<char16>) {
try {
GetStringData(searchStr) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStr: ConstSlice<char8>) {
return IsSubstringAt(string, searchStr, start);
} label SecondTwoByte(searchStr: ConstSlice<char16>) {
return IsSubstringAt(string, searchStr, start);
}
}
return True;
}
// https://tc39.github.io/ecma262/#sec-string.prototype.endswith
......@@ -58,23 +80,15 @@ transitioning javascript builtin StringPrototypeEndsWith(
const searchLength: uintptr = searchStr.length_uintptr;
// 10. Let start be end - searchLength.
const start: uintptr = end - searchLength;
const start = Signed(end - searchLength);
// 11. If start is less than 0, return false.
if (Signed(start) < 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, Convert<Number>(start));
}
return Convert<Boolean>(IsSubstringAt(string, searchStr, start));
}
}
......@@ -13,25 +13,13 @@ extern macro StringBuiltinsAssembler::GetSubstitution(
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
macro TryFastAbstractStringIndexOf(implicit context: Context)(
string: String, searchString: String, fromIndex: Smi): Smi labels Slow {
const stringLen = string.length_uintptr;
const searchLen = searchString.length_uintptr;
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchString) otherwise Slow;
const fromIndexUint = Unsigned(SmiUntag(fromIndex));
for (let i: uintptr = fromIndexUint; i < stringLen; i++) {
let j = i;
let k: uintptr = 0;
while (j < stringLen && k < searchLen &&
StringCharCodeAt(directString, j) ==
StringCharCodeAt(directSearchStr, k)) {
j++;
k++;
}
if (k == searchLen) {
return SmiTag(Signed(i));
// TODO(tebbi): 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;
......@@ -54,18 +42,23 @@ macro AbstractStringIndexOf(implicit context: Context)(
}
try {
return TryFastAbstractStringIndexOf(string, searchString, fromIndex)
otherwise Slow;
} label Slow {
for (let i: intptr = SmiUntag(fromIndex);
i + searchStringLength <= stringLength; i++) {
if (StringCompareSequence(
context, string, searchString, Convert<Number>(SmiTag(i))) ==
True) {
return SmiTag(i);
}
GetStringData(string) otherwise FirstOneByte, FirstTwoByte;
} label FirstOneByte(stringData: ConstSlice<char8>) {
try {
GetStringData(searchString) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStringData: ConstSlice<char8>) {
return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
} label SecondTwoByte(searchStringData: ConstSlice<char16>) {
return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
}
} label FirstTwoByte(stringData: ConstSlice<char16>) {
try {
GetStringData(searchString) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStringData: ConstSlice<char8>) {
return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
} label SecondTwoByte(searchStringData: ConstSlice<char16>) {
return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
}
return -1;
}
}
......
......@@ -47,14 +47,6 @@ transitioning javascript builtin StringPrototypeStartsWith(
// 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, Convert<Number>(start));
}
return Convert<Boolean>(IsSubstringAt(string, searchStr, Signed(start)));
}
}
......@@ -13,41 +13,41 @@ extern enum TrimMode extends uint31 constexpr 'String::TrimMode' {
}
@export
macro IsWhiteSpaceOrLineTerminator(charCode: int32): bool {
macro IsWhiteSpaceOrLineTerminator(charCode: char16): bool {
// 0x0020 - SPACE (Intentionally out of order to fast path a commmon case)
if (charCode == Int32Constant(0x0020)) {
if (charCode == 0x0020) {
return true;
}
// 0x0009 - HORIZONTAL TAB
if (charCode < Int32Constant(0x0009)) {
if (charCode < 0x0009) {
return false;
}
// 0x000A - LINE FEED OR NEW LINE
// 0x000B - VERTICAL TAB
// 0x000C - FORMFEED
// 0x000D - HORIZONTAL TAB
if (charCode <= Int32Constant(0x000D)) {
if (charCode <= 0x000D) {
return true;
}
// Common Non-whitespace characters
if (charCode < Int32Constant(0x00A0)) {
if (charCode < 0x00A0) {
return false;
}
// 0x00A0 - NO-BREAK SPACE
if (charCode == Int32Constant(0x00A0)) {
if (charCode == 0x00A0) {
return true;
}
// 0x1680 - Ogham Space Mark
if (charCode == Int32Constant(0x1680)) {
if (charCode == 0x1680) {
return true;
}
// 0x2000 - EN QUAD
if (charCode < Int32Constant(0x2000)) {
if (charCode < 0x2000) {
return false;
}
// 0x2001 - EM QUAD
......@@ -60,32 +60,32 @@ macro IsWhiteSpaceOrLineTerminator(charCode: int32): bool {
// 0x2008 - PUNCTUATION SPACE
// 0x2009 - THIN SPACE
// 0x200A - HAIR SPACE
if (charCode <= Int32Constant(0x200A)) {
if (charCode <= 0x200A) {
return true;
}
// 0x2028 - LINE SEPARATOR
if (charCode == Int32Constant(0x2028)) {
if (charCode == 0x2028) {
return true;
}
// 0x2029 - PARAGRAPH SEPARATOR
if (charCode == Int32Constant(0x2029)) {
if (charCode == 0x2029) {
return true;
}
// 0x202F - NARROW NO-BREAK SPACE
if (charCode == Int32Constant(0x202F)) {
if (charCode == 0x202F) {
return true;
}
// 0x205F - MEDIUM MATHEMATICAL SPACE
if (charCode == Int32Constant(0x205F)) {
if (charCode == 0x205F) {
return true;
}
// 0xFEFF - BYTE ORDER MARK
if (charCode == Int32Constant(0xFEFF)) {
if (charCode == 0xFEFF) {
return true;
}
// 0x3000 - IDEOGRAPHIC SPACE
if (charCode == Int32Constant(0x3000)) {
if (charCode == 0x3000) {
return true;
}
......
......@@ -7,6 +7,23 @@
type MutableSlice<T: type> extends torque_internal::Slice<T, &T>;
type ConstSlice<T: type> extends torque_internal::Slice<T, const &T>;
macro Subslice<T: type>(slice: ConstSlice<T>, start: intptr, length: intptr):
ConstSlice<T>labels OutOfBounds {
if (Unsigned(length) > Unsigned(slice.length)) goto OutOfBounds;
if (Unsigned(start) > Unsigned(slice.length - length)) goto OutOfBounds;
const offset = slice.offset + torque_internal::TimesSizeOf<T>(start);
return torque_internal::unsafe::NewConstSlice<T>(
slice.object, offset, length);
}
macro Subslice<T: type>(slice: MutableSlice<T>, start: intptr, length: intptr):
MutableSlice<T>labels OutOfBounds {
if (Unsigned(length) > Unsigned(slice.length)) goto OutOfBounds;
if (Unsigned(start) > Unsigned(slice.length - length)) goto OutOfBounds;
const offset = slice.offset + torque_internal::TimesSizeOf<T>(start);
return torque_internal::unsafe::NewMutableSlice<T>(
slice.object, offset, length);
}
namespace torque_internal {
// Unsafe is a marker that we require to be passed when calling internal APIs
// that might lead to unsoundness when used incorrectly. Unsafe markers should
......@@ -17,6 +34,10 @@ struct Unsafe {}
// of the pointer, not of the instance.
intrinsic %SizeOf<T: type>(): constexpr int31;
macro TimesSizeOf<T: type>(i: intptr): intptr {
return i * %SizeOf<T>();
}
struct Reference<T: type> {
const object: HeapObject|TaggedZeroPattern;
const offset: intptr;
......@@ -43,13 +64,17 @@ macro ReferenceCast<T: type, U: type>(ref:&U):&T {
UnsafeCast<T>(*ref);
return ref;
}
extern macro GCUnsafeReferenceToRawPtr(
HeapObject | TaggedZeroPattern, intptr): RawPtr;
} // namespace unsafe
struct Slice<T: type, Reference: type> {
macro TryAtIndex(index: intptr): Reference labels OutOfBounds {
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
return unsafe::NewReference<T>(
this.object, this.offset + index * %SizeOf<T>());
this.object, this.offset + TimesSizeOf<T>(index));
} else {
goto OutOfBounds;
}
......@@ -74,7 +99,7 @@ struct Slice<T: type, Reference: type> {
}
macro Iterator(): SliceIterator<T, Reference> {
const end = this.offset + this.length * %SizeOf<T>();
const end = this.offset + TimesSizeOf<T>(this.length);
return SliceIterator<T, Reference>{
object: this.object,
start: this.offset,
......@@ -87,8 +112,8 @@ struct Slice<T: type, Reference: type> {
check(
Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) &&
Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex));
const start = this.offset + startIndex * %SizeOf<T>();
const end = this.offset + endIndex * %SizeOf<T>();
const start = this.offset + TimesSizeOf<T>(startIndex);
const end = this.offset + TimesSizeOf<T>(endIndex);
return SliceIterator<T, Reference>{
object: this.object,
start,
......@@ -97,6 +122,12 @@ struct Slice<T: type, Reference: type> {
};
}
// WARNING: This can return a raw pointer into the heap, which is not GC-safe.
macro GCUnsafeStartPointer(): RawPtr<T> {
return %RawDownCast<RawPtr<T>>(
unsafe::GCUnsafeReferenceToRawPtr(this.object, this.offset));
}
const object: HeapObject|TaggedZeroPattern;
const offset: intptr;
const length: intptr;
......@@ -106,7 +137,8 @@ struct Slice<T: type, Reference: type> {
namespace unsafe {
macro NewMutableSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): MutableSlice<T> {
object: HeapObject|TaggedZeroPattern, offset: intptr,
length: intptr): MutableSlice<T> {
return %RawDownCast<MutableSlice<T>>(Slice<T, &T>{
object: object,
offset: offset,
......@@ -116,7 +148,8 @@ macro NewMutableSlice<T: type>(
}
macro NewConstSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): ConstSlice<T> {
object: HeapObject|TaggedZeroPattern, offset: intptr,
length: intptr): ConstSlice<T> {
return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{
object: object,
offset: offset,
......
......@@ -6548,11 +6548,11 @@ TNode<BoolT> CodeStubAssembler::FixedArraySizeDoesntFitInNewSpace(
element_count, IntPtrOrSmiConstant<TIndex>(max_newspace_elements));
}
TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
TNode<UintPtrT> index) {
TNode<Uint16T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
TNode<UintPtrT> index) {
CSA_ASSERT(this, UintPtrLessThan(index, LoadStringLengthAsWord(string)));
TVARIABLE(Int32T, var_result);
TVARIABLE(Uint16T, var_result);
Label return_result(this), if_runtime(this, Label::kDeferred),
if_stringistwobyte(this), if_stringisonebyte(this);
......@@ -6570,14 +6570,13 @@ TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
BIND(&if_stringisonebyte);
{
var_result = UncheckedCast<Int32T>(Load<Uint8T>(string_data, offset));
var_result = Load<Uint8T>(string_data, offset);
Goto(&return_result);
}
BIND(&if_stringistwobyte);
{
var_result = UncheckedCast<Int32T>(
Load<Uint16T>(string_data, WordShl(offset, IntPtrConstant(1))));
var_result = Load<Uint16T>(string_data, WordShl(offset, IntPtrConstant(1)));
Goto(&return_result);
}
......@@ -6586,7 +6585,7 @@ TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
TNode<Object> result =
CallRuntime(Runtime::kStringCharCodeAt, NoContextConstant(), string,
ChangeUintPtrToTagged(index));
var_result = SmiToInt32(CAST(result));
var_result = UncheckedCast<Uint16T>(SmiToInt32(CAST(result)));
Goto(&return_result);
}
......@@ -7612,6 +7611,50 @@ void CodeStubAssembler::TryToName(SloppyTNode<Object> key, Label* if_keyisindex,
}
}
void CodeStubAssembler::StringWriteToFlatOneByte(TNode<String> source,
TNode<RawPtrT> sink,
TNode<Int32T> from,
TNode<Int32T> to) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::string_write_to_flat_one_byte());
CallCFunction(function, base::nullopt,
std::make_pair(MachineType::AnyTagged(), source),
std::make_pair(MachineType::Pointer(), sink),
std::make_pair(MachineType::Int32(), from),
std::make_pair(MachineType::Int32(), to));
}
void CodeStubAssembler::StringWriteToFlatTwoByte(TNode<String> source,
TNode<RawPtrT> sink,
TNode<Int32T> from,
TNode<Int32T> to) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::string_write_to_flat_two_byte());
CallCFunction(function, base::nullopt,
std::make_pair(MachineType::AnyTagged(), source),
std::make_pair(MachineType::Pointer(), sink),
std::make_pair(MachineType::Int32(), from),
std::make_pair(MachineType::Int32(), to));
}
TNode<RawPtr<Uint8T>> CodeStubAssembler::ExternalOneByteStringGetChars(
TNode<ExternalOneByteString> string) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::external_one_byte_string_get_chars());
return UncheckedCast<RawPtr<Uint8T>>(
CallCFunction(function, MachineType::Pointer(),
std::make_pair(MachineType::AnyTagged(), string)));
}
TNode<RawPtr<Uint16T>> CodeStubAssembler::ExternalTwoByteStringGetChars(
TNode<ExternalTwoByteString> string) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::external_two_byte_string_get_chars());
return UncheckedCast<RawPtr<Uint16T>>(
CallCFunction(function, MachineType::Pointer(),
std::make_pair(MachineType::AnyTagged(), string)));
}
void CodeStubAssembler::TryInternalizeString(
TNode<String> string, Label* if_index, TVariable<IntPtrT>* var_index,
Label* if_internalized, TVariable<Name>* var_internalized,
......
......@@ -1034,6 +1034,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<RawPtrT> LoadExternalStringResourceDataPtr(
TNode<ExternalString> object) {
// This is only valid for ExternalStrings where the resource data
// pointer is cached (i.e. no uncached external strings).
CSA_ASSERT(this, Word32NotEqual(
Word32And(LoadInstanceType(object),
Int32Constant(kUncachedExternalStringMask)),
Int32Constant(kUncachedExternalStringTag)));
return LoadExternalPointerFromObject(object,
ExternalString::kResourceDataOffset,
kExternalStringResourceDataTag);
......@@ -1190,6 +1196,13 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
value, StoreToObjectWriteBarrier::kNone);
}
TNode<RawPtrT> GCUnsafeReferenceToRawPtr(TNode<Object> object,
TNode<IntPtrT> offset) {
return ReinterpretCast<RawPtrT>(
IntPtrAdd(BitcastTaggedToWord(object),
IntPtrSub(offset, IntPtrConstant(kHeapObjectTag))));
}
// Load the floating point value of a HeapNumber.
TNode<Float64T> LoadHeapNumberValue(TNode<HeapObject> object);
// Load the Map of an HeapObject.
......@@ -2520,7 +2533,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// String helpers.
// Load a character from a String (might flatten a ConsString).
TNode<Int32T> StringCharCodeAt(TNode<String> string, TNode<UintPtrT> index);
TNode<Uint16T> StringCharCodeAt(TNode<String> string, TNode<UintPtrT> index);
// Return the single character string with only {code}.
TNode<String> StringFromSingleCharCode(TNode<Int32T> code);
......@@ -2766,6 +2779,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TVariable<Name>* var_unique, Label* if_bailout,
Label* if_notinternalized = nullptr);
// Call non-allocating runtime String::WriteToFlat using fast C-calls.
void StringWriteToFlatOneByte(TNode<String> source, TNode<RawPtrT> sink,
TNode<Int32T> from, TNode<Int32T> to);
void StringWriteToFlatTwoByte(TNode<String> source, TNode<RawPtrT> sink,
TNode<Int32T> from, TNode<Int32T> to);
// Calls External{One,Two}ByteString::GetChars with a fast C-call.
TNode<RawPtr<Uint8T>> ExternalOneByteStringGetChars(
TNode<ExternalOneByteString> string);
TNode<RawPtr<Uint16T>> ExternalTwoByteStringGetChars(
TNode<ExternalTwoByteString> string);
// Performs a hash computation and string table lookup for the given string,
// and jumps to:
// - |if_index| if the string is an array index like "123"; |var_index|
......
......@@ -660,6 +660,37 @@ ExternalReference ExternalReference::search_string_raw_two_two() {
return search_string_raw<const uc16, const uc16>();
}
namespace {
void StringWriteToFlatOneByte(Address source, uint8_t* sink, int32_t from,
int32_t to) {
return String::WriteToFlat<uint8_t>(String::cast(Object(source)), sink, from,
to);
}
void StringWriteToFlatTwoByte(Address source, uint16_t* sink, int32_t from,
int32_t to) {
return String::WriteToFlat<uint16_t>(String::cast(Object(source)), sink, from,
to);
}
const uint8_t* ExternalOneByteStringGetChars(Address string) {
return ExternalOneByteString::cast(Object(string)).GetChars();
}
const uint16_t* ExternalTwoByteStringGetChars(Address string) {
return ExternalTwoByteString::cast(Object(string)).GetChars();
}
} // namespace
FUNCTION_REFERENCE(string_write_to_flat_one_byte, StringWriteToFlatOneByte)
FUNCTION_REFERENCE(string_write_to_flat_two_byte, StringWriteToFlatTwoByte)
FUNCTION_REFERENCE(external_one_byte_string_get_chars,
ExternalOneByteStringGetChars)
FUNCTION_REFERENCE(external_two_byte_string_get_chars,
ExternalTwoByteStringGetChars)
FUNCTION_REFERENCE(orderedhashmap_gethash_raw, OrderedHashMap::GetHash)
Address GetOrCreateHash(Isolate* isolate, Address raw_key) {
......
......@@ -178,6 +178,10 @@ class StatsCounter;
V(search_string_raw_one_two, "search_string_raw_one_two") \
V(search_string_raw_two_one, "search_string_raw_two_one") \
V(search_string_raw_two_two, "search_string_raw_two_two") \
V(string_write_to_flat_one_byte, "string_write_to_flat_one_byte") \
V(string_write_to_flat_two_byte, "string_write_to_flat_two_byte") \
V(external_one_byte_string_get_chars, "external_one_byte_string_get_chars") \
V(external_two_byte_string_get_chars, "external_two_byte_string_get_chars") \
V(smi_lexicographic_compare_function, "smi_lexicographic_compare_function") \
V(string_to_array_index_function, "String::ToArrayIndex") \
V(try_string_to_index_or_lookup_existing, \
......
......@@ -1177,7 +1177,7 @@ Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature,
}
Node* CodeAssembler::CallCFunction(
Node* function, MachineType return_type,
Node* function, base::Optional<MachineType> return_type,
std::initializer_list<CodeAssembler::CFunctionArg> args) {
return raw_assembler()->CallCFunction(function, return_type, args);
}
......
......@@ -1174,7 +1174,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
// Call to a C function.
template <class... CArgs>
Node* CallCFunction(Node* function, MachineType return_type, CArgs... cargs) {
Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
CArgs... cargs) {
static_assert(v8::internal::conjunction<
std::is_convertible<CArgs, CFunctionArg>...>::value,
"invalid argument types");
......@@ -1234,7 +1235,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
private:
void HandleException(Node* result);
Node* CallCFunction(Node* function, MachineType return_type,
Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
std::initializer_list<CFunctionArg> args);
Node* CallCFunctionWithoutFunctionDescriptor(
......
......@@ -731,14 +731,18 @@ namespace {
enum FunctionDescriptorMode { kHasFunctionDescriptor, kNoFunctionDescriptor };
Node* CallCFunctionImpl(
RawMachineAssembler* rasm, Node* function, MachineType return_type,
RawMachineAssembler* rasm, Node* function,
base::Optional<MachineType> return_type,
std::initializer_list<RawMachineAssembler::CFunctionArg> args,
bool caller_saved_regs, SaveFPRegsMode mode,
FunctionDescriptorMode no_function_descriptor) {
static constexpr std::size_t kNumCArgs = 10;
MachineSignature::Builder builder(rasm->zone(), 1, args.size());
builder.AddReturn(return_type);
MachineSignature::Builder builder(rasm->zone(), return_type ? 1 : 0,
args.size());
if (return_type) {
builder.AddReturn(*return_type);
}
for (const auto& arg : args) builder.AddParam(arg.first);
bool caller_saved_fp_regs = caller_saved_regs && (mode == kSaveFPRegs);
......@@ -763,7 +767,7 @@ Node* CallCFunctionImpl(
} // namespace
Node* RawMachineAssembler::CallCFunction(
Node* function, MachineType return_type,
Node* function, base::Optional<MachineType> return_type,
std::initializer_list<RawMachineAssembler::CFunctionArg> args) {
return CallCFunctionImpl(this, function, return_type, args, false,
kDontSaveFPRegs, kHasFunctionDescriptor);
......
......@@ -927,14 +927,15 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
// Call to a C function.
template <class... CArgs>
Node* CallCFunction(Node* function, MachineType return_type, CArgs... cargs) {
Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
CArgs... cargs) {
static_assert(v8::internal::conjunction<
std::is_convertible<CArgs, CFunctionArg>...>::value,
"invalid argument types");
return CallCFunction(function, return_type, {cargs...});
}
Node* CallCFunction(Node* function, MachineType return_type,
Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
std::initializer_list<CFunctionArg> args);
// Call to a C function without a function discriptor on AIX.
......
......@@ -787,6 +787,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
case Builtins::kStringPrototypeSlice:
case Builtins::kStringPrototypeSmall:
case Builtins::kStringPrototypeStartsWith:
case Builtins::kStringSlowFlatten:
case Builtins::kStringPrototypeStrike:
case Builtins::kStringPrototypeSub:
case Builtins::kStringPrototypeSubstr:
......
......@@ -89,6 +89,7 @@ macro IsSimpleObjectMap(map: Map): bool {
return false;
}
const bitField = map.bit_field;
// Using & instead of && enables Turbofan to merge the two checks into one.
return !bitField.has_named_interceptor & !bitField.is_access_check_needed;
}
......
......@@ -845,6 +845,7 @@ DEF_GETTER(ExternalOneByteString, resource,
void ExternalOneByteString::update_data_cache(Isolate* isolate) {
if (is_uncached()) return;
DisallowGarbageCollection no_gc;
WriteExternalPointerField(kResourceDataOffset, isolate,
reinterpret_cast<Address>(resource()->data()),
kExternalStringResourceDataTag);
......@@ -868,6 +869,7 @@ void ExternalOneByteString::set_resource(
}
const uint8_t* ExternalOneByteString::GetChars() {
DisallowGarbageCollection no_gc;
return reinterpret_cast<const uint8_t*>(resource()->data());
}
......@@ -883,6 +885,7 @@ DEF_GETTER(ExternalTwoByteString, resource,
void ExternalTwoByteString::update_data_cache(Isolate* isolate) {
if (is_uncached()) return;
DisallowGarbageCollection no_gc;
WriteExternalPointerField(kResourceDataOffset, isolate,
reinterpret_cast<Address>(resource()->data()),
kExternalStringResourceDataTag);
......@@ -905,7 +908,10 @@ void ExternalTwoByteString::set_resource(
if (resource != nullptr) update_data_cache(isolate);
}
const uint16_t* ExternalTwoByteString::GetChars() { return resource()->data(); }
const uint16_t* ExternalTwoByteString::GetChars() {
DisallowGarbageCollection no_gc;
return resource()->data();
}
uint16_t ExternalTwoByteString::Get(int index) {
DCHECK(index >= 0 && index < length());
......
......@@ -1703,5 +1703,25 @@ template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void String::WriteToFlat(
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void String::WriteToFlat(
String source, uint8_t* sink, int from, int to);
namespace {
// Check that the constants defined in src/objects/instance-type.h coincides
// with the Torque-definition of string instance types in src/objects/string.tq.
DEFINE_TORQUE_GENERATED_STRING_INSTANCE_TYPE()
STATIC_ASSERT(kStringRepresentationMask == RepresentationBits::kMask);
STATIC_ASSERT(kStringEncodingMask == IsOneByteBit::kMask);
STATIC_ASSERT(kTwoByteStringTag == IsOneByteBit::encode(false));
STATIC_ASSERT(kOneByteStringTag == IsOneByteBit::encode(true));
STATIC_ASSERT(kUncachedExternalStringMask == IsUncachedBit::kMask);
STATIC_ASSERT(kUncachedExternalStringTag == IsUncachedBit::encode(true));
STATIC_ASSERT(kIsNotInternalizedMask == IsNotInternalizedBit::kMask);
STATIC_ASSERT(kNotInternalizedTag == IsNotInternalizedBit::encode(true));
STATIC_ASSERT(kInternalizedTag == IsNotInternalizedBit::encode(false));
} // namespace
} // namespace internal
} // namespace v8
......@@ -6,13 +6,42 @@
@generateCppClass
@reserveBitsInInstanceType(6)
extern class String extends Name {
macro StringInstanceType(): StringInstanceType {
return %RawDownCast<StringInstanceType>(
Convert<uint16>(this.map.instance_type));
}
const length: int32;
}
extern enum StringRepresentationTag extends uint32 {
kSeqStringTag,
kConsStringTag,
kExternalStringTag,
kSlicedStringTag,
kThinStringTag
}
bitfield struct StringInstanceType extends uint16 {
representation: StringRepresentationTag: 3 bit;
is_one_byte: bool: 1 bit;
is_uncached: bool: 1 bit;
is_not_internalized: bool: 1 bit;
}
@generateCppClass
@generateBodyDescriptor
@doNotGenerateCast
extern class ConsString extends String {
// Corresponds to String::IsFlat() in the C++ runtime.
macro IsFlat(): bool {
return this.second.length == 0;
}
macro IsOneByteRepresentation(): bool {
return this.StringInstanceType().is_one_byte;
}
first: String;
second: String;
}
......@@ -21,6 +50,7 @@ extern class ConsString extends String {
@doNotGenerateCast
extern class ExternalString extends String {
resource: ExternalPointer;
// WARNING: This field is missing for uncached external strings.
resource_data: ExternalPointer;
}
......@@ -28,13 +58,36 @@ extern operator '.resource_ptr' macro LoadExternalStringResourcePtr(
ExternalString): RawPtr;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalString): RawPtr;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalOneByteString): RawPtr<char8>;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalTwoByteString): RawPtr<char16>;
extern macro ExternalOneByteStringGetChars(ExternalOneByteString):
RawPtr<char8>;
extern macro ExternalTwoByteStringGetChars(ExternalTwoByteString):
RawPtr<char16>;
@doNotGenerateCast
extern class ExternalOneByteString extends ExternalString {
macro GetChars(): RawPtr<char8> {
if (this.StringInstanceType().is_uncached) {
return ExternalOneByteStringGetChars(this);
} else {
return this.resource_data_ptr;
}
}
}
@doNotGenerateCast
extern class ExternalTwoByteString extends ExternalString {
macro GetChars(): RawPtr<char16> {
if (this.StringInstanceType().is_uncached) {
return ExternalTwoByteStringGetChars(this);
} else {
return this.resource_data_ptr;
}
}
}
@generateCppClass
......@@ -79,26 +132,153 @@ extern class ThinString extends String {
// C++ runtime. See also: ToDirectStringAssembler.
type DirectString extends String;
@export
macro AllocateSeqOneByteString(length: uint32): SeqOneByteString {
assert(length <= kStringMaxLength);
if (length == 0) return UnsafeCast<SeqOneByteString>(kEmptyString);
macro AllocateNonEmptySeqOneByteString<Iterator: type>(
length: uint32, content: Iterator): SeqOneByteString {
assert(length != 0 && length <= kStringMaxLength);
return new SeqOneByteString{
map: kOneByteStringMap,
raw_hash_field: kNameEmptyHashField,
length: Signed(length),
chars: ...UninitializedIterator {}
chars: ...content
};
}
@export
macro AllocateSeqTwoByteString(length: uint32): String {
assert(length <= kStringMaxLength);
if (length == 0) return kEmptyString;
macro AllocateNonEmptySeqTwoByteString<Iterator: type>(
length: uint32, content: Iterator): SeqTwoByteString {
assert(length > 0 && length <= kStringMaxLength);
return new SeqTwoByteString{
map: kStringMap,
raw_hash_field: kNameEmptyHashField,
length: Signed(length),
chars: ...UninitializedIterator {}
chars: ...content
};
}
macro AllocateNonEmptySeqOneByteString(length: uint32): SeqOneByteString {
return AllocateNonEmptySeqOneByteString(length, UninitializedIterator{});
}
macro AllocateNonEmptySeqTwoByteString(length: uint32): SeqTwoByteString {
return AllocateNonEmptySeqTwoByteString(length, UninitializedIterator{});
}
macro AllocateSeqOneByteString<Iterator: type>(
length: uint32, content: Iterator): SeqOneByteString|EmptyString {
if (length == 0) return kEmptyString;
return AllocateNonEmptySeqOneByteString(length, content);
}
macro AllocateSeqTwoByteString<Iterator: type>(
length: uint32, content: Iterator): SeqTwoByteString|EmptyString {
if (length == 0) return kEmptyString;
return AllocateNonEmptySeqTwoByteString(length, content);
}
@export
macro AllocateSeqOneByteString(length: uint32): SeqOneByteString|EmptyString {
return AllocateSeqOneByteString(length, UninitializedIterator{});
}
@export
macro AllocateSeqTwoByteString(length: uint32): SeqTwoByteString|EmptyString {
return AllocateSeqTwoByteString(length, UninitializedIterator{});
}
extern macro StringWriteToFlatOneByte(String, RawPtr<char8>, int32, int32);
extern macro StringWriteToFlatTwoByte(String, RawPtr<char16>, int32, int32);
// Corresponds to String::SlowFlatten in the C++ runtime.
builtin StringSlowFlatten(cons: ConsString): String {
// TurboFan can create cons strings with empty first parts.
let cons = cons;
while (cons.first.length == 0) {
// We do not want to call this function recursively. Therefore we call
// String::Flatten only in those cases where String::SlowFlatten is not
// called again.
try {
const second = Cast<ConsString>(cons.second) otherwise FoundFlatString;
if (second.IsFlat()) goto FoundFlatString;
cons = second;
} label FoundFlatString {
return Flatten(cons.second);
}
}
let flat: String;
if (cons.IsOneByteRepresentation()) {
const allocated = AllocateNonEmptySeqOneByteString(Unsigned(cons.length));
StringWriteToFlatOneByte(
cons, (&allocated.chars).GCUnsafeStartPointer(), 0, cons.length);
flat = allocated;
} else {
const allocated = UnsafeCast<SeqTwoByteString>(
AllocateNonEmptySeqTwoByteString(Unsigned(cons.length)));
StringWriteToFlatTwoByte(
cons, (&allocated.chars).GCUnsafeStartPointer(), 0, cons.length);
flat = allocated;
}
cons.first = flat;
cons.second = kEmptyString;
return flat;
}
// Corresponds to String::Flatten in the C++ runtime.
macro Flatten(string: String): String {
typeswitch (string) {
case (cons: ConsString): {
return Flatten(cons);
}
case (thin: ThinString): {
assert(!Is<ConsString>(thin.actual));
return thin.actual;
}
case (other: String): {
return other;
}
}
}
macro Flatten(cons: ConsString): String {
if (cons.IsFlat()) return cons.first;
return StringSlowFlatten(cons);
}
// Get a slice to the string data, flatten only if unavoidable for this.
macro GetStringData(string: String): never labels OneByte(ConstSlice<char8>),
TwoByte(ConstSlice<char16>) {
let string = string;
let offset: intptr = 0;
const length = Convert<intptr>(string.length);
while (true) {
typeswitch (string) {
case (s: SeqOneByteString): {
goto OneByte(Subslice(&s.chars, offset, length) otherwise unreachable);
}
case (s: SeqTwoByteString): {
goto TwoByte(Subslice(&s.chars, offset, length) otherwise unreachable);
}
case (s: ThinString): {
string = s.actual;
}
case (s: ConsString): {
string = Flatten(s);
}
case (s: SlicedString): {
offset += Convert<intptr>(s.offset);
string = s.parent;
}
case (s: ExternalOneByteString): {
const data = torque_internal::unsafe::NewOffHeapConstSlice(
s.GetChars(), Convert<intptr>(s.length));
goto OneByte(Subslice(data, offset, length) otherwise unreachable);
}
case (s: ExternalTwoByteString): {
const data = torque_internal::unsafe::NewOffHeapConstSlice(
s.GetChars(), Convert<intptr>(s.length));
goto TwoByte(Subslice(data, offset, length) otherwise unreachable);
}
case (String): {
unreachable;
}
}
}
VerifiedUnreachable();
}
......@@ -486,29 +486,6 @@ RUNTIME_FUNCTION(Runtime_StringMaxLength) {
return Smi::FromInt(String::kMaxLength);
}
RUNTIME_FUNCTION(Runtime_StringCompareSequence) {
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();
}
RUNTIME_FUNCTION(Runtime_StringEscapeQuotes) {
HandleScope handle_scope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -439,7 +439,6 @@ namespace internal {
F(StringLessThanOrEqual, 2, 1) \
F(StringMaxLength, 0, 1) \
F(StringReplaceOneCharWithString, 3, 1) \
F(StringCompareSequence, 3, 1) \
F(StringSubstring, 3, 1) \
F(StringToArray, 2, 1) \
F(StringTrim, 2, 1)
......
......@@ -471,6 +471,11 @@ std::vector<Method*> AggregateType::Methods(const std::string& name) const {
std::vector<Method*> result;
std::copy_if(methods_.begin(), methods_.end(), std::back_inserter(result),
[name](Macro* macro) { return macro->ReadableName() == name; });
if (result.empty() && parent() != nullptr) {
if (auto aggregate_parent = parent()->AggregateSupertype()) {
return (*aggregate_parent)->Methods(name);
}
}
return result;
}
......
......@@ -3202,7 +3202,8 @@ TEST(IsWhiteSpaceOrLineTerminator) {
{ // Returns true if whitespace, false otherwise.
CodeStubAssembler m(asm_tester.state());
Label if_true(&m), if_false(&m);
m.Branch(m.IsWhiteSpaceOrLineTerminator(m.SmiToInt32(m.Parameter<Smi>(1))),
m.Branch(m.IsWhiteSpaceOrLineTerminator(
m.UncheckedCast<Uint16T>(m.SmiToInt32(m.Parameter<Smi>(1)))),
&if_true, &if_false);
m.BIND(&if_true);
m.Return(m.TrueConstant());
......
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