// Copyright 2017 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. #if !V8_ENABLE_WEBASSEMBLY #error This header should only be included if WebAssembly is enabled. #endif // !V8_ENABLE_WEBASSEMBLY #ifndef V8_WASM_WASM_ENGINE_H_ #define V8_WASM_WASM_ENGINE_H_ #include #include #include #include #include #include "src/base/platform/condition-variable.h" #include "src/base/platform/mutex.h" #include "src/tasks/cancelable-task.h" #include "src/tasks/operations-barrier.h" #include "src/wasm/wasm-code-manager.h" #include "src/wasm/wasm-tier.h" #include "src/zone/accounting-allocator.h" namespace v8 { namespace internal { class AsmWasmData; class CodeTracer; class CompilationStatistics; class HeapNumber; class WasmInstanceObject; class WasmModuleObject; class JSArrayBuffer; namespace wasm { #ifdef V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING namespace gdb_server { class GdbServer; } // namespace gdb_server #endif // V8_ENABLE_WASM_GDB_REMOTE_DEBUGGING class AsyncCompileJob; class ErrorThrower; struct ModuleWireBytes; class WasmFeatures; class V8_EXPORT_PRIVATE CompilationResultResolver { public: virtual void OnCompilationSucceeded(Handle result) = 0; virtual void OnCompilationFailed(Handle error_reason) = 0; virtual ~CompilationResultResolver() = default; }; class V8_EXPORT_PRIVATE InstantiationResultResolver { public: virtual void OnInstantiationSucceeded(Handle result) = 0; virtual void OnInstantiationFailed(Handle error_reason) = 0; virtual ~InstantiationResultResolver() = default; }; // Native modules cached by their wire bytes. class NativeModuleCache { public: struct Key { // Store the prefix hash as part of the key for faster lookup, and to // quickly check existing prefixes for streaming compilation. size_t prefix_hash; base::Vector bytes; bool operator==(const Key& other) const { bool eq = bytes == other.bytes; DCHECK_IMPLIES(eq, prefix_hash == other.prefix_hash); return eq; } bool operator<(const Key& other) const { if (prefix_hash != other.prefix_hash) { DCHECK_IMPLIES(!bytes.empty() && !other.bytes.empty(), bytes != other.bytes); return prefix_hash < other.prefix_hash; } if (bytes.size() != other.bytes.size()) { return bytes.size() < other.bytes.size(); } // Fast path when the base pointers are the same. // Also handles the {nullptr} case which would be UB for memcmp. if (bytes.begin() == other.bytes.begin()) { DCHECK_EQ(prefix_hash, other.prefix_hash); return false; } DCHECK_NOT_NULL(bytes.begin()); DCHECK_NOT_NULL(other.bytes.begin()); return memcmp(bytes.begin(), other.bytes.begin(), bytes.size()) < 0; } }; std::shared_ptr MaybeGetNativeModule( ModuleOrigin origin, base::Vector wire_bytes); bool GetStreamingCompilationOwnership(size_t prefix_hash); void StreamingCompilationFailed(size_t prefix_hash); std::shared_ptr Update( std::shared_ptr native_module, bool error); void Erase(NativeModule* native_module); bool empty() { return map_.empty(); } static size_t WireBytesHash(base::Vector bytes); // Hash the wire bytes up to the code section header. Used as a heuristic to // avoid streaming compilation of modules that are likely already in the // cache. See {GetStreamingCompilationOwnership}. Assumes that the bytes have // already been validated. static size_t PrefixHash(base::Vector wire_bytes); private: // Each key points to the corresponding native module's wire bytes, so they // should always be valid as long as the native module is alive. When // the native module dies, {FreeNativeModule} deletes the entry from the // map, so that we do not leave any dangling key pointing to an expired // weak_ptr. This also serves as a way to regularly clean up the map, which // would otherwise accumulate expired entries. // A {nullopt} value is inserted to indicate that this native module is // currently being created in some thread, and that other threads should wait // before trying to get it from the cache. // By contrast, an expired {weak_ptr} indicates that the native module died // and will soon be cleaned up from the cache. std::map>> map_; base::Mutex mutex_; // This condition variable is used to synchronize threads compiling the same // module. Only one thread will create the {NativeModule}. Other threads // will wait on this variable until the first thread wakes them up. base::ConditionVariable cache_cv_; }; // The central data structure that represents an engine instance capable of // loading, instantiating, and executing Wasm code. class V8_EXPORT_PRIVATE WasmEngine { public: WasmEngine(); WasmEngine(const WasmEngine&) = delete; WasmEngine& operator=(const WasmEngine&) = delete; ~WasmEngine(); // Synchronously validates the given bytes that represent an encoded Wasm // module. bool SyncValidate(Isolate* isolate, const WasmFeatures& enabled, const ModuleWireBytes& bytes); // Synchronously compiles the given bytes that represent a translated // asm.js module. MaybeHandle SyncCompileTranslatedAsmJs( Isolate* isolate, ErrorThrower* thrower, const ModuleWireBytes& bytes, base::Vector asm_js_offset_table_bytes, Handle uses_bitset, LanguageMode language_mode); Handle FinalizeTranslatedAsmJs( Isolate* isolate, Handle asm_wasm_data, Handle