5 #ifndef V8_OBJECTS_STRING_INL_H_ 6 #define V8_OBJECTS_STRING_INL_H_ 8 #include "src/objects/string.h" 10 #include "src/conversions-inl.h" 11 #include "src/handles-inl.h" 12 #include "src/heap/factory.h" 13 #include "src/objects/name-inl.h" 14 #include "src/objects/smi-inl.h" 15 #include "src/string-hasher-inl.h" 18 #include "src/objects/object-macros.h" 23 INT32_ACCESSORS(String, length, kLengthOffset)
25 int String::synchronized_length()
const {
26 return base::AsAtomic32::Acquire_Load(
27 reinterpret_cast<const int32_t*>(FIELD_ADDR(
this, kLengthOffset)));
30 void String::synchronized_set_length(
int value) {
31 base::AsAtomic32::Release_Store(
32 reinterpret_cast<int32_t*>(FIELD_ADDR(
this, kLengthOffset)), value);
35 OBJECT_CONSTRUCTORS_IMPL(String, Name)
36 OBJECT_CONSTRUCTORS_IMPL(SeqString, String)
37 OBJECT_CONSTRUCTORS_IMPL(SeqOneByteString, SeqString)
38 OBJECT_CONSTRUCTORS_IMPL(SeqTwoByteString, SeqString)
39 OBJECT_CONSTRUCTORS_IMPL(InternalizedString, String)
40 OBJECT_CONSTRUCTORS_IMPL(ConsString, String)
41 OBJECT_CONSTRUCTORS_IMPL(ThinString, String)
42 OBJECT_CONSTRUCTORS_IMPL(SlicedString, String)
43 OBJECT_CONSTRUCTORS_IMPL(ExternalString, String)
44 OBJECT_CONSTRUCTORS_IMPL(ExternalOneByteString, ExternalString)
45 OBJECT_CONSTRUCTORS_IMPL(ExternalTwoByteString, ExternalString)
47 CAST_ACCESSOR2(ConsString)
48 CAST_ACCESSOR2(ExternalOneByteString)
49 CAST_ACCESSOR2(ExternalString)
50 CAST_ACCESSOR2(ExternalTwoByteString)
51 CAST_ACCESSOR2(InternalizedString)
52 CAST_ACCESSOR2(SeqOneByteString)
53 CAST_ACCESSOR2(SeqString)
54 CAST_ACCESSOR2(SeqTwoByteString)
55 CAST_ACCESSOR2(SlicedString)
56 CAST_ACCESSOR2(String)
57 CAST_ACCESSOR2(ThinString)
59 StringShape::StringShape(
const String str)
60 : type_(str->map()->instance_type()) {
62 DCHECK_EQ(type_ & kIsNotStringMask, kStringTag);
65 StringShape::StringShape(Map map) : type_(map->instance_type()) {
67 DCHECK_EQ(type_ & kIsNotStringMask, kStringTag);
70 StringShape::StringShape(InstanceType t) : type_(static_cast<
uint32_t>(t)) {
72 DCHECK_EQ(type_ & kIsNotStringMask, kStringTag);
75 bool StringShape::IsInternalized() {
77 STATIC_ASSERT(kNotInternalizedTag != 0);
78 return (type_ & (kIsNotStringMask | kIsNotInternalizedMask)) ==
79 (kStringTag | kInternalizedTag);
82 bool StringShape::HasOnlyOneByteChars() {
83 return (type_ & kStringEncodingMask) == kOneByteStringTag ||
84 (type_ & kOneByteDataHintMask) == kOneByteDataHintTag;
87 bool StringShape::IsCons() {
88 return (type_ & kStringRepresentationMask) == kConsStringTag;
91 bool StringShape::IsThin() {
92 return (type_ & kStringRepresentationMask) == kThinStringTag;
95 bool StringShape::IsSliced() {
96 return (type_ & kStringRepresentationMask) == kSlicedStringTag;
99 bool StringShape::IsIndirect() {
100 return (type_ & kIsIndirectStringMask) == kIsIndirectStringTag;
103 bool StringShape::IsExternal() {
104 return (type_ & kStringRepresentationMask) == kExternalStringTag;
107 bool StringShape::IsSequential() {
108 return (type_ & kStringRepresentationMask) == kSeqStringTag;
111 StringRepresentationTag StringShape::representation_tag() {
112 uint32_t tag = (type_ & kStringRepresentationMask);
113 return static_cast<StringRepresentationTag
>(tag);
116 uint32_t StringShape::encoding_tag() {
return type_ & kStringEncodingMask; }
118 uint32_t StringShape::full_representation_tag() {
119 return (type_ & (kStringRepresentationMask | kStringEncodingMask));
122 STATIC_ASSERT((kStringRepresentationMask | kStringEncodingMask) ==
123 Internals::kFullStringRepresentationMask);
125 STATIC_ASSERT(static_cast<uint32_t>(kStringEncodingMask) ==
126 Internals::kStringEncodingMask);
128 bool StringShape::IsSequentialOneByte() {
129 return full_representation_tag() == (kSeqStringTag | kOneByteStringTag);
132 bool StringShape::IsSequentialTwoByte() {
133 return full_representation_tag() == (kSeqStringTag | kTwoByteStringTag);
136 bool StringShape::IsExternalOneByte() {
137 return full_representation_tag() == (kExternalStringTag | kOneByteStringTag);
140 STATIC_ASSERT((kExternalStringTag | kOneByteStringTag) ==
141 Internals::kExternalOneByteRepresentationTag);
143 STATIC_ASSERT(v8::String::ONE_BYTE_ENCODING == kOneByteStringTag);
145 bool StringShape::IsExternalTwoByte() {
146 return full_representation_tag() == (kExternalStringTag | kTwoByteStringTag);
149 STATIC_ASSERT((kExternalStringTag | kTwoByteStringTag) ==
150 Internals::kExternalTwoByteRepresentationTag);
152 STATIC_ASSERT(v8::String::TWO_BYTE_ENCODING == kTwoByteStringTag);
154 bool String::IsOneByteRepresentation()
const {
155 uint32_t type = map()->instance_type();
156 return (type & kStringEncodingMask) == kOneByteStringTag;
159 bool String::IsTwoByteRepresentation()
const {
160 uint32_t type = map()->instance_type();
161 return (type & kStringEncodingMask) == kTwoByteStringTag;
164 bool String::IsOneByteRepresentationUnderneath() {
165 uint32_t type = map()->instance_type();
166 STATIC_ASSERT(kIsIndirectStringTag != 0);
167 STATIC_ASSERT((kIsIndirectStringMask & kStringEncodingMask) == 0);
169 switch (type & (kIsIndirectStringMask | kStringEncodingMask)) {
170 case kOneByteStringTag:
172 case kTwoByteStringTag:
175 return GetUnderlying()->IsOneByteRepresentationUnderneath();
179 bool String::IsTwoByteRepresentationUnderneath() {
180 uint32_t type = map()->instance_type();
181 STATIC_ASSERT(kIsIndirectStringTag != 0);
182 STATIC_ASSERT((kIsIndirectStringMask & kStringEncodingMask) == 0);
184 switch (type & (kIsIndirectStringMask | kStringEncodingMask)) {
185 case kOneByteStringTag:
187 case kTwoByteStringTag:
190 return GetUnderlying()->IsTwoByteRepresentationUnderneath();
194 bool String::HasOnlyOneByteChars() {
195 uint32_t type = map()->instance_type();
196 return (type & kOneByteDataHintMask) == kOneByteDataHintTag ||
197 IsOneByteRepresentation();
200 uc32 FlatStringReader::Get(
int index) {
202 return Get<uint8_t>(index);
204 return Get<uc16>(index);
208 template <
typename Char>
209 Char FlatStringReader::Get(
int index) {
210 DCHECK_EQ(is_one_byte_,
sizeof(Char) == 1);
211 DCHECK(0 <= index && index <= length_);
212 if (
sizeof(Char) == 1) {
213 return static_cast<Char
>(
static_cast<const uint8_t*
>(start_)[index]);
215 return static_cast<Char
>(
static_cast<const uc16*
>(start_)[index]);
219 template <
typename Char>
224 string.start(),
string.length(), seed)),
235 bool IsMatch(
Object*
string)
override {
236 return String::cast(
string)->IsOneByteEqualTo(string_);
248 #if defined(V8_CC_MSVC) 249 #pragma warning(push) 250 #pragma warning(disable : 4789) 253 int from,
int length)
254 :
StringTableKey(0), string_(
string), from_(from), length_(length) {
257 uint32_t hash = StringHasher::HashSequentialString(
258 string->GetChars() + from, length, isolate->heap()->HashSeed());
259 set_hash_field(hash);
261 DCHECK_LE(0, length_);
262 DCHECK_LE(from_ + length_, string_->length());
263 DCHECK(string_->IsSeqOneByteString());
265 #if defined(V8_CC_MSVC) 269 bool IsMatch(
Object*
string)
override;
283 bool IsMatch(
Object*
string)
override {
284 return String::cast(
string)->IsTwoByteEqualTo(string_);
294 :
StringTableKey(StringHasher::ComputeUtf8Hash(
string, seed, &chars_)),
297 bool IsMatch(
Object*
string)
override {
298 return String::cast(
string)->IsUtf8EqualTo(string_);
302 return isolate->factory()->NewInternalizedStringFromUtf8(string_, chars_,
311 bool String::Equals(
String other) {
312 if (other == *
this)
return true;
313 if (this->IsInternalizedString() && other->IsInternalizedString()) {
316 return SlowEquals(other);
319 bool String::Equals(Isolate* isolate, Handle<String> one, Handle<String> two) {
320 if (one.is_identical_to(two))
return true;
321 if (one->IsInternalizedString() && two->IsInternalizedString()) {
324 return SlowEquals(isolate, one, two);
327 Handle<String> String::Flatten(Isolate* isolate, Handle<String>
string,
328 PretenureFlag pretenure) {
329 if (string->IsConsString()) {
330 Handle<ConsString> cons = Handle<ConsString>::cast(
string);
331 if (cons->IsFlat()) {
332 string = handle(cons->first(), isolate);
334 return SlowFlatten(isolate, cons, pretenure);
337 if (string->IsThinString()) {
338 string = handle(Handle<ThinString>::cast(
string)->actual(), isolate);
339 DCHECK(!string->IsConsString());
344 uint16_t String::Get(
int index) {
345 DCHECK(index >= 0 && index < length());
346 switch (StringShape(*this).full_representation_tag()) {
347 case kSeqStringTag | kOneByteStringTag:
348 return SeqOneByteString::cast(*this)->SeqOneByteStringGet(index);
349 case kSeqStringTag | kTwoByteStringTag:
350 return SeqTwoByteString::cast(*this)->SeqTwoByteStringGet(index);
351 case kConsStringTag | kOneByteStringTag:
352 case kConsStringTag | kTwoByteStringTag:
353 return ConsString::cast(*this)->ConsStringGet(index);
354 case kExternalStringTag | kOneByteStringTag:
355 return ExternalOneByteString::cast(*this)->ExternalOneByteStringGet(
357 case kExternalStringTag | kTwoByteStringTag:
358 return ExternalTwoByteString::cast(*this)->ExternalTwoByteStringGet(
360 case kSlicedStringTag | kOneByteStringTag:
361 case kSlicedStringTag | kTwoByteStringTag:
362 return SlicedString::cast(*this)->SlicedStringGet(index);
363 case kThinStringTag | kOneByteStringTag:
364 case kThinStringTag | kTwoByteStringTag:
365 return ThinString::cast(*this)->ThinStringGet(index);
373 void String::Set(
int index, uint16_t value) {
374 DCHECK(index >= 0 && index < length());
375 DCHECK(StringShape(*this).IsSequential());
377 return this->IsOneByteRepresentation()
378 ? SeqOneByteString::cast(*this)->SeqOneByteStringSet(index, value)
379 : SeqTwoByteString::cast(*this)->SeqTwoByteStringSet(index, value);
382 bool String::IsFlat() {
383 if (!StringShape(*this).IsCons())
return true;
384 return ConsString::cast(*this)->second()->length() == 0;
387 String String::GetUnderlying() {
390 DCHECK(this->IsFlat());
391 DCHECK(StringShape(*this).IsIndirect());
392 STATIC_ASSERT(static_cast<int>(ConsString::kFirstOffset) ==
393 static_cast<int>(SlicedString::kParentOffset));
394 STATIC_ASSERT(static_cast<int>(ConsString::kFirstOffset) ==
395 static_cast<int>(ThinString::kActualOffset));
396 const int kUnderlyingOffset = SlicedString::kParentOffset;
397 return String::cast(READ_FIELD(
this, kUnderlyingOffset));
400 template <
class Visitor>
401 ConsString String::VisitFlat(Visitor* visitor, String
string,
403 DisallowHeapAllocation no_gc;
404 int slice_offset = offset;
405 const int length =
string->length();
406 DCHECK(offset <= length);
408 int32_t type =
string->map()->instance_type();
409 switch (type & (kStringRepresentationMask | kStringEncodingMask)) {
410 case kSeqStringTag | kOneByteStringTag:
411 visitor->VisitOneByteString(
412 SeqOneByteString::cast(
string)->GetChars() + slice_offset,
416 case kSeqStringTag | kTwoByteStringTag:
417 visitor->VisitTwoByteString(
418 SeqTwoByteString::cast(
string)->GetChars() + slice_offset,
422 case kExternalStringTag | kOneByteStringTag:
423 visitor->VisitOneByteString(
424 ExternalOneByteString::cast(
string)->GetChars() + slice_offset,
428 case kExternalStringTag | kTwoByteStringTag:
429 visitor->VisitTwoByteString(
430 ExternalTwoByteString::cast(
string)->GetChars() + slice_offset,
434 case kSlicedStringTag | kOneByteStringTag:
435 case kSlicedStringTag | kTwoByteStringTag: {
436 SlicedString slicedString = SlicedString::cast(
string);
437 slice_offset += slicedString->offset();
438 string = slicedString->parent();
442 case kConsStringTag | kOneByteStringTag:
443 case kConsStringTag | kTwoByteStringTag:
444 return ConsString::cast(
string);
446 case kThinStringTag | kOneByteStringTag:
447 case kThinStringTag | kTwoByteStringTag:
448 string = ThinString::cast(
string)->actual();
458 inline Vector<const uint8_t> String::GetCharVector() {
459 String::FlatContent flat = GetFlatContent();
460 DCHECK(flat.IsOneByte());
461 return flat.ToOneByteVector();
465 inline Vector<const uc16> String::GetCharVector() {
466 String::FlatContent flat = GetFlatContent();
467 DCHECK(flat.IsTwoByte());
468 return flat.ToUC16Vector();
471 uint32_t String::ToValidIndex(Object* number) {
472 uint32_t index = PositiveNumberToUint32(number);
474 if (index > length_value)
return length_value;
478 uint16_t SeqOneByteString::SeqOneByteStringGet(
int index) {
479 DCHECK(index >= 0 && index < length());
480 return READ_BYTE_FIELD(
this, kHeaderSize + index * kCharSize);
483 void SeqOneByteString::SeqOneByteStringSet(
int index, uint16_t value) {
484 DCHECK(index >= 0 && index < length() && value <= kMaxOneByteCharCode);
485 WRITE_BYTE_FIELD(
this, kHeaderSize + index * kCharSize,
486 static_cast<byte>(value));
489 Address SeqOneByteString::GetCharsAddress() {
490 return FIELD_ADDR(
this, kHeaderSize);
493 uint8_t* SeqOneByteString::GetChars() {
494 DCHECK(!AllowHeapAllocation::IsAllowed());
495 return reinterpret_cast<uint8_t*
>(GetCharsAddress());
498 Address SeqTwoByteString::GetCharsAddress() {
499 return FIELD_ADDR(
this, kHeaderSize);
502 uc16* SeqTwoByteString::GetChars() {
503 DCHECK(!AllowHeapAllocation::IsAllowed());
504 return reinterpret_cast<uc16*
>(FIELD_ADDR(
this, kHeaderSize));
507 uint16_t SeqTwoByteString::SeqTwoByteStringGet(
int index) {
508 DCHECK(index >= 0 && index < length());
509 return READ_UINT16_FIELD(
this, kHeaderSize + index * kShortSize);
512 void SeqTwoByteString::SeqTwoByteStringSet(
int index, uint16_t value) {
513 DCHECK(index >= 0 && index < length());
514 WRITE_UINT16_FIELD(
this, kHeaderSize + index * kShortSize, value);
517 int SeqTwoByteString::SeqTwoByteStringSize(InstanceType instance_type) {
518 return SizeFor(length());
521 int SeqOneByteString::SeqOneByteStringSize(InstanceType instance_type) {
522 return SizeFor(length());
525 String SlicedString::parent() {
526 return String::cast(READ_FIELD(
this, kParentOffset));
529 void SlicedString::set_parent(Isolate* isolate, String parent,
530 WriteBarrierMode mode) {
531 DCHECK(parent->IsSeqString() || parent->IsExternalString());
532 WRITE_FIELD(
this, kParentOffset, parent);
533 CONDITIONAL_WRITE_BARRIER(
this, kParentOffset, parent, mode);
536 SMI_ACCESSORS(SlicedString, offset, kOffsetOffset)
538 String ConsString::first() {
539 return String::cast(READ_FIELD(
this, kFirstOffset));
542 Object* ConsString::unchecked_first() {
return READ_FIELD(
this, kFirstOffset); }
544 void ConsString::set_first(Isolate* isolate, String value,
545 WriteBarrierMode mode) {
546 WRITE_FIELD(
this, kFirstOffset, value);
547 CONDITIONAL_WRITE_BARRIER(
this, kFirstOffset, value, mode);
550 String ConsString::second() {
551 return String::cast(READ_FIELD(
this, kSecondOffset));
554 Object* ConsString::unchecked_second() {
555 return RELAXED_READ_FIELD(
this, kSecondOffset);
558 void ConsString::set_second(Isolate* isolate, String value,
559 WriteBarrierMode mode) {
560 WRITE_FIELD(
this, kSecondOffset, value);
561 CONDITIONAL_WRITE_BARRIER(
this, kSecondOffset, value, mode);
564 ACCESSORS2(ThinString, actual, String, kActualOffset);
566 HeapObject* ThinString::unchecked_actual()
const {
567 return reinterpret_cast<HeapObject*
>(READ_FIELD(
this, kActualOffset));
570 bool ExternalString::is_uncached()
const {
571 InstanceType type = map()->instance_type();
572 return (type & kUncachedExternalStringMask) == kUncachedExternalStringTag;
575 Address ExternalString::resource_as_address() {
576 return *
reinterpret_cast<Address*
>(FIELD_ADDR(
this, kResourceOffset));
579 void ExternalString::set_address_as_resource(Address address) {
580 *
reinterpret_cast<Address*
>(FIELD_ADDR(
this, kResourceOffset)) = address;
581 if (IsExternalOneByteString()) {
582 ExternalOneByteString::cast(*this)->update_data_cache();
584 ExternalTwoByteString::cast(*this)->update_data_cache();
588 uint32_t ExternalString::resource_as_uint32() {
590 *
reinterpret_cast<uintptr_t*
>(FIELD_ADDR(
this, kResourceOffset)));
593 void ExternalString::set_uint32_as_resource(
uint32_t value) {
594 *
reinterpret_cast<uintptr_t*
>(FIELD_ADDR(
this, kResourceOffset)) = value;
595 if (is_uncached())
return;
596 const char** data_field =
597 reinterpret_cast<const char**
>(FIELD_ADDR(
this, kResourceDataOffset));
598 *data_field =
nullptr;
601 const ExternalOneByteString::Resource* ExternalOneByteString::resource() {
602 return *
reinterpret_cast<Resource**
>(FIELD_ADDR(
this, kResourceOffset));
605 void ExternalOneByteString::update_data_cache() {
606 if (is_uncached())
return;
607 const char** data_field =
608 reinterpret_cast<const char**
>(FIELD_ADDR(
this, kResourceDataOffset));
609 *data_field = resource()->
data();
612 void ExternalOneByteString::SetResource(
613 Isolate* isolate,
const ExternalOneByteString::Resource* resource) {
614 set_resource(resource);
615 size_t new_payload = resource ==
nullptr ? 0 : resource->length();
617 isolate->heap()->UpdateExternalString(*
this, 0, new_payload);
620 void ExternalOneByteString::set_resource(
621 const ExternalOneByteString::Resource* resource) {
622 *
reinterpret_cast<const Resource**
>(FIELD_ADDR(
this, kResourceOffset)) =
624 if (resource !=
nullptr) update_data_cache();
627 const uint8_t* ExternalOneByteString::GetChars() {
628 return reinterpret_cast<const uint8_t*
>(resource()->data());
631 uint16_t ExternalOneByteString::ExternalOneByteStringGet(
int index) {
632 DCHECK(index >= 0 && index < length());
633 return GetChars()[index];
636 const ExternalTwoByteString::Resource* ExternalTwoByteString::resource() {
637 return *
reinterpret_cast<Resource**
>(FIELD_ADDR(
this, kResourceOffset));
640 void ExternalTwoByteString::update_data_cache() {
641 if (is_uncached())
return;
642 const uint16_t** data_field =
643 reinterpret_cast<const uint16_t**
>(FIELD_ADDR(
this, kResourceDataOffset));
644 *data_field = resource()->data();
647 void ExternalTwoByteString::SetResource(
648 Isolate* isolate,
const ExternalTwoByteString::Resource* resource) {
649 set_resource(resource);
650 size_t new_payload = resource ==
nullptr ? 0 : resource->length() * 2;
652 isolate->heap()->UpdateExternalString(*
this, 0, new_payload);
655 void ExternalTwoByteString::set_resource(
656 const ExternalTwoByteString::Resource* resource) {
657 *
reinterpret_cast<const Resource**
>(FIELD_ADDR(
this, kResourceOffset)) =
659 if (resource !=
nullptr) update_data_cache();
662 const uint16_t* ExternalTwoByteString::GetChars() {
return resource()->data(); }
664 uint16_t ExternalTwoByteString::ExternalTwoByteStringGet(
int index) {
665 DCHECK(index >= 0 && index < length());
666 return GetChars()[index];
669 const uint16_t* ExternalTwoByteString::ExternalTwoByteStringGetData(
671 return GetChars() + start;
674 int ConsStringIterator::OffsetForDepth(
int depth) {
return depth & kDepthMask; }
676 void ConsStringIterator::PushLeft(ConsString
string) {
677 frames_[depth_++ & kDepthMask] = string;
680 void ConsStringIterator::PushRight(ConsString
string) {
682 frames_[(depth_ - 1) & kDepthMask] =
string;
685 void ConsStringIterator::AdjustMaximumDepth() {
686 if (depth_ > maximum_depth_) maximum_depth_ = depth_;
689 void ConsStringIterator::Pop() {
690 DCHECK_GT(depth_, 0);
691 DCHECK(depth_ <= maximum_depth_);
695 uint16_t StringCharacterStream::GetNext() {
696 DCHECK(buffer8_ !=
nullptr && end_ !=
nullptr);
698 if (buffer8_ == end_) HasMore();
699 DCHECK(buffer8_ < end_);
700 return is_one_byte_ ? *buffer8_++ : *buffer16_++;
703 StringCharacterStream::StringCharacterStream(String
string,
int offset)
704 : is_one_byte_(false) {
705 Reset(
string, offset);
708 void StringCharacterStream::Reset(String
string,
int offset) {
711 ConsString cons_string = String::VisitFlat(
this,
string, offset);
712 iter_.Reset(cons_string, offset);
713 if (!cons_string.is_null()) {
714 string = iter_.Next(&offset);
715 if (!
string.is_null()) String::VisitFlat(
this,
string, offset);
719 bool StringCharacterStream::HasMore() {
720 if (buffer8_ != end_)
return true;
722 String
string = iter_.Next(&offset);
723 DCHECK_EQ(offset, 0);
724 if (
string.is_null())
return false;
725 String::VisitFlat(
this,
string);
726 DCHECK(buffer8_ != end_);
730 void StringCharacterStream::VisitOneByteString(
const uint8_t* chars,
734 end_ = chars + length;
737 void StringCharacterStream::VisitTwoByteString(
const uint16_t* chars,
739 is_one_byte_ =
false;
741 end_ =
reinterpret_cast<const uint8_t*
>(chars + length);
744 bool String::AsArrayIndex(
uint32_t* index) {
746 if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
749 return SlowAsArrayIndex(index);
752 SubStringRange::SubStringRange(String
string,
int first,
int length)
755 length_(length == -1 ? string->length() : length) {}
759 typedef std::forward_iterator_tag iterator_category;
761 typedef uc16 value_type;
762 typedef uc16* pointer;
763 typedef uc16& reference;
767 uc16 operator*() {
return content_.Get(offset_); }
768 bool operator==(
const iterator& other)
const {
769 return content_.UsesSameString(other.content_) && offset_ == other.offset_;
771 bool operator!=(
const iterator& other)
const {
772 return !content_.UsesSameString(other.content_) || offset_ != other.offset_;
784 : content_(from->GetFlatContent()), offset_(offset) {}
800 #include "src/objects/object-macros-undef.h" 802 #endif // V8_OBJECTS_STRING_INL_H_
virtual const char * data() const =0