| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_INTL_SUPPORT |
| #error Internationalization is expected to be enabled. |
| #endif // V8_INTL_SUPPORT |
| |
| #include "src/objects/js-segment-iterator.h" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "src/execution/isolate.h" |
| #include "src/heap/factory.h" |
| #include "src/objects/intl-objects.h" |
| #include "src/objects/js-segment-iterator-inl.h" |
| #include "src/objects/js-segments.h" |
| #include "src/objects/managed-inl.h" |
| #include "src/objects/objects-inl.h" |
| #include "unicode/brkiter.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| Handle<String> JSSegmentIterator::GranularityAsString(Isolate* isolate) const { |
| return JSSegmenter::GetGranularityString(isolate, granularity()); |
| } |
| |
| // https://tc39.es/ecma402/#sec-createsegmentiterator |
| MaybeDirectHandle<JSSegmentIterator> JSSegmentIterator::Create( |
| Isolate* isolate, DirectHandle<String> input_string, |
| const icu::BreakIterator& incoming_break_iterator, |
| JSSegmenter::Granularity granularity) { |
| // Clone a copy for both the ownership and not sharing with containing and |
| // other calls to the iterator because icu::BreakIterator keep the iteration |
| // position internally and cannot be shared across multiple calls to |
| // JSSegmentIterator::Create and JSSegments::Containing. |
| std::unique_ptr<icu::BreakIterator> cloned_iterator{ |
| incoming_break_iterator.clone()}; |
| auto iterator_with_text = std::make_shared<IcuBreakIteratorWithText>( |
| isolate, std::move(cloned_iterator), input_string); |
| DirectHandle<CppGCManaged<IcuBreakIteratorWithText>> managed = |
| CppGCManaged<IcuBreakIteratorWithText>::Create(isolate, 0, |
| iterator_with_text); |
| |
| // 5. Set iterator.[[IteratedStringNextSegmentCodeUnitIndex]] to 0. |
| iterator_with_text->iterator()->first(); |
| |
| DirectHandle<Map> map(isolate->native_context()->intl_segment_iterator_map(), |
| isolate); |
| |
| // Now all properties are ready, so we can allocate the result object. |
| DirectHandle<JSObject> result = isolate->factory()->NewJSObjectFromMap(map); |
| DisallowGarbageCollection no_gc; |
| DirectHandle<JSSegmentIterator> segment_iterator = |
| Cast<JSSegmentIterator>(result); |
| |
| segment_iterator->set_flags(0); |
| segment_iterator->set_granularity(granularity); |
| segment_iterator->set_icu_iterator_with_text(*managed); |
| segment_iterator->set_raw_string(*input_string); |
| |
| return segment_iterator; |
| } |
| |
| // https://tc39.es/ecma402/#sec-%segmentiteratorprototype%.next |
| MaybeDirectHandle<JSReceiver> JSSegmentIterator::Next( |
| Isolate* isolate, DirectHandle<JSSegmentIterator> segment_iterator) { |
| // Sketches of ideas for future performance improvements, roughly in order |
| // of difficulty: |
| // - Add a fast path for grapheme segmentation of one-byte strings that |
| // entirely skips calling into ICU. |
| // - When we enter this function, perform a batch of calls into ICU and |
| // stash away the results, so the next couple of invocations can access |
| // them from a (Torque?) builtin without calling into C++. |
| // - Implement compiler support for escape-analyzing the JSSegmentDataObject |
| // and avoid allocating it when possible. |
| |
| // TODO(v8:14681): We StackCheck here to break execution in the event of an |
| // interrupt. Ordinarily in JS loops, this stack check should already be |
| // occurring, however some loops implemented within CodeStubAssembler and |
| // Torque builtins do not currently implement these checks. A preferable |
| // solution which would benefit other iterators implemented in C++ include: |
| // 1) Performing the stack check in CEntry, which would provide a solution |
| // for all methods implemented in C++. |
| // |
| // 2) Rewriting the loop to include an outer loop, which performs periodic |
| // stack checks every N loop bodies (where N is some arbitrary heuristic |
| // selected to allow short loop counts to run with few interruptions). |
| STACK_CHECK(isolate, MaybeDirectHandle<JSReceiver>()); |
| |
| Factory* factory = isolate->factory(); |
| CppGCManaged<IcuBreakIteratorWithText>::Ptr iterator_with_text = |
| segment_iterator->icu_iterator_with_text()->ptr(); |
| icu::BreakIterator* const icu_break_iterator = iterator_with_text->iterator(); |
| |
| // 5. Let startIndex be iterator.[[IteratedStringNextSegmentCodeUnitIndex]]. |
| int32_t start_index = icu_break_iterator->current(); |
| // 6. Let endIndex be ! FindBoundary(segmenter, string, startIndex, after). |
| int32_t end_index = icu_break_iterator->next(); |
| |
| // 7. If endIndex is not finite, then |
| if (end_index == icu::BreakIterator::DONE) { |
| // a. Return ! CreateIterResultObject(undefined, true). |
| return factory->NewJSIteratorResult(isolate->factory()->undefined_value(), |
| true); |
| } |
| |
| // 8. Set iterator.[[IteratedStringNextSegmentCodeUnitIndex]] to endIndex. |
| |
| // 9. Let segmentData be ! CreateSegmentDataObject(segmenter, string, |
| // startIndex, endIndex). |
| |
| DirectHandle<JSSegmentDataObject> segment_data; |
| if (segment_iterator->granularity() == JSSegmenter::Granularity::GRAPHEME && |
| start_index == end_index - 1) { |
| // Fast path: use cached segment string and skip avoidable handle creations. |
| DirectHandle<String> segment; |
| uint16_t code = segment_iterator->raw_string()->Get(start_index); |
| if (code > unibrow::Latin1::kMaxChar) { |
| segment = factory->LookupSingleCharacterStringFromCode(code); |
| } |
| DirectHandle<Number> index; |
| if (!Smi::IsValid(start_index)) index = factory->NewHeapNumber(start_index); |
| DirectHandle<Map> map( |
| isolate->native_context()->intl_segment_data_object_map(), isolate); |
| segment_data = Cast<JSSegmentDataObject>(factory->NewJSObjectFromMap(map)); |
| Tagged<JSSegmentDataObject> raw = *segment_data; |
| DisallowHeapAllocation no_gc; |
| // We can skip write barriers because {segment_data} is the last object |
| // that was allocated. |
| raw->set_segment(code <= unibrow::Latin1::kMaxChar |
| ? ReadOnlyRoots(isolate).single_character_string(code) |
| : *segment, |
| SKIP_WRITE_BARRIER); |
| raw->set_index( |
| Smi::IsValid(start_index) ? Smi::FromInt(start_index) : *index, |
| SKIP_WRITE_BARRIER); |
| raw->set_input(segment_iterator->raw_string(), SKIP_WRITE_BARRIER); |
| } else { |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, segment_data, |
| JSSegments::CreateSegmentDataObject( |
| isolate, segment_iterator->granularity(), icu_break_iterator, |
| direct_handle(segment_iterator->raw_string(), isolate), |
| *iterator_with_text->text(), start_index, end_index)); |
| } |
| |
| // 10. Return ! CreateIterResultObject(segmentData, false). |
| return factory->NewJSIteratorResult(segment_data, false); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |