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:
parent
8f1fc88f8c
commit
44f2d534b1
47
include/v8.h
47
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<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);
|
||||
|
50
src/api.cc
50
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<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()");
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user