Reland^2 "[string] Support shared strings in Value{Serializer,Deserializer}"
This is a reland of5320fe8d58
Changes since revert: - Remove stale DCHECK in deserializer Original change's description: > Reland "[string] Support shared strings in Value{Serializer,Deserializer}" > > This is a reland of3cb4039cd1
> > Changes since revert: > - Fix FLAG_stress_scavenge interaction with shared Isolate > - Use the shared Isolate's global handles to keep shared values > alive in transit during a postMessage > > Original change's description: > > [string] Support shared strings in Value{Serializer,Deserializer} > > > > When FLAG_shared_string_table is true, postMessaging strings will share > > instead of copy. > > > > Note that not all operations on shared strings are supported, and shared > > strings may be slower than non-shared strings for some operations. Bug: v8:12007 Change-Id: I70782978ed05558615eca03bafc4c12eba3644ca Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel_ng Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3417189 Reviewed-by: Dominik Inführ <dinfuehr@chromium.org> Reviewed-by: Camillo Bruni <cbruni@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Commit-Queue: Shu-yu Guo <syg@chromium.org> Cr-Commit-Position: refs/heads/main@{#78791}
This commit is contained in:
parent
2f0ae10e33
commit
fc23bc1de2
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
|
|
||||||
constexpr uint32_t CurrentValueSerializerFormatVersion() { return 14; }
|
constexpr uint32_t CurrentValueSerializerFormatVersion() { return 15; }
|
||||||
|
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
|
||||||
|
@ -67,6 +67,23 @@ class V8_EXPORT ValueSerializer {
|
|||||||
|
|
||||||
virtual Maybe<uint32_t> GetWasmModuleTransferId(
|
virtual Maybe<uint32_t> GetWasmModuleTransferId(
|
||||||
Isolate* isolate, Local<WasmModuleObject> module);
|
Isolate* isolate, Local<WasmModuleObject> module);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether shared values are supported. GetSharedValueId is only
|
||||||
|
* called if SupportsSharedValues() returns true.
|
||||||
|
*/
|
||||||
|
virtual bool SupportsSharedValues() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the ValueSerializer serializes a value that is shared across
|
||||||
|
* Isolates. The embedder must return an ID for the object. This function
|
||||||
|
* must be idempotent for the same object. When deserializing, the ID will
|
||||||
|
* be passed to ValueDeserializer::Delegate::GetSharedValueFromId as
|
||||||
|
* |shared_value_id|.
|
||||||
|
*/
|
||||||
|
virtual Maybe<uint32_t> GetSharedValueId(Isolate* isolate,
|
||||||
|
Local<Value> shared_value);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates memory for the buffer of at least the size provided. The actual
|
* Allocates memory for the buffer of at least the size provided. The actual
|
||||||
* size (which may be greater or equal) is written to |actual_size|. If no
|
* size (which may be greater or equal) is written to |actual_size|. If no
|
||||||
@ -166,17 +183,30 @@ class V8_EXPORT ValueDeserializer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a WasmModuleObject given a transfer_id previously provided
|
* Get a WasmModuleObject given a transfer_id previously provided
|
||||||
* by ValueSerializer::GetWasmModuleTransferId
|
* by ValueSerializer::Delegate::GetWasmModuleTransferId
|
||||||
*/
|
*/
|
||||||
virtual MaybeLocal<WasmModuleObject> GetWasmModuleFromId(
|
virtual MaybeLocal<WasmModuleObject> GetWasmModuleFromId(
|
||||||
Isolate* isolate, uint32_t transfer_id);
|
Isolate* isolate, uint32_t transfer_id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a SharedArrayBuffer given a clone_id previously provided
|
* Get a SharedArrayBuffer given a clone_id previously provided
|
||||||
* by ValueSerializer::GetSharedArrayBufferId
|
* by ValueSerializer::Delegate::GetSharedArrayBufferId
|
||||||
*/
|
*/
|
||||||
virtual MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
|
virtual MaybeLocal<SharedArrayBuffer> GetSharedArrayBufferFromId(
|
||||||
Isolate* isolate, uint32_t clone_id);
|
Isolate* isolate, uint32_t clone_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether shared values are supported. GetSharedValueFromId is only
|
||||||
|
* called if SupportsSharedValues() returns true.
|
||||||
|
*/
|
||||||
|
virtual bool SupportsSharedValues() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a value shared across Isolates given a shared_value_id provided by
|
||||||
|
* ValueSerializer::Delegate::GetSharedValueId.
|
||||||
|
*/
|
||||||
|
virtual MaybeLocal<Value> GetSharedValueFromId(Isolate* isolate,
|
||||||
|
uint32_t shared_value_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
ValueDeserializer(Isolate* isolate, const uint8_t* data, size_t size);
|
ValueDeserializer(Isolate* isolate, const uint8_t* data, size_t size);
|
||||||
|
@ -3306,6 +3306,17 @@ Maybe<uint32_t> ValueSerializer::Delegate::GetWasmModuleTransferId(
|
|||||||
return Nothing<uint32_t>();
|
return Nothing<uint32_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ValueSerializer::Delegate::SupportsSharedValues() const { return false; }
|
||||||
|
|
||||||
|
Maybe<uint32_t> ValueSerializer::Delegate::GetSharedValueId(
|
||||||
|
Isolate* v8_isolate, Local<Value> shared_value) {
|
||||||
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||||
|
isolate->ScheduleThrow(*isolate->factory()->NewError(
|
||||||
|
isolate->error_function(), i::MessageTemplate::kDataCloneError,
|
||||||
|
Utils::OpenHandle(*shared_value)));
|
||||||
|
return Nothing<uint32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
void* ValueSerializer::Delegate::ReallocateBufferMemory(void* old_buffer,
|
void* ValueSerializer::Delegate::ReallocateBufferMemory(void* old_buffer,
|
||||||
size_t size,
|
size_t size,
|
||||||
size_t* actual_size) {
|
size_t* actual_size) {
|
||||||
@ -3395,6 +3406,17 @@ MaybeLocal<WasmModuleObject> ValueDeserializer::Delegate::GetWasmModuleFromId(
|
|||||||
return MaybeLocal<WasmModuleObject>();
|
return MaybeLocal<WasmModuleObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ValueDeserializer::Delegate::SupportsSharedValues() const { return false; }
|
||||||
|
|
||||||
|
MaybeLocal<Value> ValueDeserializer::Delegate::GetSharedValueFromId(
|
||||||
|
Isolate* v8_isolate, uint32_t shared_value_id) {
|
||||||
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||||
|
isolate->ScheduleThrow(*isolate->factory()->NewError(
|
||||||
|
isolate->error_function(),
|
||||||
|
i::MessageTemplate::kDataCloneDeserializationError));
|
||||||
|
return MaybeLocal<Value>();
|
||||||
|
}
|
||||||
|
|
||||||
MaybeLocal<SharedArrayBuffer>
|
MaybeLocal<SharedArrayBuffer>
|
||||||
ValueDeserializer::Delegate::GetSharedArrayBufferFromId(Isolate* v8_isolate,
|
ValueDeserializer::Delegate::GetSharedArrayBufferFromId(Isolate* v8_isolate,
|
||||||
uint32_t id) {
|
uint32_t id) {
|
||||||
|
64
src/d8/d8.cc
64
src/d8/d8.cc
@ -28,6 +28,7 @@
|
|||||||
#include "include/v8-initialization.h"
|
#include "include/v8-initialization.h"
|
||||||
#include "include/v8-inspector.h"
|
#include "include/v8-inspector.h"
|
||||||
#include "include/v8-json.h"
|
#include "include/v8-json.h"
|
||||||
|
#include "include/v8-locker.h"
|
||||||
#include "include/v8-profiler.h"
|
#include "include/v8-profiler.h"
|
||||||
#include "include/v8-wasm.h"
|
#include "include/v8-wasm.h"
|
||||||
#include "src/api/api-inl.h"
|
#include "src/api/api-inl.h"
|
||||||
@ -45,6 +46,7 @@
|
|||||||
#include "src/debug/debug-interface.h"
|
#include "src/debug/debug-interface.h"
|
||||||
#include "src/deoptimizer/deoptimizer.h"
|
#include "src/deoptimizer/deoptimizer.h"
|
||||||
#include "src/diagnostics/basic-block-profiler.h"
|
#include "src/diagnostics/basic-block-profiler.h"
|
||||||
|
#include "src/execution/v8threads.h"
|
||||||
#include "src/execution/vm-state-inl.h"
|
#include "src/execution/vm-state-inl.h"
|
||||||
#include "src/flags/flags.h"
|
#include "src/flags/flags.h"
|
||||||
#include "src/handles/maybe-handles.h"
|
#include "src/handles/maybe-handles.h"
|
||||||
@ -2594,11 +2596,18 @@ void Shell::QuitOnce(v8::FunctionCallbackInfo<v8::Value>* args) {
|
|||||||
->Int32Value(args->GetIsolate()->GetCurrentContext())
|
->Int32Value(args->GetIsolate()->GetCurrentContext())
|
||||||
.FromMaybe(0);
|
.FromMaybe(0);
|
||||||
WaitForRunningWorkers();
|
WaitForRunningWorkers();
|
||||||
args->GetIsolate()->Exit();
|
Isolate* isolate = args->GetIsolate();
|
||||||
|
isolate->Exit();
|
||||||
|
|
||||||
// As we exit the process anyway, we do not dispose the platform and other
|
// As we exit the process anyway, we do not dispose the platform and other
|
||||||
// global data. Other isolates might still be running, so disposing here can
|
// global data and manually unlock to quell DCHECKs. Other isolates might
|
||||||
// cause them to crash.
|
// still be running, so disposing here can cause them to crash.
|
||||||
OnExit(args->GetIsolate(), false);
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||||
|
if (i_isolate->thread_manager()->IsLockedByCurrentThread()) {
|
||||||
|
i_isolate->thread_manager()->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
OnExit(isolate, false);
|
||||||
base::OS::ExitProcess(exit_code);
|
base::OS::ExitProcess(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4862,6 +4871,30 @@ class Serializer : public ValueSerializer::Delegate {
|
|||||||
|
|
||||||
void FreeBufferMemory(void* buffer) override { base::Free(buffer); }
|
void FreeBufferMemory(void* buffer) override { base::Free(buffer); }
|
||||||
|
|
||||||
|
bool SupportsSharedValues() const override { return true; }
|
||||||
|
|
||||||
|
Maybe<uint32_t> GetSharedValueId(Isolate* isolate,
|
||||||
|
Local<Value> shared_value) override {
|
||||||
|
DCHECK_NOT_NULL(data_);
|
||||||
|
for (size_t index = 0; index < data_->shared_values_.size(); ++index) {
|
||||||
|
if (data_->shared_values_[index] == shared_value) {
|
||||||
|
return Just<uint32_t>(static_cast<uint32_t>(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = data_->shared_values_.size();
|
||||||
|
// Shared values in transit are kept alive by global handles in the shared
|
||||||
|
// isolate. No code ever runs in the shared Isolate, so locking it does not
|
||||||
|
// contend with long-running tasks.
|
||||||
|
{
|
||||||
|
DCHECK_EQ(reinterpret_cast<i::Isolate*>(isolate)->shared_isolate(),
|
||||||
|
reinterpret_cast<i::Isolate*>(Shell::shared_isolate));
|
||||||
|
v8::Locker locker(Shell::shared_isolate);
|
||||||
|
data_->shared_values_.emplace_back(Shell::shared_isolate, shared_value);
|
||||||
|
}
|
||||||
|
return Just<uint32_t>(static_cast<uint32_t>(index));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
|
Maybe<bool> PrepareTransfer(Local<Context> context, Local<Value> transfer) {
|
||||||
if (transfer->IsArray()) {
|
if (transfer->IsArray()) {
|
||||||
@ -4928,6 +4961,12 @@ class Serializer : public ValueSerializer::Delegate {
|
|||||||
size_t current_memory_usage_;
|
size_t current_memory_usage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SerializationData::ClearSharedValuesUnderLockIfNeeded() {
|
||||||
|
if (shared_values_.empty()) return;
|
||||||
|
v8::Locker locker(Shell::shared_isolate);
|
||||||
|
shared_values_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
class Deserializer : public ValueDeserializer::Delegate {
|
class Deserializer : public ValueDeserializer::Delegate {
|
||||||
public:
|
public:
|
||||||
Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
|
Deserializer(Isolate* isolate, std::unique_ptr<SerializationData> data)
|
||||||
@ -4937,6 +4976,12 @@ class Deserializer : public ValueDeserializer::Delegate {
|
|||||||
deserializer_.SetSupportsLegacyWireFormat(true);
|
deserializer_.SetSupportsLegacyWireFormat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Deserializer() {
|
||||||
|
DCHECK_EQ(reinterpret_cast<i::Isolate*>(isolate_)->shared_isolate(),
|
||||||
|
reinterpret_cast<i::Isolate*>(Shell::shared_isolate));
|
||||||
|
data_->ClearSharedValuesUnderLockIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
Deserializer(const Deserializer&) = delete;
|
Deserializer(const Deserializer&) = delete;
|
||||||
Deserializer& operator=(const Deserializer&) = delete;
|
Deserializer& operator=(const Deserializer&) = delete;
|
||||||
|
|
||||||
@ -4974,6 +5019,17 @@ class Deserializer : public ValueDeserializer::Delegate {
|
|||||||
isolate_, data_->compiled_wasm_modules().at(transfer_id));
|
isolate_, data_->compiled_wasm_modules().at(transfer_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SupportsSharedValues() const override { return true; }
|
||||||
|
|
||||||
|
MaybeLocal<Value> GetSharedValueFromId(Isolate* isolate,
|
||||||
|
uint32_t id) override {
|
||||||
|
DCHECK_NOT_NULL(data_);
|
||||||
|
if (id < data_->shared_values().size()) {
|
||||||
|
return data_->shared_values().at(id).Get(isolate);
|
||||||
|
}
|
||||||
|
return MaybeLocal<Value>();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
ValueDeserializer deserializer_;
|
ValueDeserializer deserializer_;
|
||||||
|
@ -151,6 +151,11 @@ class SerializationData {
|
|||||||
const std::vector<CompiledWasmModule>& compiled_wasm_modules() {
|
const std::vector<CompiledWasmModule>& compiled_wasm_modules() {
|
||||||
return compiled_wasm_modules_;
|
return compiled_wasm_modules_;
|
||||||
}
|
}
|
||||||
|
const std::vector<v8::Global<v8::Value>>& shared_values() {
|
||||||
|
return shared_values_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearSharedValuesUnderLockIfNeeded();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct DataDeleter {
|
struct DataDeleter {
|
||||||
@ -162,6 +167,7 @@ class SerializationData {
|
|||||||
std::vector<std::shared_ptr<v8::BackingStore>> backing_stores_;
|
std::vector<std::shared_ptr<v8::BackingStore>> backing_stores_;
|
||||||
std::vector<std::shared_ptr<v8::BackingStore>> sab_backing_stores_;
|
std::vector<std::shared_ptr<v8::BackingStore>> sab_backing_stores_;
|
||||||
std::vector<CompiledWasmModule> compiled_wasm_modules_;
|
std::vector<CompiledWasmModule> compiled_wasm_modules_;
|
||||||
|
std::vector<v8::Global<v8::Value>> shared_values_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Serializer;
|
friend class Serializer;
|
||||||
|
@ -60,7 +60,7 @@ class ThreadVisitor {
|
|||||||
class ThreadManager {
|
class ThreadManager {
|
||||||
public:
|
public:
|
||||||
void Lock();
|
void Lock();
|
||||||
void Unlock();
|
V8_EXPORT_PRIVATE void Unlock();
|
||||||
|
|
||||||
void InitThread(const ExecutionAccess&);
|
void InitThread(const ExecutionAccess&);
|
||||||
void ArchiveThread();
|
void ArchiveThread();
|
||||||
|
@ -1388,7 +1388,7 @@ class V8_NODISCARD GCCallbacksScope {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void Heap::HandleGCRequest() {
|
void Heap::HandleGCRequest() {
|
||||||
if (FLAG_stress_scavenge > 0 && stress_scavenge_observer_->HasRequestedGC()) {
|
if (IsStressingScavenge() && stress_scavenge_observer_->HasRequestedGC()) {
|
||||||
CollectAllGarbage(NEW_SPACE, GarbageCollectionReason::kTesting);
|
CollectAllGarbage(NEW_SPACE, GarbageCollectionReason::kTesting);
|
||||||
stress_scavenge_observer_->RequestedGCDone();
|
stress_scavenge_observer_->RequestedGCDone();
|
||||||
} else if (HighMemoryPressure()) {
|
} else if (HighMemoryPressure()) {
|
||||||
@ -5772,7 +5772,7 @@ void Heap::SetUpSpaces(LinearAllocationArea* new_allocation_info,
|
|||||||
AddAllocationObserversToAllSpaces(stress_marking_observer_,
|
AddAllocationObserversToAllSpaces(stress_marking_observer_,
|
||||||
stress_marking_observer_);
|
stress_marking_observer_);
|
||||||
}
|
}
|
||||||
if (FLAG_stress_scavenge > 0 && new_space()) {
|
if (IsStressingScavenge()) {
|
||||||
stress_scavenge_observer_ = new StressScavengeObserver(this);
|
stress_scavenge_observer_ = new StressScavengeObserver(this);
|
||||||
new_space()->AddAllocationObserver(stress_scavenge_observer_);
|
new_space()->AddAllocationObserver(stress_scavenge_observer_);
|
||||||
}
|
}
|
||||||
@ -6006,7 +6006,7 @@ void Heap::TearDown() {
|
|||||||
if (FLAG_stress_marking > 0) {
|
if (FLAG_stress_marking > 0) {
|
||||||
PrintMaxMarkingLimitReached();
|
PrintMaxMarkingLimitReached();
|
||||||
}
|
}
|
||||||
if (FLAG_stress_scavenge > 0) {
|
if (IsStressingScavenge()) {
|
||||||
PrintMaxNewSpaceSizeReached();
|
PrintMaxNewSpaceSizeReached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6031,7 +6031,7 @@ void Heap::TearDown() {
|
|||||||
delete stress_marking_observer_;
|
delete stress_marking_observer_;
|
||||||
stress_marking_observer_ = nullptr;
|
stress_marking_observer_ = nullptr;
|
||||||
}
|
}
|
||||||
if (FLAG_stress_scavenge > 0 && new_space()) {
|
if (IsStressingScavenge()) {
|
||||||
new_space()->RemoveAllocationObserver(stress_scavenge_observer_);
|
new_space()->RemoveAllocationObserver(stress_scavenge_observer_);
|
||||||
delete stress_scavenge_observer_;
|
delete stress_scavenge_observer_;
|
||||||
stress_scavenge_observer_ = nullptr;
|
stress_scavenge_observer_ = nullptr;
|
||||||
@ -7372,6 +7372,10 @@ void Heap::IncrementObjectCounters() {
|
|||||||
}
|
}
|
||||||
#endif // DEBUG
|
#endif // DEBUG
|
||||||
|
|
||||||
|
bool Heap::IsStressingScavenge() {
|
||||||
|
return FLAG_stress_scavenge > 0 && new_space();
|
||||||
|
}
|
||||||
|
|
||||||
// StrongRootBlocks are allocated as a block of addresses, prefixed with a
|
// StrongRootBlocks are allocated as a block of addresses, prefixed with a
|
||||||
// StrongRootsEntry pointer:
|
// StrongRootsEntry pointer:
|
||||||
//
|
//
|
||||||
|
@ -2189,6 +2189,8 @@ class Heap {
|
|||||||
return allocation_type_for_in_place_internalizable_strings_;
|
return allocation_type_for_in_place_internalizable_strings_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsStressingScavenge();
|
||||||
|
|
||||||
ExternalMemoryAccounting external_memory_;
|
ExternalMemoryAccounting external_memory_;
|
||||||
|
|
||||||
// This can be calculated directly from a pointer to the heap; however, it is
|
// This can be calculated directly from a pointer to the heap; however, it is
|
||||||
|
@ -50,6 +50,7 @@ namespace internal {
|
|||||||
// Version 13: host objects have an explicit tag (rather than handling all
|
// Version 13: host objects have an explicit tag (rather than handling all
|
||||||
// unknown tags)
|
// unknown tags)
|
||||||
// Version 14: flags for JSArrayBufferViews
|
// Version 14: flags for JSArrayBufferViews
|
||||||
|
// Version 15: support for shared objects with an explicit tag
|
||||||
//
|
//
|
||||||
// WARNING: Increasing this value is a change which cannot safely be rolled
|
// WARNING: Increasing this value is a change which cannot safely be rolled
|
||||||
// back without breaking compatibility with data stored on disk. It is
|
// back without breaking compatibility with data stored on disk. It is
|
||||||
@ -58,7 +59,7 @@ namespace internal {
|
|||||||
//
|
//
|
||||||
// Recent changes are routinely reverted in preparation for branch, and this
|
// Recent changes are routinely reverted in preparation for branch, and this
|
||||||
// has been the cause of at least one bug in the past.
|
// has been the cause of at least one bug in the past.
|
||||||
static const uint32_t kLatestVersion = 14;
|
static const uint32_t kLatestVersion = 15;
|
||||||
static_assert(kLatestVersion == v8::CurrentValueSerializerFormatVersion(),
|
static_assert(kLatestVersion == v8::CurrentValueSerializerFormatVersion(),
|
||||||
"Exported format version must match latest version.");
|
"Exported format version must match latest version.");
|
||||||
|
|
||||||
@ -154,6 +155,8 @@ enum class SerializationTag : uint8_t {
|
|||||||
kArrayBufferView = 'V',
|
kArrayBufferView = 'V',
|
||||||
// Shared array buffer. transferID:uint32_t
|
// Shared array buffer. transferID:uint32_t
|
||||||
kSharedArrayBuffer = 'u',
|
kSharedArrayBuffer = 'u',
|
||||||
|
// A HeapObject shared across Isolates. sharedValueID:uint32_t
|
||||||
|
kSharedObject = 'p',
|
||||||
// A wasm module object transfer. next value is its index.
|
// A wasm module object transfer. next value is its index.
|
||||||
kWasmModuleTransfer = 'w',
|
kWasmModuleTransfer = 'w',
|
||||||
// The delegate is responsible for processing all following data.
|
// The delegate is responsible for processing all following data.
|
||||||
@ -245,6 +248,7 @@ ValueSerializer::ValueSerializer(Isolate* isolate,
|
|||||||
v8::ValueSerializer::Delegate* delegate)
|
v8::ValueSerializer::Delegate* delegate)
|
||||||
: isolate_(isolate),
|
: isolate_(isolate),
|
||||||
delegate_(delegate),
|
delegate_(delegate),
|
||||||
|
supports_shared_values_(delegate && delegate->SupportsSharedValues()),
|
||||||
zone_(isolate->allocator(), ZONE_NAME),
|
zone_(isolate->allocator(), ZONE_NAME),
|
||||||
id_map_(isolate->heap(), ZoneAllocationPolicy(&zone_)),
|
id_map_(isolate->heap(), ZoneAllocationPolicy(&zone_)),
|
||||||
array_buffer_transfer_map_(isolate->heap(),
|
array_buffer_transfer_map_(isolate->heap(),
|
||||||
@ -444,7 +448,11 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (InstanceTypeChecker::IsString(instance_type)) {
|
if (InstanceTypeChecker::IsString(instance_type)) {
|
||||||
WriteString(Handle<String>::cast(object));
|
auto string = Handle<String>::cast(object);
|
||||||
|
if (FLAG_shared_string_table && supports_shared_values_) {
|
||||||
|
return WriteSharedObject(String::Share(isolate_, string));
|
||||||
|
}
|
||||||
|
WriteString(string);
|
||||||
return ThrowIfOutOfMemory();
|
return ThrowIfOutOfMemory();
|
||||||
} else if (InstanceTypeChecker::IsJSReceiver(instance_type)) {
|
} else if (InstanceTypeChecker::IsJSReceiver(instance_type)) {
|
||||||
return WriteJSReceiver(Handle<JSReceiver>::cast(object));
|
return WriteJSReceiver(Handle<JSReceiver>::cast(object));
|
||||||
@ -1050,6 +1058,23 @@ Maybe<bool> ValueSerializer::WriteWasmMemory(Handle<WasmMemoryObject> object) {
|
|||||||
}
|
}
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
|
|
||||||
|
Maybe<bool> ValueSerializer::WriteSharedObject(Handle<HeapObject> object) {
|
||||||
|
// Currently only strings are shareable.
|
||||||
|
DCHECK(String::cast(*object).IsShared());
|
||||||
|
DCHECK(supports_shared_values_);
|
||||||
|
DCHECK_NOT_NULL(delegate_);
|
||||||
|
DCHECK(delegate_->SupportsSharedValues());
|
||||||
|
|
||||||
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
|
||||||
|
Maybe<uint32_t> index =
|
||||||
|
delegate_->GetSharedValueId(v8_isolate, Utils::ToLocal(object));
|
||||||
|
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
|
||||||
|
|
||||||
|
WriteTag(SerializationTag::kSharedObject);
|
||||||
|
WriteVarint(index.FromJust());
|
||||||
|
return ThrowIfOutOfMemory();
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) {
|
Maybe<bool> ValueSerializer::WriteHostObject(Handle<JSObject> object) {
|
||||||
WriteTag(SerializationTag::kHostObject);
|
WriteTag(SerializationTag::kHostObject);
|
||||||
if (!delegate_) {
|
if (!delegate_) {
|
||||||
@ -1127,6 +1152,7 @@ ValueDeserializer::ValueDeserializer(Isolate* isolate,
|
|||||||
delegate_(delegate),
|
delegate_(delegate),
|
||||||
position_(data.begin()),
|
position_(data.begin()),
|
||||||
end_(data.end()),
|
end_(data.end()),
|
||||||
|
supports_shared_values_(delegate && delegate->SupportsSharedValues()),
|
||||||
id_map_(isolate->global_handles()->Create(
|
id_map_(isolate->global_handles()->Create(
|
||||||
ReadOnlyRoots(isolate_).empty_fixed_array())) {}
|
ReadOnlyRoots(isolate_).empty_fixed_array())) {}
|
||||||
|
|
||||||
@ -1136,6 +1162,7 @@ ValueDeserializer::ValueDeserializer(Isolate* isolate, const uint8_t* data,
|
|||||||
delegate_(nullptr),
|
delegate_(nullptr),
|
||||||
position_(data),
|
position_(data),
|
||||||
end_(data + size),
|
end_(data + size),
|
||||||
|
supports_shared_values_(false),
|
||||||
id_map_(isolate->global_handles()->Create(
|
id_map_(isolate->global_handles()->Create(
|
||||||
ReadOnlyRoots(isolate_).empty_fixed_array())) {}
|
ReadOnlyRoots(isolate_).empty_fixed_array())) {}
|
||||||
|
|
||||||
@ -1394,6 +1421,13 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
|
|||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
case SerializationTag::kHostObject:
|
case SerializationTag::kHostObject:
|
||||||
return ReadHostObject();
|
return ReadHostObject();
|
||||||
|
case SerializationTag::kSharedObject:
|
||||||
|
if (version_ >= 15 && supports_shared_values_) {
|
||||||
|
return ReadSharedObject();
|
||||||
|
}
|
||||||
|
// If the delegate doesn't support shared values (e.g. older version, or
|
||||||
|
// is for deserializing from storage), treat the tag as unknown.
|
||||||
|
V8_FALLTHROUGH;
|
||||||
default:
|
default:
|
||||||
// Before there was an explicit tag for host objects, all unknown tags
|
// Before there was an explicit tag for host objects, all unknown tags
|
||||||
// were delegated to the host.
|
// were delegated to the host.
|
||||||
@ -2039,6 +2073,28 @@ MaybeHandle<WasmMemoryObject> ValueDeserializer::ReadWasmMemory() {
|
|||||||
}
|
}
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
|
|
||||||
|
MaybeHandle<HeapObject> ValueDeserializer::ReadSharedObject() {
|
||||||
|
STACK_CHECK(isolate_, MaybeHandle<HeapObject>());
|
||||||
|
DCHECK_GE(version_, 15);
|
||||||
|
DCHECK(supports_shared_values_);
|
||||||
|
DCHECK_NOT_NULL(delegate_);
|
||||||
|
DCHECK(delegate_->SupportsSharedValues());
|
||||||
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate_);
|
||||||
|
uint32_t shared_value_id;
|
||||||
|
Local<Value> shared_value;
|
||||||
|
if (!ReadVarint<uint32_t>().To(&shared_value_id) ||
|
||||||
|
!delegate_->GetSharedValueFromId(v8_isolate, shared_value_id)
|
||||||
|
.ToLocal(&shared_value)) {
|
||||||
|
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, HeapObject);
|
||||||
|
return MaybeHandle<HeapObject>();
|
||||||
|
}
|
||||||
|
Handle<HeapObject> shared_object =
|
||||||
|
Handle<HeapObject>::cast(Utils::OpenHandle(*shared_value));
|
||||||
|
// Currently only strings are shareable.
|
||||||
|
DCHECK(String::cast(*shared_object).IsShared());
|
||||||
|
return shared_object;
|
||||||
|
}
|
||||||
|
|
||||||
MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() {
|
MaybeHandle<JSObject> ValueDeserializer::ReadHostObject() {
|
||||||
if (!delegate_) return MaybeHandle<JSObject>();
|
if (!delegate_) return MaybeHandle<JSObject>();
|
||||||
STACK_CHECK(isolate_, MaybeHandle<JSObject>());
|
STACK_CHECK(isolate_, MaybeHandle<JSObject>());
|
||||||
|
@ -137,6 +137,8 @@ class ValueSerializer {
|
|||||||
Maybe<bool> WriteWasmMemory(Handle<WasmMemoryObject> object)
|
Maybe<bool> WriteWasmMemory(Handle<WasmMemoryObject> object)
|
||||||
V8_WARN_UNUSED_RESULT;
|
V8_WARN_UNUSED_RESULT;
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
|
Maybe<bool> WriteSharedObject(Handle<HeapObject> object)
|
||||||
|
V8_WARN_UNUSED_RESULT;
|
||||||
Maybe<bool> WriteHostObject(Handle<JSObject> object) V8_WARN_UNUSED_RESULT;
|
Maybe<bool> WriteHostObject(Handle<JSObject> object) V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -162,6 +164,7 @@ class ValueSerializer {
|
|||||||
uint8_t* buffer_ = nullptr;
|
uint8_t* buffer_ = nullptr;
|
||||||
size_t buffer_size_ = 0;
|
size_t buffer_size_ = 0;
|
||||||
size_t buffer_capacity_ = 0;
|
size_t buffer_capacity_ = 0;
|
||||||
|
const bool supports_shared_values_;
|
||||||
bool treat_array_buffer_views_as_host_objects_ = false;
|
bool treat_array_buffer_views_as_host_objects_ = false;
|
||||||
bool out_of_memory_ = false;
|
bool out_of_memory_ = false;
|
||||||
Zone zone_;
|
Zone zone_;
|
||||||
@ -286,6 +289,7 @@ class ValueDeserializer {
|
|||||||
MaybeHandle<JSObject> ReadWasmModuleTransfer() V8_WARN_UNUSED_RESULT;
|
MaybeHandle<JSObject> ReadWasmModuleTransfer() V8_WARN_UNUSED_RESULT;
|
||||||
MaybeHandle<WasmMemoryObject> ReadWasmMemory() V8_WARN_UNUSED_RESULT;
|
MaybeHandle<WasmMemoryObject> ReadWasmMemory() V8_WARN_UNUSED_RESULT;
|
||||||
#endif // V8_ENABLE_WEBASSEMBLY
|
#endif // V8_ENABLE_WEBASSEMBLY
|
||||||
|
MaybeHandle<HeapObject> ReadSharedObject() V8_WARN_UNUSED_RESULT;
|
||||||
MaybeHandle<JSObject> ReadHostObject() V8_WARN_UNUSED_RESULT;
|
MaybeHandle<JSObject> ReadHostObject() V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -305,6 +309,7 @@ class ValueDeserializer {
|
|||||||
v8::ValueDeserializer::Delegate* const delegate_;
|
v8::ValueDeserializer::Delegate* const delegate_;
|
||||||
const uint8_t* position_;
|
const uint8_t* position_;
|
||||||
const uint8_t* const end_;
|
const uint8_t* const end_;
|
||||||
|
const bool supports_shared_values_;
|
||||||
uint32_t version_ = 0;
|
uint32_t version_ = 0;
|
||||||
uint32_t next_id_ = 0;
|
uint32_t next_id_ = 0;
|
||||||
|
|
||||||
|
@ -1460,5 +1460,21 @@ RUNTIME_FUNCTION(Runtime_BigIntMaxLengthBits) {
|
|||||||
return *isolate->factory()->NewNumber(BigInt::kMaxLengthBits);
|
return *isolate->factory()->NewNumber(BigInt::kMaxLengthBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RUNTIME_FUNCTION(Runtime_IsSameHeapObject) {
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
DCHECK_EQ(2, args.length());
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(HeapObject, obj1, 0);
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(HeapObject, obj2, 1);
|
||||||
|
return isolate->heap()->ToBoolean(obj1->address() == obj2->address());
|
||||||
|
}
|
||||||
|
|
||||||
|
RUNTIME_FUNCTION(Runtime_IsSharedString) {
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
DCHECK_EQ(1, args.length());
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(HeapObject, obj, 0);
|
||||||
|
return isolate->heap()->ToBoolean(obj->IsString() &&
|
||||||
|
Handle<String>::cast(obj)->IsShared());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -521,6 +521,8 @@ namespace internal {
|
|||||||
F(IsConcatSpreadableProtector, 0, 1) \
|
F(IsConcatSpreadableProtector, 0, 1) \
|
||||||
F(IsConcurrentRecompilationSupported, 0, 1) \
|
F(IsConcurrentRecompilationSupported, 0, 1) \
|
||||||
F(IsDictPropertyConstTrackingEnabled, 0, 1) \
|
F(IsDictPropertyConstTrackingEnabled, 0, 1) \
|
||||||
|
F(IsSameHeapObject, 2, 1) \
|
||||||
|
F(IsSharedString, 1, 1) \
|
||||||
F(MapIteratorProtector, 0, 1) \
|
F(MapIteratorProtector, 0, 1) \
|
||||||
F(NeverOptimizeFunction, 1, 1) \
|
F(NeverOptimizeFunction, 1, 1) \
|
||||||
F(NewRegExpWithBacktrackLimit, 3, 1) \
|
F(NewRegExpWithBacktrackLimit, 3, 1) \
|
||||||
|
@ -431,7 +431,6 @@ void Deserializer<IsolateT>::PostProcessNewObject(Handle<Map> map,
|
|||||||
isolate()->string_table()->LookupKey(isolate(), &key);
|
isolate()->string_table()->LookupKey(isolate(), &key);
|
||||||
|
|
||||||
if (*result != *string) {
|
if (*result != *string) {
|
||||||
DCHECK(!string->IsShared());
|
|
||||||
string->MakeThin(isolate(), *result);
|
string->MakeThin(isolate(), *result);
|
||||||
// Mutate the given object handle so that the backreference entry is
|
// Mutate the given object handle so that the backreference entry is
|
||||||
// also updated.
|
// also updated.
|
||||||
|
37
test/mjsunit/shared-string.js
Normal file
37
test/mjsunit/shared-string.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2021 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.
|
||||||
|
//
|
||||||
|
// Flags: --shared-string-table --allow-natives-syntax
|
||||||
|
|
||||||
|
if (this.Worker) {
|
||||||
|
|
||||||
|
(function TestSharedStringPostMessage() {
|
||||||
|
let workerScript =
|
||||||
|
`postMessage("started");
|
||||||
|
onmessage = function(str) {
|
||||||
|
if (!%IsSharedString(str)) {
|
||||||
|
throw new Error("str isn't shared");
|
||||||
|
}
|
||||||
|
postMessage(str);
|
||||||
|
};`;
|
||||||
|
|
||||||
|
let worker = new Worker(workerScript, { type: 'string' });
|
||||||
|
let started = worker.getMessage();
|
||||||
|
assertTrue(%IsSharedString(started));
|
||||||
|
assertEquals("started", started);
|
||||||
|
|
||||||
|
// The string literal appears in source and is internalized, so should
|
||||||
|
// already be shared.
|
||||||
|
let str_to_send = 'foo';
|
||||||
|
assertTrue(%IsSharedString(str_to_send));
|
||||||
|
worker.postMessage(str_to_send);
|
||||||
|
let str_received = worker.getMessage();
|
||||||
|
assertTrue(%IsSharedString(str_received));
|
||||||
|
// Object.is and === won't check pointer equality of Strings.
|
||||||
|
assertTrue(%IsSameHeapObject(str_to_send, str_received));
|
||||||
|
|
||||||
|
worker.terminate();
|
||||||
|
})();
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user