* external string resource.
*/
static Local<String> NewExternal(ExternalStringResource* resource);
+
+ /**
+ * Associate an external string resource with this string by transforming it
+ * in place so that existing references to this string in the JavaScript heap
+ * will use the external string resource. The external string resource's
+ * character contents needs to be equivalent to this string.
+ * Returns true if the string has been changed to be an external string.
+ * The string is not modified if the operation fails.
+ */
+ bool MakeExternal(ExternalStringResource* resource);
/**
* Creates a new external string using the ascii data defined in the given
* external string resource.
*/
static Local<String> NewExternal(ExternalAsciiStringResource* resource);
+
+ /**
+ * Associate an external string resource with this string by transforming it
+ * in place so that existing references to this string in the JavaScript heap
+ * will use the external string resource. The external string resource's
+ * character contents needs to be equivalent to this string.
+ * Returns true if the string has been changed to be an external string.
+ * The string is not modified if the operation fails.
+ */
+ bool MakeExternal(ExternalAsciiStringResource* resource);
/** Creates an undetectable string from the supplied ascii or utf-8 data.*/
static Local<String> NewUndetectable(const char* data, int length = -1);
static void DisposeExternalString(v8::Persistent<v8::Value> obj,
void* parameter) {
- v8::String::ExternalStringResource* resource =
- reinterpret_cast<v8::String::ExternalStringResource*>(parameter);
- const size_t total_size = resource->length() * sizeof(*resource->data());
- i::Counters::total_external_string_memory.Decrement(total_size);
- delete resource;
+ i::ExternalTwoByteString* str =
+ i::ExternalTwoByteString::cast(*Utils::OpenHandle(*obj));
+
+ // External symbols are deleted when they are pruned out of the symbol
+ // table. Generally external symbols are not registered with the weak handle
+ // callbacks unless they are upgraded to a symbol after being externalized.
+ if (!str->IsSymbol()) {
+ v8::String::ExternalStringResource* resource =
+ reinterpret_cast<v8::String::ExternalStringResource*>(parameter);
+ if (resource != NULL) {
+ const size_t total_size = resource->length() * sizeof(*resource->data());
+ i::Counters::total_external_string_memory.Decrement(total_size);
+
+ // The object will continue to live in the JavaScript heap until the
+ // handle is entirely cleaned out by the next GC. For example the
+ // destructor for the resource below could bring it back to life again.
+ // Which is why we make sure to not have a dangling pointer here.
+ str->set_resource(NULL);
+ delete resource;
+ }
+ }
+
+ // In any case we do not need this handle any longer.
obj.Dispose();
}
static void DisposeExternalAsciiString(v8::Persistent<v8::Value> obj,
void* parameter) {
- v8::String::ExternalAsciiStringResource* resource =
- reinterpret_cast<v8::String::ExternalAsciiStringResource*>(parameter);
- const size_t total_size = resource->length() * sizeof(*resource->data());
- i::Counters::total_external_string_memory.Decrement(total_size);
- delete resource;
+ i::ExternalAsciiString* str =
+ i::ExternalAsciiString::cast(*Utils::OpenHandle(*obj));
+
+ // External symbols are deleted when they are pruned out of the symbol
+ // table. Generally external symbols are not registered with the weak handle
+ // callbacks unless they are upgraded to a symbol after being externalized.
+ if (!str->IsSymbol()) {
+ v8::String::ExternalAsciiStringResource* resource =
+ reinterpret_cast<v8::String::ExternalAsciiStringResource*>(parameter);
+ if (resource != NULL) {
+ const size_t total_size = resource->length() * sizeof(*resource->data());
+ i::Counters::total_external_string_memory.Decrement(total_size);
+
+ // The object will continue to live in the JavaScript heap until the
+ // handle is entirely cleaned out by the next GC. For example the
+ // destructor for the resource below could bring it back to life again.
+ // Which is why we make sure to not have a dangling pointer here.
+ str->set_resource(NULL);
+ delete resource;
+ }
+ }
+
+ // In any case we do not need this handle any longer.
obj.Dispose();
}
}
+bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
+ if (IsDeadCheck("v8::String::MakeExternal()")) return false;
+ if (this->IsExternal()) return false; // Already an external string.
+ i::Handle <i::String> obj = Utils::OpenHandle(this);
+ bool result = obj->MakeExternal(resource);
+ if (result && !obj->IsSymbol()) {
+ // Operation was successful and the string is not a symbol. In this case
+ // we need to make sure that the we call the destructor for the external
+ // resource when no strong references to the string remain.
+ i::Handle<i::Object> handle = i::GlobalHandles::Create(*obj);
+ i::GlobalHandles::MakeWeak(handle.location(),
+ resource,
+ &DisposeExternalString);
+ }
+ return result;
+}
+
+
Local<String> v8::String::NewExternal(
v8::String::ExternalAsciiStringResource* resource) {
EnsureInitialized("v8::String::NewExternal()");
}
+bool v8::String::MakeExternal(
+ v8::String::ExternalAsciiStringResource* resource) {
+ if (IsDeadCheck("v8::String::MakeExternal()")) return false;
+ if (this->IsExternal()) return false; // Already an external string.
+ i::Handle <i::String> obj = Utils::OpenHandle(this);
+ bool result = obj->MakeExternal(resource);
+ if (result && !obj->IsSymbol()) {
+ // Operation was successful and the string is not a symbol. In this case
+ // we need to make sure that the we call the destructor for the external
+ // resource when no strong references to the string remain.
+ i::Handle<i::Object> handle = i::GlobalHandles::Create(*obj);
+ i::GlobalHandles::MakeWeak(handle.location(),
+ resource,
+ &DisposeExternalAsciiString);
+ }
+ return result;
+}
+
+
Local<v8::Object> v8::Object::New() {
EnsureInitialized("v8::Object::New()");
LOG_API("Object::New");
Object* Heap::AllocateExternalStringFromTwoByte(
ExternalTwoByteString::Resource* resource) {
- Map* map;
int length = resource->length();
- if (length <= String::kMaxShortStringSize) {
- map = short_external_string_map();
- } else if (length <= String::kMaxMediumStringSize) {
- map = medium_external_string_map();
- } else {
- map = long_external_string_map();
- }
+ Map* map = ExternalTwoByteString::StringMap(length);
Object* result = Allocate(map, NEW_SPACE);
if (result->IsFailure()) return result;
Object* Heap::AllocateExternalSymbolFromTwoByte(
ExternalTwoByteString::Resource* resource) {
- Map* map;
int length = resource->length();
- if (length <= String::kMaxShortStringSize) {
- map = short_external_symbol_map();
- } else if (length <= String::kMaxMediumStringSize) {
- map = medium_external_symbol_map();
- } else {
- map = long_external_symbol_map();
- }
+ Map* map = ExternalTwoByteString::SymbolMap(length);
Object* result = Allocate(map, OLD_DATA_SPACE);
if (result->IsFailure()) return result;
}
+void Heap::CreateFillerObjectAt(Address addr, int size) {
+ if (size == 0) return;
+ HeapObject* filler = HeapObject::FromAddress(addr);
+ if (size == kPointerSize) {
+ filler->set_map(Heap::one_word_filler_map());
+ } else {
+ filler->set_map(Heap::byte_array_map());
+ ByteArray::cast(filler)->set_length(ByteArray::LengthFor(size));
+ }
+}
+
+
Object* Heap::CreateCode(const CodeDesc& desc,
ScopeInfo<>* sinfo,
Code::Flags flags,
return long_sliced_ascii_symbol_map();
}
- if (map == short_external_string_map()) return short_external_string_map();
- if (map == medium_external_string_map()) return medium_external_string_map();
- if (map == long_external_string_map()) return long_external_string_map();
+ if (map == short_external_string_map()) {
+ return short_external_symbol_map();
+ }
+ if (map == medium_external_string_map()) {
+ return medium_external_symbol_map();
+ }
+ if (map == long_external_string_map()) {
+ return long_external_symbol_map();
+ }
if (map == short_external_ascii_string_map()) {
- return short_external_ascii_string_map();
+ return short_external_ascii_symbol_map();
}
if (map == medium_external_ascii_string_map()) {
- return medium_external_ascii_string_map();
+ return medium_external_ascii_symbol_map();
}
if (map == long_external_ascii_string_map()) {
- return long_external_ascii_string_map();
+ return long_external_ascii_symbol_map();
}
// No match found.
AllocationSpace space,
AllocationSpace retry_space);
+ // Initialize a filler object to keep the ability to iterate over the heap
+ // when shortening objects.
+ static void CreateFillerObjectAt(Address addr, int size);
+
// Makes a new native code object
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed. On success, the pointer to the Code object is stored in the
// Visit all HeapObject pointers in [start, end).
for (Object** p = start; p < end; p++) {
if ((*p)->IsHeapObject() && !HeapObject::cast(*p)->IsMarked()) {
+ // Check if the symbol being pruned is an external symbol. We need to
+ // delete the associated external data as this symbol is going away.
+
+ // Since the object is not marked we can access its map word safely
+ // without having to worry about marking bits in the object header.
+ Map* map = HeapObject::cast(*p)->map();
+ // Since no objects have yet been moved we can safely access the map of
+ // the object.
+ uint32_t type = map->instance_type();
+ bool is_external = (type & kStringRepresentationMask) ==
+ kExternalStringTag;
+ if (is_external) {
+ bool is_two_byte = (type & kStringEncodingMask) == kTwoByteStringTag;
+ byte* resource_addr = reinterpret_cast<byte*>(*p) +
+ ExternalString::kResourceOffset -
+ kHeapObjectTag;
+ if (is_two_byte) {
+ v8::String::ExternalStringResource* resource =
+ *reinterpret_cast<v8::String::ExternalStringResource**>
+ (resource_addr);
+ delete resource;
+ } else {
+ v8::String::ExternalAsciiStringResource* resource =
+ *reinterpret_cast<v8::String::ExternalAsciiStringResource**>
+ (resource_addr);
+ delete resource;
+ }
+ }
// Set the entry to null_value (as deleted).
*p = Heap::null_value();
pointers_removed_++;
}
+Map* ExternalAsciiString::StringMap(int length) {
+ Map* map;
+ // Number of characters: determines the map.
+ if (length <= String::kMaxShortStringSize) {
+ map = Heap::short_external_ascii_string_map();
+ } else if (length <= String::kMaxMediumStringSize) {
+ map = Heap::medium_external_ascii_string_map();
+ } else {
+ map = Heap::long_external_ascii_string_map();
+ }
+ return map;
+}
+
+
+Map* ExternalAsciiString::SymbolMap(int length) {
+ Map* map;
+ // Number of characters: determines the map.
+ if (length <= String::kMaxShortStringSize) {
+ map = Heap::short_external_ascii_symbol_map();
+ } else if (length <= String::kMaxMediumStringSize) {
+ map = Heap::medium_external_ascii_symbol_map();
+ } else {
+ map = Heap::long_external_ascii_symbol_map();
+ }
+ return map;
+}
+
+
ExternalTwoByteString::Resource* ExternalTwoByteString::resource() {
return *reinterpret_cast<Resource**>(FIELD_ADDR(this, kResourceOffset));
}
}
+Map* ExternalTwoByteString::StringMap(int length) {
+ Map* map;
+ // Number of characters: determines the map.
+ if (length <= String::kMaxShortStringSize) {
+ map = Heap::short_external_string_map();
+ } else if (length <= String::kMaxMediumStringSize) {
+ map = Heap::medium_external_string_map();
+ } else {
+ map = Heap::long_external_string_map();
+ }
+ return map;
+}
+
+
+Map* ExternalTwoByteString::SymbolMap(int length) {
+ Map* map;
+ // Number of characters: determines the map.
+ if (length <= String::kMaxShortStringSize) {
+ map = Heap::short_external_symbol_map();
+ } else if (length <= String::kMaxMediumStringSize) {
+ map = Heap::medium_external_symbol_map();
+ } else {
+ map = Heap::long_external_symbol_map();
+ }
+ return map;
+}
+
+
byte ByteArray::get(int index) {
ASSERT(index >= 0 && index < this->length());
return READ_BYTE_FIELD(this, kHeaderSize + index * kCharSize);
}
+bool String::MakeExternal(v8::String::ExternalStringResource* resource) {
+#ifdef DEBUG
+ { // NOLINT (presubmit.py gets confused about if and braces)
+ // Assert that the resource and the string are equivalent.
+ ASSERT(static_cast<size_t>(this->length()) == resource->length());
+ SmartPointer<uc16> smart_chars = this->ToWideCString();
+ ASSERT(memcmp(*smart_chars,
+ resource->data(),
+ resource->length()*sizeof(**smart_chars)) == 0);
+ }
+#endif // DEBUG
+
+ int size = this->Size(); // Byte size of the original string.
+ if (size < ExternalString::kSize) {
+ // The string is too small to fit an external String in its place. This can
+ // only happen for zero length strings.
+ return false;
+ }
+ ASSERT(size >= ExternalString::kSize);
+ bool is_symbol = this->IsSymbol();
+ int length = this->length();
+
+ // Morph the object to an external string by adjusting the map and
+ // reinitializing the fields.
+ this->set_map(ExternalTwoByteString::StringMap(length));
+ ExternalTwoByteString* self = ExternalTwoByteString::cast(this);
+ self->set_length(length);
+ self->set_resource(resource);
+ // Additionally make the object into an external symbol if the original string
+ // was a symbol to start with.
+ if (is_symbol) {
+ self->Hash(); // Force regeneration of the hash value.
+ // Now morph this external string into a external symbol.
+ self->set_map(ExternalTwoByteString::SymbolMap(length));
+ }
+
+ // Fill the remainder of the string with dead wood.
+ int new_size = this->Size(); // Byte size of the external String object.
+ Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
+ return true;
+}
+
+
+bool String::MakeExternal(v8::String::ExternalAsciiStringResource* resource) {
+#ifdef DEBUG
+ { // NOLINT (presubmit.py gets confused about if and braces)
+ // Assert that the resource and the string are equivalent.
+ ASSERT(static_cast<size_t>(this->length()) == resource->length());
+ SmartPointer<char> smart_chars = this->ToCString();
+ ASSERT(memcmp(*smart_chars,
+ resource->data(),
+ resource->length()*sizeof(**smart_chars)) == 0);
+ }
+#endif // DEBUG
+
+ int size = this->Size(); // Byte size of the original string.
+ if (size < ExternalString::kSize) {
+ // The string is too small to fit an external String in its place. This can
+ // only happen for zero length strings.
+ return false;
+ }
+ ASSERT(size >= ExternalString::kSize);
+ bool is_symbol = this->IsSymbol();
+ int length = this->length();
+
+ // Morph the object to an external string by adjusting the map and
+ // reinitializing the fields.
+ this->set_map(ExternalAsciiString::StringMap(length));
+ ExternalAsciiString* self = ExternalAsciiString::cast(this);
+ self->set_length(length);
+ self->set_resource(resource);
+ // Additionally make the object into an external symbol if the original string
+ // was a symbol to start with.
+ if (is_symbol) {
+ self->Hash(); // Force regeneration of the hash value.
+ // Now morph this external string into a external symbol.
+ self->set_map(ExternalAsciiString::SymbolMap(length));
+ }
+
+ // Fill the remainder of the string with dead wood.
+ int new_size = this->Size(); // Byte size of the external String object.
+ Heap::CreateFillerObjectAt(this->address() + new_size, size - new_size);
+ return true;
+}
+
+
void String::StringShortPrint(StringStream* accumulator) {
StringShape shape(this);
int len = length(shape);
if (obj->IsFailure()) return obj;
Map* new_map = Map::cast(obj);
- // Clear inobject properties if needed by adjusting the instance
- // size and putting in a filler or byte array instead of the
- // inobject properties.
+ // Clear inobject properties if needed by adjusting the instance size and
+ // putting in a filler object instead of the inobject properties.
if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
int instance_size_delta = map()->inobject_properties() * kPointerSize;
int new_instance_size = map()->instance_size() - instance_size_delta;
new_map->set_inobject_properties(0);
new_map->set_instance_size(new_instance_size);
- if (instance_size_delta == kPointerSize) {
- WRITE_FIELD(this, new_instance_size, Heap::one_word_filler_map());
- } else {
- int byte_array_length = ByteArray::LengthFor(instance_size_delta);
- int byte_array_length_offset = new_instance_size + kPointerSize;
- WRITE_FIELD(this, new_instance_size, Heap::byte_array_map());
- WRITE_INT_FIELD(this, byte_array_length_offset, byte_array_length);
- }
- WRITE_BARRIER(this, new_instance_size);
+ Heap::CreateFillerObjectAt(this->address() + new_instance_size,
+ instance_size_delta);
}
new_map->set_unused_property_fields(0);
// Bits used by the marking phase of the garbage collector.
//
- // The first word of a heap object is normall a map pointer. The last two
+ // The first word of a heap object is normally a map pointer. The last two
// bits are tagged as '01' (kHeapObjectTag). We reuse the last two bits to
// mark an object as live and/or overflowed:
// last bit = 0, marked as alive
uint32_t* index,
int length);
+ // Externalization.
+ bool MakeExternal(v8::String::ExternalStringResource* resource);
+ bool MakeExternal(v8::String::ExternalAsciiStringResource* resource);
+
// Conversion.
inline bool AsArrayIndex(uint32_t* index);
unsigned* offset,
unsigned chars);
+ // Identify the map for the external string/symbol with a particular length.
+ static inline Map* StringMap(int length);
+ static inline Map* SymbolMap(int length);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalAsciiString);
};
unsigned* offset_ptr,
unsigned chars);
+ // Identify the map for the external string/symbol with a particular length.
+ static inline Map* StringMap(int length);
+ static inline Map* SymbolMap(int length);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalTwoByteString);
};
}
+THREADED_TEST(ScriptMakingExternalString) {
+ TestResource::dispose_count = 0;
+ uint16_t* two_byte_source = AsciiToTwoByteString("1 + 2 * 3");
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ Local<String> source = String::New(two_byte_source);
+ bool success = source->MakeExternal(new TestResource(two_byte_source));
+ CHECK(success);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> value = script->Run();
+ CHECK(value->IsNumber());
+ CHECK_EQ(7, value->Int32Value());
+ v8::internal::Heap::CollectAllGarbage();
+ CHECK_EQ(0, TestResource::dispose_count);
+ }
+ v8::internal::Heap::CollectAllGarbage();
+ CHECK_EQ(1, TestResource::dispose_count);
+}
+
+
+THREADED_TEST(ScriptMakingExternalAsciiString) {
+ TestAsciiResource::dispose_count = 0;
+ const char* c_source = "1 + 2 * 3";
+ {
+ v8::HandleScope scope;
+ LocalContext env;
+ Local<String> source = v8_str(c_source);
+ bool success = source->MakeExternal(
+ new TestAsciiResource(i::StrDup(c_source)));
+ CHECK(success);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> value = script->Run();
+ CHECK(value->IsNumber());
+ CHECK_EQ(7, value->Int32Value());
+ v8::internal::Heap::CollectAllGarbage();
+ CHECK_EQ(0, TestAsciiResource::dispose_count);
+ }
+ v8::internal::Heap::CollectAllGarbage();
+ CHECK_EQ(1, TestAsciiResource::dispose_count);
+}
+
+
+THREADED_TEST(UsingExternalString) {
+ v8::HandleScope scope;
+ uint16_t* two_byte_string = AsciiToTwoByteString("test string");
+ Local<String> string = String::NewExternal(new TestResource(two_byte_string));
+ i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
+ i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
+ CHECK(isymbol->IsSymbol());
+}
+
+
+THREADED_TEST(UsingExternalAsciiString) {
+ v8::HandleScope scope;
+ const char* one_byte_string = "test string";
+ Local<String> string = String::NewExternal(
+ new TestAsciiResource(i::StrDup(one_byte_string)));
+ i::Handle<i::String> istring = v8::Utils::OpenHandle(*string);
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
+ i::Handle<i::String> isymbol = i::Factory::SymbolFromString(istring);
+ CHECK(isymbol->IsSymbol());
+}
+
+
THREADED_TEST(GlobalProperties) {
v8::HandleScope scope;
LocalContext env;