// 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 "factory.h" #include "macro-assembler.h" #include "isolate-inl.h" #include "v8conversions.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::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) { ASSERT(0 <= size); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateFixedArray(size, pretenure), FixedArray); } Handle Factory::NewFixedArrayWithHoles(int size, PretenureFlag pretenure) { ASSERT(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) { ASSERT(0 <= size); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateUninitializedFixedDoubleArray(size, pretenure), FixedDoubleArray); } Handle Factory::NewFixedDoubleArrayWithHoles( int size, PretenureFlag pretenure) { ASSERT(0 <= size); Handle array = NewFixedDoubleArray(size, pretenure); for (int i = 0; i < size; ++i) { array->set_the_hole(i); } return array; } Handle Factory::NewConstantPoolArray( int number_of_int64_entries, int number_of_code_ptr_entries, int number_of_heap_ptr_entries, int number_of_int32_entries) { ASSERT(number_of_int64_entries > 0 || number_of_code_ptr_entries > 0 || number_of_heap_ptr_entries > 0 || number_of_int32_entries > 0); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateConstantPoolArray(number_of_int64_entries, number_of_code_ptr_entries, number_of_heap_ptr_entries, number_of_int32_entries), ConstantPoolArray); } Handle Factory::NewNameDictionary(int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), NameDictionary::Allocate(isolate()->heap(), at_least_space_for), NameDictionary); } Handle Factory::NewSeededNumberDictionary( int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), SeededNumberDictionary::Allocate(isolate()->heap(), at_least_space_for), SeededNumberDictionary); } Handle Factory::NewUnseededNumberDictionary( int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), UnseededNumberDictionary::Allocate(isolate()->heap(), at_least_space_for), UnseededNumberDictionary); } Handle Factory::NewOrderedHashSet() { return OrderedHashSet::Allocate(isolate(), 4); } Handle Factory::NewOrderedHashMap() { return OrderedHashMap::Allocate(isolate(), 4); } Handle Factory::NewObjectHashTable( int at_least_space_for, MinimumCapacity capacity_option) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION(isolate(), ObjectHashTable::Allocate(isolate()->heap(), at_least_space_for, capacity_option), ObjectHashTable); } Handle Factory::NewWeakHashTable(int at_least_space_for) { ASSERT(0 <= at_least_space_for); CALL_HEAP_FUNCTION( isolate(), WeakHashTable::Allocate(isolate()->heap(), at_least_space_for, USE_DEFAULT_MINIMUM_CAPACITY, TENURED), WeakHashTable); } Handle Factory::NewDeoptimizationInputData( int deopt_entry_count, PretenureFlag pretenure) { ASSERT(deopt_entry_count > 0); CALL_HEAP_FUNCTION(isolate(), DeoptimizationInputData::Allocate(isolate(), deopt_entry_count, pretenure), DeoptimizationInputData); } Handle Factory::NewDeoptimizationOutputData( int deopt_entry_count, PretenureFlag pretenure) { ASSERT(deopt_entry_count > 0); CALL_HEAP_FUNCTION(isolate(), DeoptimizationOutputData::Allocate(isolate(), deopt_entry_count, pretenure), DeoptimizationOutputData); } 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); accessors->set_access_flags(Smi::FromInt(0), SKIP_WRITE_BARRIER); return accessors; } Handle Factory::NewTypeFeedbackInfo() { Handle info = Handle::cast(NewStruct(TYPE_FEEDBACK_INFO_TYPE)); info->initialize_storage(); info->set_feedback_vector(*empty_fixed_array(), SKIP_WRITE_BARRIER); 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) { CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->InternalizeString(*string), String); } Handle Factory::InternalizeOneByteString(Vector string) { OneByteStringKey key(string, isolate()->heap()->HashSeed()); return InternalizeStringWithKey(&key); } Handle Factory::InternalizeOneByteString( Handle string, int from, int length) { SubStringKey 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) { CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->InternalizeStringWithKey(key), String); } template Handle Factory::InternalizeStringWithKey< SubStringKey > (SubStringKey* key); template Handle Factory::InternalizeStringWithKey< SubStringKey > (SubStringKey* key); Handle Factory::NewStringFromOneByte(Vector string, PretenureFlag pretenure) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateStringFromOneByte(string, pretenure), String); } Handle 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. CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateStringFromUtf8Slow(string, non_ascii_start, pretenure), String); } Handle Factory::NewStringFromTwoByte(Vector string, PretenureFlag pretenure) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateStringFromTwoByte(string, pretenure), String); } MaybeHandle Factory::NewRawOneByteString( int length, PretenureFlag pretenure) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateRawOneByteString(length, pretenure), SeqOneByteString); } MaybeHandle Factory::NewRawTwoByteString( int length, PretenureFlag pretenure) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateRawTwoByteString(length, pretenure), SeqTwoByteString); } Handle Factory::LookupSingleCharacterStringFromCode(uint32_t index) { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->LookupSingleCharacterStringFromCode(index), String); } // 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')) { String* result; StringTable* table = isolate->heap()->string_table(); if (table->LookupTwoCharsStringIfExists(c1, c2, &result)) { return handle(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. ASSERT(IsPowerOf2(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; } Handle Factory::NewRawConsString(String::Encoding encoding) { Handle map = (encoding == String::ONE_BYTE_ENCODING) ? cons_ascii_string_map() : cons_string_map(); return New(map, NEW_SPACE); } 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) { return isolate()->Throw(NewInvalidStringLengthError()); } 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 ASCII strings below, but // we can try to save memory if all chars actually fit in ASCII. 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_ascii()->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); ASSERT(left->IsFlat()); ASSERT(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 result = NewRawConsString( (is_one_byte || is_one_byte_data_in_two_byte_string) ? String::ONE_BYTE_ENCODING : String::TWO_BYTE_ENCODING); 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::NewFlatConcatString(Handle first, Handle second) { int total_length = first->length() + second->length(); if (first->IsOneByteRepresentation() && second->IsOneByteRepresentation()) { return ConcatStringContent( NewRawOneByteString(total_length).ToHandleChecked(), first, second); } else { return ConcatStringContent( NewRawTwoByteString(total_length).ToHandleChecked(), first, second); } } Handle Factory::NewRawSlicedString(String::Encoding encoding) { Handle map = (encoding == String::ONE_BYTE_ENCODING) ? sliced_ascii_string_map() : sliced_string_map(); return New(map, NEW_SPACE); } Handle Factory::NewProperSubString(Handle str, int begin, int end) { #if VERIFY_HEAP if (FLAG_verify_heap) str->StringVerify(); #endif ASSERT(begin > 0 || end < str->length()); 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; while (str->IsConsString()) { Handle cons = Handle::cast(str); int split = cons->first()->length(); if (split <= offset) { // Slice is fully contained in the second part. str = Handle(cons->second(), isolate()); offset -= split; // Adjust for offset. continue; } else if (offset + length <= split) { // Slice is fully contained in the first part. str = Handle(cons->first(), isolate()); continue; } break; } if (str->IsSlicedString()) { Handle slice = Handle::cast(str); str = Handle(slice->parent(), isolate()); offset += slice->offset(); } else { str = String::Flatten(str); } ASSERT(str->IsSeqString() || str->IsExternalString()); Handle slice = NewRawSlicedString( str->IsOneByteRepresentation() ? String::ONE_BYTE_ENCODING : String::TWO_BYTE_ENCODING); slice->set_hash_field(String::kEmptyHashField); slice->set_length(length); slice->set_parent(*str); slice->set_offset(offset); return slice; } MaybeHandle Factory::NewExternalStringFromAscii( const ExternalAsciiString::Resource* resource) { size_t length = resource->length(); if (length > static_cast(String::kMaxLength)) { isolate()->ThrowInvalidStringLength(); return MaybeHandle(); } Handle map = external_ascii_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)) { isolate()->ThrowInvalidStringLength(); return MaybeHandle(); } // 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() { CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocatePrivateSymbol(), 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()); ASSERT(context->IsNativeContext()); return context; } Handle Factory::NewGlobalContext(Handle function, Handle scope_info) { Handle array = NewFixedArray(scope_info->ContextLength(), TENURED); array->set_map_no_write_barrier(*global_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()); ASSERT(context->IsGlobalContext()); return context; } 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) { ASSERT(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); 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::NewDeclaredAccessorDescriptor() { return Handle::cast( NewStruct(DECLARED_ACCESSOR_DESCRIPTOR_TYPE)); } Handle Factory::NewDeclaredAccessorInfo() { Handle info = Handle::cast( NewStruct(DECLARED_ACCESSOR_INFO_TYPE)); info->set_flag(0); // Must clear the flag, it was initialized as undefined. return info; } 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