Index: src/builtins/builtins-string.cc |
diff --git a/src/builtins/builtins-string.cc b/src/builtins/builtins-string.cc |
index 58e3ea4d650a081d4b798190c24e26d97337d1c4..73b1e8c1c1d9babef28ad616ae1d2c041c9b01d7 100644 |
--- a/src/builtins/builtins-string.cc |
+++ b/src/builtins/builtins-string.cc |
@@ -14,6 +14,46 @@ namespace internal { |
typedef CodeStubAssembler::ResultMode ResultMode; |
typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode; |
+class StringBuiltinsAssembler : public CodeStubAssembler { |
+ public: |
+ explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state) |
+ : CodeStubAssembler(state) {} |
+ |
+ protected: |
+ Node* LoadOneByteChar(Node* string, Node* index) { |
+ return Load(MachineType::Uint8(), string, OneByteCharOffset(index)); |
+ } |
+ |
+ Node* OneByteCharAddress(Node* string, Node* index) { |
+ Node* offset = OneByteCharOffset(index); |
+ return IntPtrAdd(BitcastTaggedToWord(string), offset); |
+ } |
+ |
+ Node* OneByteCharOffset(Node* index) { |
+ return CharOffset(String::ONE_BYTE_ENCODING, index); |
+ } |
+ |
+ Node* CharOffset(String::Encoding encoding, Node* index) { |
+ const int header = SeqOneByteString::kHeaderSize - kHeapObjectTag; |
+ Node* offset = index; |
+ if (encoding == String::TWO_BYTE_ENCODING) { |
+ offset = IntPtrAddFoldConstants(offset, offset); |
+ } |
+ offset = IntPtrAddFoldConstants(offset, IntPtrConstant(header)); |
+ return offset; |
+ } |
+ |
+ void BranchIfSimpleOneByteStringInstanceType(Node* instance_type, |
+ Label* if_true, |
+ Label* if_false) { |
+ const int kMask = kStringRepresentationMask | kStringEncodingMask; |
+ const int kType = kOneByteStringTag | kSeqStringTag; |
+ Branch(Word32Equal(Word32And(instance_type, Int32Constant(kMask)), |
+ Int32Constant(kType)), |
+ if_true, if_false); |
+ } |
+}; |
+ |
namespace { |
void GenerateStringEqual(CodeStubAssembler* assembler, ResultMode mode) { |
@@ -567,7 +607,7 @@ bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) { |
} |
uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) { |
- Handle<Object> value = args.at<Object>(1 + index); |
+ Handle<Object> value = args.at(1 + index); |
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1); |
if (!IsValidCodePoint(isolate, value)) { |
isolate->Throw(*isolate->factory()->NewRangeError( |
@@ -831,13 +871,131 @@ BUILTIN(StringPrototypeIncludes) { |
return *isolate->factory()->ToBoolean(index_in_str != -1); |
} |
-// ES6 section 21.1.3.8 String.prototype.indexOf ( searchString [ , position ] ) |
-BUILTIN(StringPrototypeIndexOf) { |
- HandleScope handle_scope(isolate); |
+// ES6 #sec-string.prototype.indexof |
+TF_BUILTIN(StringPrototypeIndexOf, StringBuiltinsAssembler) { |
+ Variable search_string(this, MachineRepresentation::kTagged), |
+ position(this, MachineRepresentation::kTagged); |
+ Label call_runtime(this), call_runtime_unchecked(this), argc_0(this), |
+ no_argc_0(this), argc_1(this), no_argc_1(this), argc_2(this), |
+ fast_path(this), return_minus_1(this); |
+ |
+ Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount); |
+ Node* context = Parameter(BuiltinDescriptor::kContext); |
+ |
+ CodeStubArguments arguments(this, argc); |
+ Node* receiver = arguments.GetReceiver(); |
+ // From now on use word-size argc value. |
+ argc = arguments.GetLength(); |
+ |
+ GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &argc_0); |
+ GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1); |
+ Goto(&argc_2); |
+ Bind(&argc_0); |
+ { |
+ Comment("0 Argument case"); |
+ Node* undefined = UndefinedConstant(); |
+ search_string.Bind(undefined); |
+ position.Bind(undefined); |
+ Goto(&call_runtime); |
+ } |
+ Bind(&argc_1); |
+ { |
+ Comment("1 Argument case"); |
+ search_string.Bind(arguments.AtIndex(0)); |
+ position.Bind(SmiConstant(0)); |
+ Goto(&fast_path); |
+ } |
+ Bind(&argc_2); |
+ { |
+ Comment("2 Argument case"); |
+ search_string.Bind(arguments.AtIndex(0)); |
+ position.Bind(arguments.AtIndex(1)); |
+ GotoUnless(TaggedIsSmi(position.value()), &call_runtime); |
+ Goto(&fast_path); |
+ } |
+ |
+ Bind(&fast_path); |
+ { |
+ Comment("Fast Path"); |
+ Label zero_length_needle(this); |
+ GotoIf(TaggedIsSmi(receiver), &call_runtime); |
+ Node* needle = search_string.value(); |
+ GotoIf(TaggedIsSmi(needle), &call_runtime); |
+ Node* instance_type = LoadInstanceType(receiver); |
+ GotoUnless(IsStringInstanceType(instance_type), &call_runtime); |
+ |
+ Node* needle_instance_type = LoadInstanceType(needle); |
+ GotoUnless(IsStringInstanceType(needle_instance_type), &call_runtime); |
+ |
+ // At this point we know that the receiver and the needle are Strings and |
+ // that position is a Smi. |
+ |
+ Node* needle_length = SmiUntag(LoadStringLength(needle)); |
+ // Use possibly faster runtime fallback for long search strings. |
+ GotoIf(IntPtrLessThan(IntPtrConstant(1), needle_length), |
+ &call_runtime_unchecked); |
+ Node* string_length = SmiUntag(LoadStringLength(receiver)); |
+ Node* start_position = SmiUntag(position.value()); |
+ |
+ GotoIf(IntPtrEqual(IntPtrConstant(0), needle_length), &zero_length_needle); |
+ // Check that the needle fits in the start position. |
+ GotoUnless(IntPtrLessThanOrEqual(needle_length, |
+ IntPtrSub(string_length, start_position)), |
+ &return_minus_1); |
+ // Only support one-byte strings on the fast path. |
+ Label check_needle(this), continue_fast_path(this); |
+ BranchIfSimpleOneByteStringInstanceType(instance_type, &check_needle, |
+ &call_runtime_unchecked); |
+ Bind(&check_needle); |
+ BranchIfSimpleOneByteStringInstanceType( |
+ needle_instance_type, &continue_fast_path, &call_runtime_unchecked); |
+ Bind(&continue_fast_path); |
+ { |
+ Node* needle_byte = |
+ ChangeInt32ToIntPtr(LoadOneByteChar(needle, IntPtrConstant(0))); |
+ Node* start_address = OneByteCharAddress(receiver, start_position); |
+ Node* search_length = IntPtrSub(string_length, start_position); |
+ // Call out to the highly optimized memchr to perform the actual byte |
+ // search. |
+ Node* memchr = |
+ ExternalConstant(ExternalReference::libc_memchr_function(isolate())); |
+ Node* result_address = |
+ CallCFunction3(MachineType::Pointer(), MachineType::Pointer(), |
+ MachineType::IntPtr(), MachineType::UintPtr(), memchr, |
+ start_address, needle_byte, search_length); |
+ GotoIf(WordEqual(result_address, IntPtrConstant(0)), &return_minus_1); |
+ Node* result_index = |
+ IntPtrAdd(IntPtrSub(result_address, start_address), start_position); |
+ arguments.PopAndReturn(SmiTag(result_index)); |
+ } |
+ Bind(&zero_length_needle); |
+ { |
+ Comment("0-length needle"); |
+ arguments.PopAndReturn(SmiTag(IntPtrMin(string_length, start_position))); |
+ } |
+ } |
- return String::IndexOf(isolate, args.receiver(), |
- args.atOrUndefined(isolate, 1), |
- args.atOrUndefined(isolate, 2)); |
+ Bind(&return_minus_1); |
+ { arguments.PopAndReturn(SmiConstant(-1)); } |
+ |
+ Bind(&call_runtime); |
+ { |
+ Comment("Call Runtime"); |
+ Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver, |
+ search_string.value(), position.value()); |
+ arguments.PopAndReturn(result); |
+ } |
+ |
+ 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"); |
+ Node* result = |
+ CallRuntime(Runtime::kStringIndexOfUnchecked, context, receiver, |
+ search_string.value(), position.value()); |
+ arguments.PopAndReturn(result); |
+ } |
} |
// ES6 section 21.1.3.9 |
@@ -861,8 +1019,8 @@ BUILTIN(StringPrototypeLocaleCompare) { |
TO_THIS_STRING(str1, "String.prototype.localeCompare"); |
Handle<String> str2; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
- isolate, str2, Object::ToString(isolate, args.at<Object>(1))); |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str2, |
+ Object::ToString(isolate, args.at(1))); |
if (str1.is_identical_to(str2)) return Smi::kZero; // Equal. |
int str1_length = str1->length(); |