From 44f2d534b162edc5da6f03d73a44c993b7dcdeb3 Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Thu, 25 Apr 2013 12:02:23 +0000 Subject: [PATCH] First cut at API for ES6 ArrayBuffers R=rossberg@chromium.org BUG= Review URL: https://codereview.chromium.org/13958007 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14438 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 47 ++++++++++++++++++++++++++- src/api.cc | 50 +++++++++++++++++++++++++++++ src/api.h | 4 +++ src/bootstrapper.cc | 10 +++--- src/contexts.h | 2 ++ src/factory.cc | 10 ++++++ src/factory.h | 2 ++ src/heap.h | 6 ++++ src/runtime.cc | 71 +++++++++++++++++++++++++---------------- src/runtime.h | 10 ++++++ test/cctest/test-api.cc | 67 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 246 insertions(+), 33 deletions(-) diff --git a/include/v8.h b/include/v8.h index e906bd9e2..d8a8a7833 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2003,6 +2003,43 @@ class V8EXPORT Function : public Object { }; +/** + * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5). + * This API is experimental and may change significantly. + */ +class V8EXPORT ArrayBuffer : public Object { + public: + /** + * Data length in bytes. + */ + size_t ByteLength() const; + /** + * Raw pointer to the array buffer data + */ + void* Data() const; + + /** + * Create a new ArrayBuffer. Allocate |byte_length| bytes. + * Allocated memory will be owned by a created ArrayBuffer and + * will be deallocated when it is garbage-collected. + */ + static Local New(size_t byte_length); + + /** + * Create a new ArrayBuffer over an existing memory block. + * The memory block will not be reclaimed when a created ArrayBuffer + * is garbage-collected. + */ + static Local New(void* data, size_t byte_length); + + V8_INLINE(static ArrayBuffer* Cast(Value* obj)); + + private: + ArrayBuffer(); + static void CheckCast(Value* obj); +}; + + /** * An instance of the built-in Date constructor (ECMA-262, 15.9). */ @@ -4499,7 +4536,7 @@ class Internals { static const int kJSObjectHeaderSize = 3 * kApiPointerSize; static const int kFixedArrayHeaderSize = 2 * kApiPointerSize; static const int kContextHeaderSize = 2 * kApiPointerSize; - static const int kContextEmbedderDataIndex = 55; + static const int kContextEmbedderDataIndex = 56; static const int kFullStringRepresentationMask = 0x07; static const int kStringEncodingMask = 0x4; static const int kExternalTwoByteRepresentationTag = 0x02; @@ -5172,6 +5209,14 @@ Array* Array::Cast(v8::Value* value) { } +ArrayBuffer* ArrayBuffer::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 40a5d0875..0ca745a4f 100644 --- a/src/api.cc +++ b/src/api.cc @@ -52,6 +52,7 @@ #include "profile-generator-inl.h" #include "property-details.h" #include "property.h" +#include "runtime.h" #include "runtime-profiler.h" #include "scanner-character-streams.h" #include "snapshot.h" @@ -2745,6 +2746,15 @@ void v8::Array::CheckCast(Value* that) { } +void v8::ArrayBuffer::CheckCast(Value* that) { + if (IsDeadCheck(i::Isolate::Current(), "v8::ArrayBuffer::Cast()")) return; + i::Handle obj = Utils::OpenHandle(that); + ApiCheck(obj->IsJSArrayBuffer(), + "v8::ArrayBuffer::Cast()", + "Could not convert to ArrayBuffer"); +} + + void v8::Date::CheckCast(v8::Value* that) { i::Isolate* isolate = i::Isolate::Current(); if (IsDeadCheck(isolate, "v8::Date::Cast()")) return; @@ -5783,6 +5793,46 @@ Local Array::CloneElementAt(uint32_t index) { } +size_t v8::ArrayBuffer::ByteLength() const { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0; + i::Handle obj = Utils::OpenHandle(this); + return static_cast(obj->byte_length()->Number()); +} + + +void* v8::ArrayBuffer::Data() const { + i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); + if (IsDeadCheck(isolate, "v8::ArrayBuffer::Data()")) return 0; + i::Handle obj = Utils::OpenHandle(this); + return obj->backing_store(); +} + + +Local v8::ArrayBuffer::New(size_t byte_length) { + i::Isolate* isolate = i::Isolate::Current(); + EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(size_t)"); + LOG_API(isolate, "v8::ArrayBuffer::New(size_t)"); + ENTER_V8(isolate); + i::Handle obj = + isolate->factory()->NewJSArrayBuffer(); + i::Runtime::SetupArrayBufferAllocatingData(isolate, obj, byte_length); + return Utils::ToLocal(obj); +} + + +Local v8::ArrayBuffer::New(void* data, size_t byte_length) { + i::Isolate* isolate = i::Isolate::Current(); + EnsureInitializedForIsolate(isolate, "v8::ArrayBuffer::New(void*, size_t)"); + LOG_API(isolate, "v8::ArrayBuffer::New(void*, size_t)"); + ENTER_V8(isolate); + i::Handle obj = + isolate->factory()->NewJSArrayBuffer(); + i::Runtime::SetupArrayBuffer(isolate, obj, data, byte_length); + return Utils::ToLocal(obj); +} + + Local v8::Symbol::New(Isolate* isolate) { i::Isolate* i_isolate = reinterpret_cast(isolate); EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()"); diff --git a/src/api.h b/src/api.h index 0cd16f1f0..f62541dc0 100644 --- a/src/api.h +++ b/src/api.h @@ -170,6 +170,7 @@ class RegisteredExtension { V(RegExp, JSRegExp) \ V(Object, JSObject) \ V(Array, JSArray) \ + V(ArrayBuffer, JSArrayBuffer) \ V(String, String) \ V(Symbol, Symbol) \ V(Script, Object) \ @@ -205,6 +206,8 @@ class Utils { v8::internal::Handle obj); static inline Local ToLocal( v8::internal::Handle obj); + static inline Local ToLocal( + v8::internal::Handle obj); static inline Local MessageToLocal( v8::internal::Handle obj); static inline Local StackTraceToLocal( @@ -275,6 +278,7 @@ MAKE_TO_LOCAL(ToLocal, Symbol, Symbol) MAKE_TO_LOCAL(ToLocal, JSRegExp, RegExp) MAKE_TO_LOCAL(ToLocal, JSObject, Object) MAKE_TO_LOCAL(ToLocal, JSArray, Array) +MAKE_TO_LOCAL(ToLocal, JSArrayBuffer, ArrayBuffer) MAKE_TO_LOCAL(ToLocal, FunctionTemplateInfo, FunctionTemplate) MAKE_TO_LOCAL(ToLocal, ObjectTemplateInfo, ObjectTemplate) MAKE_TO_LOCAL(ToLocal, SignatureInfo, Signature) diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 12f0cdac6..35ae9690b 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1303,10 +1303,12 @@ void Genesis::InitializeExperimentalGlobal() { if (FLAG_harmony_typed_arrays) { { // -- A r r a y B u f f e r - InstallFunction(global, "__ArrayBuffer", JS_ARRAY_BUFFER_TYPE, - JSArrayBuffer::kSize, - isolate()->initial_object_prototype(), - Builtins::kIllegal, true); + Handle array_buffer_fun = + InstallFunction(global, "__ArrayBuffer", JS_ARRAY_BUFFER_TYPE, + JSArrayBuffer::kSize, + isolate()->initial_object_prototype(), + Builtins::kIllegal, true); + native_context()->set_array_buffer_fun(*array_buffer_fun); } { // -- T y p e d A r r a y s diff --git a/src/contexts.h b/src/contexts.h index abeb8121c..0024e13d6 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -123,6 +123,7 @@ enum BindingFlags { V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \ V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \ V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \ + V(ARRAY_BUFFER_FUN_INDEX, JSFunction, array_buffer_fun) \ V(FUNCTION_MAP_INDEX, Map, function_map) \ V(STRICT_MODE_FUNCTION_MAP_INDEX, Map, strict_mode_function_map) \ V(FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX, Map, function_without_prototype_map) \ @@ -276,6 +277,7 @@ class Context: public FixedArray { GLOBAL_EVAL_FUN_INDEX, INSTANTIATE_FUN_INDEX, CONFIGURE_INSTANCE_FUN_INDEX, + ARRAY_BUFFER_FUN_INDEX, MESSAGE_LISTENERS_INDEX, MAKE_MESSAGE_FUN_INDEX, GET_STACK_TRACE_LINE_INDEX, diff --git a/src/factory.cc b/src/factory.cc index e668e2797..f36006c11 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1046,6 +1046,16 @@ void Factory::EnsureCanContainElements(Handle array, } +Handle Factory::NewJSArrayBuffer() { + JSFunction* array_buffer_fun = + isolate()->context()->native_context()->array_buffer_fun(); + CALL_HEAP_FUNCTION( + isolate(), + isolate()->heap()->AllocateJSObject(array_buffer_fun), + JSArrayBuffer); +} + + Handle Factory::NewJSProxy(Handle handler, Handle prototype) { CALL_HEAP_FUNCTION( diff --git a/src/factory.h b/src/factory.h index b6bfa8ae4..caac78df4 100644 --- a/src/factory.h +++ b/src/factory.h @@ -313,6 +313,8 @@ class Factory { uint32_t length, EnsureElementsMode mode); + Handle NewJSArrayBuffer(); + Handle NewJSProxy(Handle handler, Handle prototype); // Change the type of the argument into a JS object/function and reinitialize. diff --git a/src/heap.h b/src/heap.h index 4cd63442f..91948389b 100644 --- a/src/heap.h +++ b/src/heap.h @@ -693,6 +693,12 @@ class Heap { // Please note this does not perform a garbage collection. MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function); + // Allocates a JS ArrayBuffer object. + // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation + // failed. + // Please note this does not perform a garbage collection. + MUST_USE_RESULT MaybeObject* AllocateJSArrayBuffer(); + // Allocates a Harmony proxy or function proxy. // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation // failed. diff --git a/src/runtime.cc b/src/runtime.cc index ff630b8b7..4b61b6919 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -662,6 +662,47 @@ static void ArrayBufferWeakCallback(v8::Isolate* external_isolate, } +bool Runtime::SetupArrayBuffer(Isolate* isolate, + Handle array_buffer, + void* data, + size_t allocated_length) { + array_buffer->set_backing_store(data); + + Handle byte_length = + isolate->factory()->NewNumber(static_cast(allocated_length)); + CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); + array_buffer->set_byte_length(*byte_length); + return true; +} + + +bool Runtime::SetupArrayBufferAllocatingData( + Isolate* isolate, + Handle array_buffer, + size_t allocated_length) { + void* data; + if (allocated_length != 0) { + data = malloc(allocated_length); + if (data == NULL) return false; + memset(data, 0, allocated_length); + } else { + data = NULL; + } + + if (!SetupArrayBuffer(isolate, array_buffer, data, allocated_length)) + return false; + + v8::Isolate* external_isolate = reinterpret_cast(isolate); + v8::Persistent weak_handle = v8::Persistent::New( + external_isolate, v8::Utils::ToLocal(Handle::cast(array_buffer))); + weak_handle.MakeWeak(external_isolate, data, ArrayBufferWeakCallback); + weak_handle.MarkIndependent(external_isolate); + isolate->heap()->AdjustAmountOfExternalAllocatedMemory(allocated_length); + + return true; +} + + RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferInitialize) { HandleScope scope(isolate); ASSERT(args.length() == 2); @@ -685,38 +726,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferInitialize) { allocated_length = static_cast(value); } - void* data; - if (allocated_length != 0) { - data = malloc(allocated_length); - - if (data == NULL) { + if (!Runtime::SetupArrayBufferAllocatingData(isolate, + holder, allocated_length)) { return isolate->Throw(*isolate->factory()-> NewRangeError("invalid_array_buffer_length", HandleVector(NULL, 0))); - } - - memset(data, 0, allocated_length); - } else { - data = NULL; - } - holder->set_backing_store(data); - - Object* byte_length; - { - MaybeObject* maybe_byte_length = - isolate->heap()->NumberFromDouble( - static_cast(allocated_length)); - if (!maybe_byte_length->ToObject(&byte_length)) return maybe_byte_length; } - CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber()); - holder->set_byte_length(byte_length); - - v8::Isolate* external_isolate = reinterpret_cast(isolate); - v8::Persistent weak_handle = v8::Persistent::New( - external_isolate, v8::Utils::ToLocal(Handle::cast(holder))); - weak_handle.MakeWeak(external_isolate, data, ArrayBufferWeakCallback); - weak_handle.MarkIndependent(external_isolate); - isolate->heap()->AdjustAmountOfExternalAllocatedMemory(allocated_length); return *holder; } diff --git a/src/runtime.h b/src/runtime.h index a70d8c498..4f5e18b12 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -748,6 +748,16 @@ class Runtime : public AllStatic { Handle object, Handle key); + static bool SetupArrayBuffer(Isolate* isolate, + Handle array_buffer, + void* data, + size_t allocated_length); + + static bool SetupArrayBufferAllocatingData( + Isolate* isolate, + Handle array_buffer, + size_t allocated_length); + // Helper functions used stubs. static void PerformGC(Object* result); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 1df1211b6..4068a16e3 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2253,6 +2253,73 @@ THREADED_TEST(SymbolProperties) { } +THREADED_TEST(ArrayBuffer) { + i::FLAG_harmony_typed_arrays = true; + + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + Local ab = v8::ArrayBuffer::New(1024); + HEAP->CollectAllGarbage(i::Heap::kNoGCFlags); + + CHECK_EQ(1024, ab->ByteLength()); + uint8_t* data = static_cast(ab->Data()); + ASSERT(data != NULL); + env->Global()->Set(v8_str("ab"), ab); + + v8::Handle result = CompileRun("ab.byteLength"); + CHECK_EQ(1024, result->Int32Value()); + + result = CompileRun("var u8 = new __Uint8Array(ab);" + "u8[0] = 0xFF;" + "u8[1] = 0xAA;" + "u8.length"); + CHECK_EQ(1024, result->Int32Value()); + CHECK_EQ(0xFF, data[0]); + CHECK_EQ(0xAA, data[1]); + data[0] = 0xCC; + data[1] = 0x11; + result = CompileRun("u8[0] + u8[1]"); + CHECK_EQ(0xDD, result->Int32Value()); + + result = CompileRun("var ab1 = new __ArrayBuffer(2);" + "var u8_a = new __Uint8Array(ab1);" + "u8_a[0] = 0xAA;" + "u8_a[1] = 0xFF; u8_a.buffer"); + Local ab1 = v8::ArrayBuffer::Cast(*result); + CHECK_EQ(2, ab1->ByteLength()); + uint8_t* ab1_data = static_cast(ab1->Data()); + CHECK_EQ(0xAA, ab1_data[0]); + CHECK_EQ(0xFF, ab1_data[1]); + ab1_data[0] = 0xCC; + ab1_data[1] = 0x11; + result = CompileRun("u8_a[0] + u8_a[1]"); + CHECK_EQ(0xDD, result->Int32Value()); + + uint8_t* my_data = new uint8_t[100]; + memset(my_data, 0, 100); + Local ab3 = v8::ArrayBuffer::New(my_data, 100); + CHECK_EQ(100, ab3->ByteLength()); + CHECK_EQ(my_data, ab3->Data()); + env->Global()->Set(v8_str("ab3"), ab3); + result = CompileRun("var u8_b = new __Uint8Array(ab3);" + "u8_b[0] = 0xBB;" + "u8_b[1] = 0xCC;" + "u8_b.length"); + CHECK_EQ(100, result->Int32Value()); + CHECK_EQ(0xBB, my_data[0]); + CHECK_EQ(0xCC, my_data[1]); + my_data[0] = 0xCC; + my_data[1] = 0x11; + result = CompileRun("u8_b[0] + u8_b[1]"); + CHECK_EQ(0xDD, result->Int32Value()); + + + delete[] my_data; +} + + THREADED_TEST(HiddenProperties) { LocalContext env; v8::HandleScope scope(env->GetIsolate()); -- 2.34.1