Index: src/builtins/builtins-string.cc |
diff --git a/src/builtins/builtins-string.cc b/src/builtins/builtins-string.cc |
index 1a92b8bdb20bc3786d1eaafca050fc1dfbd605ee..982ea6a77c5d9a822e9c4ce9b144f6713e86faa2 100644 |
--- a/src/builtins/builtins-string.cc |
+++ b/src/builtins/builtins-string.cc |
@@ -1028,58 +1028,443 @@ void Builtins::Generate_StringPrototypeValueOf(CodeStubAssembler* assembler) { |
assembler->Return(result); |
} |
-BUILTIN(StringPrototypeIterator) { |
- HandleScope scope(isolate); |
- TO_THIS_STRING(object, "String.prototype[Symbol.iterator]"); |
+void Builtins::Generate_StringPrototypeIterator(CodeStubAssembler* assembler) { |
+ typedef CodeStubAssembler::Label Label; |
+ typedef compiler::Node Node; |
+ typedef CodeStubAssembler::Variable Variable; |
+ |
+ Variable var_string(assembler, MachineRepresentation::kTagged); |
+ Variable var_index(assembler, MachineRepresentation::kTagged); |
+ |
+ Variable* loop_inputs[] = {&var_string, &var_index}; |
+ Label loop(assembler, 2, loop_inputs); |
+ Label allocate_iterator(assembler); |
+ |
+ Node* receiver = assembler->Parameter(0); |
+ Node* context = assembler->Parameter(3); |
+ |
+ var_string.Bind(assembler->ToThisString(context, receiver, |
+ "String.prototype[Symbol.iterator]")); |
+ var_index.Bind(assembler->SmiConstant(Smi::FromInt(0))); |
+ |
+ assembler->Goto(&loop); |
+ assembler->Bind(&loop); |
+ { |
+ Node* string = var_string.value(); |
+ // Load the instance type of the {string}. |
+ Node* string_instance_type = assembler->LoadInstanceType(string); |
+ |
+ // Check if the {string} is a SeqString. |
+ Label if_stringisnotsequential(assembler); |
+ assembler->Branch(assembler->Word32Equal( |
+ assembler->Word32And(string_instance_type, |
+ assembler->Int32Constant( |
+ kStringRepresentationMask)), |
+ assembler->Int32Constant(kSeqStringTag)), |
+ &allocate_iterator, &if_stringisnotsequential); |
+ |
+ assembler->Bind(&if_stringisnotsequential); |
+ { |
+ // Check if the {string} is a ConsString. |
+ Label if_stringiscons(assembler), if_stringisnotcons(assembler); |
+ assembler->Branch( |
+ assembler->Word32Equal( |
+ assembler->Word32And( |
+ string_instance_type, |
+ assembler->Int32Constant(kStringRepresentationMask)), |
+ assembler->Int32Constant(kConsStringTag)), |
+ &if_stringiscons, &if_stringisnotcons); |
+ |
+ assembler->Bind(&if_stringiscons); |
+ { |
+ // Flatten cons-string and finish. |
+ var_string.Bind(assembler->CallRuntime( |
+ Runtime::kFlattenString, assembler->NoContextConstant(), string)); |
+ assembler->Goto(&allocate_iterator); |
+ } |
+ |
+ assembler->Bind(&if_stringisnotcons); |
+ { |
+ // Check if the {string} is an ExternalString. |
+ Label if_stringisnotexternal(assembler); |
+ assembler->Branch( |
+ assembler->Word32Equal( |
+ assembler->Word32And( |
+ string_instance_type, |
+ assembler->Int32Constant(kStringRepresentationMask)), |
+ assembler->Int32Constant(kExternalStringTag)), |
+ &allocate_iterator, &if_stringisnotexternal); |
- Handle<String> string; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string, |
- Object::ToString(isolate, object)); |
+ assembler->Bind(&if_stringisnotexternal); |
+ { |
+ // The {string} is a SlicedString, continue with its parent. |
+ Node* index = var_index.value(); |
+ Node* string_offset = |
+ assembler->LoadObjectField(string, SlicedString::kOffsetOffset); |
+ Node* string_parent = |
+ assembler->LoadObjectField(string, SlicedString::kParentOffset); |
+ var_index.Bind(assembler->SmiAdd(index, string_offset)); |
+ var_string.Bind(string_parent); |
+ assembler->Goto(&loop); |
+ } |
+ } |
+ } |
+ } |
- return *isolate->factory()->NewJSStringIterator(string); |
+ assembler->Bind(&allocate_iterator); |
+ { |
+ Node* native_context = assembler->LoadNativeContext(context); |
+ Node* map = assembler->LoadFixedArrayElement( |
+ native_context, |
+ assembler->IntPtrConstant(Context::STRING_ITERATOR_MAP_INDEX), 0, |
+ CodeStubAssembler::INTPTR_PARAMETERS); |
+ Node* iterator = assembler->Allocate(JSStringIterator::kSize); |
+ assembler->StoreMapNoWriteBarrier(iterator, map); |
+ assembler->StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset, |
+ Heap::kEmptyFixedArrayRootIndex); |
+ assembler->StoreObjectFieldRoot(iterator, JSObject::kElementsOffset, |
+ Heap::kEmptyFixedArrayRootIndex); |
+ assembler->StoreObjectFieldNoWriteBarrier( |
+ iterator, JSStringIterator::kStringOffset, var_string.value()); |
+ |
+ assembler->StoreObjectFieldNoWriteBarrier( |
+ iterator, JSStringIterator::kNextIndexOffset, var_index.value()); |
+ assembler->Return(iterator); |
+ } |
} |
-BUILTIN(StringIteratorPrototypeNext) { |
- HandleScope scope(isolate); |
+namespace { |
- if (!args.receiver()->IsJSStringIterator()) { |
- Handle<String> reason = isolate->factory()->NewStringFromAsciiChecked( |
- "String Iterator.prototype.next"); |
- THROW_NEW_ERROR_RETURN_FAILURE( |
- isolate, |
- NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, reason)); |
+// Return the |word32| codepoint at {index}. Supports SeqStrings and |
+// ExternalStrings. |
+compiler::Node* LoadSurrogatePairInternal(CodeStubAssembler* assembler, |
+ compiler::Node* string, |
+ compiler::Node* length, |
+ compiler::Node* index, |
+ UnicodeEncoding encoding) { |
+ typedef CodeStubAssembler::Label Label; |
+ typedef compiler::Node Node; |
+ typedef CodeStubAssembler::Variable Variable; |
+ Label handle_surrogate_pair(assembler), return_result(assembler); |
+ Variable var_result(assembler, MachineRepresentation::kWord32); |
+ Variable var_trail(assembler, MachineRepresentation::kWord16); |
+ var_result.Bind(assembler->Int32Constant(0)); |
+ var_trail.Bind(assembler->Int32Constant(0)); |
+ |
+ Node* string_instance_type = assembler->LoadInstanceType(string); |
+ |
+ Label if_stringissequential(assembler), if_stringisexternal(assembler); |
+ assembler->Branch(assembler->Word32Equal( |
+ assembler->Word32And(string_instance_type, |
+ assembler->Int32Constant( |
+ kStringRepresentationMask)), |
+ assembler->Int32Constant(kSeqStringTag)), |
+ &if_stringissequential, &if_stringisexternal); |
+ |
+ assembler->Bind(&if_stringissequential); |
+ { |
+ Label if_stringisonebyte(assembler), if_stringistwobyte(assembler); |
+ assembler->Branch( |
+ assembler->Word32Equal( |
+ assembler->Word32And(string_instance_type, |
+ assembler->Int32Constant(kStringEncodingMask)), |
+ assembler->Int32Constant(kOneByteStringTag)), |
+ &if_stringisonebyte, &if_stringistwobyte); |
+ |
+ assembler->Bind(&if_stringisonebyte); |
+ { |
+ var_result.Bind(assembler->Load( |
+ MachineType::Uint8(), string, |
+ assembler->IntPtrAdd( |
+ index, assembler->IntPtrConstant(SeqOneByteString::kHeaderSize - |
+ kHeapObjectTag)))); |
+ assembler->Goto(&return_result); |
+ } |
+ |
+ assembler->Bind(&if_stringistwobyte); |
+ { |
+ Node* lead = assembler->Load( |
+ MachineType::Uint16(), string, |
+ assembler->IntPtrAdd( |
+ assembler->WordShl(index, assembler->IntPtrConstant(1)), |
+ assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize - |
+ kHeapObjectTag))); |
+ var_result.Bind(lead); |
+ Node* next_pos = assembler->Int32Add(index, assembler->Int32Constant(1)); |
+ |
+ Label if_isdoublecodeunit(assembler); |
+ assembler->GotoIf(assembler->Int32GreaterThanOrEqual(next_pos, length), |
+ &return_result); |
+ assembler->GotoIf( |
+ assembler->Uint32LessThan(lead, assembler->Int32Constant(0xD800)), |
+ &return_result); |
+ assembler->Branch( |
+ assembler->Uint32LessThan(lead, assembler->Int32Constant(0xDC00)), |
+ &if_isdoublecodeunit, &return_result); |
+ |
+ assembler->Bind(&if_isdoublecodeunit); |
+ { |
+ Node* trail = assembler->Load( |
+ MachineType::Uint16(), string, |
+ assembler->IntPtrAdd( |
+ assembler->WordShl(next_pos, assembler->IntPtrConstant(1)), |
+ assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize - |
+ kHeapObjectTag))); |
+ assembler->GotoIf( |
+ assembler->Uint32LessThan(trail, assembler->Int32Constant(0xDC00)), |
+ &return_result); |
+ assembler->GotoIf(assembler->Uint32GreaterThanOrEqual( |
+ trail, assembler->Int32Constant(0xE000)), |
+ &return_result); |
+ |
+ var_trail.Bind(trail); |
+ assembler->Goto(&handle_surrogate_pair); |
+ } |
+ } |
} |
- Handle<JSStringIterator> iterator = |
- Handle<JSStringIterator>::cast(args.receiver()); |
- Handle<String> string(iterator->string()); |
- |
- int position = iterator->index(); |
- int length = string->length(); |
- |
- if (position < length) { |
- uint16_t lead = string->Get(position); |
- if (lead >= 0xD800 && lead <= 0xDBFF && position + 1 < length) { |
- uint16_t trail = string->Get(position + 1); |
- if (V8_LIKELY(trail >= 0xDC00 && trail <= 0xDFFF)) { |
- // Return surrogate pair code units |
- iterator->set_index(position + 2); |
- Handle<String> value = |
- isolate->factory()->NewSurrogatePairString(lead, trail); |
- return *isolate->factory()->NewJSIteratorResult(value, false); |
+ |
+ assembler->Bind(&if_stringisexternal); |
+ { |
+ assembler->Assert(assembler->Word32Equal( |
+ assembler->Word32And( |
+ string_instance_type, |
+ assembler->Int32Constant(kStringRepresentationMask)), |
+ assembler->Int32Constant(kExternalStringTag))); |
+ Label if_stringisshort(assembler), if_stringisnotshort(assembler); |
+ |
+ assembler->Branch(assembler->Word32Equal( |
+ assembler->Word32And(string_instance_type, |
+ assembler->Int32Constant( |
+ kShortExternalStringMask)), |
+ assembler->Int32Constant(0)), |
+ &if_stringisshort, &if_stringisnotshort); |
+ |
+ assembler->Bind(&if_stringisshort); |
+ { |
+ // Load the actual resource data from the {string}. |
+ Node* string_resource_data = assembler->LoadObjectField( |
+ string, ExternalString::kResourceDataOffset, MachineType::Pointer()); |
+ |
+ Label if_stringistwobyte(assembler), if_stringisonebyte(assembler); |
+ assembler->Branch(assembler->Word32Equal( |
+ assembler->Word32And( |
+ string_instance_type, |
+ assembler->Int32Constant(kStringEncodingMask)), |
+ assembler->Int32Constant(kTwoByteStringTag)), |
+ &if_stringistwobyte, &if_stringisonebyte); |
+ |
+ assembler->Bind(&if_stringisonebyte); |
+ { |
+ var_result.Bind( |
+ assembler->Load(MachineType::Uint8(), string_resource_data, index)); |
+ assembler->Goto(&return_result); |
+ } |
+ |
+ assembler->Bind(&if_stringistwobyte); |
+ { |
+ Label if_isdoublecodeunit(assembler); |
+ Node* lead = assembler->Load( |
+ MachineType::Uint16(), string_resource_data, |
+ assembler->WordShl(index, assembler->IntPtrConstant(1))); |
+ var_result.Bind(lead); |
+ Node* next_pos = |
+ assembler->Int32Add(index, assembler->Int32Constant(1)); |
+ |
+ assembler->GotoIf(assembler->Int32GreaterThanOrEqual(next_pos, length), |
+ &return_result); |
+ assembler->GotoIf( |
+ assembler->Uint32LessThan(lead, assembler->Int32Constant(0xD800)), |
+ &return_result); |
+ assembler->Branch( |
+ assembler->Uint32LessThan(lead, assembler->Int32Constant(0xDC00)), |
+ &if_isdoublecodeunit, &return_result); |
+ |
+ assembler->Bind(&if_isdoublecodeunit); |
+ { |
+ Node* trail = assembler->Load( |
+ MachineType::Uint16(), string, |
+ assembler->IntPtrAdd( |
+ assembler->WordShl(next_pos, assembler->IntPtrConstant(1)), |
+ assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize - |
+ kHeapObjectTag))); |
+ assembler->GotoIf(assembler->Uint32LessThan( |
+ trail, assembler->Int32Constant(0xDC00)), |
+ &return_result); |
+ assembler->GotoIf(assembler->Uint32GreaterThanOrEqual( |
+ trail, assembler->Int32Constant(0xE000)), |
+ &return_result); |
+ |
+ var_trail.Bind(trail); |
+ assembler->Goto(&handle_surrogate_pair); |
+ } |
} |
} |
- // Return single code unit |
- iterator->set_index(position + 1); |
- Handle<String> value = |
- isolate->factory()->LookupSingleCharacterStringFromCode(lead); |
- return *isolate->factory()->NewJSIteratorResult(value, false); |
+ assembler->Bind(&if_stringisnotshort); |
+ { |
+ Label if_isdoublecodeunit(assembler); |
+ Node* lead = assembler->SmiToWord32(assembler->CallRuntime( |
+ Runtime::kExternalStringGetChar, assembler->NoContextConstant(), |
+ string, assembler->SmiTag(index))); |
+ var_result.Bind(lead); |
+ Node* next_pos = assembler->Int32Add(index, assembler->Int32Constant(1)); |
+ |
+ assembler->GotoIf(assembler->Int32GreaterThanOrEqual(next_pos, length), |
+ &return_result); |
+ assembler->GotoIf( |
+ assembler->Uint32LessThan(lead, assembler->Int32Constant(0xD800)), |
+ &return_result); |
+ assembler->Branch(assembler->Uint32GreaterThanOrEqual( |
+ lead, assembler->Int32Constant(0xDC00)), |
+ &return_result, &if_isdoublecodeunit); |
+ |
+ assembler->Bind(&if_isdoublecodeunit); |
+ { |
+ Node* trail = assembler->SmiToWord32(assembler->CallRuntime( |
+ Runtime::kExternalStringGetChar, assembler->NoContextConstant(), |
+ string, assembler->SmiTag(next_pos))); |
+ assembler->GotoIf( |
+ assembler->Uint32LessThan(trail, assembler->Int32Constant(0xDC00)), |
+ &return_result); |
+ assembler->GotoIf(assembler->Uint32GreaterThanOrEqual( |
+ trail, assembler->Int32Constant(0xE000)), |
+ &return_result); |
+ var_trail.Bind(trail); |
+ assembler->Goto(&handle_surrogate_pair); |
+ } |
+ } |
} |
- iterator->set_string(isolate->heap()->empty_string()); |
+ assembler->Bind(&handle_surrogate_pair); |
+ { |
+ Node* lead = var_result.value(); |
+ Node* trail = var_trail.value(); |
+#ifdef ENABLE_SLOW_DCHECKS |
+ // Check that this path is only taken if a surrogate pair is found |
+ assembler->Assert(assembler->Uint32GreaterThanOrEqual( |
+ lead, assembler->Int32Constant(0xD800))); |
+ assembler->Assert( |
+ assembler->Uint32LessThan(lead, assembler->Int32Constant(0xDC00))); |
+ assembler->Assert(assembler->Uint32GreaterThanOrEqual( |
+ trail, assembler->Int32Constant(0xDC00))); |
+ assembler->Assert( |
+ assembler->Uint32LessThan(trail, assembler->Int32Constant(0xE000))); |
+#endif |
+ |
+ switch (encoding) { |
+ case UnicodeEncoding::UTF16: |
+ var_result.Bind(assembler->WordOr( |
+ assembler->WordShl(trail, assembler->Int32Constant(16)), lead)); |
+ break; |
+ |
+ case UnicodeEncoding::UTF32: { |
+ // Convert UTF16 surrogate pair into |word32| code point, encoded as |
+ // UTF32. |
+ Node* surrogate_offset = |
+ assembler->Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00); |
+ |
+ // (lead << 10) + trail + SURROGATE_OFFSET |
+ var_result.Bind(assembler->Int32Add( |
+ assembler->WordShl(lead, assembler->Int32Constant(10)), |
+ assembler->Int32Add(trail, surrogate_offset))); |
+ break; |
+ } |
+ } |
+ assembler->Goto(&return_result); |
+ } |
- return *isolate->factory()->NewJSIteratorResult( |
- isolate->factory()->undefined_value(), true); |
+ assembler->Bind(&return_result); |
+ return var_result.value(); |
+} |
+ |
+compiler::Node* LoadSurrogatePairAt(CodeStubAssembler* assembler, |
+ compiler::Node* string, |
+ compiler::Node* length, |
+ compiler::Node* index) { |
+ return LoadSurrogatePairInternal(assembler, string, length, index, |
+ UnicodeEncoding::UTF16); |
+} |
+ |
+} // namespace |
+ |
+void Builtins::Generate_StringIteratorPrototypeNext( |
+ CodeStubAssembler* assembler) { |
+ typedef CodeStubAssembler::Label Label; |
+ typedef compiler::Node Node; |
+ typedef CodeStubAssembler::Variable Variable; |
+ |
+ Variable var_value(assembler, MachineRepresentation::kTagged); |
+ Variable var_done(assembler, MachineRepresentation::kTagged); |
+ |
+ var_value.Bind(assembler->UndefinedConstant()); |
+ var_done.Bind(assembler->BooleanConstant(true)); |
+ |
+ Label throw_bad_receiver(assembler), next_codepoint(assembler), |
+ return_result(assembler); |
+ |
+ Node* iterator = assembler->Parameter(0); |
+ Node* context = assembler->Parameter(3); |
+ |
+ assembler->GotoIf(assembler->WordIsSmi(iterator), &throw_bad_receiver); |
+ assembler->GotoUnless( |
+ assembler->WordEqual(assembler->LoadInstanceType(iterator), |
+ assembler->Int32Constant(JS_STRING_ITERATOR_TYPE)), |
+ &throw_bad_receiver); |
+ |
+ Node* string = |
+ assembler->LoadObjectField(iterator, JSStringIterator::kStringOffset); |
+ Node* position = |
+ assembler->LoadObjectField(iterator, JSStringIterator::kNextIndexOffset); |
+ Node* length = assembler->LoadObjectField(string, String::kLengthOffset); |
+ |
+ assembler->Branch(assembler->SmiLessThan(position, length), &next_codepoint, |
+ &return_result); |
+ |
+ assembler->Bind(&next_codepoint); |
+ { |
+ Node* ch = |
+ LoadSurrogatePairAt(assembler, string, assembler->SmiUntag(length), |
+ assembler->SmiUntag(position)); |
+ Node* value = assembler->StringFromCodePoint(ch, UnicodeEncoding::UTF16); |
+ var_value.Bind(value); |
+ Node* length = assembler->LoadObjectField(value, String::kLengthOffset); |
+ assembler->StoreObjectFieldNoWriteBarrier( |
+ iterator, JSStringIterator::kNextIndexOffset, |
+ assembler->SmiAdd(position, length)); |
+ var_done.Bind(assembler->BooleanConstant(false)); |
+ assembler->Goto(&return_result); |
+ } |
+ |
+ assembler->Bind(&return_result); |
+ { |
+ Node* native_context = assembler->LoadNativeContext(context); |
+ Node* map = assembler->LoadFixedArrayElement( |
+ native_context, |
+ assembler->IntPtrConstant(Context::ITERATOR_RESULT_MAP_INDEX), 0, |
+ CodeStubAssembler::INTPTR_PARAMETERS); |
+ Node* result = assembler->Allocate(JSIteratorResult::kSize); |
+ assembler->StoreMapNoWriteBarrier(result, map); |
+ assembler->StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset, |
+ Heap::kEmptyFixedArrayRootIndex); |
+ assembler->StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, |
+ Heap::kEmptyFixedArrayRootIndex); |
+ assembler->StoreObjectFieldNoWriteBarrier( |
+ result, JSIteratorResult::kValueOffset, var_value.value()); |
+ assembler->StoreObjectFieldNoWriteBarrier( |
+ result, JSIteratorResult::kDoneOffset, var_done.value()); |
+ assembler->Return(result); |
+ } |
+ |
+ assembler->Bind(&throw_bad_receiver); |
+ { |
+ // The {receiver} is not a valid JSGeneratorObject. |
+ Node* result = assembler->CallRuntime( |
+ Runtime::kThrowIncompatibleMethodReceiver, context, |
+ assembler->HeapConstant(assembler->factory()->NewStringFromAsciiChecked( |
+ "String Iterator.prototype.next", TENURED)), |
+ iterator); |
+ assembler->Return(result); // Never reached. |
+ } |
} |
} // namespace internal |