diff options
Diffstat (limited to 'deps/v8/test/cctest/wasm/test-wasm-serialization.cc')
-rw-r--r-- | deps/v8/test/cctest/wasm/test-wasm-serialization.cc | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/deps/v8/test/cctest/wasm/test-wasm-serialization.cc b/deps/v8/test/cctest/wasm/test-wasm-serialization.cc new file mode 100644 index 0000000000..9475332ad0 --- /dev/null +++ b/deps/v8/test/cctest/wasm/test-wasm-serialization.cc @@ -0,0 +1,319 @@ +// Copyright 2015 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 <stdlib.h> +#include <string.h> + +#include "src/api.h" +#include "src/objects-inl.h" +#include "src/snapshot/code-serializer.h" +#include "src/version.h" +#include "src/wasm/module-decoder.h" +#include "src/wasm/wasm-engine.h" +#include "src/wasm/wasm-memory.h" +#include "src/wasm/wasm-module-builder.h" +#include "src/wasm/wasm-module.h" +#include "src/wasm/wasm-objects-inl.h" +#include "src/wasm/wasm-opcodes.h" + +#include "test/cctest/cctest.h" +#include "test/common/wasm/flag-utils.h" +#include "test/common/wasm/test-signatures.h" +#include "test/common/wasm/wasm-macro-gen.h" +#include "test/common/wasm/wasm-module-runner.h" + +namespace v8 { +namespace internal { +namespace wasm { +namespace test_wasm_serialization { + +namespace { +void Cleanup(Isolate* isolate = CcTest::InitIsolateOnce()) { + // By sending a low memory notifications, we will try hard to collect all + // garbage and will therefore also invoke all weak callbacks of actually + // unreachable persistent handles. + reinterpret_cast<v8::Isolate*>(isolate)->LowMemoryNotification(); +} + +#define EMIT_CODE_WITH_END(f, code) \ + do { \ + f->EmitCode(code, sizeof(code)); \ + f->Emit(kExprEnd); \ + } while (false) + +} // namespace + +// Approximate gtest TEST_F style, in case we adopt gtest. +class WasmSerializationTest { + public: + WasmSerializationTest() : zone_(&allocator_, ZONE_NAME) { + // Don't call here if we move to gtest. + SetUp(); + } + + static void BuildWireBytes(Zone* zone, ZoneBuffer* buffer) { + WasmModuleBuilder* builder = new (zone) WasmModuleBuilder(zone); + TestSignatures sigs; + + WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i()); + byte code[] = {WASM_GET_LOCAL(0), kExprI32Const, 1, kExprI32Add}; + EMIT_CODE_WITH_END(f, code); + builder->AddExport(CStrVector(kFunctionName), f); + + builder->WriteTo(*buffer); + } + + void ClearSerializedData() { serialized_bytes_ = {nullptr, 0}; } + + void InvalidateVersion() { + uint32_t* slot = reinterpret_cast<uint32_t*>( + const_cast<uint8_t*>(serialized_bytes_.start) + + SerializedCodeData::kVersionHashOffset); + *slot = Version::Hash() + 1; + } + + void InvalidateWireBytes() { + memset(const_cast<uint8_t*>(wire_bytes_.start), 0, wire_bytes_.size / 2); + } + + void InvalidateLength() { + uint32_t* slot = reinterpret_cast<uint32_t*>( + const_cast<uint8_t*>(serialized_bytes_.start) + + SerializedCodeData::kPayloadLengthOffset); + *slot = 0u; + } + + v8::MaybeLocal<v8::WasmCompiledModule> Deserialize() { + ErrorThrower thrower(current_isolate(), ""); + v8::MaybeLocal<v8::WasmCompiledModule> deserialized = + v8::WasmCompiledModule::DeserializeOrCompile( + current_isolate_v8(), serialized_bytes_, wire_bytes_); + return deserialized; + } + + void DeserializeAndRun() { + ErrorThrower thrower(current_isolate(), ""); + v8::Local<v8::WasmCompiledModule> deserialized_module; + CHECK(Deserialize().ToLocal(&deserialized_module)); + Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast( + v8::Utils::OpenHandle(*deserialized_module)); + { + DisallowHeapAllocation assume_no_gc; + Vector<const byte> deserialized_module_wire_bytes = + module_object->native_module()->wire_bytes(); + CHECK_EQ(deserialized_module_wire_bytes.size(), wire_bytes_.size); + CHECK_EQ(memcmp(deserialized_module_wire_bytes.start(), wire_bytes_.start, + wire_bytes_.size), + 0); + } + Handle<WasmInstanceObject> instance = + current_isolate() + ->wasm_engine() + ->SyncInstantiate(current_isolate(), &thrower, module_object, + Handle<JSReceiver>::null(), + MaybeHandle<JSArrayBuffer>()) + .ToHandleChecked(); + Handle<Object> params[1] = { + Handle<Object>(Smi::FromInt(41), current_isolate())}; + int32_t result = testing::CallWasmFunctionForTesting( + current_isolate(), instance, &thrower, kFunctionName, 1, params); + CHECK_EQ(42, result); + } + + Isolate* current_isolate() { + return reinterpret_cast<Isolate*>(current_isolate_v8_); + } + + ~WasmSerializationTest() { + // Don't call from here if we move to gtest + TearDown(); + } + + v8::Isolate* current_isolate_v8() { return current_isolate_v8_; } + + private: + static const char* kFunctionName; + + Zone* zone() { return &zone_; } + + void SetUp() { + ZoneBuffer buffer(&zone_); + WasmSerializationTest::BuildWireBytes(zone(), &buffer); + + Isolate* serialization_isolate = CcTest::InitIsolateOnce(); + ErrorThrower thrower(serialization_isolate, ""); + { + HandleScope scope(serialization_isolate); + testing::SetupIsolateForWasmModule(serialization_isolate); + + MaybeHandle<WasmModuleObject> maybe_module_object = + serialization_isolate->wasm_engine()->SyncCompile( + serialization_isolate, &thrower, + ModuleWireBytes(buffer.begin(), buffer.end())); + Handle<WasmModuleObject> module_object = + maybe_module_object.ToHandleChecked(); + + v8::Local<v8::Object> v8_module_obj = + v8::Utils::ToLocal(Handle<JSObject>::cast(module_object)); + CHECK(v8_module_obj->IsWebAssemblyCompiledModule()); + + v8::Local<v8::WasmCompiledModule> v8_compiled_module = + v8_module_obj.As<v8::WasmCompiledModule>(); + v8::WasmCompiledModule::BufferReference uncompiled_bytes = + v8_compiled_module->GetWasmWireBytesRef(); + uint8_t* bytes_copy = zone()->NewArray<uint8_t>(uncompiled_bytes.size); + memcpy(bytes_copy, uncompiled_bytes.start, uncompiled_bytes.size); + wire_bytes_ = {bytes_copy, uncompiled_bytes.size}; + // keep alive data_ until the end + data_ = v8_compiled_module->Serialize(); + } + + serialized_bytes_ = {data_.first.get(), data_.second}; + + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = + serialization_isolate->array_buffer_allocator(); + + current_isolate_v8_ = v8::Isolate::New(create_params); + v8::HandleScope new_scope(current_isolate_v8()); + v8::Local<v8::Context> deserialization_context = + v8::Context::New(current_isolate_v8()); + deserialization_context->Enter(); + testing::SetupIsolateForWasmModule(current_isolate()); + } + + void TearDown() { + current_isolate_v8()->Dispose(); + current_isolate_v8_ = nullptr; + } + + v8::internal::AccountingAllocator allocator_; + Zone zone_; + v8::WasmCompiledModule::SerializedModule data_; + v8::WasmCompiledModule::BufferReference wire_bytes_ = {nullptr, 0}; + v8::WasmCompiledModule::BufferReference serialized_bytes_ = {nullptr, 0}; + v8::Isolate* current_isolate_v8_; +}; + +const char* WasmSerializationTest::kFunctionName = "increment"; + +TEST(DeserializeValidModule) { + WasmSerializationTest test; + { + HandleScope scope(test.current_isolate()); + test.DeserializeAndRun(); + } + Cleanup(test.current_isolate()); + Cleanup(); +} + +TEST(DeserializeMismatchingVersion) { + WasmSerializationTest test; + { + HandleScope scope(test.current_isolate()); + test.InvalidateVersion(); + test.DeserializeAndRun(); + } + Cleanup(test.current_isolate()); + Cleanup(); +} + +TEST(DeserializeNoSerializedData) { + WasmSerializationTest test; + { + HandleScope scope(test.current_isolate()); + test.ClearSerializedData(); + test.DeserializeAndRun(); + } + Cleanup(test.current_isolate()); + Cleanup(); +} + +TEST(DeserializeInvalidLength) { + WasmSerializationTest test; + { + HandleScope scope(test.current_isolate()); + test.InvalidateLength(); + test.DeserializeAndRun(); + } + Cleanup(test.current_isolate()); + Cleanup(); +} + +TEST(DeserializeWireBytesAndSerializedDataInvalid) { + WasmSerializationTest test; + { + HandleScope scope(test.current_isolate()); + test.InvalidateVersion(); + test.InvalidateWireBytes(); + test.Deserialize(); + } + Cleanup(test.current_isolate()); + Cleanup(); +} + +bool False(v8::Local<v8::Context> context, v8::Local<v8::String> source) { + return false; +} + +TEST(BlockWasmCodeGenAtDeserialization) { + WasmSerializationTest test; + { + HandleScope scope(test.current_isolate()); + test.current_isolate_v8()->SetAllowCodeGenerationFromStringsCallback(False); + v8::MaybeLocal<v8::WasmCompiledModule> nothing = test.Deserialize(); + CHECK(nothing.IsEmpty()); + } + Cleanup(test.current_isolate()); + 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 = + from_isolate->wasm_engine()->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()); + } + to_isolate->Dispose(); + } +} + +#undef EMIT_CODE_WITH_END + +} // namespace test_wasm_serialization +} // namespace wasm +} // namespace internal +} // namespace v8 |