306cf40344
This is a reland ofbc33f5aeba
Original change's description: > Reland "[arraybuffer] Rearchitect backing store ownership" > > This is a reland of31cd5d83d3
> > Original change's description: > > [arraybuffer] Rearchitect backing store ownership > > > > This CL completely rearchitects the ownership of array buffer backing stores, > > consolidating ownership into a {BackingStore} C++ object that is tracked > > throughout V8 using unique_ptr and shared_ptr where appropriate. > > > > Overall, lifetime management is simpler and more explicit. The numerous > > ways that array buffers were initialized have been streamlined to one > > Attach() method on JSArrayBuffer. The array buffer tracker in the > > GC implementation now manages std::shared_ptr<BackingStore> pointers, > > and the construction and destruction of the BackingStore object itself > > handles the underlying page or embedder-allocated memory. > > > > The embedder API remains unchanged for now. We use the > > v8::ArrayBuffer::Contents struct to hide an additional shared_ptr to > > keep the backing store alive properly, even in the case of aliases > > from live heap objects. Thus the embedder has a lower chance of making > > a mistake. Long-term, we should move the embedder to a model where they > > manage backing stores using shared_ptr to an opaque backing store object. > > > > R=mlippautz@chromium.org > > BUG=v8:9380,v8:9221 > > > > Change-Id: I48fae5ac85dcf6172a83f252439e77e7c1a16ccd > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1584323 > > Commit-Queue: Ben Titzer <titzer@chromium.org> > > Reviewed-by: Ben Titzer <titzer@chromium.org> > > Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> > > Reviewed-by: Yang Guo <yangguo@chromium.org> > > Reviewed-by: Deepti Gandluri <gdeepti@chromium.org> > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org> > > Reviewed-by: Michael Lippautz <mlippautz@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#62572} > > Bug: v8:9380, v8:9221 > Change-Id: If3f72967a8ebeb067c0edcfc16ed631e36829dbc > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1691906 > Commit-Queue: Ben Titzer <titzer@chromium.org> > Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> > Reviewed-by: Michael Lippautz <mlippautz@chromium.org> > Reviewed-by: Deepti Gandluri <gdeepti@chromium.org> > Reviewed-by: Yang Guo <yangguo@chromium.org> > Reviewed-by: Ulan Degenbaev <ulan@chromium.org> > Cr-Commit-Position: refs/heads/master@{#62809} Bug: v8:9380, v8:9221 Change-Id: I9a2525753ae2424108d074fa81df5f25d945c824 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1709409 Commit-Queue: Ben Titzer <titzer@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Cr-Commit-Position: refs/heads/master@{#62847}
536 lines
18 KiB
C++
536 lines
18 KiB
C++
// Copyright 2019 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "test/cctest/test-api.h"
|
|
|
|
#include "src/api/api-inl.h"
|
|
|
|
using ::v8::Array;
|
|
using ::v8::Context;
|
|
using ::v8::Local;
|
|
using ::v8::Value;
|
|
|
|
namespace {
|
|
|
|
class ScopedArrayBufferContents {
|
|
public:
|
|
explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents)
|
|
: contents_(contents) {}
|
|
~ScopedArrayBufferContents() {
|
|
contents_.Deleter()(contents_.Data(), contents_.ByteLength(),
|
|
contents_.DeleterData());
|
|
}
|
|
void* Data() const { return contents_.Data(); }
|
|
size_t ByteLength() const { return contents_.ByteLength(); }
|
|
|
|
void* AllocationBase() const { return contents_.AllocationBase(); }
|
|
size_t AllocationLength() const { return contents_.AllocationLength(); }
|
|
v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
|
|
return contents_.AllocationMode();
|
|
}
|
|
|
|
private:
|
|
const v8::ArrayBuffer::Contents contents_;
|
|
};
|
|
|
|
class ScopedSharedArrayBufferContents {
|
|
public:
|
|
explicit ScopedSharedArrayBufferContents(
|
|
const v8::SharedArrayBuffer::Contents& contents)
|
|
: contents_(contents) {}
|
|
~ScopedSharedArrayBufferContents() {
|
|
contents_.Deleter()(contents_.Data(), contents_.ByteLength(),
|
|
contents_.DeleterData());
|
|
}
|
|
void* Data() const { return contents_.Data(); }
|
|
size_t ByteLength() const { return contents_.ByteLength(); }
|
|
|
|
void* AllocationBase() const { return contents_.AllocationBase(); }
|
|
size_t AllocationLength() const { return contents_.AllocationLength(); }
|
|
v8::ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
|
|
return contents_.AllocationMode();
|
|
}
|
|
|
|
private:
|
|
const v8::SharedArrayBuffer::Contents contents_;
|
|
};
|
|
|
|
void CheckDataViewIsDetached(v8::Local<v8::DataView> dv) {
|
|
CHECK_EQ(0, static_cast<int>(dv->ByteLength()));
|
|
CHECK_EQ(0, static_cast<int>(dv->ByteOffset()));
|
|
}
|
|
|
|
void CheckIsDetached(v8::Local<v8::TypedArray> ta) {
|
|
CHECK_EQ(0, static_cast<int>(ta->ByteLength()));
|
|
CHECK_EQ(0, static_cast<int>(ta->Length()));
|
|
CHECK_EQ(0, static_cast<int>(ta->ByteOffset()));
|
|
}
|
|
|
|
void CheckIsTypedArrayVarDetached(const char* name) {
|
|
i::ScopedVector<char> source(1024);
|
|
i::SNPrintF(source,
|
|
"%s.byteLength == 0 && %s.byteOffset == 0 && %s.length == 0",
|
|
name, name, name);
|
|
CHECK(CompileRun(source.begin())->IsTrue());
|
|
v8::Local<v8::TypedArray> ta =
|
|
v8::Local<v8::TypedArray>::Cast(CompileRun(name));
|
|
CheckIsDetached(ta);
|
|
}
|
|
|
|
template <typename TypedArray, int kElementSize>
|
|
Local<TypedArray> CreateAndCheck(Local<v8::ArrayBuffer> ab, int byteOffset,
|
|
int length) {
|
|
v8::Local<TypedArray> ta = TypedArray::New(ab, byteOffset, length);
|
|
CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta);
|
|
CHECK_EQ(byteOffset, static_cast<int>(ta->ByteOffset()));
|
|
CHECK_EQ(length, static_cast<int>(ta->Length()));
|
|
CHECK_EQ(length * kElementSize, static_cast<int>(ta->ByteLength()));
|
|
return ta;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
THREADED_TEST(ArrayBuffer_ApiInternalToExternal) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 1024);
|
|
CheckInternalFieldsAreZero(ab);
|
|
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
|
|
CHECK(!ab->IsExternal());
|
|
CcTest::CollectAllGarbage();
|
|
|
|
ScopedArrayBufferContents ab_contents(ab->Externalize());
|
|
CHECK(ab->IsExternal());
|
|
|
|
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
|
|
uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
|
|
CHECK_NOT_NULL(data);
|
|
CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust());
|
|
|
|
v8::Local<v8::Value> result = CompileRun("ab.byteLength");
|
|
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
|
|
|
|
result = CompileRun(
|
|
"var u8 = new Uint8Array(ab);"
|
|
"u8[0] = 0xFF;"
|
|
"u8[1] = 0xAA;"
|
|
"u8.length");
|
|
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
|
|
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(env.local()).FromJust());
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_JSInternalToExternal) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Value> 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 = Local<v8::ArrayBuffer>::Cast(result);
|
|
CheckInternalFieldsAreZero(ab1);
|
|
CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
|
|
CHECK(!ab1->IsExternal());
|
|
ScopedArrayBufferContents ab1_contents(ab1->Externalize());
|
|
CHECK(ab1->IsExternal());
|
|
|
|
result = CompileRun("ab1.byteLength");
|
|
CHECK_EQ(2, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun("u8_a[0]");
|
|
CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun("u8_a[1]");
|
|
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun(
|
|
"var u8_b = new Uint8Array(ab1);"
|
|
"u8_b[0] = 0xBB;"
|
|
"u8_a[0]");
|
|
CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun("u8_b[1]");
|
|
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
|
|
|
|
CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
|
|
uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
|
|
CHECK_EQ(0xBB, 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(env.local()).FromJust());
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_External) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
i::ScopedVector<uint8_t> my_data(100);
|
|
memset(my_data.begin(), 0, 100);
|
|
Local<v8::ArrayBuffer> ab3 =
|
|
v8::ArrayBuffer::New(isolate, my_data.begin(), 100);
|
|
CheckInternalFieldsAreZero(ab3);
|
|
CHECK_EQ(100, static_cast<int>(ab3->ByteLength()));
|
|
CHECK(ab3->IsExternal());
|
|
|
|
CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust());
|
|
|
|
v8::Local<v8::Value> result = CompileRun("ab3.byteLength");
|
|
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
|
|
|
|
result = CompileRun(
|
|
"var u8_b = new Uint8Array(ab3);"
|
|
"u8_b[0] = 0xBB;"
|
|
"u8_b[1] = 0xCC;"
|
|
"u8_b.length");
|
|
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
|
|
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(env.local()).FromJust());
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_DisableDetach) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
i::ScopedVector<uint8_t> my_data(100);
|
|
memset(my_data.begin(), 0, 100);
|
|
Local<v8::ArrayBuffer> ab =
|
|
v8::ArrayBuffer::New(isolate, my_data.begin(), 100);
|
|
CHECK(ab->IsDetachable());
|
|
|
|
i::Handle<i::JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab);
|
|
buf->set_is_detachable(false);
|
|
|
|
CHECK(!ab->IsDetachable());
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_DetachingApi) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1024);
|
|
|
|
v8::Local<v8::Uint8Array> u8a =
|
|
CreateAndCheck<v8::Uint8Array, 1>(buffer, 1, 1023);
|
|
v8::Local<v8::Uint8ClampedArray> u8c =
|
|
CreateAndCheck<v8::Uint8ClampedArray, 1>(buffer, 1, 1023);
|
|
v8::Local<v8::Int8Array> i8a =
|
|
CreateAndCheck<v8::Int8Array, 1>(buffer, 1, 1023);
|
|
|
|
v8::Local<v8::Uint16Array> u16a =
|
|
CreateAndCheck<v8::Uint16Array, 2>(buffer, 2, 511);
|
|
v8::Local<v8::Int16Array> i16a =
|
|
CreateAndCheck<v8::Int16Array, 2>(buffer, 2, 511);
|
|
|
|
v8::Local<v8::Uint32Array> u32a =
|
|
CreateAndCheck<v8::Uint32Array, 4>(buffer, 4, 255);
|
|
v8::Local<v8::Int32Array> i32a =
|
|
CreateAndCheck<v8::Int32Array, 4>(buffer, 4, 255);
|
|
|
|
v8::Local<v8::Float32Array> f32a =
|
|
CreateAndCheck<v8::Float32Array, 4>(buffer, 4, 255);
|
|
v8::Local<v8::Float64Array> f64a =
|
|
CreateAndCheck<v8::Float64Array, 8>(buffer, 8, 127);
|
|
|
|
v8::Local<v8::DataView> dv = v8::DataView::New(buffer, 1, 1023);
|
|
CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv);
|
|
CHECK_EQ(1, static_cast<int>(dv->ByteOffset()));
|
|
CHECK_EQ(1023, static_cast<int>(dv->ByteLength()));
|
|
|
|
ScopedArrayBufferContents contents(buffer->Externalize());
|
|
buffer->Detach();
|
|
CHECK_EQ(0, static_cast<int>(buffer->ByteLength()));
|
|
CheckIsDetached(u8a);
|
|
CheckIsDetached(u8c);
|
|
CheckIsDetached(i8a);
|
|
CheckIsDetached(u16a);
|
|
CheckIsDetached(i16a);
|
|
CheckIsDetached(u32a);
|
|
CheckIsDetached(i32a);
|
|
CheckIsDetached(f32a);
|
|
CheckIsDetached(f64a);
|
|
CheckDataViewIsDetached(dv);
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_DetachingScript) {
|
|
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);"
|
|
"var dv = new DataView(ab, 1, 1023);");
|
|
|
|
v8::Local<v8::ArrayBuffer> ab =
|
|
Local<v8::ArrayBuffer>::Cast(CompileRun("ab"));
|
|
|
|
v8::Local<v8::DataView> dv = v8::Local<v8::DataView>::Cast(CompileRun("dv"));
|
|
|
|
ScopedArrayBufferContents contents(ab->Externalize());
|
|
ab->Detach();
|
|
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
|
|
CHECK_EQ(0, v8_run_int32value(v8_compile("ab.byteLength")));
|
|
|
|
CheckIsTypedArrayVarDetached("u8a");
|
|
CheckIsTypedArrayVarDetached("u8c");
|
|
CheckIsTypedArrayVarDetached("i8a");
|
|
CheckIsTypedArrayVarDetached("u16a");
|
|
CheckIsTypedArrayVarDetached("i16a");
|
|
CheckIsTypedArrayVarDetached("u32a");
|
|
CheckIsTypedArrayVarDetached("i32a");
|
|
CheckIsTypedArrayVarDetached("f32a");
|
|
CheckIsTypedArrayVarDetached("f64a");
|
|
|
|
CHECK(CompileRun("dv.byteLength == 0 && dv.byteOffset == 0")->IsTrue());
|
|
CheckDataViewIsDetached(dv);
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_AllocationInformation) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
const size_t ab_size = 1024;
|
|
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, ab_size);
|
|
ScopedArrayBufferContents contents(ab->Externalize());
|
|
|
|
// Array buffers should have normal allocation mode.
|
|
CHECK_EQ(contents.AllocationMode(),
|
|
v8::ArrayBuffer::Allocator::AllocationMode::kNormal);
|
|
// The allocation must contain the buffer (normally they will be equal, but
|
|
// this is not required by the contract).
|
|
CHECK_NOT_NULL(contents.AllocationBase());
|
|
const uintptr_t alloc =
|
|
reinterpret_cast<uintptr_t>(contents.AllocationBase());
|
|
const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data());
|
|
CHECK_LE(alloc, data);
|
|
CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength());
|
|
}
|
|
|
|
THREADED_TEST(ArrayBuffer_ExternalizeEmpty) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 0);
|
|
CheckInternalFieldsAreZero(ab);
|
|
CHECK_EQ(0, static_cast<int>(ab->ByteLength()));
|
|
CHECK(!ab->IsExternal());
|
|
|
|
// Externalize the buffer (taking ownership of the backing store memory).
|
|
ScopedArrayBufferContents ab_contents(ab->Externalize());
|
|
|
|
Local<v8::Uint8Array> u8a = v8::Uint8Array::New(ab, 0, 0);
|
|
// Calling Buffer() will materialize the ArrayBuffer (transitioning it from
|
|
// on-heap to off-heap if need be). This should not affect whether it is
|
|
// marked as is_external or not.
|
|
USE(u8a->Buffer());
|
|
|
|
CHECK(ab->IsExternal());
|
|
}
|
|
|
|
THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) {
|
|
i::FLAG_harmony_sharedarraybuffer = true;
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
Local<v8::SharedArrayBuffer> ab = v8::SharedArrayBuffer::New(isolate, 1024);
|
|
CheckInternalFieldsAreZero(ab);
|
|
CHECK_EQ(1024, static_cast<int>(ab->ByteLength()));
|
|
CHECK(!ab->IsExternal());
|
|
CcTest::CollectAllGarbage();
|
|
|
|
ScopedSharedArrayBufferContents ab_contents(ab->Externalize());
|
|
CHECK(ab->IsExternal());
|
|
|
|
CHECK_EQ(1024, static_cast<int>(ab_contents.ByteLength()));
|
|
uint8_t* data = static_cast<uint8_t*>(ab_contents.Data());
|
|
CHECK_NOT_NULL(data);
|
|
CHECK(env->Global()->Set(env.local(), v8_str("ab"), ab).FromJust());
|
|
|
|
v8::Local<v8::Value> result = CompileRun("ab.byteLength");
|
|
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
|
|
|
|
result = CompileRun(
|
|
"var u8 = new Uint8Array(ab);"
|
|
"u8[0] = 0xFF;"
|
|
"u8[1] = 0xAA;"
|
|
"u8.length");
|
|
CHECK_EQ(1024, result->Int32Value(env.local()).FromJust());
|
|
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(env.local()).FromJust());
|
|
}
|
|
|
|
THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) {
|
|
i::FLAG_harmony_sharedarraybuffer = true;
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Value> result = CompileRun(
|
|
"var ab1 = new SharedArrayBuffer(2);"
|
|
"var u8_a = new Uint8Array(ab1);"
|
|
"u8_a[0] = 0xAA;"
|
|
"u8_a[1] = 0xFF; u8_a.buffer");
|
|
Local<v8::SharedArrayBuffer> ab1 = Local<v8::SharedArrayBuffer>::Cast(result);
|
|
CheckInternalFieldsAreZero(ab1);
|
|
CHECK_EQ(2, static_cast<int>(ab1->ByteLength()));
|
|
CHECK(!ab1->IsExternal());
|
|
ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize());
|
|
CHECK(ab1->IsExternal());
|
|
|
|
result = CompileRun("ab1.byteLength");
|
|
CHECK_EQ(2, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun("u8_a[0]");
|
|
CHECK_EQ(0xAA, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun("u8_a[1]");
|
|
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun(
|
|
"var u8_b = new Uint8Array(ab1);"
|
|
"u8_b[0] = 0xBB;"
|
|
"u8_a[0]");
|
|
CHECK_EQ(0xBB, result->Int32Value(env.local()).FromJust());
|
|
result = CompileRun("u8_b[1]");
|
|
CHECK_EQ(0xFF, result->Int32Value(env.local()).FromJust());
|
|
|
|
CHECK_EQ(2, static_cast<int>(ab1_contents.ByteLength()));
|
|
uint8_t* ab1_data = static_cast<uint8_t*>(ab1_contents.Data());
|
|
CHECK_EQ(0xBB, 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(env.local()).FromJust());
|
|
}
|
|
|
|
THREADED_TEST(SharedArrayBuffer_External) {
|
|
i::FLAG_harmony_sharedarraybuffer = true;
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
i::ScopedVector<uint8_t> my_data(100);
|
|
memset(my_data.begin(), 0, 100);
|
|
Local<v8::SharedArrayBuffer> ab3 =
|
|
v8::SharedArrayBuffer::New(isolate, my_data.begin(), 100);
|
|
CheckInternalFieldsAreZero(ab3);
|
|
CHECK_EQ(100, static_cast<int>(ab3->ByteLength()));
|
|
CHECK(ab3->IsExternal());
|
|
|
|
CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust());
|
|
|
|
v8::Local<v8::Value> result = CompileRun("ab3.byteLength");
|
|
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
|
|
|
|
result = CompileRun(
|
|
"var u8_b = new Uint8Array(ab3);"
|
|
"u8_b[0] = 0xBB;"
|
|
"u8_b[1] = 0xCC;"
|
|
"u8_b.length");
|
|
CHECK_EQ(100, result->Int32Value(env.local()).FromJust());
|
|
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(env.local()).FromJust());
|
|
}
|
|
|
|
THREADED_TEST(SharedArrayBuffer_AllocationInformation) {
|
|
i::FLAG_harmony_sharedarraybuffer = true;
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
const size_t ab_size = 1024;
|
|
Local<v8::SharedArrayBuffer> ab =
|
|
v8::SharedArrayBuffer::New(isolate, ab_size);
|
|
ScopedSharedArrayBufferContents contents(ab->Externalize());
|
|
|
|
// Array buffers should have normal allocation mode.
|
|
CHECK_EQ(contents.AllocationMode(),
|
|
v8::ArrayBuffer::Allocator::AllocationMode::kNormal);
|
|
// The allocation must contain the buffer (normally they will be equal, but
|
|
// this is not required by the contract).
|
|
CHECK_NOT_NULL(contents.AllocationBase());
|
|
const uintptr_t alloc =
|
|
reinterpret_cast<uintptr_t>(contents.AllocationBase());
|
|
const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data());
|
|
CHECK_LE(alloc, data);
|
|
CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength());
|
|
}
|
|
|
|
THREADED_TEST(SkipArrayBufferBackingStoreDuringGC) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
// Make sure the pointer looks like a heap object
|
|
uint8_t* store_ptr = reinterpret_cast<uint8_t*>(i::kHeapObjectTag);
|
|
|
|
// Create ArrayBuffer with pointer-that-cannot-be-visited in the backing store
|
|
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store_ptr, 8);
|
|
|
|
// Should not crash
|
|
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
|
|
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
|
|
CcTest::CollectAllGarbage();
|
|
CcTest::CollectAllGarbage();
|
|
|
|
// Should not move the pointer
|
|
CHECK_EQ(ab->GetContents().Data(), store_ptr);
|
|
}
|
|
|
|
THREADED_TEST(SkipArrayBufferDuringScavenge) {
|
|
LocalContext env;
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
// Make sure the pointer looks like a heap object
|
|
Local<v8::Object> tmp = v8::Object::New(isolate);
|
|
uint8_t* store_ptr =
|
|
reinterpret_cast<uint8_t*>(*reinterpret_cast<uintptr_t*>(*tmp));
|
|
|
|
// Make `store_ptr` point to from space
|
|
CcTest::CollectGarbage(i::NEW_SPACE);
|
|
|
|
// Create ArrayBuffer with pointer-that-cannot-be-visited in the backing store
|
|
Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store_ptr, 8);
|
|
|
|
// Should not crash,
|
|
// i.e. backing store pointer should not be treated as a heap object pointer
|
|
CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now
|
|
CcTest::CollectGarbage(i::NEW_SPACE); // in old gen now
|
|
|
|
// Use `ab` to silence compiler warning
|
|
CHECK_EQ(ab->GetContents().Data(), store_ptr);
|
|
}
|