From cbb11dbe6c269a8e78e6e3500ba036c4a718f93c Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Fri, 7 Jun 2013 15:02:20 +0000 Subject: [PATCH] Neutering API for v8::ArrayBuffer R=svenpanne@chromium.org Review URL: https://codereview.chromium.org/16562005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15006 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- include/v8.h | 8 ++++ src/api.cc | 19 ++++++++ src/objects.cc | 15 +++++++ src/objects.h | 6 +++ test/cctest/test-api.cc | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+) diff --git a/include/v8.h b/include/v8.h index 6144c61..68f2de0 100644 --- a/include/v8.h +++ b/include/v8.h @@ -2432,6 +2432,14 @@ class V8EXPORT ArrayBuffer : public Object { bool IsExternal() const; /** + * Neuters this ArrayBuffer and all its views (typed arrays). + * Neutering sets the byte length of the buffer and all typed arrays to zero, + * preventing JavaScript from ever accessing underlying backing store. + * ArrayBuffer should have been externalized. + */ + void Neuter(); + + /** * Pass the ownership of this ArrayBuffer's backing store to * a given ArrayBufferContents. */ diff --git a/src/api.cc b/src/api.cc index 920ca87..ed1e265 100644 --- a/src/api.cc +++ b/src/api.cc @@ -6112,6 +6112,25 @@ void v8::ArrayBuffer::Externalize(ArrayBufferContents* contents) { } +void v8::ArrayBuffer::Neuter() { + i::Handle obj = Utils::OpenHandle(this); + i::Isolate* isolate = obj->GetIsolate(); + ApiCheck(obj->is_external(), + "v8::ArrayBuffer::Neuter", + "Only externalized ArrayBuffers can be neutered"); + LOG_API(obj->GetIsolate(), "v8::ArrayBuffer::Neuter()"); + ENTER_V8(isolate); + + for (i::Handle array_obj(obj->weak_first_array(), isolate); + *array_obj != i::Smi::FromInt(0);) { + i::Handle typed_array(i::JSTypedArray::cast(*array_obj)); + typed_array->Neuter(); + array_obj = i::handle(typed_array->weak_next(), isolate); + } + obj->Neuter(); +} + + size_t v8::ArrayBuffer::ByteLength() const { i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); if (IsDeadCheck(isolate, "v8::ArrayBuffer::ByteLength()")) return 0; diff --git a/src/objects.cc b/src/objects.cc index 5bba0b7..4fecb0b 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -15777,4 +15777,19 @@ void JSDate::SetLocalFields(int64_t local_time_ms, DateCache* date_cache) { set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER); } + +void JSArrayBuffer::Neuter() { + ASSERT(is_external()); + set_backing_store(NULL); + set_byte_length(Smi::FromInt(0)); +} + + +void JSTypedArray::Neuter() { + set_byte_offset(Smi::FromInt(0)); + set_byte_length(Smi::FromInt(0)); + set_length(Smi::FromInt(0)); + set_elements(GetHeap()->EmptyExternalArrayForMap(map())); +} + } } // namespace v8::internal diff --git a/src/objects.h b/src/objects.h index 771bacc..3ebe9c0 100644 --- a/src/objects.h +++ b/src/objects.h @@ -8813,6 +8813,9 @@ class JSArrayBuffer: public JSObject { // Casting. static inline JSArrayBuffer* cast(Object* obj); + // Neutering. Only neuters the buffer, not associated typed arrays. + void Neuter(); + // Dispatched behavior. DECLARE_PRINTER(JSArrayBuffer) DECLARE_VERIFIER(JSArrayBuffer) @@ -8852,6 +8855,9 @@ class JSTypedArray: public JSObject { // [weak_next]: linked list of typed arrays over the same array buffer. DECL_ACCESSORS(weak_next, Object) + // Neutering. Only neuters this typed array. + void Neuter(); + // Casting. static inline JSTypedArray* cast(Object* obj); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index cb34bed..a59dd16 100755 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2657,6 +2657,122 @@ THREADED_TEST(ArrayBuffer_External) { } +static void CheckIsNeutered(v8::Handle ta) { + CHECK_EQ(0, static_cast(ta->ByteLength())); + CHECK_EQ(0, static_cast(ta->Length())); + CHECK_EQ(0, static_cast(ta->ByteOffset())); +} + +template +static Handle CreateAndCheck(Handle ab, + int byteOffset, + int length) { + v8::Handle ta = TypedArray::New(ab, byteOffset, length); + CHECK_EQ(byteOffset, static_cast(ta->ByteOffset())); + CHECK_EQ(length, static_cast(ta->Length())); + CHECK_EQ(length * kElementSize, static_cast(ta->ByteLength())); + return ta; +} + + +THREADED_TEST(ArrayBuffer_NeuteringApi) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + v8::Handle buffer = v8::ArrayBuffer::New(1024); + + v8::Handle u8a = + CreateAndCheck(buffer, 1, 1023); + v8::Handle u8c = + CreateAndCheck(buffer, 1, 1023); + v8::Handle i8a = + CreateAndCheck(buffer, 1, 1023); + + v8::Handle u16a = + CreateAndCheck(buffer, 2, 511); + v8::Handle i16a = + CreateAndCheck(buffer, 2, 511); + + v8::Handle u32a = + CreateAndCheck(buffer, 4, 255); + v8::Handle i32a = + CreateAndCheck(buffer, 4, 255); + + v8::Handle f32a = + CreateAndCheck(buffer, 4, 255); + v8::Handle f64a = + CreateAndCheck(buffer, 8, 127); + + v8::ArrayBufferContents contents; + buffer->Externalize(&contents); + buffer->Neuter(); + CHECK_EQ(0, static_cast(buffer->ByteLength())); + CheckIsNeutered(u8a); + CheckIsNeutered(u8c); + CheckIsNeutered(i8a); + CheckIsNeutered(u16a); + CheckIsNeutered(i16a); + CheckIsNeutered(u32a); + CheckIsNeutered(i32a); + CheckIsNeutered(f32a); + CheckIsNeutered(f64a); +} + +THREADED_TEST(ArrayBuffer_NeuteringScript) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + CompileRun( + "var ab = new ArrayBuffer(1024);" + "var u8a = new Uint8Array(ab, 1, 1023);" + "var u8c = new Uint8ClampedArray(ab, 1, 1023);" + "var i8a = new Int8Array(ab, 1, 1023);" + "var u16a = new Uint16Array(ab, 2, 511);" + "var i16a = new Int16Array(ab, 2, 511);" + "var u32a = new Uint32Array(ab, 4, 255);" + "var i32a = new Int32Array(ab, 4, 255);" + "var f32a = new Float32Array(ab, 4, 255);" + "var f64a = new Float64Array(ab, 8, 127);"); + + v8::Handle ab(v8::ArrayBuffer::Cast(*CompileRun("ab"))); + + v8::Handle u8a(v8::Uint8Array::Cast(*CompileRun("u8a"))); + v8::Handle u8c( + v8::Uint8ClampedArray::Cast(*CompileRun("u8c"))); + v8::Handle i8a(v8::Int8Array::Cast(*CompileRun("i8a"))); + + v8::Handle u16a( + v8::Uint16Array::Cast(*CompileRun("u16a"))); + v8::Handle i16a( + v8::Int16Array::Cast(*CompileRun("i16a"))); + v8::Handle u32a( + v8::Uint32Array::Cast(*CompileRun("u32a"))); + v8::Handle i32a( + v8::Int32Array::Cast(*CompileRun("i32a"))); + v8::Handle f32a( + v8::Float32Array::Cast(*CompileRun("f32a"))); + v8::Handle f64a( + v8::Float64Array::Cast(*CompileRun("f64a"))); + + v8::ArrayBufferContents contents; + ab->Externalize(&contents); + ab->Neuter(); + CHECK_EQ(0, static_cast(ab->ByteLength())); + CheckIsNeutered(u8a); + CheckIsNeutered(u8c); + CheckIsNeutered(i8a); + CheckIsNeutered(u16a); + CheckIsNeutered(i16a); + CheckIsNeutered(u32a); + CheckIsNeutered(i32a); + CheckIsNeutered(f32a); + CheckIsNeutered(f64a); +} + + + THREADED_TEST(HiddenProperties) { LocalContext env; v8::HandleScope scope(env->GetIsolate()); -- 2.7.4