From b3282c290e077a571eb6619b45b5e786406105ba Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Fri, 7 Jun 2013 10:52:11 +0000 Subject: [PATCH] Recording array buffer views. R=hpayer@chromium.org BUG= Review URL: https://codereview.chromium.org/15562008 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15000 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 8 + src/api.cc | 3 + src/bootstrapper.cc | 3 +- src/heap.cc | 119 ++++++++++- src/heap.h | 11 ++ src/objects-inl.h | 5 + src/objects-printer.cc | 4 +- src/objects-visiting-inl.h | 80 ++++++++ src/objects-visiting.cc | 8 +- src/objects-visiting.h | 7 + src/objects.h | 19 +- src/runtime.cc | 6 + test/cctest/cctest.gyp | 3 +- test/cctest/test-weaktypedarrays.cc | 380 ++++++++++++++++++++++++++++++++++++ 14 files changed, 642 insertions(+), 14 deletions(-) create mode 100644 test/cctest/test-weaktypedarrays.cc diff --git a/include/v8.h b/include/v8.h index 04e608b..6144c61 100644 --- a/include/v8.h +++ b/include/v8.h @@ -6173,6 +6173,14 @@ Float64Array* Float64Array::Cast(v8::Value* value) { } +Uint8ClampedArray* Uint8ClampedArray::Cast(v8::Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + + Function* Function::Cast(v8::Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/src/api.cc b/src/api.cc index 4cdd77e..920ca87 100644 --- a/src/api.cc +++ b/src/api.cc @@ -6206,6 +6206,9 @@ i::Handle NewTypedArray( obj->set_buffer(*buffer); + obj->set_weak_next(buffer->weak_first_array()); + buffer->set_weak_first_array(*obj); + i::Handle byte_offset_object = isolate->factory()->NewNumber( static_cast(byte_offset)); obj->set_byte_offset(*byte_offset_object); diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 72d144f..a51a9b1 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1324,8 +1324,7 @@ void Genesis::InitializeExperimentalGlobal() { Handle array_buffer_fun = InstallFunction( global, "ArrayBuffer", JS_ARRAY_BUFFER_TYPE, - JSArrayBuffer::kSize + - v8::ArrayBuffer::kInternalFieldCount * kPointerSize, + JSArrayBuffer::kSizeWithInternalFields, isolate()->initial_object_prototype(), Builtins::kIllegal, true, true); native_context()->set_array_buffer_fun(*array_buffer_fun); diff --git a/src/heap.cc b/src/heap.cc index 7e1688b..2817fcb 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -180,6 +180,7 @@ Heap::Heap() memset(roots_, 0, sizeof(roots_[0]) * kRootListLength); native_contexts_list_ = NULL; + array_buffers_list_ = Smi::FromInt(0); mark_compact_collector_.heap_ = this; external_string_table_.heap_ = this; // Put a dummy entry in the remembered pages so we can find the list the @@ -1539,11 +1540,6 @@ static Object* ProcessFunctionWeakReferences(Heap* heap, void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { - Object* undefined = undefined_value(); - Object* head = undefined; - Context* tail = NULL; - Object* candidate = native_contexts_list_; - // We don't record weak slots during marking or scavenges. // Instead we do it once when we complete mark-compact cycle. // Note that write barrier has no effect if we are already in the middle of @@ -1551,6 +1547,16 @@ void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { bool record_slots = gc_state() == MARK_COMPACT && mark_compact_collector()->is_compacting(); + ProcessArrayBuffers(retainer, record_slots); + ProcessNativeContexts(retainer, record_slots); +} + +void Heap::ProcessNativeContexts(WeakObjectRetainer* retainer, + bool record_slots) { + Object* undefined = undefined_value(); + Object* head = undefined; + Context* tail = NULL; + Object* candidate = native_contexts_list_; while (candidate != undefined) { // Check whether to keep the candidate in the list. @@ -1619,6 +1625,101 @@ void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { } +template +struct WeakListVisitor; + + +template +static Object* VisitWeakList(Object* list, + MarkCompactCollector* collector, + WeakObjectRetainer* retainer, bool record_slots) { + Object* head = Smi::FromInt(0); + T* tail = NULL; + while (list != Smi::FromInt(0)) { + Object* retained = retainer->RetainAs(list); + if (retained != NULL) { + if (head == Smi::FromInt(0)) { + head = retained; + } else { + ASSERT(tail != NULL); + WeakListVisitor::set_weak_next(tail, retained); + if (record_slots) { + Object** next_slot = + HeapObject::RawField(tail, WeakListVisitor::kWeakNextOffset); + collector->RecordSlot(next_slot, next_slot, retained); + } + } + tail = reinterpret_cast(retained); + WeakListVisitor::VisitLiveObject( + tail, collector, retainer, record_slots); + } + list = WeakListVisitor::get_weak_next(reinterpret_cast(list)); + } + if (tail != NULL) { + tail->set_weak_next(Smi::FromInt(0)); + } + return head; +} + + +template<> +struct WeakListVisitor { + static void set_weak_next(JSTypedArray* obj, Object* next) { + obj->set_weak_next(next); + } + + static Object* get_weak_next(JSTypedArray* obj) { + return obj->weak_next(); + } + + static void VisitLiveObject(JSTypedArray* obj, + MarkCompactCollector* collector, + WeakObjectRetainer* retainer, + bool record_slots) {} + + static const int kWeakNextOffset = JSTypedArray::kWeakNextOffset; +}; + + +template<> +struct WeakListVisitor { + static void set_weak_next(JSArrayBuffer* obj, Object* next) { + obj->set_weak_next(next); + } + + static Object* get_weak_next(JSArrayBuffer* obj) { + return obj->weak_next(); + } + + static void VisitLiveObject(JSArrayBuffer* array_buffer, + MarkCompactCollector* collector, + WeakObjectRetainer* retainer, + bool record_slots) { + Object* typed_array_obj = + VisitWeakList(array_buffer->weak_first_array(), + collector, retainer, record_slots); + array_buffer->set_weak_first_array(typed_array_obj); + if (typed_array_obj != Smi::FromInt(0) && record_slots) { + Object** slot = HeapObject::RawField( + array_buffer, JSArrayBuffer::kWeakFirstArrayOffset); + collector->RecordSlot(slot, slot, typed_array_obj); + } + } + + static const int kWeakNextOffset = JSArrayBuffer::kWeakNextOffset; +}; + + +void Heap::ProcessArrayBuffers(WeakObjectRetainer* retainer, + bool record_slots) { + Object* array_buffer_obj = + VisitWeakList(array_buffers_list(), + mark_compact_collector(), + retainer, record_slots); + set_array_buffers_list(array_buffer_obj); +} + + void Heap::VisitExternalResources(v8::ExternalResourceVisitor* visitor) { DisallowHeapAllocation no_allocation; @@ -1794,6 +1895,14 @@ class ScavengingVisitor : public StaticVisitorBase { &ObjectEvacuationStrategy:: Visit); + table_.Register(kVisitJSArrayBuffer, + &ObjectEvacuationStrategy:: + Visit); + + table_.Register(kVisitJSTypedArray, + &ObjectEvacuationStrategy:: + Visit); + table_.Register(kVisitJSRegExp, &ObjectEvacuationStrategy:: Visit); diff --git a/src/heap.h b/src/heap.h index d3daaf7..92cc660 100644 --- a/src/heap.h +++ b/src/heap.h @@ -1352,6 +1352,12 @@ class Heap { } Object* native_contexts_list() { return native_contexts_list_; } + void set_array_buffers_list(Object* object) { + array_buffers_list_ = object; + } + Object* array_buffers_list() { return array_buffers_list_; } + + // Number of mark-sweeps. unsigned int ms_count() { return ms_count_; } @@ -2022,6 +2028,8 @@ class Heap { Object* native_contexts_list_; + Object* array_buffers_list_; + StoreBufferRebuilder store_buffer_rebuilder_; struct StringTypeTable { @@ -2165,6 +2173,9 @@ class Heap { // Code to be run before and after mark-compact. void MarkCompactPrologue(); + void ProcessNativeContexts(WeakObjectRetainer* retainer, bool record_slots); + void ProcessArrayBuffers(WeakObjectRetainer* retainer, bool record_slots); + // Record statistics before and after garbage collection. void ReportStatisticsBeforeGC(); void ReportStatisticsAfterGC(); diff --git a/src/objects-inl.h b/src/objects-inl.h index 69b1ed9..e60f0f3 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -5330,10 +5330,15 @@ void JSArrayBuffer::set_is_external(bool value) { } +ACCESSORS(JSArrayBuffer, weak_next, Object, kWeakNextOffset) +ACCESSORS(JSArrayBuffer, weak_first_array, Object, kWeakFirstArrayOffset) + + ACCESSORS(JSTypedArray, buffer, Object, kBufferOffset) ACCESSORS(JSTypedArray, byte_offset, Object, kByteOffsetOffset) ACCESSORS(JSTypedArray, byte_length, Object, kByteLengthOffset) ACCESSORS(JSTypedArray, length, Object, kLengthOffset) +ACCESSORS(JSTypedArray, weak_next, Object, kWeakNextOffset) ACCESSORS(JSRegExp, data, Object, kDataOffset) diff --git a/src/objects-printer.cc b/src/objects-printer.cc index a374c37..357d984 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -540,8 +540,6 @@ static const char* TypeToString(InstanceType type) { case JS_FUNCTION_TYPE: return "JS_FUNCTION"; case CODE_TYPE: return "CODE"; case JS_ARRAY_TYPE: return "JS_ARRAY"; - case JS_ARRAY_BUFFER_TYPE: return "JS_ARRAY_BUFFER"; - case JS_TYPED_ARRAY_TYPE: return "JS_TYPED_ARRAY"; case JS_PROXY_TYPE: return "JS_PROXY"; case JS_WEAK_MAP_TYPE: return "JS_WEAK_MAP"; case JS_REGEXP_TYPE: return "JS_REGEXP"; @@ -549,6 +547,8 @@ static const char* TypeToString(InstanceType type) { case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT"; case JS_BUILTINS_OBJECT_TYPE: return "JS_BUILTINS_OBJECT"; case JS_GLOBAL_PROXY_TYPE: return "JS_GLOBAL_PROXY"; + case JS_TYPED_ARRAY_TYPE: return "JS_TYPED_ARRAY"; + case JS_ARRAY_BUFFER_TYPE: return "JS_ARRAY_BUFFER"; case FOREIGN_TYPE: return "FOREIGN"; case JS_MESSAGE_OBJECT_TYPE: return "JS_MESSAGE_OBJECT_TYPE"; #define MAKE_STRUCT_CASE(NAME, Name, name) case NAME##_TYPE: return #NAME; diff --git a/src/objects-visiting-inl.h b/src/objects-visiting-inl.h index f83f00f..829eab8 100644 --- a/src/objects-visiting-inl.h +++ b/src/objects-visiting-inl.h @@ -79,6 +79,10 @@ void StaticNewSpaceVisitor::Initialize() { table_.Register(kVisitJSFunction, &VisitJSFunction); + table_.Register(kVisitJSArrayBuffer, &VisitJSArrayBuffer); + + table_.Register(kVisitJSTypedArray, &VisitJSTypedArray); + table_.Register(kVisitFreeSpace, &VisitFreeSpace); table_.Register(kVisitJSWeakMap, &JSObjectVisitor::Visit); @@ -99,6 +103,43 @@ void StaticNewSpaceVisitor::Initialize() { template +int StaticNewSpaceVisitor::VisitJSArrayBuffer( + Map* map, HeapObject* object) { + Heap* heap = map->GetHeap(); + + STATIC_ASSERT( + JSArrayBuffer::kWeakFirstArrayOffset == + JSArrayBuffer::kWeakNextOffset + kPointerSize); + VisitPointers( + heap, + HeapObject::RawField(object, JSArrayBuffer::BodyDescriptor::kStartOffset), + HeapObject::RawField(object, JSArrayBuffer::kWeakNextOffset)); + VisitPointers( + heap, + HeapObject::RawField(object, + JSArrayBuffer::kWeakNextOffset + 2 * kPointerSize), + HeapObject::RawField(object, JSArrayBuffer::kSizeWithInternalFields)); + return JSArrayBuffer::kSizeWithInternalFields; +} + + +template +int StaticNewSpaceVisitor::VisitJSTypedArray( + Map* map, HeapObject* object) { + VisitPointers( + map->GetHeap(), + HeapObject::RawField(object, JSTypedArray::BodyDescriptor::kStartOffset), + HeapObject::RawField(object, JSTypedArray::kWeakNextOffset)); + VisitPointers( + map->GetHeap(), + HeapObject::RawField(object, + JSTypedArray::kWeakNextOffset + kPointerSize), + HeapObject::RawField(object, JSTypedArray::kSize)); + return JSTypedArray::kSize; +} + + +template void StaticMarkingVisitor::Initialize() { table_.Register(kVisitShortcutCandidate, &FixedBodyVisitor::Initialize() { table_.Register(kVisitJSFunction, &VisitJSFunction); + table_.Register(kVisitJSArrayBuffer, &VisitJSArrayBuffer); + + table_.Register(kVisitJSTypedArray, &VisitJSTypedArray); + // Registration for kVisitJSRegExp is done by StaticVisitor. table_.Register(kVisitPropertyCell, @@ -401,6 +446,41 @@ void StaticMarkingVisitor::VisitJSRegExp( template +void StaticMarkingVisitor::VisitJSArrayBuffer( + Map* map, HeapObject* object) { + Heap* heap = map->GetHeap(); + + STATIC_ASSERT( + JSArrayBuffer::kWeakFirstArrayOffset == + JSArrayBuffer::kWeakNextOffset + kPointerSize); + StaticVisitor::VisitPointers( + heap, + HeapObject::RawField(object, JSArrayBuffer::BodyDescriptor::kStartOffset), + HeapObject::RawField(object, JSArrayBuffer::kWeakNextOffset)); + StaticVisitor::VisitPointers( + heap, + HeapObject::RawField(object, + JSArrayBuffer::kWeakNextOffset + 2 * kPointerSize), + HeapObject::RawField(object, JSArrayBuffer::kSizeWithInternalFields)); +} + + +template +void StaticMarkingVisitor::VisitJSTypedArray( + Map* map, HeapObject* object) { + StaticVisitor::VisitPointers( + map->GetHeap(), + HeapObject::RawField(object, JSTypedArray::BodyDescriptor::kStartOffset), + HeapObject::RawField(object, JSTypedArray::kWeakNextOffset)); + StaticVisitor::VisitPointers( + map->GetHeap(), + HeapObject::RawField(object, + JSTypedArray::kWeakNextOffset + kPointerSize), + HeapObject::RawField(object, JSTypedArray::kSize)); +} + + +template void StaticMarkingVisitor::MarkMapContents( Heap* heap, Map* map) { // Make sure that the back pointer stored either in the map itself or diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc index 7b5c8be..4bf2804 100644 --- a/src/objects-visiting.cc +++ b/src/objects-visiting.cc @@ -134,6 +134,12 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case FILLER_TYPE: return kVisitDataObjectGeneric; + case JS_ARRAY_BUFFER_TYPE: + return kVisitJSArrayBuffer; + + case JS_TYPED_ARRAY_TYPE: + return kVisitJSTypedArray; + case JS_OBJECT_TYPE: case JS_CONTEXT_EXTENSION_OBJECT_TYPE: case JS_GENERATOR_OBJECT_TYPE: @@ -145,8 +151,6 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( case JS_GLOBAL_OBJECT_TYPE: case JS_BUILTINS_OBJECT_TYPE: case JS_MESSAGE_OBJECT_TYPE: - case JS_ARRAY_BUFFER_TYPE: - case JS_TYPED_ARRAY_TYPE: return GetVisitorIdForSize(kVisitJSObject, kVisitJSObjectGeneric, instance_size); diff --git a/src/objects-visiting.h b/src/objects-visiting.h index d4a2ed2..c4d1cc3 100644 --- a/src/objects-visiting.h +++ b/src/objects-visiting.h @@ -92,6 +92,8 @@ class StaticVisitorBase : public AllStatic { V(SharedFunctionInfo) \ V(JSFunction) \ V(JSWeakMap) \ + V(JSArrayBuffer) \ + V(JSTypedArray) \ V(JSRegExp) // For data objects, JS objects and structs along with generic visitor which @@ -333,6 +335,9 @@ class StaticNewSpaceVisitor : public StaticVisitorBase { return FreeSpace::cast(object)->Size(); } + INLINE(static int VisitJSArrayBuffer(Map* map, HeapObject* object)); + INLINE(static int VisitJSTypedArray(Map* map, HeapObject* object)); + class DataObjectVisitor { public: template @@ -407,6 +412,8 @@ class StaticMarkingVisitor : public StaticVisitorBase { INLINE(static void VisitSharedFunctionInfo(Map* map, HeapObject* object)); INLINE(static void VisitJSFunction(Map* map, HeapObject* object)); INLINE(static void VisitJSRegExp(Map* map, HeapObject* object)); + INLINE(static void VisitJSArrayBuffer(Map* map, HeapObject* object)); + INLINE(static void VisitJSTypedArray(Map* map, HeapObject* object)); INLINE(static void VisitNativeContext(Map* map, HeapObject* object)); // Mark pointers in a Map and its TransitionArray together, possibly diff --git a/src/objects.h b/src/objects.h index ec78d02..0c8ee71 100644 --- a/src/objects.h +++ b/src/objects.h @@ -8786,6 +8786,12 @@ class JSArrayBuffer: public JSObject { inline bool is_external(); inline void set_is_external(bool value); + // [weak_next]: linked list of array buffers. + DECL_ACCESSORS(weak_next, Object) + + // [weak_first_array]: weak linked list of typed arrays. + DECL_ACCESSORS(weak_first_array, Object) + // Casting. static inline JSArrayBuffer* cast(Object* obj); @@ -8796,7 +8802,12 @@ class JSArrayBuffer: public JSObject { static const int kBackingStoreOffset = JSObject::kHeaderSize; static const int kByteLengthOffset = kBackingStoreOffset + kPointerSize; static const int kFlagOffset = kByteLengthOffset + kPointerSize; - static const int kSize = kFlagOffset + kPointerSize; + static const int kWeakNextOffset = kFlagOffset + kPointerSize; + static const int kWeakFirstArrayOffset = kWeakNextOffset + kPointerSize; + static const int kSize = kWeakFirstArrayOffset + kPointerSize; + + static const int kSizeWithInternalFields = + kSize + v8::ArrayBuffer::kInternalFieldCount * kPointerSize; private: // Bit position in a flag @@ -8820,6 +8831,9 @@ class JSTypedArray: public JSObject { // [length]: length of typed array in elements. DECL_ACCESSORS(length, Object) + // [weak_next]: linked list of typed arrays over the same array buffer. + DECL_ACCESSORS(weak_next, Object) + // Casting. static inline JSTypedArray* cast(Object* obj); @@ -8834,7 +8848,8 @@ class JSTypedArray: public JSObject { static const int kByteOffsetOffset = kBufferOffset + kPointerSize; static const int kByteLengthOffset = kByteOffsetOffset + kPointerSize; static const int kLengthOffset = kByteLengthOffset + kPointerSize; - static const int kSize = kLengthOffset + kPointerSize; + static const int kWeakNextOffset = kLengthOffset + kPointerSize; + static const int kSize = kWeakNextOffset + kPointerSize; private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSTypedArray); diff --git a/src/runtime.cc b/src/runtime.cc index 0516c9c..e8d6ea9 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -687,6 +687,10 @@ void Runtime::SetupArrayBuffer(Isolate* isolate, isolate->factory()->NewNumberFromSize(allocated_length); CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); array_buffer->set_byte_length(*byte_length); + + array_buffer->set_weak_next(isolate->heap()->array_buffers_list()); + isolate->heap()->set_array_buffers_list(*array_buffer); + array_buffer->set_weak_first_array(Smi::FromInt(0)); } @@ -855,6 +859,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) { Handle length_obj = isolate->factory()->NewNumberFromSize(length); holder->set_length(*length_obj); + holder->set_weak_next(buffer->weak_first_array()); + buffer->set_weak_first_array(*holder); Handle elements = isolate->factory()->NewExternalArray( diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 75e4158..0811093 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -103,7 +103,8 @@ 'test-unbound-queue.cc', 'test-utils.cc', 'test-version.cc', - 'test-weakmaps.cc' + 'test-weakmaps.cc', + 'test-weaktypedarrays.cc' ], 'conditions': [ ['v8_target_arch=="ia32"', { diff --git a/test/cctest/test-weaktypedarrays.cc b/test/cctest/test-weaktypedarrays.cc new file mode 100644 index 0000000..aef610d --- /dev/null +++ b/test/cctest/test-weaktypedarrays.cc @@ -0,0 +1,380 @@ +// Copyright 2013 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 + +#include "v8.h" +#include "api.h" +#include "heap.h" +#include "objects.h" + +#include "cctest.h" + +using namespace v8::internal; + +static Isolate* GetIsolateFrom(LocalContext* context) { + return reinterpret_cast((*context)->GetIsolate()); +} + + +static int CountArrayBuffersInWeakList(Heap* heap) { + int count = 0; + for (Object* o = heap->array_buffers_list(); + o != Smi::FromInt(0); + o = JSArrayBuffer::cast(o)->weak_next()) { + count++; + } + return count; +} + + +static bool HasArrayBufferInWeakList(Heap* heap, JSArrayBuffer* ab) { + for (Object* o = heap->array_buffers_list(); + o != Smi::FromInt(0); + o = JSArrayBuffer::cast(o)->weak_next()) { + if (ab == o) return true; + } + return false; +} + + +static int CountTypedArrays(JSArrayBuffer* array_buffer) { + int count = 0; + for (Object* o = array_buffer->weak_first_array(); + o != Smi::FromInt(0); + o = JSTypedArray::cast(o)->weak_next()) { + count++; + } + + return count; +} + +static bool HasTypedArrayInWeakList(JSArrayBuffer* array_buffer, + JSTypedArray* ta) { + for (Object* o = array_buffer->weak_first_array(); + o != Smi::FromInt(0); + o = JSTypedArray::cast(o)->weak_next()) { + if (ta == o) return true; + } + return false; +} + + +TEST(WeakArrayBuffersFromApi) { + v8::V8::Initialize(); + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); + { + v8::HandleScope s1(context->GetIsolate()); + v8::Handle ab1 = v8::ArrayBuffer::New(256); + { + v8::HandleScope s2(context->GetIsolate()); + v8::Handle ab2 = v8::ArrayBuffer::New(128); + + Handle iab1 = v8::Utils::OpenHandle(*ab1); + Handle iab2 = v8::Utils::OpenHandle(*ab2); + CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap())); + CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab1)); + CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab2)); + } + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); + { + HandleScope scope2(isolate); + Handle iab1 = v8::Utils::OpenHandle(*ab1); + + CHECK(HasArrayBufferInWeakList(isolate->heap(), *iab1)); + } + } + + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); +} + + +TEST(WeakArrayBuffersFromScript) { + v8::V8::Initialize(); + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + + for (int i = 1; i <= 3; i++) { + // Create 3 array buffers, make i-th of them garbage, + // validate correct state of array buffer weak list. + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); + { + v8::HandleScope scope(context->GetIsolate()); + + { + v8::HandleScope s1(context->GetIsolate()); + CompileRun("var ab1 = new ArrayBuffer(256);" + "var ab2 = new ArrayBuffer(256);" + "var ab3 = new ArrayBuffer(256);"); + v8::Handle ab1( + v8::ArrayBuffer::Cast(*CompileRun("ab1"))); + v8::Handle ab2( + v8::ArrayBuffer::Cast(*CompileRun("ab2"))); + v8::Handle ab3( + v8::ArrayBuffer::Cast(*CompileRun("ab3"))); + + CHECK_EQ(3, CountArrayBuffersInWeakList(isolate->heap())); + CHECK(HasArrayBufferInWeakList(isolate->heap(), + *v8::Utils::OpenHandle(*ab1))); + CHECK(HasArrayBufferInWeakList(isolate->heap(), + *v8::Utils::OpenHandle(*ab2))); + CHECK(HasArrayBufferInWeakList(isolate->heap(), + *v8::Utils::OpenHandle(*ab3))); + } + + i::ScopedVector source(1024); + i::OS::SNPrintF(source, "ab%d = null;", i); + CompileRun(source.start()); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + + CHECK_EQ(2, CountArrayBuffersInWeakList(isolate->heap())); + + { + v8::HandleScope s2(context->GetIsolate()); + for (int j = 1; j <= 3; j++) { + if (j == i) continue; + i::OS::SNPrintF(source, "ab%d", j); + v8::Handle ab( + v8::ArrayBuffer::Cast(*CompileRun(source.start()))); + CHECK(HasArrayBufferInWeakList(isolate->heap(), + *v8::Utils::OpenHandle(*ab))); + } + } + + CompileRun("ab1 = null; ab2 = null; ab3 = null;"); + } + + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + CHECK_EQ(0, CountArrayBuffersInWeakList(isolate->heap())); + } +} + +template +void TestTypedArrayFromApi() { + v8::V8::Initialize(); + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + + v8::HandleScope s1(context->GetIsolate()); + v8::Handle ab = v8::ArrayBuffer::New(2048); + Handle iab = v8::Utils::OpenHandle(*ab); + { + v8::HandleScope s2(context->GetIsolate()); + v8::Handle ta1 = TypedArray::New(ab, 0, 256); + { + v8::HandleScope s3(context->GetIsolate()); + v8::Handle ta2 = TypedArray::New(ab, 0, 128); + + Handle ita1 = v8::Utils::OpenHandle(*ta1); + Handle ita2 = v8::Utils::OpenHandle(*ta2); + CHECK_EQ(2, CountTypedArrays(*iab)); + CHECK(HasTypedArrayInWeakList(*iab, *ita1)); + CHECK(HasTypedArrayInWeakList(*iab, *ita2)); + } + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + CHECK_EQ(1, CountTypedArrays(*iab)); + Handle ita1 = v8::Utils::OpenHandle(*ta1); + CHECK(HasTypedArrayInWeakList(*iab, *ita1)); + } + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + + CHECK_EQ(0, CountTypedArrays(*iab)); +} + + +TEST(Uint8ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Int8ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Uint16ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Int16ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Uint32ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Int32ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Float32ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Float64ArrayFromApi) { + TestTypedArrayFromApi(); +} + + +TEST(Uint8ClampedArrayFromApi) { + TestTypedArrayFromApi(); +} + +template +static void TestTypedArrayFromScript(const char* constructor) { + v8::V8::Initialize(); + LocalContext context; + Isolate* isolate = GetIsolateFrom(&context); + v8::HandleScope scope(context->GetIsolate()); + CompileRun("var ab = new ArrayBuffer(2048);"); + for (int i = 1; i <= 3; i++) { + // Create 3 typed arrays, make i-th of them garbage, + // validate correct state of typed array weak list. + v8::HandleScope s0(context->GetIsolate()); + i::ScopedVector source(2048); + + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); + + { + v8::HandleScope s1(context->GetIsolate()); + i::OS::SNPrintF(source, + "var ta1 = new %s(ab);" + "var ta2 = new %s(ab);" + "var ta3 = new %s(ab)", + constructor, constructor, constructor); + + CompileRun(source.start()); + v8::Handle ab(v8::ArrayBuffer::Cast(*CompileRun("ab"))); + v8::Handle ta1(TypedArray::Cast(*CompileRun("ta1"))); + v8::Handle ta2(TypedArray::Cast(*CompileRun("ta2"))); + v8::Handle ta3(TypedArray::Cast(*CompileRun("ta3"))); + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); + Handle iab = v8::Utils::OpenHandle(*ab); + CHECK_EQ(3, CountTypedArrays(*iab)); + CHECK(HasTypedArrayInWeakList(*iab, *v8::Utils::OpenHandle(*ta1))); + CHECK(HasTypedArrayInWeakList(*iab, *v8::Utils::OpenHandle(*ta2))); + CHECK(HasTypedArrayInWeakList(*iab, *v8::Utils::OpenHandle(*ta3))); + } + + i::OS::SNPrintF(source, "ta%d = null;", i); + CompileRun(source.start()); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); + + { + v8::HandleScope s2(context->GetIsolate()); + v8::Handle ab(v8::ArrayBuffer::Cast(*CompileRun("ab"))); + Handle iab = v8::Utils::OpenHandle(*ab); + CHECK_EQ(2, CountTypedArrays(*iab)); + for (int j = 1; j <= 3; j++) { + if (j == i) continue; + i::OS::SNPrintF(source, "ta%d", j); + v8::Handle ta( + TypedArray::Cast(*CompileRun(source.start()))); + CHECK(HasTypedArrayInWeakList(*iab, *v8::Utils::OpenHandle(*ta))); + } + } + + CompileRun("ta1 = null; ta2 = null; ta3 = null;"); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + isolate->heap()->CollectAllGarbage(Heap::kNoGCFlags); + + CHECK_EQ(1, CountArrayBuffersInWeakList(isolate->heap())); + + { + v8::HandleScope s3(context->GetIsolate()); + v8::Handle ab(v8::ArrayBuffer::Cast(*CompileRun("ab"))); + Handle iab = v8::Utils::OpenHandle(*ab); + CHECK_EQ(0, CountTypedArrays(*iab)); + } + } +} + + +TEST(Uint8ArrayFromScript) { + TestTypedArrayFromScript("Uint8Array"); +} + + +TEST(Int8ArrayFromScript) { + TestTypedArrayFromScript("Int8Array"); +} + + +TEST(Uint16ArrayFromScript) { + TestTypedArrayFromScript("Uint16Array"); +} + + +TEST(Int16ArrayFromScript) { + TestTypedArrayFromScript("Int16Array"); +} + + +TEST(Uint32ArrayFromScript) { + TestTypedArrayFromScript("Uint32Array"); +} + + +TEST(Int32ArrayFromScript) { + TestTypedArrayFromScript("Int32Array"); +} + + +TEST(Float32ArrayFromScript) { + TestTypedArrayFromScript("Float32Array"); +} + + +TEST(Float64ArrayFromScript) { + TestTypedArrayFromScript("Float64Array"); +} + + +TEST(Uint8ClampedArrayFromScript) { + TestTypedArrayFromScript("Uint8ClampedArray"); +} + -- 2.7.4