// Copyright 2020 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 "src/api/api-inl.h" #include "src/init/v8.h" #include "src/wasm/streaming-decoder.h" #include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-module-builder.h" #include "test/cctest/cctest.h" #include "test/common/wasm/test-signatures.h" #include "test/common/wasm/wasm-macro-gen.h" namespace v8 { namespace internal { namespace wasm { namespace { class TestResolver : public CompilationResultResolver { public: explicit TestResolver(std::atomic* pending) : native_module_(nullptr), pending_(pending) {} void OnCompilationSucceeded(i::Handle module) override { if (!module.is_null()) { native_module_ = module->shared_native_module(); pending_->fetch_sub(1); } } void OnCompilationFailed(i::Handle error_reason) override { CHECK(false); } std::shared_ptr native_module() { return native_module_; } private: std::shared_ptr native_module_; std::atomic* pending_; }; class StreamTester { public: explicit StreamTester(std::shared_ptr test_resolver) : internal_scope_(CcTest::i_isolate()), test_resolver_(test_resolver) { i::Isolate* i_isolate = CcTest::i_isolate(); Handle context = i_isolate->native_context(); stream_ = i_isolate->wasm_engine()->StartStreamingCompilation( i_isolate, WasmFeatures::All(), context, "WebAssembly.compileStreaming()", test_resolver_); } void OnBytesReceived(const uint8_t* start, size_t length) { stream_->OnBytesReceived(Vector(start, length)); } void FinishStream() { stream_->Finish(); } void SetCompiledModuleBytes(const uint8_t* start, size_t length) { stream_->SetCompiledModuleBytes(Vector(start, length)); } private: i::HandleScope internal_scope_; std::shared_ptr stream_; std::shared_ptr test_resolver_; }; // Create a valid module such that the bytes depend on {n}. ZoneBuffer GetValidModuleBytes(Zone* zone, int n) { ZoneBuffer buffer(zone); TestSignatures sigs; WasmModuleBuilder builder(zone); { WasmFunctionBuilder* f = builder.AddFunction(sigs.v_v()); uint8_t code[] = {kExprI32Const, n, kExprDrop, kExprEnd}; f->EmitCode(code, arraysize(code)); } builder.WriteTo(&buffer); return buffer; } std::shared_ptr SyncCompile(Vector bytes) { ErrorThrower thrower(CcTest::i_isolate(), "Test"); auto enabled_features = WasmFeatures::FromIsolate(CcTest::i_isolate()); auto wire_bytes = ModuleWireBytes(bytes.begin(), bytes.end()); Handle module = CcTest::i_isolate() ->wasm_engine() ->SyncCompile(CcTest::i_isolate(), enabled_features, &thrower, wire_bytes) .ToHandleChecked(); return module->shared_native_module(); } // Shared prefix. constexpr uint8_t kPrefix[] = { WASM_MODULE_HEADER, // module header kTypeSectionCode, // section code U32V_1(1 + SIZEOF_SIG_ENTRY_v_v), // section size U32V_1(1), // type count SIG_ENTRY_v_v, // signature entry kFunctionSectionCode, // section code U32V_1(2), // section size U32V_1(1), // functions count 0, // signature index kCodeSectionCode, // section code U32V_1(7), // section size U32V_1(1), // functions count 5, // body size }; constexpr uint8_t kFunctionA[] = { U32V_1(0), kExprI32Const, U32V_1(0), kExprDrop, kExprEnd, }; constexpr uint8_t kFunctionB[] = { U32V_1(0), kExprI32Const, U32V_1(1), kExprDrop, kExprEnd, }; constexpr size_t kPrefixSize = arraysize(kPrefix); constexpr size_t kFunctionSize = arraysize(kFunctionA); } // namespace TEST(TestAsyncCache) { CcTest::InitializeVM(); i::HandleScope internal_scope(CcTest::i_isolate()); AccountingAllocator allocator; Zone zone(&allocator, "CompilationCacheTester"); auto bufferA = GetValidModuleBytes(&zone, 0); auto bufferB = GetValidModuleBytes(&zone, 1); std::atomic pending(3); auto resolverA1 = std::make_shared(&pending); auto resolverA2 = std::make_shared(&pending); auto resolverB = std::make_shared(&pending); CcTest::i_isolate()->wasm_engine()->AsyncCompile( CcTest::i_isolate(), WasmFeatures::All(), resolverA1, ModuleWireBytes(bufferA.begin(), bufferA.end()), true, "WebAssembly.compile"); CcTest::i_isolate()->wasm_engine()->AsyncCompile( CcTest::i_isolate(), WasmFeatures::All(), resolverA2, ModuleWireBytes(bufferA.begin(), bufferA.end()), true, "WebAssembly.compile"); CcTest::i_isolate()->wasm_engine()->AsyncCompile( CcTest::i_isolate(), WasmFeatures::All(), resolverB, ModuleWireBytes(bufferB.begin(), bufferB.end()), true, "WebAssembly.compile"); while (pending > 0) { v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), CcTest::isolate()); } CHECK_EQ(resolverA1->native_module(), resolverA2->native_module()); CHECK_NE(resolverA1->native_module(), resolverB->native_module()); } TEST(TestStreamingCache) { CcTest::InitializeVM(); std::atomic pending(3); auto resolverA1 = std::make_shared(&pending); auto resolverA2 = std::make_shared(&pending); auto resolverB = std::make_shared(&pending); StreamTester testerA1(resolverA1); StreamTester testerA2(resolverA2); StreamTester testerB(resolverB); // Start receiving kPrefix bytes. testerA1.OnBytesReceived(kPrefix, kPrefixSize); testerA2.OnBytesReceived(kPrefix, kPrefixSize); testerB.OnBytesReceived(kPrefix, kPrefixSize); // Receive function bytes and start streaming compilation. testerA1.OnBytesReceived(kFunctionA, kFunctionSize); testerA1.FinishStream(); testerA2.OnBytesReceived(kFunctionA, kFunctionSize); testerA2.FinishStream(); testerB.OnBytesReceived(kFunctionB, kFunctionSize); testerB.FinishStream(); while (pending > 0) { v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), CcTest::isolate()); } std::shared_ptr native_module_A1 = resolverA1->native_module(); std::shared_ptr native_module_A2 = resolverA2->native_module(); std::shared_ptr native_module_B = resolverB->native_module(); CHECK_EQ(native_module_A1, native_module_A2); CHECK_NE(native_module_A1, native_module_B); } TEST(TestStreamingAndSyncCache) { CcTest::InitializeVM(); std::atomic pending(1); auto resolver = std::make_shared(&pending); StreamTester tester(resolver); tester.OnBytesReceived(kPrefix, kPrefixSize); // Compile the same module synchronously to make sure we don't deadlock // waiting for streaming compilation to finish. auto full_bytes = OwnedVector::New(kPrefixSize + kFunctionSize); memcpy(full_bytes.begin(), kPrefix, kPrefixSize); memcpy(full_bytes.begin() + kPrefixSize, kFunctionA, kFunctionSize); auto native_module_sync = SyncCompile(full_bytes.as_vector()); // Streaming compilation should just discard its native module now and use the // one inserted in the cache by sync compilation. tester.OnBytesReceived(kFunctionA, kFunctionSize); tester.FinishStream(); while (pending > 0) { v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(), CcTest::isolate()); } std::shared_ptr native_module_streaming = resolver->native_module(); CHECK_EQ(native_module_streaming, native_module_sync); } } // namespace wasm } // namespace internal } // namespace v8