// Copyright 2014 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "src/ast/ast-value-factory.h" #include "src/base/hashmap-entry.h" #include "src/base/logging.h" #include "src/base/platform/wrappers.h" #include "src/common/globals.h" #include "src/heap/factory-inl.h" #include "src/heap/local-factory-inl.h" #include "src/objects/objects-inl.h" #include "src/objects/objects.h" #include "src/objects/string.h" #include "src/strings/char-predicates-inl.h" #include "src/strings/string-hasher.h" #include "src/utils/utils-inl.h" namespace v8 { namespace internal { namespace { // For using StringToIndex. class OneByteStringStream { public: explicit OneByteStringStream(Vector lb) : literal_bytes_(lb), pos_(0) {} bool HasMore() { return pos_ < literal_bytes_.length(); } uint16_t GetNext() { return literal_bytes_[pos_++]; } private: Vector literal_bytes_; int pos_; }; } // namespace template void AstRawString::Internalize(IsolateT* isolate) { DCHECK(!has_string_); if (literal_bytes_.length() == 0) { set_string(isolate->factory()->empty_string()); } else if (is_one_byte()) { OneByteStringKey key(raw_hash_field_, literal_bytes_); set_string(isolate->factory()->InternalizeStringWithKey(&key)); } else { TwoByteStringKey key(raw_hash_field_, Vector::cast(literal_bytes_)); set_string(isolate->factory()->InternalizeStringWithKey(&key)); } } template EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) void AstRawString::Internalize(Isolate* isolate); template EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) void AstRawString::Internalize(LocalIsolate* isolate); bool AstRawString::AsArrayIndex(uint32_t* index) const { // The StringHasher will set up the hash. Bail out early if we know it // can't be convertible to an array index. if ((raw_hash_field_ & Name::kIsNotIntegerIndexMask) != 0) return false; if (length() <= Name::kMaxCachedArrayIndexLength) { *index = Name::ArrayIndexValueBits::decode(raw_hash_field_); return true; } // Might be an index, but too big to cache it. Do the slow conversion. This // might fail if the string is outside uint32_t (but within "safe integer") // range. OneByteStringStream stream(literal_bytes_); return StringToIndex(&stream, index); } bool AstRawString::IsIntegerIndex() const { return (raw_hash_field_ & Name::kIsNotIntegerIndexMask) == 0; } bool AstRawString::IsOneByteEqualTo(const char* data) const { if (!is_one_byte()) return false; size_t length = static_cast(literal_bytes_.length()); if (length != strlen(data)) return false; return 0 == strncmp(reinterpret_cast(literal_bytes_.begin()), data, length); } uint16_t AstRawString::FirstCharacter() const { if (is_one_byte()) return literal_bytes_[0]; const uint16_t* c = reinterpret_cast(literal_bytes_.begin()); return *c; } bool AstRawString::Equal(const AstRawString* lhs, const AstRawString* rhs) { DCHECK_EQ(lhs->Hash(), rhs->Hash()); if (lhs->length() != rhs->length()) return false; if (lhs->length() == 0) return true; const unsigned char* l = lhs->raw_data(); const unsigned char* r = rhs->raw_data(); size_t length = rhs->length(); if (lhs->is_one_byte()) { if (rhs->is_one_byte()) { return CompareCharsEqualUnsigned(reinterpret_cast(l), reinterpret_cast(r), length); } else { return CompareCharsEqualUnsigned(reinterpret_cast(l), reinterpret_cast(r), length); } } else { if (rhs->is_one_byte()) { return CompareCharsEqualUnsigned(reinterpret_cast(l), reinterpret_cast(r), length); } else { return CompareCharsEqualUnsigned(reinterpret_cast(l), reinterpret_cast(r), length); } } } int AstRawString::Compare(const AstRawString* lhs, const AstRawString* rhs) { // Fast path for equal pointers. if (lhs == rhs) return 0; const unsigned char* lhs_data = lhs->raw_data(); const unsigned char* rhs_data = rhs->raw_data(); size_t length = std::min(lhs->length(), rhs->length()); // Code point order by contents. if (lhs->is_one_byte()) { if (rhs->is_one_byte()) { if (int result = CompareCharsUnsigned( reinterpret_cast(lhs_data), reinterpret_cast(rhs_data), length)) return result; } else { if (int result = CompareCharsUnsigned( reinterpret_cast(lhs_data), reinterpret_cast(rhs_data), length)) return result; } } else { if (rhs->is_one_byte()) { if (int result = CompareCharsUnsigned( reinterpret_cast(lhs_data), reinterpret_cast(rhs_data), length)) return result; } else { if (int result = CompareCharsUnsigned( reinterpret_cast(lhs_data), reinterpret_cast(rhs_data), length)) return result; } } return lhs->byte_length() - rhs->byte_length(); } template Handle AstConsString::Allocate(IsolateT* isolate) const { DCHECK(string_.is_null()); if (IsEmpty()) { return isolate->factory()->empty_string(); } // AstRawStrings are internalized before AstConsStrings are allocated, so // AstRawString::string() will just work. Handle tmp = segment_.string->string(); for (AstConsString::Segment* current = segment_.next; current != nullptr; current = current->next) { tmp = isolate->factory() ->NewConsString(current->string->string(), tmp, AllocationType::kOld) .ToHandleChecked(); } return tmp; } template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle AstConsString::Allocate(Isolate* isolate) const; template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle AstConsString::Allocate( LocalIsolate* isolate) const; template Handle AstConsString::AllocateFlat(IsolateT* isolate) const { if (IsEmpty()) { return isolate->factory()->empty_string(); } if (!segment_.next) { return segment_.string->string(); } int result_length = 0; bool is_one_byte = true; for (const AstConsString::Segment* current = &segment_; current != nullptr; current = current->next) { result_length += current->string->length(); is_one_byte = is_one_byte && current->string->is_one_byte(); } if (is_one_byte) { Handle result = isolate->factory() ->NewRawOneByteString(result_length, AllocationType::kOld) .ToHandleChecked(); DisallowGarbageCollection no_gc; uint8_t* dest = result->GetChars(no_gc, SharedStringAccessGuardIfNeeded::NotNeeded()) + result_length; for (const AstConsString::Segment* current = &segment_; current != nullptr; current = current->next) { int length = current->string->length(); dest -= length; CopyChars(dest, current->string->raw_data(), length); } DCHECK_EQ(dest, result->GetChars( no_gc, SharedStringAccessGuardIfNeeded::NotNeeded())); return result; } Handle result = isolate->factory() ->NewRawTwoByteString(result_length, AllocationType::kOld) .ToHandleChecked(); DisallowGarbageCollection no_gc; uint16_t* dest = result->GetChars(no_gc, SharedStringAccessGuardIfNeeded::NotNeeded()) + result_length; for (const AstConsString::Segment* current = &segment_; current != nullptr; current = current->next) { int length = current->string->length(); dest -= length; if (current->string->is_one_byte()) { CopyChars(dest, current->string->raw_data(), length); } else { CopyChars(dest, reinterpret_cast(current->string->raw_data()), length); } } DCHECK_EQ(dest, result->GetChars( no_gc, SharedStringAccessGuardIfNeeded::NotNeeded())); return result; } template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle AstConsString::AllocateFlat(Isolate* isolate) const; template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) Handle AstConsString::AllocateFlat( LocalIsolate* isolate) const; std::forward_list AstConsString::ToRawStrings() const { std::forward_list result; if (IsEmpty()) { return result; } result.emplace_front(segment_.string); for (AstConsString::Segment* current = segment_.next; current != nullptr; current = current->next) { result.emplace_front(current->string); } return result; } AstStringConstants::AstStringConstants(Isolate* isolate, uint64_t hash_seed) : zone_(isolate->allocator(), ZONE_NAME), string_table_(), hash_seed_(hash_seed) { DCHECK_EQ(ThreadId::Current(), isolate->thread_id()); #define F(name, str) \ { \ const char* data = str; \ Vector literal(reinterpret_cast(data), \ static_cast(strlen(data))); \ uint32_t raw_hash_field = StringHasher::HashSequentialString( \ literal.begin(), literal.length(), hash_seed_); \ name##_string_ = zone_.New(true, literal, raw_hash_field); \ /* The Handle returned by the factory is located on the roots */ \ /* array, not on the temporary HandleScope, so this is safe. */ \ name##_string_->set_string(isolate->factory()->name##_string()); \ string_table_.InsertNew(name##_string_, name##_string_->Hash()); \ } AST_STRING_CONSTANTS(F) #undef F } const AstRawString* AstValueFactory::GetOneByteStringInternal( Vector literal) { if (literal.length() == 1 && literal[0] < kMaxOneCharStringValue) { int key = literal[0]; if (V8_UNLIKELY(one_character_strings_[key] == nullptr)) { uint32_t raw_hash_field = StringHasher::HashSequentialString( literal.begin(), literal.length(), hash_seed_); one_character_strings_[key] = GetString(raw_hash_field, true, literal); } return one_character_strings_[key]; } uint32_t raw_hash_field = StringHasher::HashSequentialString( literal.begin(), literal.length(), hash_seed_); return GetString(raw_hash_field, true, literal); } const AstRawString* AstValueFactory::GetTwoByteStringInternal( Vector literal) { uint32_t raw_hash_field = StringHasher::HashSequentialString( literal.begin(), literal.length(), hash_seed_); return GetString(raw_hash_field, false, Vector::cast(literal)); } const AstRawString* AstValueFactory::GetString(Handle literal) { const AstRawString* result = nullptr; DisallowGarbageCollection no_gc; String::FlatContent content = literal->GetFlatContent(no_gc); if (content.IsOneByte()) { result = GetOneByteStringInternal(content.ToOneByteVector()); } else { DCHECK(content.IsTwoByte()); result = GetTwoByteStringInternal(content.ToUC16Vector()); } return result; } const AstRawString* AstValueFactory::CloneFromOtherFactory( const AstRawString* raw_string) { const AstRawString* result = GetString( raw_string->raw_hash_field(), raw_string->is_one_byte(), Vector(raw_string->raw_data(), raw_string->byte_length())); return result; } AstConsString* AstValueFactory::NewConsString() { return zone()->New(); } AstConsString* AstValueFactory::NewConsString(const AstRawString* str) { return NewConsString()->AddString(zone(), str); } AstConsString* AstValueFactory::NewConsString(const AstRawString* str1, const AstRawString* str2) { return NewConsString()->AddString(zone(), str1)->AddString(zone(), str2); } template void AstValueFactory::Internalize(IsolateT* isolate) { if (!zone_) return; // Strings need to be internalized before values, because values refer to // strings. for (AstRawString* current = strings_; current != nullptr;) { AstRawString* next = current->next(); current->Internalize(isolate); current = next; } ResetStrings(); zone_ = nullptr; } template EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(Isolate* isolate); template EXPORT_TEMPLATE_DEFINE( V8_EXPORT_PRIVATE) void AstValueFactory::Internalize(LocalIsolate* isolate); const AstRawString* AstValueFactory::GetString( uint32_t raw_hash_field, bool is_one_byte, Vector literal_bytes) { // literal_bytes here points to whatever the user passed, and this is OK // because we use vector_compare (which checks the contents) to compare // against the AstRawStrings which are in the string_table_. We should not // return this AstRawString. AstRawString key(is_one_byte, literal_bytes, raw_hash_field); AstRawStringMap::Entry* entry = string_table_.LookupOrInsert( &key, key.Hash(), [&]() { // Copy literal contents for later comparison. int length = literal_bytes.length(); byte* new_literal_bytes = zone()->NewArray(length); base::Memcpy(new_literal_bytes, literal_bytes.begin(), length); AstRawString* new_string = zone()->New( is_one_byte, Vector(new_literal_bytes, length), raw_hash_field); CHECK_NOT_NULL(new_string); AddString(new_string); return new_string; }, [&]() { return base::NoHashMapValue(); }); return entry->key; } } // namespace internal } // namespace v8