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
This commit is contained in:
dslomov@chromium.org 2013-04-25 12:02:23 +00:00
parent 8f1fc88f8c
commit 44f2d534b1
11 changed files with 246 additions and 33 deletions

View File

@ -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<ArrayBuffer> 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<ArrayBuffer> 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<ArrayBuffer*>(value);
}
Function* Function::Cast(v8::Value* value) {
#ifdef V8_ENABLE_CHECKS
CheckCast(value);

View File

@ -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<i::Object> 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<Object> 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<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
return static_cast<size_t>(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<i::JSArrayBuffer> obj = Utils::OpenHandle(this);
return obj->backing_store();
}
Local<ArrayBuffer> 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<i::JSArrayBuffer> obj =
isolate->factory()->NewJSArrayBuffer();
i::Runtime::SetupArrayBufferAllocatingData(isolate, obj, byte_length);
return Utils::ToLocal(obj);
}
Local<ArrayBuffer> 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<i::JSArrayBuffer> obj =
isolate->factory()->NewJSArrayBuffer();
i::Runtime::SetupArrayBuffer(isolate, obj, data, byte_length);
return Utils::ToLocal(obj);
}
Local<Symbol> v8::Symbol::New(Isolate* isolate) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
EnsureInitializedForIsolate(i_isolate, "v8::Symbol::New()");

View File

@ -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<v8::internal::JSObject> obj);
static inline Local<Array> ToLocal(
v8::internal::Handle<v8::internal::JSArray> obj);
static inline Local<ArrayBuffer> ToLocal(
v8::internal::Handle<v8::internal::JSArrayBuffer> obj);
static inline Local<Message> MessageToLocal(
v8::internal::Handle<v8::internal::Object> obj);
static inline Local<StackTrace> 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)

View File

@ -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<JSFunction> 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

View File

@ -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,

View File

@ -1046,6 +1046,16 @@ void Factory::EnsureCanContainElements(Handle<JSArray> array,
}
Handle<JSArrayBuffer> 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<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
Handle<Object> prototype) {
CALL_HEAP_FUNCTION(

View File

@ -313,6 +313,8 @@ class Factory {
uint32_t length,
EnsureElementsMode mode);
Handle<JSArrayBuffer> NewJSArrayBuffer();
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
// Change the type of the argument into a JS object/function and reinitialize.

View File

@ -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.

View File

@ -662,6 +662,47 @@ static void ArrayBufferWeakCallback(v8::Isolate* external_isolate,
}
bool Runtime::SetupArrayBuffer(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer,
void* data,
size_t allocated_length) {
array_buffer->set_backing_store(data);
Handle<Object> byte_length =
isolate->factory()->NewNumber(static_cast<double>(allocated_length));
CHECK(byte_length->IsSmi() || byte_length->IsHeapNumber());
array_buffer->set_byte_length(*byte_length);
return true;
}
bool Runtime::SetupArrayBufferAllocatingData(
Isolate* isolate,
Handle<JSArrayBuffer> 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<v8::Isolate*>(isolate);
v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New(
external_isolate, v8::Utils::ToLocal(Handle<Object>::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<size_t>(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<Object>(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<double>(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<v8::Isolate*>(isolate);
v8::Persistent<v8::Value> weak_handle = v8::Persistent<v8::Value>::New(
external_isolate, v8::Utils::ToLocal(Handle<Object>::cast(holder)));
weak_handle.MakeWeak(external_isolate, data, ArrayBufferWeakCallback);
weak_handle.MarkIndependent(external_isolate);
isolate->heap()->AdjustAmountOfExternalAllocatedMemory(allocated_length);
return *holder;
}

View File

@ -748,6 +748,16 @@ class Runtime : public AllStatic {
Handle<Object> object,
Handle<Object> key);
static bool SetupArrayBuffer(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer,
void* data,
size_t allocated_length);
static bool SetupArrayBufferAllocatingData(
Isolate* isolate,
Handle<JSArrayBuffer> array_buffer,
size_t allocated_length);
// Helper functions used stubs.
static void PerformGC(Object* result);

View File

@ -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<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(1024);
HEAP->CollectAllGarbage(i::Heap::kNoGCFlags);
CHECK_EQ(1024, ab->ByteLength());
uint8_t* data = static_cast<uint8_t*>(ab->Data());
ASSERT(data != NULL);
env->Global()->Set(v8_str("ab"), ab);
v8::Handle<v8::Value> 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<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::Cast(*result);
CHECK_EQ(2, ab1->ByteLength());
uint8_t* ab1_data = static_cast<uint8_t*>(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<v8::ArrayBuffer> 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());