diff --git a/include/v8.h b/include/v8.h index ff5fb17d17..fe01e30cf6 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1122,11 +1122,13 @@ class V8EXPORT String : public Primitive { /** * Creates a new external string using the data defined in the given - * resource. The resource is deleted when the external string is no - * longer live on V8's heap. The caller of this function should not - * delete or modify the resource. Neither should the underlying buffer be - * deallocated or modified except through the destructor of the - * external string resource. + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed. If a disposal callback has been set using + * SetExternalStringDiposeCallback this callback will be called to dispose + * the resource. Otherwise, V8 will dispose the resource using the C++ delete + * operator. The caller of this function should not otherwise delete or + * modify the resource. Neither should the underlying buffer be deallocated + * or modified except through the destructor of the external string resource. */ static Local NewExternal(ExternalStringResource* resource); @@ -1136,17 +1138,20 @@ class V8EXPORT String : public Primitive { * 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. + * The string is not modified if the operation fails. See NewExternal for + * information on the lifetime of the resource. */ bool MakeExternal(ExternalStringResource* resource); /** * Creates a new external string using the ascii data defined in the given - * resource. The resource is deleted when the external string is no - * longer live on V8's heap. The caller of this function should not - * delete or modify the resource. Neither should the underlying buffer be - * deallocated or modified except through the destructor of the - * external string resource. + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed. If a disposal callback has been set using + * SetExternalStringDiposeCallback this callback will be called to dispose + * the resource. Otherwise, V8 will dispose the resource using the C++ delete + * operator. The caller of this function should not otherwise delete or + * modify the resource. Neither should the underlying buffer be deallocated + * or modified except through the destructor of the external string resource. */ static Local NewExternal(ExternalAsciiStringResource* resource); @@ -1156,7 +1161,8 @@ class V8EXPORT String : public Primitive { * 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. + * The string is not modified if the operation fails. See NewExternal for + * information on the lifetime of the resource. */ bool MakeExternal(ExternalAsciiStringResource* resource); @@ -1245,6 +1251,10 @@ class V8EXPORT String : public Primitive { }; +typedef void (*ExternalStringDiposeCallback) + (String::ExternalStringResourceBase* resource); + + /** * A JavaScript number value (ECMA-262, 4.3.20) */ @@ -2461,6 +2471,15 @@ class V8EXPORT V8 { */ static void RemoveMessageListeners(MessageCallback that); + /** + * Set a callback to be called when an external string is no longer live on + * V8's heap. The resource will no longer be needed by V8 and the embedder + * can dispose of if. If this callback is not set V8 will free the resource + * using the C++ delete operator. + */ + static void SetExternalStringDiposeCallback( + ExternalStringDiposeCallback that); + /** * Sets V8 flags from a string. */ diff --git a/src/api.cc b/src/api.cc index 2f5e23495f..dfdd7bd7b2 100644 --- a/src/api.cc +++ b/src/api.cc @@ -3692,6 +3692,14 @@ void V8::RemoveMessageListeners(MessageCallback that) { } +void V8::SetExternalStringDiposeCallback( + ExternalStringDiposeCallback callback) { + if (IsDeadCheck("v8::V8::SetExternalStringDiposeCallback()")) + return; + i::Heap::SetExternalStringDiposeCallback(callback); +} + + void V8::SetCounterFunction(CounterLookupCallback callback) { if (IsDeadCheck("v8::V8::SetCounterFunction()")) return; i::StatsTable::SetCounterFunction(callback); diff --git a/src/heap-inl.h b/src/heap-inl.h index 80157d019d..feda2d1114 100644 --- a/src/heap-inl.h +++ b/src/heap-inl.h @@ -117,7 +117,14 @@ void Heap::FinalizeExternalString(String* string) { reinterpret_cast(string) + ExternalString::kResourceOffset - kHeapObjectTag); - delete *resource_addr; + + // Dispose of the C++ object. + if (external_string_dispose_callback_ != NULL) { + external_string_dispose_callback_(*resource_addr); + } else { + delete *resource_addr; + } + // Clear the resource pointer in the string. *resource_addr = NULL; } diff --git a/src/heap.cc b/src/heap.cc index d3e6cdb37d..9f811c03c2 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -98,6 +98,8 @@ size_t Heap::code_range_size_ = 0; // set up by ConfigureHeap otherwise. int Heap::reserved_semispace_size_ = Heap::max_semispace_size_; +ExternalStringDiposeCallback Heap::external_string_dispose_callback_ = NULL; + List Heap::gc_prologue_callbacks_; List Heap::gc_epilogue_callbacks_; diff --git a/src/heap.h b/src/heap.h index b67418e059..e99c538946 100644 --- a/src/heap.h +++ b/src/heap.h @@ -690,6 +690,11 @@ class Heap : public AllStatic { static bool GarbageCollectionGreedyCheck(); #endif + static void SetExternalStringDiposeCallback( + ExternalStringDiposeCallback callback) { + external_string_dispose_callback_ = callback; + } + static void AddGCPrologueCallback( GCEpilogueCallback callback, GCType gc_type_filter); static void RemoveGCPrologueCallback(GCEpilogueCallback callback); @@ -1138,6 +1143,9 @@ class Heap : public AllStatic { // any string when looked up in properties. static String* hidden_symbol_; + static ExternalStringDiposeCallback + external_string_dispose_callback_; + // GC callback function, called before and after mark-compact GC. // Allocations in the callback function are disallowed. struct GCPrologueCallbackPair { diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 503ff93b03..b520e568a9 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -612,6 +612,71 @@ THREADED_TEST(ScavengeExternalAsciiString) { } +static int dispose_count = 0; +static void DisposeExternalStringCount( + String::ExternalStringResourceBase* resource) { + dispose_count++; +} + + +static void DisposeExternalStringDeleteAndCount( + String::ExternalStringResourceBase* resource) { + delete resource; + dispose_count++; +} + + +TEST(ExternalStringWithResourceDisposeCallback) { + const char* c_source = "1 + 2 * 3"; + + // Set an external string collected callback which does not delete the object. + v8::V8::SetExternalStringDiposeCallback(DisposeExternalStringCount); + + // Use a stack allocated external string resource allocated object. + dispose_count = 0; + TestAsciiResource::dispose_count = 0; + TestAsciiResource res_stack(i::StrDup(c_source)); + { + v8::HandleScope scope; + LocalContext env; + Local source = String::NewExternal(&res_stack); + Local