Revert of [wasm] Transferrable modules (patchset #13 id:280001 of https://codereview.chromium.org/2748473004/ )
Reason for revert:
Breaks layout tests:
https://build.chromium.org/p/client.v8.fyi/builders/V8-Blink%20Linux%2064/builds/14312
See https://github.com/v8/v8/wiki/Blink-layout-tests
Original issue's description:
> [wasm] Transferrable modules
>
> We want to restrict structured cloning in Chrome to:
> - postMessage senders and receivers that are co-located
> in the same process
> - indexedDB (just https).
>
> For context, on the Chrome side, we will achieve the postMessage part
> by using a mechanism similar to transferrables: the
> SerializedScriptValue will have a list of wasm modules, separate from
> the serialized data stream; and this list won't be copied cross
> process boundaries. The IDB part is achieved by explicitly opting in
> reading/writing to the serialization stream. To block attack vectors
> in IPC cases, the default for deserialization will be to expect data
> in the wasm transfers list.
>
> This change is the V8 side necessary to enabling this design. We
> introduce TransferrableModule, an opaque datatype exposed to the
> embedder. Internally, TransferrableModules are just serialized data,
> because we don't have a better mechanism, at the moment, for
> de-contextualizing/re-contextualizing wasm modules (wrt Isolate and
> Context).
>
> The chrome defaults will be implemented in the
> serialization/deserialization delegates on that side. For the v8 side
> of things, in the absence of a serialization delegate, the V8
> serializer will write to serialization stream. In the absence of a
> deserialization delegate, the deserializer won't work. This asymmetry
> is intentional - it communicates to the embedder the need to make a
> policy decision, otherwise wasm serialization/deserialization won't
> work "out of the box".
>
> BUG=v8:6079
>
> Review-Url: https://codereview.chromium.org/2748473004
> Cr-Commit-Position: refs/heads/master@{#43955}
> Committed: 99743ad460
TBR=jbroman@chromium.org,bradnelson@chromium.org,mtrofin@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:6079
Review-Url: https://codereview.chromium.org/2762163002
Cr-Commit-Position: refs/heads/master@{#43981}
This commit is contained in:
parent
6deb226705
commit
e538b70e1a
51
include/v8.h
51
include/v8.h
@ -108,7 +108,6 @@ class Private;
|
||||
class Uint32;
|
||||
class Utils;
|
||||
class Value;
|
||||
class WasmCompiledModule;
|
||||
template <class T> class Local;
|
||||
template <class T>
|
||||
class MaybeLocal;
|
||||
@ -1710,8 +1709,6 @@ class V8_EXPORT ValueSerializer {
|
||||
virtual Maybe<uint32_t> GetSharedArrayBufferId(
|
||||
Isolate* isolate, Local<SharedArrayBuffer> shared_array_buffer);
|
||||
|
||||
virtual Maybe<uint32_t> GetWasmModuleTransferId(
|
||||
Isolate* isolate, Local<WasmCompiledModule> module);
|
||||
/*
|
||||
* 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
|
||||
@ -1822,13 +1819,6 @@ class V8_EXPORT ValueDeserializer {
|
||||
* MaybeLocal<Object>() returned.
|
||||
*/
|
||||
virtual MaybeLocal<Object> ReadHostObject(Isolate* isolate);
|
||||
|
||||
/*
|
||||
* Get a WasmCompiledModule given a transfer_id previously provided
|
||||
* by ValueSerializer::GetWasmModuleTransferId
|
||||
*/
|
||||
virtual MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(
|
||||
Isolate* isolate, uint32_t transfer_id);
|
||||
};
|
||||
|
||||
ValueDeserializer(Isolate* isolate, const uint8_t* data, size_t size);
|
||||
@ -1871,11 +1861,6 @@ class V8_EXPORT ValueDeserializer {
|
||||
*/
|
||||
void SetSupportsLegacyWireFormat(bool supports_legacy_wire_format);
|
||||
|
||||
/*
|
||||
* Expect inline wasm in the data stream (rather than in-memory transfer)
|
||||
*/
|
||||
void SetExpectInlineWasm(bool allow_inline_wasm);
|
||||
|
||||
/*
|
||||
* Reads the underlying wire format version. Likely mostly to be useful to
|
||||
* legacy code reading old wire format versions. Must be called after
|
||||
@ -3918,37 +3903,6 @@ class V8_EXPORT WasmCompiledModule : public Object {
|
||||
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> SerializedModule;
|
||||
// A buffer that is owned by the caller.
|
||||
typedef std::pair<const uint8_t*, size_t> CallerOwnedBuffer;
|
||||
|
||||
// An opaque, native heap object for transferring wasm modules. It
|
||||
// supports move semantics, and does not support copy semantics.
|
||||
class TransferrableModule final {
|
||||
public:
|
||||
TransferrableModule(TransferrableModule&& src) = default;
|
||||
TransferrableModule(const TransferrableModule& src) = delete;
|
||||
|
||||
TransferrableModule& operator=(TransferrableModule&& src) = default;
|
||||
TransferrableModule& operator=(const TransferrableModule& src) = delete;
|
||||
|
||||
private:
|
||||
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> OwnedBuffer;
|
||||
friend class WasmCompiledModule;
|
||||
TransferrableModule(OwnedBuffer&& code, OwnedBuffer&& bytes)
|
||||
: compiled_code(std::move(code)), wire_bytes(std::move(bytes)) {}
|
||||
|
||||
OwnedBuffer compiled_code = {nullptr, 0};
|
||||
OwnedBuffer wire_bytes = {nullptr, 0};
|
||||
};
|
||||
|
||||
// Get an in-memory, non-persistable, and context-independent (meaning,
|
||||
// suitable for transfer to another Isolate and Context) representation
|
||||
// of this wasm compiled module.
|
||||
TransferrableModule GetTransferrableModule();
|
||||
|
||||
// Efficiently re-create a WasmCompiledModule, without recompiling, from
|
||||
// a TransferrableModule.
|
||||
static MaybeLocal<WasmCompiledModule> FromTransferrableModule(
|
||||
Isolate* isolate, const TransferrableModule&);
|
||||
|
||||
// Get the wasm-encoded bytes that were used to compile this module.
|
||||
Local<String> GetWasmWireBytes();
|
||||
|
||||
@ -3970,11 +3924,6 @@ class V8_EXPORT WasmCompiledModule : public Object {
|
||||
static MaybeLocal<WasmCompiledModule> Compile(Isolate* isolate,
|
||||
const uint8_t* start,
|
||||
size_t length);
|
||||
static CallerOwnedBuffer AsCallerOwned(
|
||||
const TransferrableModule::OwnedBuffer& buff) {
|
||||
return {buff.first.get(), buff.second};
|
||||
}
|
||||
|
||||
WasmCompiledModule();
|
||||
static void CheckCast(Value* obj);
|
||||
};
|
||||
|
48
src/api.cc
48
src/api.cc
@ -3125,11 +3125,6 @@ Maybe<uint32_t> ValueSerializer::Delegate::GetSharedArrayBufferId(
|
||||
return Nothing<uint32_t>();
|
||||
}
|
||||
|
||||
Maybe<uint32_t> ValueSerializer::Delegate::GetWasmModuleTransferId(
|
||||
Isolate* v8_isolate, Local<WasmCompiledModule> module) {
|
||||
return Nothing<uint32_t>();
|
||||
}
|
||||
|
||||
void* ValueSerializer::Delegate::ReallocateBufferMemory(void* old_buffer,
|
||||
size_t size,
|
||||
size_t* actual_size) {
|
||||
@ -3218,15 +3213,6 @@ MaybeLocal<Object> ValueDeserializer::Delegate::ReadHostObject(
|
||||
return MaybeLocal<Object>();
|
||||
}
|
||||
|
||||
MaybeLocal<WasmCompiledModule> ValueDeserializer::Delegate::GetWasmModuleFromId(
|
||||
Isolate* v8_isolate, uint32_t id) {
|
||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||
isolate->ScheduleThrow(*isolate->factory()->NewError(
|
||||
isolate->error_function(),
|
||||
i::MessageTemplate::kDataCloneDeserializationError));
|
||||
return MaybeLocal<WasmCompiledModule>();
|
||||
}
|
||||
|
||||
struct ValueDeserializer::PrivateData {
|
||||
PrivateData(i::Isolate* i, i::Vector<const uint8_t> data, Delegate* delegate)
|
||||
: isolate(i), deserializer(i, data, delegate) {}
|
||||
@ -3289,10 +3275,6 @@ void ValueDeserializer::SetSupportsLegacyWireFormat(
|
||||
private_->supports_legacy_wire_format = supports_legacy_wire_format;
|
||||
}
|
||||
|
||||
void ValueDeserializer::SetExpectInlineWasm(bool expect_inline_wasm) {
|
||||
private_->deserializer.set_expect_inline_wasm(expect_inline_wasm);
|
||||
}
|
||||
|
||||
uint32_t ValueDeserializer::GetWireFormatVersion() const {
|
||||
CHECK(!private_->has_aborted);
|
||||
return private_->deserializer.GetWireFormatVersion();
|
||||
@ -7524,36 +7506,6 @@ Local<String> WasmCompiledModule::GetWasmWireBytes() {
|
||||
return Local<String>::Cast(Utils::ToLocal(wire_bytes));
|
||||
}
|
||||
|
||||
// Currently, wasm modules are bound, both to Isolate and to
|
||||
// the Context they were created in. The currently-supported means to
|
||||
// decontextualize and then re-contextualize a module is via
|
||||
// serialization/deserialization.
|
||||
WasmCompiledModule::TransferrableModule
|
||||
WasmCompiledModule::GetTransferrableModule() {
|
||||
i::DisallowHeapAllocation no_gc;
|
||||
WasmCompiledModule::SerializedModule compiled_part = Serialize();
|
||||
|
||||
Local<String> wire_bytes = GetWasmWireBytes();
|
||||
size_t wire_size = static_cast<size_t>(wire_bytes->Length());
|
||||
uint8_t* bytes = new uint8_t[wire_size];
|
||||
wire_bytes->WriteOneByte(bytes, 0, wire_bytes->Length());
|
||||
|
||||
return TransferrableModule(
|
||||
std::move(compiled_part),
|
||||
std::make_pair(
|
||||
std::unique_ptr<const uint8_t[]>(const_cast<const uint8_t*>(bytes)),
|
||||
wire_size));
|
||||
}
|
||||
|
||||
MaybeLocal<WasmCompiledModule> WasmCompiledModule::FromTransferrableModule(
|
||||
Isolate* isolate,
|
||||
const WasmCompiledModule::TransferrableModule& transferrable_module) {
|
||||
MaybeLocal<WasmCompiledModule> ret =
|
||||
Deserialize(isolate, AsCallerOwned(transferrable_module.compiled_code),
|
||||
AsCallerOwned(transferrable_module.wire_bytes));
|
||||
return ret;
|
||||
}
|
||||
|
||||
WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
|
||||
i::Handle<i::JSObject> obj =
|
||||
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
|
||||
|
@ -126,8 +126,6 @@ enum class SerializationTag : uint8_t {
|
||||
// wasmWireByteLength:uint32_t, then raw data
|
||||
// compiledDataLength:uint32_t, then raw data
|
||||
kWasmModule = 'W',
|
||||
// A wasm module object transfer. next value is its index.
|
||||
kWasmModuleTransfer = 'w',
|
||||
// The delegate is responsible for processing all following data.
|
||||
// This "escapes" to whatever wire format the delegate chooses.
|
||||
kHostObject = '\\',
|
||||
@ -805,19 +803,6 @@ Maybe<bool> ValueSerializer::WriteJSArrayBufferView(JSArrayBufferView* view) {
|
||||
}
|
||||
|
||||
Maybe<bool> ValueSerializer::WriteWasmModule(Handle<JSObject> object) {
|
||||
if (delegate_ != nullptr) {
|
||||
Maybe<uint32_t> transfer_id = delegate_->GetWasmModuleTransferId(
|
||||
reinterpret_cast<v8::Isolate*>(isolate_),
|
||||
v8::Local<v8::WasmCompiledModule>::Cast(Utils::ToLocal(object)));
|
||||
RETURN_VALUE_IF_SCHEDULED_EXCEPTION(isolate_, Nothing<bool>());
|
||||
uint32_t id = 0;
|
||||
if (transfer_id.To(&id)) {
|
||||
WriteTag(SerializationTag::kWasmModuleTransfer);
|
||||
WriteVarint<uint32_t>(id);
|
||||
return Just(true);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<WasmCompiledModule> compiled_part(
|
||||
WasmCompiledModule::cast(object->GetEmbedderField(0)), isolate_);
|
||||
WasmEncodingTag encoding_tag = WasmEncodingTag::kRawBytes;
|
||||
@ -1165,8 +1150,6 @@ MaybeHandle<Object> ValueDeserializer::ReadObjectInternal() {
|
||||
}
|
||||
case SerializationTag::kWasmModule:
|
||||
return ReadWasmModule();
|
||||
case SerializationTag::kWasmModuleTransfer:
|
||||
return ReadWasmModuleTransfer();
|
||||
case SerializationTag::kHostObject:
|
||||
return ReadHostObject();
|
||||
default:
|
||||
@ -1612,32 +1595,8 @@ MaybeHandle<JSArrayBufferView> ValueDeserializer::ReadJSArrayBufferView(
|
||||
return typed_array;
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModuleTransfer() {
|
||||
if (FLAG_wasm_disable_structured_cloning || expect_inline_wasm()) {
|
||||
return MaybeHandle<JSObject>();
|
||||
}
|
||||
|
||||
uint32_t transfer_id = 0;
|
||||
Local<Value> module_value;
|
||||
if (!ReadVarint<uint32_t>().To(&transfer_id) || delegate_ == nullptr ||
|
||||
!delegate_
|
||||
->GetWasmModuleFromId(reinterpret_cast<v8::Isolate*>(isolate_),
|
||||
transfer_id)
|
||||
.ToLocal(&module_value)) {
|
||||
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
|
||||
return MaybeHandle<JSObject>();
|
||||
}
|
||||
uint32_t id = next_id_++;
|
||||
Handle<JSObject> module =
|
||||
Handle<JSObject>::cast(Utils::OpenHandle(*module_value));
|
||||
AddObjectWithID(id, module);
|
||||
return module;
|
||||
}
|
||||
|
||||
MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
|
||||
if (FLAG_wasm_disable_structured_cloning || !expect_inline_wasm()) {
|
||||
return MaybeHandle<JSObject>();
|
||||
}
|
||||
if (FLAG_wasm_disable_structured_cloning) return MaybeHandle<JSObject>();
|
||||
|
||||
Vector<const uint8_t> encoding_tag;
|
||||
if (!ReadRawBytes(sizeof(WasmEncodingTag)).To(&encoding_tag) ||
|
||||
@ -1666,22 +1625,21 @@ MaybeHandle<JSObject> ValueDeserializer::ReadWasmModule() {
|
||||
// Try to deserialize the compiled module first.
|
||||
ScriptData script_data(compiled_bytes.start(), compiled_bytes.length());
|
||||
Handle<FixedArray> compiled_part;
|
||||
MaybeHandle<JSObject> result;
|
||||
if (WasmCompiledModuleSerializer::DeserializeWasmModule(
|
||||
isolate_, &script_data, wire_bytes)
|
||||
.ToHandle(&compiled_part)) {
|
||||
result = WasmModuleObject::New(
|
||||
return WasmModuleObject::New(
|
||||
isolate_, Handle<WasmCompiledModule>::cast(compiled_part));
|
||||
} else {
|
||||
}
|
||||
|
||||
// If that fails, recompile.
|
||||
MaybeHandle<JSObject> result;
|
||||
{
|
||||
wasm::ErrorThrower thrower(isolate_, "ValueDeserializer::ReadWasmModule");
|
||||
result = wasm::SyncCompile(isolate_, &thrower,
|
||||
wasm::ModuleWireBytes(wire_bytes));
|
||||
}
|
||||
RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate_, JSObject);
|
||||
uint32_t id = next_id_++;
|
||||
if (!result.is_null()) {
|
||||
AddObjectWithID(id, result.ToHandleChecked());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,6 @@ class JSValue;
|
||||
class Object;
|
||||
class Oddball;
|
||||
class Smi;
|
||||
class WasmModuleObject;
|
||||
|
||||
enum class SerializationTag : uint8_t;
|
||||
|
||||
@ -219,9 +218,6 @@ class ValueDeserializer {
|
||||
bool ReadUint64(uint64_t* value) WARN_UNUSED_RESULT;
|
||||
bool ReadDouble(double* value) WARN_UNUSED_RESULT;
|
||||
bool ReadRawBytes(size_t length, const void** data) WARN_UNUSED_RESULT;
|
||||
void set_expect_inline_wasm(bool expect_inline_wasm) {
|
||||
expect_inline_wasm_ = expect_inline_wasm;
|
||||
}
|
||||
|
||||
private:
|
||||
// Reading the wire format.
|
||||
@ -234,7 +230,6 @@ class ValueDeserializer {
|
||||
Maybe<T> ReadZigZag() WARN_UNUSED_RESULT;
|
||||
Maybe<double> ReadDouble() WARN_UNUSED_RESULT;
|
||||
Maybe<Vector<const uint8_t>> ReadRawBytes(int size) WARN_UNUSED_RESULT;
|
||||
bool expect_inline_wasm() const { return expect_inline_wasm_; }
|
||||
|
||||
// Reads a string if it matches the one provided.
|
||||
// Returns true if this was the case. Otherwise, nothing is consumed.
|
||||
@ -268,7 +263,6 @@ class ValueDeserializer {
|
||||
MaybeHandle<JSArrayBufferView> ReadJSArrayBufferView(
|
||||
Handle<JSArrayBuffer> buffer) WARN_UNUSED_RESULT;
|
||||
MaybeHandle<JSObject> ReadWasmModule() WARN_UNUSED_RESULT;
|
||||
MaybeHandle<JSObject> ReadWasmModuleTransfer() WARN_UNUSED_RESULT;
|
||||
MaybeHandle<JSObject> ReadHostObject() WARN_UNUSED_RESULT;
|
||||
|
||||
/*
|
||||
@ -291,7 +285,6 @@ class ValueDeserializer {
|
||||
PretenureFlag pretenure_;
|
||||
uint32_t version_ = 0;
|
||||
uint32_t next_id_ = 0;
|
||||
bool expect_inline_wasm_ = false;
|
||||
|
||||
// Always global handles.
|
||||
Handle<FixedArray> id_map_;
|
||||
|
@ -469,43 +469,6 @@ TEST(BlockWasmCodeGenAtDeserialization) {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
TEST(TransferrableWasmModules) {
|
||||
v8::internal::AccountingAllocator allocator;
|
||||
Zone zone(&allocator, ZONE_NAME);
|
||||
|
||||
ZoneBuffer buffer(&zone);
|
||||
WasmSerializationTest::BuildWireBytes(&zone, &buffer);
|
||||
|
||||
Isolate* from_isolate = CcTest::InitIsolateOnce();
|
||||
ErrorThrower thrower(from_isolate, "");
|
||||
std::vector<v8::WasmCompiledModule::TransferrableModule> store;
|
||||
{
|
||||
HandleScope scope(from_isolate);
|
||||
testing::SetupIsolateForWasmModule(from_isolate);
|
||||
|
||||
MaybeHandle<WasmModuleObject> module_object = SyncCompile(
|
||||
from_isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
|
||||
v8::Local<v8::WasmCompiledModule> v8_module =
|
||||
v8::Local<v8::WasmCompiledModule>::Cast(v8::Utils::ToLocal(
|
||||
Handle<JSObject>::cast(module_object.ToHandleChecked())));
|
||||
store.push_back(v8_module->GetTransferrableModule());
|
||||
}
|
||||
|
||||
{
|
||||
v8::Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator =
|
||||
from_isolate->array_buffer_allocator();
|
||||
v8::Isolate* to_isolate = v8::Isolate::New(create_params);
|
||||
v8::HandleScope new_scope(to_isolate);
|
||||
v8::Local<v8::Context> deserialization_context =
|
||||
v8::Context::New(to_isolate);
|
||||
deserialization_context->Enter();
|
||||
v8::MaybeLocal<v8::WasmCompiledModule> mod =
|
||||
v8::WasmCompiledModule::FromTransferrableModule(to_isolate, store[0]);
|
||||
CHECK(!mod.IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MemorySize) {
|
||||
{
|
||||
// Initial memory size is 16, see wasm-module-builder.cc
|
||||
|
@ -74,9 +74,6 @@ class ValueSerializerTest : public TestWithIsolate {
|
||||
return deserialization_context_;
|
||||
}
|
||||
|
||||
bool ExpectInlineWasm() const { return expect_inline_wasm_; }
|
||||
void SetExpectInlineWasm(bool value) { expect_inline_wasm_ = value; }
|
||||
|
||||
// Overridden in more specific fixtures.
|
||||
virtual ValueSerializer::Delegate* GetSerializerDelegate() { return nullptr; }
|
||||
virtual void BeforeEncode(ValueSerializer*) {}
|
||||
@ -175,7 +172,6 @@ class ValueSerializerTest : public TestWithIsolate {
|
||||
static_cast<int>(data.size()),
|
||||
GetDeserializerDelegate());
|
||||
deserializer.SetSupportsLegacyWireFormat(true);
|
||||
deserializer.SetExpectInlineWasm(ExpectInlineWasm());
|
||||
BeforeDecode(&deserializer);
|
||||
ASSERT_TRUE(deserializer.ReadHeader(context).FromMaybe(false));
|
||||
Local<Value> result;
|
||||
@ -200,7 +196,6 @@ class ValueSerializerTest : public TestWithIsolate {
|
||||
static_cast<int>(data.size()),
|
||||
GetDeserializerDelegate());
|
||||
deserializer.SetSupportsLegacyWireFormat(true);
|
||||
deserializer.SetExpectInlineWasm(ExpectInlineWasm());
|
||||
BeforeDecode(&deserializer);
|
||||
ASSERT_TRUE(deserializer.ReadHeader(context).FromMaybe(false));
|
||||
ASSERT_EQ(0u, deserializer.GetWireFormatVersion());
|
||||
@ -224,7 +219,6 @@ class ValueSerializerTest : public TestWithIsolate {
|
||||
static_cast<int>(data.size()),
|
||||
GetDeserializerDelegate());
|
||||
deserializer.SetSupportsLegacyWireFormat(true);
|
||||
deserializer.SetExpectInlineWasm(ExpectInlineWasm());
|
||||
BeforeDecode(&deserializer);
|
||||
Maybe<bool> header_result = deserializer.ReadHeader(context);
|
||||
if (header_result.IsNothing()) {
|
||||
@ -281,7 +275,6 @@ class ValueSerializerTest : public TestWithIsolate {
|
||||
Local<Context> deserialization_context_;
|
||||
Local<FunctionTemplate> host_object_constructor_template_;
|
||||
i::Isolate* isolate_;
|
||||
bool expect_inline_wasm_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ValueSerializerTest);
|
||||
};
|
||||
@ -2601,44 +2594,7 @@ TEST_F(ValueSerializerTestWithHostArrayBufferView, RoundTripUint8ArrayInput) {
|
||||
// mostly checks that the logic to embed it in structured clone serialization
|
||||
// works correctly.
|
||||
|
||||
// A simple module which exports an "increment" function.
|
||||
// Copied from test/mjsunit/wasm/incrementer.wasm.
|
||||
const unsigned char kIncrementerWasm[] = {
|
||||
0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127,
|
||||
3, 2, 1, 0, 7, 13, 1, 9, 105, 110, 99, 114, 101, 109, 101, 110,
|
||||
116, 0, 0, 10, 9, 1, 7, 0, 32, 0, 65, 1, 106, 11,
|
||||
};
|
||||
|
||||
class ValueSerializerTestWithWasm : public ValueSerializerTest {
|
||||
public:
|
||||
static const char* kUnsupportedSerialization;
|
||||
|
||||
ValueSerializerTestWithWasm()
|
||||
: serialize_delegate_(&transfer_modules_),
|
||||
deserialize_delegate_(&transfer_modules_) {}
|
||||
|
||||
void Reset() {
|
||||
current_serializer_delegate_ = nullptr;
|
||||
transfer_modules_.clear();
|
||||
SetExpectInlineWasm(false);
|
||||
}
|
||||
|
||||
void EnableTransferSerialization() {
|
||||
current_serializer_delegate_ = &serialize_delegate_;
|
||||
}
|
||||
|
||||
void EnableTransferDeserialization() {
|
||||
current_deserializer_delegate_ = &deserialize_delegate_;
|
||||
}
|
||||
|
||||
void EnableThrowingSerializer() {
|
||||
current_serializer_delegate_ = &throwing_serializer_;
|
||||
}
|
||||
|
||||
void EnableDefaultDeserializer() {
|
||||
current_deserializer_delegate_ = &default_deserializer_;
|
||||
}
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
g_saved_flag = i::FLAG_expose_wasm;
|
||||
@ -2652,243 +2608,32 @@ class ValueSerializerTestWithWasm : public ValueSerializerTest {
|
||||
g_saved_flag = false;
|
||||
}
|
||||
|
||||
class ThrowingSerializer : public ValueSerializer::Delegate {
|
||||
public:
|
||||
Maybe<uint32_t> GetWasmModuleTransferId(
|
||||
Isolate* isolate, Local<WasmCompiledModule> module) override {
|
||||
isolate->ThrowException(Exception::Error(
|
||||
String::NewFromOneByte(
|
||||
isolate,
|
||||
reinterpret_cast<const uint8_t*>(kUnsupportedSerialization),
|
||||
NewStringType::kNormal)
|
||||
.ToLocalChecked()));
|
||||
return Nothing<uint32_t>();
|
||||
}
|
||||
|
||||
void ThrowDataCloneError(Local<String> message) override { UNREACHABLE(); }
|
||||
};
|
||||
|
||||
class SerializeToTransfer : public ValueSerializer::Delegate {
|
||||
public:
|
||||
SerializeToTransfer(
|
||||
std::vector<WasmCompiledModule::TransferrableModule>* modules)
|
||||
: modules_(modules) {}
|
||||
Maybe<uint32_t> GetWasmModuleTransferId(
|
||||
Isolate* isolate, Local<WasmCompiledModule> module) override {
|
||||
modules_->push_back(module->GetTransferrableModule());
|
||||
return Just(static_cast<uint32_t>(modules_->size()) - 1);
|
||||
}
|
||||
|
||||
void ThrowDataCloneError(Local<String> message) override { UNREACHABLE(); }
|
||||
|
||||
private:
|
||||
std::vector<WasmCompiledModule::TransferrableModule>* modules_;
|
||||
};
|
||||
|
||||
class DeserializeFromTransfer : public ValueDeserializer::Delegate {
|
||||
public:
|
||||
DeserializeFromTransfer(
|
||||
std::vector<WasmCompiledModule::TransferrableModule>* modules)
|
||||
: modules_(modules) {}
|
||||
|
||||
MaybeLocal<WasmCompiledModule> GetWasmModuleFromId(Isolate* isolate,
|
||||
uint32_t id) override {
|
||||
return WasmCompiledModule::FromTransferrableModule(isolate,
|
||||
modules_->at(id));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<WasmCompiledModule::TransferrableModule>* modules_;
|
||||
};
|
||||
|
||||
ValueSerializer::Delegate* GetSerializerDelegate() override {
|
||||
return current_serializer_delegate_;
|
||||
}
|
||||
|
||||
ValueDeserializer::Delegate* GetDeserializerDelegate() override {
|
||||
return current_deserializer_delegate_;
|
||||
}
|
||||
|
||||
Local<WasmCompiledModule> MakeWasm() {
|
||||
return WasmCompiledModule::DeserializeOrCompile(
|
||||
isolate(), {nullptr, 0},
|
||||
{kIncrementerWasm, sizeof(kIncrementerWasm)})
|
||||
.ToLocalChecked();
|
||||
}
|
||||
|
||||
void ExpectPass() {
|
||||
RoundTripTest(
|
||||
[this]() { return MakeWasm(); },
|
||||
[this](Local<Value> value) {
|
||||
ASSERT_TRUE(value->IsWebAssemblyCompiledModule());
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool(
|
||||
"new WebAssembly.Instance(result).exports.increment(8) === 9"));
|
||||
});
|
||||
}
|
||||
|
||||
void ExpectFail() {
|
||||
EncodeTest(
|
||||
[this]() { return MakeWasm(); },
|
||||
[this](const std::vector<uint8_t>& data) { InvalidDecodeTest(data); });
|
||||
}
|
||||
|
||||
Local<Value> GetComplexObjectWithDuplicate() {
|
||||
Local<Value> wasm_module = MakeWasm();
|
||||
serialization_context()
|
||||
->Global()
|
||||
->CreateDataProperty(serialization_context(),
|
||||
StringFromUtf8("wasm_module"), wasm_module)
|
||||
.FromMaybe(false);
|
||||
Local<Script> script =
|
||||
Script::Compile(
|
||||
serialization_context(),
|
||||
StringFromUtf8("({mod1: wasm_module, num: 2, mod2: wasm_module})"))
|
||||
.ToLocalChecked();
|
||||
return script->Run(serialization_context()).ToLocalChecked();
|
||||
}
|
||||
|
||||
void VerifyComplexObject(Local<Value> value) {
|
||||
ASSERT_TRUE(value->IsObject());
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool(
|
||||
"result.mod1 instanceof WebAssembly.Module"));
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool(
|
||||
"result.mod2 instanceof WebAssembly.Module"));
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool("result.num === 2"));
|
||||
}
|
||||
|
||||
Local<Value> GetComplexObjectWithMany() {
|
||||
Local<Value> wasm_module1 = MakeWasm();
|
||||
Local<Value> wasm_module2 = MakeWasm();
|
||||
serialization_context()
|
||||
->Global()
|
||||
->CreateDataProperty(serialization_context(),
|
||||
StringFromUtf8("wasm_module1"), wasm_module1)
|
||||
.FromMaybe(false);
|
||||
serialization_context()
|
||||
->Global()
|
||||
->CreateDataProperty(serialization_context(),
|
||||
StringFromUtf8("wasm_module2"), wasm_module2)
|
||||
.FromMaybe(false);
|
||||
Local<Script> script =
|
||||
Script::Compile(
|
||||
serialization_context(),
|
||||
StringFromUtf8(
|
||||
"({mod1: wasm_module1, num: 2, mod2: wasm_module2})"))
|
||||
.ToLocalChecked();
|
||||
return script->Run(serialization_context()).ToLocalChecked();
|
||||
}
|
||||
|
||||
private:
|
||||
static bool g_saved_flag;
|
||||
std::vector<WasmCompiledModule::TransferrableModule> transfer_modules_;
|
||||
SerializeToTransfer serialize_delegate_;
|
||||
DeserializeFromTransfer deserialize_delegate_;
|
||||
ValueSerializer::Delegate* current_serializer_delegate_ = nullptr;
|
||||
ValueDeserializer::Delegate* current_deserializer_delegate_ = nullptr;
|
||||
ThrowingSerializer throwing_serializer_;
|
||||
ValueDeserializer::Delegate default_deserializer_;
|
||||
};
|
||||
|
||||
bool ValueSerializerTestWithWasm::g_saved_flag = false;
|
||||
const char* ValueSerializerTestWithWasm::kUnsupportedSerialization =
|
||||
"Wasm Serialization Not Supported";
|
||||
|
||||
// The default implementation of the serialization
|
||||
// delegate throws when trying to serialize wasm. The
|
||||
// embedder must decide serialization policy.
|
||||
TEST_F(ValueSerializerTestWithWasm, DefaultSerializationDelegate) {
|
||||
EnableThrowingSerializer();
|
||||
InvalidEncodeTest(
|
||||
[this]() { return MakeWasm(); },
|
||||
[](Local<Message> message) {
|
||||
size_t msg_len = static_cast<size_t>(message->Get()->Length());
|
||||
std::unique_ptr<char[]> buff(new char[msg_len + 1]);
|
||||
message->Get()->WriteOneByte(reinterpret_cast<uint8_t*>(buff.get()));
|
||||
// the message ends with the custom error string
|
||||
size_t custom_msg_len = strlen(kUnsupportedSerialization);
|
||||
ASSERT_GE(msg_len, custom_msg_len);
|
||||
size_t start_pos = msg_len - custom_msg_len;
|
||||
ASSERT_EQ(strcmp(&buff.get()[start_pos], kUnsupportedSerialization), 0);
|
||||
});
|
||||
}
|
||||
// A simple module which exports an "increment" function.
|
||||
// Copied from test/mjsunit/wasm/incrementer.wasm.
|
||||
const unsigned char kIncrementerWasm[] = {
|
||||
0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127,
|
||||
3, 2, 1, 0, 7, 13, 1, 9, 105, 110, 99, 114, 101, 109, 101, 110,
|
||||
116, 0, 0, 10, 9, 1, 7, 0, 32, 0, 65, 1, 106, 11,
|
||||
};
|
||||
|
||||
// The default deserializer throws if wasm transfer is attempted
|
||||
TEST_F(ValueSerializerTestWithWasm, DefaultDeserializationDelegate) {
|
||||
EnableTransferSerialization();
|
||||
EnableDefaultDeserializer();
|
||||
EncodeTest(
|
||||
[this]() { return MakeWasm(); },
|
||||
[this](const std::vector<uint8_t>& data) { InvalidDecodeTest(data); });
|
||||
}
|
||||
|
||||
// We only want to allow deserialization through
|
||||
// transferred modules - which requres both serializer
|
||||
// and deserializer to understand that - or through
|
||||
// explicitly allowing inlined data, which requires
|
||||
// deserializer opt-in (we default the serializer to
|
||||
// inlined data because we don't trust that data on the
|
||||
// receiving end anyway).
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, RoundtripWasmTransfer) {
|
||||
EnableTransferSerialization();
|
||||
EnableTransferDeserialization();
|
||||
ExpectPass();
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, RountripWasmInline) {
|
||||
SetExpectInlineWasm(true);
|
||||
ExpectPass();
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, CannotDeserializeWasmInlineData) {
|
||||
ExpectFail();
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, CannotTransferWasmWhenExpectingInline) {
|
||||
EnableTransferSerialization();
|
||||
SetExpectInlineWasm(true);
|
||||
ExpectFail();
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, ComplexObjectDuplicateTransfer) {
|
||||
EnableTransferSerialization();
|
||||
EnableTransferDeserialization();
|
||||
TEST_F(ValueSerializerTestWithWasm, RoundTripWasmModule) {
|
||||
RoundTripTest(
|
||||
[this]() { return GetComplexObjectWithDuplicate(); },
|
||||
[this]() {
|
||||
return WasmCompiledModule::DeserializeOrCompile(
|
||||
isolate(), {nullptr, 0},
|
||||
{kIncrementerWasm, sizeof(kIncrementerWasm)})
|
||||
.ToLocalChecked();
|
||||
},
|
||||
[this](Local<Value> value) {
|
||||
VerifyComplexObject(value);
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 === result.mod2"));
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, ComplexObjectDuplicateInline) {
|
||||
SetExpectInlineWasm(true);
|
||||
RoundTripTest(
|
||||
[this]() { return GetComplexObjectWithDuplicate(); },
|
||||
[this](Local<Value> value) {
|
||||
VerifyComplexObject(value);
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 === result.mod2"));
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, ComplexObjectWithManyTransfer) {
|
||||
EnableTransferSerialization();
|
||||
EnableTransferDeserialization();
|
||||
RoundTripTest(
|
||||
[this]() { return GetComplexObjectWithMany(); },
|
||||
[this](Local<Value> value) {
|
||||
VerifyComplexObject(value);
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 != result.mod2"));
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ValueSerializerTestWithWasm, ComplexObjectWithManyInline) {
|
||||
SetExpectInlineWasm(true);
|
||||
RoundTripTest(
|
||||
[this]() { return GetComplexObjectWithMany(); },
|
||||
[this](Local<Value> value) {
|
||||
VerifyComplexObject(value);
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool("result.mod1 != result.mod2"));
|
||||
ASSERT_TRUE(value->IsWebAssemblyCompiledModule());
|
||||
EXPECT_TRUE(EvaluateScriptForResultBool(
|
||||
"new WebAssembly.Instance(result).exports.increment(8) === 9"));
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user