Commit b1181181 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Refactor string builtin code

This CL shares most of the implementation in the string
builtins String.prototype.charAt/charCodeAt/codePointAt.

Bug: v8:7270
Change-Id: Ibe43a0a22aa17fb5cd7f0519fd877fa8ae483863
Reviewed-on: https://chromium-review.googlesource.com/861786
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50546}
parent df342b8a
...@@ -319,6 +319,31 @@ void StringBuiltinsAssembler::StringEqual_Loop( ...@@ -319,6 +319,31 @@ void StringBuiltinsAssembler::StringEqual_Loop(
} }
} }
void StringBuiltinsAssembler::GenerateStringAt(char const* method_name,
TNode<Context> context,
Node* receiver,
TNode<Object> maybe_position,
TNode<Object> default_return,
StringAtAccessor accessor) {
// Check that {receiver} is coercible to Object and convert it to a String.
TNode<String> string = ToThisString(context, receiver, method_name);
// Convert the {position} to a Smi and check that it's in bounds of the
// {string}.
Label if_outofbounds(this, Label::kDeferred);
TNode<Number> position = ToInteger_Inline(
context, maybe_position, CodeStubAssembler::kTruncateMinusZero);
GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
TNode<IntPtrT> index = SmiUntag(CAST(position));
TNode<IntPtrT> length = LoadStringLengthAsWord(string);
GotoIfNot(UintPtrLessThan(index, length), &if_outofbounds);
TNode<Object> result = accessor(string, length, index);
Return(result);
BIND(&if_outofbounds);
Return(default_return);
}
void StringBuiltinsAssembler::GenerateStringRelationalComparison(Node* context, void StringBuiltinsAssembler::GenerateStringRelationalComparison(Node* context,
Node* left, Node* left,
Node* right, Node* right,
...@@ -526,7 +551,7 @@ TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) { ...@@ -526,7 +551,7 @@ TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
Operation::kGreaterThanOrEqual); Operation::kGreaterThanOrEqual);
} }
TF_BUILTIN(StringCharAt, CodeStubAssembler) { TF_BUILTIN(StringCharAt, StringBuiltinsAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver); Node* receiver = Parameter(Descriptor::kReceiver);
Node* position = Parameter(Descriptor::kPosition); Node* position = Parameter(Descriptor::kPosition);
...@@ -538,7 +563,7 @@ TF_BUILTIN(StringCharAt, CodeStubAssembler) { ...@@ -538,7 +563,7 @@ TF_BUILTIN(StringCharAt, CodeStubAssembler) {
Return(result); Return(result);
} }
TF_BUILTIN(StringCharCodeAt, CodeStubAssembler) { TF_BUILTIN(StringCharCodeAt, StringBuiltinsAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver); Node* receiver = Parameter(Descriptor::kReceiver);
Node* position = Parameter(Descriptor::kPosition); Node* position = Parameter(Descriptor::kPosition);
...@@ -663,86 +688,33 @@ TF_BUILTIN(StringFromCharCode, CodeStubAssembler) { ...@@ -663,86 +688,33 @@ TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
} }
// ES6 #sec-string.prototype.charat // ES6 #sec-string.prototype.charat
TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) { TF_BUILTIN(StringPrototypeCharAt, StringBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
Node* receiver = Parameter(Descriptor::kReceiver); Node* receiver = Parameter(Descriptor::kReceiver);
TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition)); TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// Check that {receiver} is coercible to Object and convert it to a String.
receiver = ToThisString(context, receiver, "String.prototype.charAt");
// Convert the {position} to a Smi and check that it's in bounds of the
// {receiver}.
TNode<Number> position;
{
Label return_emptystring(this, Label::kDeferred);
position = ToInteger_Inline(context, maybe_position,
CodeStubAssembler::kTruncateMinusZero);
GotoIfNot(TaggedIsSmi(position), &return_emptystring);
// Determine the actual length of the {receiver} String.
TNode<Smi> receiver_length = LoadStringLengthAsSmi(receiver);
// Return "" if the Smi {position} is outside the bounds of the {receiver}. GenerateStringAt("String.prototype.charAt", context, receiver, maybe_position,
Label if_positioninbounds(this); EmptyStringConstant(),
Branch(SmiAboveOrEqual(position, receiver_length), &return_emptystring, [this](TNode<String> string, TNode<IntPtrT> length,
&if_positioninbounds); TNode<IntPtrT> index) {
TNode<Int32T> code = StringCharCodeAt(string, index);
BIND(&return_emptystring); return StringFromCharCode(code);
Return(EmptyStringConstant()); });
BIND(&if_positioninbounds);
}
// Load the character code at the {position} from the {receiver}.
TNode<Smi> position_smi = CAST(position);
CSA_ASSERT(this, IntPtrLessThan(SmiUntag(position_smi),
LoadStringLengthAsWord(receiver)));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(SmiUntag(position_smi),
IntPtrConstant(0)));
TNode<Int32T> code = StringCharCodeAt(receiver, SmiUntag(position_smi));
// And return the single character string with only that {code}.
Node* result = StringFromCharCode(code);
Return(result);
} }
// ES6 #sec-string.prototype.charcodeat // ES6 #sec-string.prototype.charcodeat
TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) { TF_BUILTIN(StringPrototypeCharCodeAt, StringBuiltinsAssembler) {
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
Node* receiver = Parameter(Descriptor::kReceiver); Node* receiver = Parameter(Descriptor::kReceiver);
TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition)); TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// Check that {receiver} is coercible to Object and convert it to a String.
receiver = ToThisString(context, receiver, "String.prototype.charCodeAt");
// Convert the {position} to a Smi and check that it's in bounds of the
// {receiver}.
TNode<Number> position;
{
Label return_nan(this, Label::kDeferred);
position = ToInteger_Inline(context, maybe_position,
CodeStubAssembler::kTruncateMinusZero);
GotoIfNot(TaggedIsSmi(position), &return_nan);
// Determine the actual length of the {receiver} String. GenerateStringAt("String.prototype.charCodeAt", context, receiver,
TNode<Smi> receiver_length = LoadStringLengthAsSmi(receiver); maybe_position, NanConstant(),
[this](TNode<String> receiver, TNode<IntPtrT> length,
// Return NaN if the Smi {position} is outside the bounds of the {receiver}. TNode<IntPtrT> index) {
Label if_positioninbounds(this); Node* value = StringCharCodeAt(receiver, index);
Branch(SmiAboveOrEqual(position, receiver_length), &return_nan, return SmiFromWord32(value);
&if_positioninbounds); });
BIND(&return_nan);
Return(NaNConstant());
BIND(&if_positioninbounds);
}
// Load the character at the {position} from the {receiver}.
Node* value = StringCharCodeAt(receiver, SmiUntag(CAST(position)));
Node* result = SmiFromWord32(value);
Return(result);
} }
// ES6 #sec-string.prototype.codepointat // ES6 #sec-string.prototype.codepointat
...@@ -751,31 +723,14 @@ TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) { ...@@ -751,31 +723,14 @@ TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) {
Node* receiver = Parameter(Descriptor::kReceiver); Node* receiver = Parameter(Descriptor::kReceiver);
TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition)); TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
// Check that {receiver} is coercible to Object and convert it to a String. GenerateStringAt("String.prototype.codePointAt", context, receiver,
receiver = ToThisString(context, receiver, "String.prototype.codePointAt"); maybe_position, UndefinedConstant(),
[this](TNode<String> receiver, TNode<IntPtrT> length,
// Convert the {position} to a Smi and check that it's in bounds of the TNode<IntPtrT> index) {
// {receiver}. Node* value = LoadSurrogatePairAt(receiver, length, index,
Label if_inbounds(this), if_outofbounds(this, Label::kDeferred); UnicodeEncoding::UTF32);
TNode<Number> position = ToInteger_Inline( return SmiFromWord32(value);
context, maybe_position, CodeStubAssembler::kTruncateMinusZero); });
GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
TNode<IntPtrT> untagged_position = SmiUntag(CAST(position));
TNode<IntPtrT> receiver_length = LoadStringLengthAsWord(CAST(receiver));
Branch(UintPtrLessThan(untagged_position, receiver_length), &if_inbounds,
&if_outofbounds);
BIND(&if_inbounds);
{
Node* value =
LoadSurrogatePairAt(CAST(receiver), receiver_length, untagged_position,
UnicodeEncoding::UTF32);
Node* result = SmiFromWord32(value);
Return(result);
}
BIND(&if_outofbounds);
Return(UndefinedConstant());
} }
// ES6 String.prototype.concat(...args) // ES6 String.prototype.concat(...args)
......
...@@ -57,6 +57,15 @@ class StringBuiltinsAssembler : public CodeStubAssembler { ...@@ -57,6 +57,15 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
SloppyTNode<Object> value, SloppyTNode<Object> value,
SloppyTNode<Smi> limit); SloppyTNode<Smi> limit);
typedef std::function<TNode<Object>(
TNode<String> receiver, TNode<IntPtrT> length, TNode<IntPtrT> index)>
StringAtAccessor;
void GenerateStringAt(const char* method_name, TNode<Context> context,
Node* receiver, TNode<Object> maybe_position,
TNode<Object> default_return,
StringAtAccessor accessor);
TNode<Int32T> LoadSurrogatePairAt(SloppyTNode<String> string, TNode<Int32T> LoadSurrogatePairAt(SloppyTNode<String> string,
SloppyTNode<IntPtrT> length, SloppyTNode<IntPtrT> length,
SloppyTNode<IntPtrT> index, SloppyTNode<IntPtrT> index,
......
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