Add allocation information to ArrayBuffer::Contents
Array buffers can now have an allocation that is larger than the actual buffer, such as when WebAssembly guard regions are enabled. Embedders need to know the actual allocation start and length when externalizing a buffer so they can deallocate it properly. Bug: chromium:720302, v8:5277 Cq-Include-Trybots: master.tryserver.chromium.linux:linux_chromium_rel_ng Change-Id: Ifc184fdd59d77af01c07a64d2c0229ca859a01b0 Reviewed-on: https://chromium-review.googlesource.com/523271 Commit-Queue: Eric Holk <eholk@chromium.org> Reviewed-by: Jochen Eisinger <jochen@chromium.org> Cr-Commit-Position: refs/heads/master@{#45777}
This commit is contained in:
parent
e285b71220
commit
8f39e07d80
32
include/v8.h
32
include/v8.h
@ -4262,12 +4262,26 @@ class V8_EXPORT ArrayBuffer : public Object {
|
||||
*/
|
||||
class V8_EXPORT Contents { // NOLINT
|
||||
public:
|
||||
Contents() : data_(NULL), byte_length_(0) {}
|
||||
Contents()
|
||||
: allocation_base_(nullptr),
|
||||
allocation_length_(0),
|
||||
allocation_mode_(Allocator::AllocationMode::kNormal),
|
||||
data_(nullptr),
|
||||
byte_length_(0) {}
|
||||
|
||||
void* AllocationBase() const { return allocation_base_; }
|
||||
size_t AllocationLength() const { return allocation_length_; }
|
||||
Allocator::AllocationMode AllocationMode() const {
|
||||
return allocation_mode_;
|
||||
}
|
||||
|
||||
void* Data() const { return data_; }
|
||||
size_t ByteLength() const { return byte_length_; }
|
||||
|
||||
private:
|
||||
void* allocation_base_;
|
||||
size_t allocation_length_;
|
||||
Allocator::AllocationMode allocation_mode_;
|
||||
void* data_;
|
||||
size_t byte_length_;
|
||||
|
||||
@ -4618,12 +4632,26 @@ class V8_EXPORT SharedArrayBuffer : public Object {
|
||||
*/
|
||||
class V8_EXPORT Contents { // NOLINT
|
||||
public:
|
||||
Contents() : data_(NULL), byte_length_(0) {}
|
||||
Contents()
|
||||
: allocation_base_(nullptr),
|
||||
allocation_length_(0),
|
||||
allocation_mode_(ArrayBuffer::Allocator::AllocationMode::kNormal),
|
||||
data_(nullptr),
|
||||
byte_length_(0) {}
|
||||
|
||||
void* AllocationBase() const { return allocation_base_; }
|
||||
size_t AllocationLength() const { return allocation_length_; }
|
||||
ArrayBuffer::Allocator::AllocationMode AllocationMode() const {
|
||||
return allocation_mode_;
|
||||
}
|
||||
|
||||
void* Data() const { return data_; }
|
||||
size_t ByteLength() const { return byte_length_; }
|
||||
|
||||
private:
|
||||
void* allocation_base_;
|
||||
size_t allocation_length_;
|
||||
ArrayBuffer::Allocator::Allocator::AllocationMode allocation_mode_;
|
||||
void* data_;
|
||||
size_t byte_length_;
|
||||
|
||||
|
11
src/api.cc
11
src/api.cc
@ -7784,6 +7784,11 @@ v8::ArrayBuffer::Contents v8::ArrayBuffer::GetContents() {
|
||||
i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this);
|
||||
size_t byte_length = static_cast<size_t>(self->byte_length()->Number());
|
||||
Contents contents;
|
||||
contents.allocation_base_ = self->allocation_base();
|
||||
contents.allocation_length_ = self->allocation_length();
|
||||
contents.allocation_mode_ = self->has_guard_region()
|
||||
? Allocator::AllocationMode::kReservation
|
||||
: Allocator::AllocationMode::kNormal;
|
||||
contents.data_ = self->backing_store();
|
||||
contents.byte_length_ = byte_length;
|
||||
return contents;
|
||||
@ -7992,6 +7997,12 @@ v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::GetContents() {
|
||||
Contents contents;
|
||||
contents.data_ = self->backing_store();
|
||||
contents.byte_length_ = byte_length;
|
||||
// SharedArrayBuffers never have guard regions, so their allocation and data
|
||||
// are equivalent.
|
||||
contents.allocation_base_ = self->backing_store();
|
||||
contents.allocation_length_ = byte_length;
|
||||
contents.allocation_mode_ =
|
||||
ArrayBufferAllocator::Allocator::AllocationMode::kNormal;
|
||||
return contents;
|
||||
}
|
||||
|
||||
|
@ -3416,10 +3416,16 @@ class ScopedArrayBufferContents {
|
||||
public:
|
||||
explicit ScopedArrayBufferContents(const v8::ArrayBuffer::Contents& contents)
|
||||
: contents_(contents) {}
|
||||
~ScopedArrayBufferContents() { free(contents_.Data()); }
|
||||
~ScopedArrayBufferContents() { free(contents_.AllocationBase()); }
|
||||
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_;
|
||||
};
|
||||
@ -3695,16 +3701,43 @@ THREADED_TEST(ArrayBuffer_NeuteringScript) {
|
||||
CheckDataViewIsNeutered(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(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());
|
||||
}
|
||||
|
||||
class ScopedSharedArrayBufferContents {
|
||||
public:
|
||||
explicit ScopedSharedArrayBufferContents(
|
||||
const v8::SharedArrayBuffer::Contents& contents)
|
||||
: contents_(contents) {}
|
||||
~ScopedSharedArrayBufferContents() { free(contents_.Data()); }
|
||||
~ScopedSharedArrayBufferContents() { free(contents_.AllocationBase()); }
|
||||
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_;
|
||||
};
|
||||
@ -25952,6 +25985,29 @@ TEST(FutexInterruption) {
|
||||
timeout_thread.Join();
|
||||
}
|
||||
|
||||
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(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());
|
||||
}
|
||||
|
||||
static int nb_uncaught_exception_callback_calls = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user