// Copyright 2014 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. #include "src/factory.h" #include "src/allocation-site-scopes.h" #include "src/base/bits.h" #include "src/conversions.h" #include "src/isolate-inl.h" #include "src/macro-assembler.h" namespace v8 { namespace internal { template Handle Factory::New(Handle map, AllocationSpace space) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->Allocate(*map, space), T); } template Handle Factory::New(Handle map, AllocationSpace space, Handle allocation_site) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->Allocate(*map, space, *allocation_site), T); } Handle Factory::NewFillerObject(int size, bool double_align, AllocationSpace space) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateFillerObject(size, double_align, space), HeapObject); } Handle Factory::NewBox(Handle value) { Handle result = Handle::cast(NewStruct(BOX_TYPE)); result->set_value(*value); return result; } Handle Factory::NewOddball(Handle map, const char* to_string, Handle to_number, byte kind) { Handle oddball = New(map, OLD_POINTER_SPACE); Oddball::Initialize(isolate(), oddball, to_string, to_number, kind); return oddball; } Handle Factory::NewFixedArray(int size, PretenureFlag pretenure) { DCHECK(0 <= size); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateFixedArray(size, pretenure), FixedArray); } Handle Factory::NewFixedArrayWithHoles(int size, PretenureFlag pretenure) { DCHECK(0 <= size); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateFixedArrayWithFiller(size, pretenure, *the_hole_value()), FixedArray); } Handle Factory::NewUninitializedFixedArray(int size) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateUninitializedFixedArray(size), FixedArray); } Handle Factory::NewFixedDoubleArray(int size, PretenureFlag pretenure) { DCHECK(0 <= size); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateUninitializedFixedDoubleArray(size, pretenure), FixedArrayBase); } Handle Factory::NewFixedDoubleArrayWithHoles( int size, PretenureFlag pretenure) { DCHECK(0 <= size); Handle array = NewFixedDoubleArray(size, pretenure); if (size > 0) { Handle double_array = Handle::cast(array); for (int i = 0; i < size; ++i) { double_array->set_the_hole(i); } } return array; } Handle Factory::NewConstantPoolArray( const ConstantPoolArray::NumberOfEntries& small) { DCHECK(small.total_count() > 0); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateConstantPoolArray(small), ConstantPoolArray); } Handle Factory::NewExtendedConstantPoolArray( const ConstantPoolArray::NumberOfEntries& small, const ConstantPoolArray::NumberOfEntries& extended) { DCHECK(small.total_count() > 0); DCHECK(extended.total_count() > 0); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateExtendedConstantPoolArray(small, extended), ConstantPoolArray); } Handle Factory::NewOrderedHashSet() { return OrderedHashSet::Allocate(isolate(), OrderedHashSet::kMinCapacity); } Handle Factory::NewOrderedHashMap() { return OrderedHashMap::Allocate(isolate(), OrderedHashMap::kMinCapacity); } Handle Factory::NewAccessorPair() { Handle accessors = Handle::cast(NewStruct(ACCESSOR_PAIR_TYPE)); accessors->set_getter(*the_hole_value(), SKIP_WRITE_BARRIER); accessors->set_setter(*the_hole_value(), SKIP_WRITE_BARRIER); return accessors; } Handle Factory::NewTypeFeedbackInfo() { Handle info = Handle::cast(NewStruct(TYPE_FEEDBACK_INFO_TYPE)); info->initialize_storage(); return info; } // Internalized strings are created in the old generation (data space). Handle Factory::InternalizeUtf8String(Vector string) { Utf8StringKey key(string, isolate()->heap()->HashSeed()); return InternalizeStringWithKey(&key); } // Internalized strings are created in the old generation (data space). Handle Factory::InternalizeString(Handle string) { if (string->IsInternalizedString()) return string; return StringTable::LookupString(isolate(), string); } Handle Factory::InternalizeOneByteString(Vector string) { OneByteStringKey key(string, isolate()->heap()->HashSeed()); return InternalizeStringWithKey(&key); } Handle Factory::InternalizeOneByteString( Handle string, int from, int length) { SeqOneByteSubStringKey key(string, from, length); return InternalizeStringWithKey(&key); } Handle Factory::InternalizeTwoByteString(Vector string) { TwoByteStringKey key(string, isolate()->heap()->HashSeed()); return InternalizeStringWithKey(&key); } template Handle Factory::InternalizeStringWithKey(StringTableKey* key) { return StringTable::LookupKey(isolate(), key); } MaybeHandle Factory::NewStringFromOneByte(Vector string, PretenureFlag pretenure) { int length = string.length(); if (length == 1) return LookupSingleCharacterStringFromCode(string[0]); Handle result; ASSIGN_RETURN_ON_EXCEPTION( isolate(), result, NewRawOneByteString(string.length(), pretenure), String); DisallowHeapAllocation no_gc; // Copy the characters into the new object. CopyChars(SeqOneByteString::cast(*result)->GetChars(), string.start(), length); return result; } MaybeHandle Factory::NewStringFromUtf8(Vector string, PretenureFlag pretenure) { // Check for ASCII first since this is the common case. const char* start = string.start(); int length = string.length(); int non_ascii_start = String::NonAsciiStart(start, length); if (non_ascii_start >= length) { // If the string is ASCII, we do not need to convert the characters // since UTF8 is backwards compatible with ASCII. return NewStringFromOneByte(Vector::cast(string), pretenure); } // Non-ASCII and we need to decode. Access decoder(isolate()->unicode_cache()->utf8_decoder()); decoder->Reset(string.start() + non_ascii_start, length - non_ascii_start); int utf16_length = decoder->Utf16Length(); DCHECK(utf16_length > 0); // Allocate string. Handle result; ASSIGN_RETURN_ON_EXCEPTION( isolate(), result, NewRawTwoByteString(non_ascii_start + utf16_length, pretenure), String); // Copy ASCII portion. uint16_t* data = result->GetChars(); const char* ascii_data = string.start(); for (int i = 0; i < non_ascii_start; i++) { *data++ = *ascii_data++; } // Now write the remainder. decoder->WriteUtf16(data, utf16_length); return result; } MaybeHandle Factory::NewStringFromTwoByte(Vector string, PretenureFlag pretenure) { int length = string.length(); const uc16* start = string.start(); if (String::IsOneByte(start, length)) { if (length == 1) return LookupSingleCharacterStringFromCode(string[0]); Handle result; ASSIGN_RETURN_ON_EXCEPTION( isolate(), result, NewRawOneByteString(length, pretenure), String); CopyChars(result->GetChars(), start, length); return result; } else { Handle result; ASSIGN_RETURN_ON_EXCEPTION( isolate(), result, NewRawTwoByteString(length, pretenure), String); CopyChars(result->GetChars(), start, length); return result; } } Handle Factory::NewInternalizedStringFromUtf8(Vector str, int chars, uint32_t hash_field) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateInternalizedStringFromUtf8( str, chars, hash_field), String); } MUST_USE_RESULT Handle Factory::NewOneByteInternalizedString( Vector str, uint32_t hash_field) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateOneByteInternalizedString(str, hash_field), String); } MUST_USE_RESULT Handle Factory::NewOneByteInternalizedSubString( Handle string, int offset, int length, uint32_t hash_field) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateOneByteInternalizedString( Vector(string->GetChars() + offset, length), hash_field), String); } MUST_USE_RESULT Handle Factory::NewTwoByteInternalizedString( Vector str, uint32_t hash_field) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateTwoByteInternalizedString(str, hash_field), String); } Handle Factory::NewInternalizedStringImpl( Handle string, int chars, uint32_t hash_field) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateInternalizedStringImpl( *string, chars, hash_field), String); } MaybeHandle Factory::InternalizedStringMapForString( Handle string) { // If the string is in new space it cannot be used as internalized. if (isolate()->heap()->InNewSpace(*string)) return MaybeHandle(); // Find the corresponding internalized string map for strings. switch (string->map()->instance_type()) { case STRING_TYPE: return internalized_string_map(); case ONE_BYTE_STRING_TYPE: return one_byte_internalized_string_map(); case EXTERNAL_STRING_TYPE: return external_internalized_string_map(); case EXTERNAL_ONE_BYTE_STRING_TYPE: return external_one_byte_internalized_string_map(); case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE: return external_internalized_string_with_one_byte_data_map(); case SHORT_EXTERNAL_STRING_TYPE: return short_external_internalized_string_map(); case SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE: return short_external_one_byte_internalized_string_map(); case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE: return short_external_internalized_string_with_one_byte_data_map(); default: return MaybeHandle(); // No match found. } } MaybeHandle Factory::NewRawOneByteString( int length, PretenureFlag pretenure) { if (length > String::kMaxLength || length < 0) { THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqOneByteString); } CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateRawOneByteString(length, pretenure), SeqOneByteString); } MaybeHandle Factory::NewRawTwoByteString( int length, PretenureFlag pretenure) { if (length > String::kMaxLength || length < 0) { THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), SeqTwoByteString); } CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateRawTwoByteString(length, pretenure), SeqTwoByteString); } Handle Factory::LookupSingleCharacterStringFromCode(uint32_t code) { if (code <= String::kMaxOneByteCharCodeU) { { DisallowHeapAllocation no_allocation; Object* value = single_character_string_cache()->get(code); if (value != *undefined_value()) { return handle(String::cast(value), isolate()); } } uint8_t buffer[1]; buffer[0] = static_cast(code); Handle result = InternalizeOneByteString(Vector(buffer, 1)); single_character_string_cache()->set(code, *result); return result; } DCHECK(code <= String::kMaxUtf16CodeUnitU); Handle result = NewRawTwoByteString(1).ToHandleChecked(); result->SeqTwoByteStringSet(0, static_cast(code)); return result; } // Returns true for a character in a range. Both limits are inclusive. static inline bool Between(uint32_t character, uint32_t from, uint32_t to) { // This makes uses of the the unsigned wraparound. return character - from <= to - from; } static inline Handle MakeOrFindTwoCharacterString(Isolate* isolate, uint16_t c1, uint16_t c2) { // Numeric strings have a different hash algorithm not known by // LookupTwoCharsStringIfExists, so we skip this step for such strings. if (!Between(c1, '0', '9') || !Between(c2, '0', '9')) { Handle result; if (StringTable::LookupTwoCharsStringIfExists(isolate, c1, c2). ToHandle(&result)) { return result; } } // Now we know the length is 2, we might as well make use of that fact // when building the new string. if (static_cast(c1 | c2) <= String::kMaxOneByteCharCodeU) { // We can do this. DCHECK(base::bits::IsPowerOfTwo32(String::kMaxOneByteCharCodeU + 1)); // because of this. Handle str = isolate->factory()->NewRawOneByteString(2).ToHandleChecked(); uint8_t* dest = str->GetChars(); dest[0] = static_cast(c1); dest[1] = static_cast(c2); return str; } else { Handle str = isolate->factory()->NewRawTwoByteString(2).ToHandleChecked(); uc16* dest = str->GetChars(); dest[0] = c1; dest[1] = c2; return str; } } template Handle ConcatStringContent(Handle result, Handle first, Handle second) { DisallowHeapAllocation pointer_stays_valid; SinkChar* sink = result->GetChars(); String::WriteToFlat(*first, sink, 0, first->length()); String::WriteToFlat(*second, sink + first->length(), 0, second->length()); return result; } MaybeHandle Factory::NewConsString(Handle left, Handle right) { int left_length = left->length(); if (left_length == 0) return right; int right_length = right->length(); if (right_length == 0) return left; int length = left_length + right_length; if (length == 2) { uint16_t c1 = left->Get(0); uint16_t c2 = right->Get(0); return MakeOrFindTwoCharacterString(isolate(), c1, c2); } // Make sure that an out of memory exception is thrown if the length // of the new cons string is too large. if (length > String::kMaxLength || length < 0) { THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), String); } bool left_is_one_byte = left->IsOneByteRepresentation(); bool right_is_one_byte = right->IsOneByteRepresentation(); bool is_one_byte = left_is_one_byte && right_is_one_byte; bool is_one_byte_data_in_two_byte_string = false; if (!is_one_byte) { // At least one of the strings uses two-byte representation so we // can't use the fast case code for short one-byte strings below, but // we can try to save memory if all chars actually fit in one-byte. is_one_byte_data_in_two_byte_string = left->HasOnlyOneByteChars() && right->HasOnlyOneByteChars(); if (is_one_byte_data_in_two_byte_string) { isolate()->counters()->string_add_runtime_ext_to_one_byte()->Increment(); } } // If the resulting string is small make a flat string. if (length < ConsString::kMinLength) { // Note that neither of the two inputs can be a slice because: STATIC_ASSERT(ConsString::kMinLength <= SlicedString::kMinLength); DCHECK(left->IsFlat()); DCHECK(right->IsFlat()); STATIC_ASSERT(ConsString::kMinLength <= String::kMaxLength); if (is_one_byte) { Handle result = NewRawOneByteString(length).ToHandleChecked(); DisallowHeapAllocation no_gc; uint8_t* dest = result->GetChars(); // Copy left part. const uint8_t* src = left->IsExternalString() ? Handle::cast(left)->GetChars() : Handle::cast(left)->GetChars(); for (int i = 0; i < left_length; i++) *dest++ = src[i]; // Copy right part. src = right->IsExternalString() ? Handle::cast(right)->GetChars() : Handle::cast(right)->GetChars(); for (int i = 0; i < right_length; i++) *dest++ = src[i]; return result; } return (is_one_byte_data_in_two_byte_string) ? ConcatStringContent( NewRawOneByteString(length).ToHandleChecked(), left, right) : ConcatStringContent( NewRawTwoByteString(length).ToHandleChecked(), left, right); } Handle map = (is_one_byte || is_one_byte_data_in_two_byte_string) ? cons_one_byte_string_map() : cons_string_map(); Handle result = New(map, NEW_SPACE); DisallowHeapAllocation no_gc; WriteBarrierMode mode = result->GetWriteBarrierMode(no_gc); result->set_hash_field(String::kEmptyHashField); result->set_length(length); result->set_first(*left, mode); result->set_second(*right, mode); return result; } Handle Factory::NewProperSubString(Handle str, int begin, int end) { #if VERIFY_HEAP if (FLAG_verify_heap) str->StringVerify(); #endif DCHECK(begin > 0 || end < str->length()); str = String::Flatten(str); int length = end - begin; if (length <= 0) return empty_string(); if (length == 1) { return LookupSingleCharacterStringFromCode(str->Get(begin)); } if (length == 2) { // Optimization for 2-byte strings often used as keys in a decompression // dictionary. Check whether we already have the string in the string // table to prevent creation of many unnecessary strings. uint16_t c1 = str->Get(begin); uint16_t c2 = str->Get(begin + 1); return MakeOrFindTwoCharacterString(isolate(), c1, c2); } if (!FLAG_string_slices || length < SlicedString::kMinLength) { if (str->IsOneByteRepresentation()) { Handle result = NewRawOneByteString(length).ToHandleChecked(); uint8_t* dest = result->GetChars(); DisallowHeapAllocation no_gc; String::WriteToFlat(*str, dest, begin, end); return result; } else { Handle result = NewRawTwoByteString(length).ToHandleChecked(); uc16* dest = result->GetChars(); DisallowHeapAllocation no_gc; String::WriteToFlat(*str, dest, begin, end); return result; } } int offset = begin; if (str->IsSlicedString()) { Handle slice = Handle::cast(str); str = Handle(slice->parent(), isolate()); offset += slice->offset(); } DCHECK(str->IsSeqString() || str->IsExternalString()); Handle map = str->IsOneByteRepresentation() ? sliced_one_byte_string_map() : sliced_string_map(); Handle slice = New(map, NEW_SPACE); slice->set_hash_field(String::kEmptyHashField); slice->set_length(length); slice->set_parent(*str); slice->set_offset(offset); return slice; } MaybeHandle Factory::NewExternalStringFromOneByte( const ExternalOneByteString::Resource* resource) { size_t length = resource->length(); if (length > static_cast(String::kMaxLength)) { THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), String); } Handle map = external_one_byte_string_map(); Handle external_string = New(map, NEW_SPACE); external_string->set_length(static_cast(length)); external_string->set_hash_field(String::kEmptyHashField); external_string->set_resource(resource); return external_string; } MaybeHandle Factory::NewExternalStringFromTwoByte( const ExternalTwoByteString::Resource* resource) { size_t length = resource->length(); if (length > static_cast(String::kMaxLength)) { THROW_NEW_ERROR(isolate(), NewInvalidStringLengthError(), String); } // For small strings we check whether the resource contains only // one byte characters. If yes, we use a different string map. static const size_t kOneByteCheckLengthLimit = 32; bool is_one_byte = length <= kOneByteCheckLengthLimit && String::IsOneByte(resource->data(), static_cast(length)); Handle map = is_one_byte ? external_string_with_one_byte_data_map() : external_string_map(); Handle external_string = New(map, NEW_SPACE); external_string->set_length(static_cast(length)); external_string->set_hash_field(String::kEmptyHashField); external_string->set_resource(resource); return external_string; } Handle Factory::NewSymbol() { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateSymbol(), Symbol); } Handle Factory::NewPrivateSymbol() { Handle symbol = NewSymbol(); symbol->set_is_private(true); return symbol; } Handle Factory::NewPrivateOwnSymbol() { Handle symbol = NewSymbol(); symbol->set_is_private(true); symbol->set_is_own(true); return symbol; } Handle Factory::NewNativeContext() { Handle array = NewFixedArray(Context::NATIVE_CONTEXT_SLOTS); array->set_map_no_write_barrier(*native_context_map()); Handle context = Handle::cast(array); context->set_js_array_maps(*undefined_value()); DCHECK(context->IsNativeContext()); return context; } Handle Factory::NewScriptContext(Handle function, Handle scope_info) { Handle array = NewFixedArray(scope_info->ContextLength(), TENURED); array->set_map_no_write_barrier(*script_context_map()); Handle context = Handle::cast(array); context->set_closure(*function); context->set_previous(function->context()); context->set_extension(*scope_info); context->set_global_object(function->context()->global_object()); DCHECK(context->IsScriptContext()); return context; } Handle Factory::NewScriptContextTable() { Handle array = NewFixedArray(1); array->set_map_no_write_barrier(*script_context_table_map()); Handle context_table = Handle::cast(array); context_table->set_used(0); return context_table; } Handle Factory::NewModuleContext(Handle scope_info) { Handle array = NewFixedArray(scope_info->ContextLength(), TENURED); array->set_map_no_write_barrier(*module_context_map()); // Instance link will be set later. Handle context = Handle::cast(array); context->set_extension(Smi::FromInt(0)); return context; } Handle Factory::NewFunctionContext(int length, Handle function) { DCHECK(length >= Context::MIN_CONTEXT_SLOTS); Handle array = NewFixedArray(length); array->set_map_no_write_barrier(*function_context_map()); Handle context = Handle::cast(array); context->set_closure(*function); context->set_previous(function->context()); context->set_extension(Smi::FromInt(0)); context->set_global_object(function->context()->global_object()); return context; } Handle Factory::NewCatchContext(Handle function, Handle previous, Handle name, Handle thrown_object) { STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == Context::THROWN_OBJECT_INDEX); Handle array = NewFixedArray(Context::MIN_CONTEXT_SLOTS + 1); array->set_map_no_write_barrier(*catch_context_map()); Handle context = Handle::cast(array); context->set_closure(*function); context->set_previous(*previous); context->set_extension(*name); context->set_global_object(previous->global_object()); context->set(Context::THROWN_OBJECT_INDEX, *thrown_object); return context; } Handle Factory::NewWithContext(Handle function, Handle previous, Handle extension) { Handle array = NewFixedArray(Context::MIN_CONTEXT_SLOTS); array->set_map_no_write_barrier(*with_context_map()); Handle context = Handle::cast(array); context->set_closure(*function); context->set_previous(*previous); context->set_extension(*extension); context->set_global_object(previous->global_object()); return context; } Handle Factory::NewBlockContext(Handle function, Handle previous, Handle scope_info) { Handle array = NewFixedArrayWithHoles(scope_info->ContextLength()); array->set_map_no_write_barrier(*block_context_map()); Handle context = Handle::cast(array); context->set_closure(*function); context->set_previous(*previous); context->set_extension(*scope_info); context->set_global_object(previous->global_object()); return context; } Handle Factory::NewStruct(InstanceType type) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateStruct(type), Struct); } Handle Factory::NewCodeCache() { Handle code_cache = Handle::cast(NewStruct(CODE_CACHE_TYPE)); code_cache->set_default_cache(*empty_fixed_array(), SKIP_WRITE_BARRIER); code_cache->set_normal_type_cache(*undefined_value(), SKIP_WRITE_BARRIER); code_cache->set_weak_cell_cache(*undefined_value(), SKIP_WRITE_BARRIER); return code_cache; } Handle Factory::NewAliasedArgumentsEntry( int aliased_context_slot) { Handle entry = Handle::cast( NewStruct(ALIASED_ARGUMENTS_ENTRY_TYPE)); entry->set_aliased_context_slot(aliased_context_slot); return entry; } Handle Factory::NewExecutableAccessorInfo() { Handle info = Handle::cast( NewStruct(EXECUTABLE_ACCESSOR_INFO_TYPE)); info->set_flag(0); // Must clear the flag, it was initialized as undefined. return info; } Handle