| Index: src/builtins/builtins-string.cc
|
| diff --git a/src/builtins/builtins-string.cc b/src/builtins/builtins-string.cc
|
| index 640e425d23e47b6678af913a5da40d04ab055dc5..8ea56fbee52d4e2b115e8385b050ac0521269936 100644
|
| --- a/src/builtins/builtins-string.cc
|
| +++ b/src/builtins/builtins-string.cc
|
| @@ -616,58 +616,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);
|
|
|
| - Handle<String> string;
|
| - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string,
|
| - Object::ToString(isolate, object));
|
| + Node* receiver = assembler->Parameter(0);
|
| + Node* context = assembler->Parameter(3);
|
| +
|
| + Node* string = assembler->ToThisString(context, receiver,
|
| + "String.prototype[Symbol.iterator]");
|
| + var_string.Bind(string);
|
| + var_index.Bind(assembler->SmiConstant(Smi::FromInt(0)));
|
|
|
| - return *isolate->factory()->NewJSStringIterator(string);
|
| + assembler->Goto(&loop);
|
| + assembler->Bind(&loop);
|
| + {
|
| + // 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);
|
| +
|
| + 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);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + 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);
|
| + }
|
| + }
|
| + }
|
| +
|
| + 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);
|
| }
|
|
|
| - iterator->set_string(isolate->heap()->empty_string());
|
| + 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
|
|
|
| - return *isolate->factory()->NewJSIteratorResult(
|
| - isolate->factory()->undefined_value(), true);
|
| +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
|
|
|