Line data Source code
1 : // Copyright 2015 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include <stdlib.h>
6 : #include <string.h>
7 :
8 : #include "src/api-inl.h"
9 : #include "src/objects-inl.h"
10 : #include "src/snapshot/code-serializer.h"
11 : #include "src/version.h"
12 : #include "src/wasm/module-decoder.h"
13 : #include "src/wasm/wasm-engine.h"
14 : #include "src/wasm/wasm-memory.h"
15 : #include "src/wasm/wasm-module-builder.h"
16 : #include "src/wasm/wasm-module.h"
17 : #include "src/wasm/wasm-objects-inl.h"
18 : #include "src/wasm/wasm-opcodes.h"
19 :
20 : #include "test/cctest/cctest.h"
21 : #include "test/common/wasm/flag-utils.h"
22 : #include "test/common/wasm/test-signatures.h"
23 : #include "test/common/wasm/wasm-macro-gen.h"
24 : #include "test/common/wasm/wasm-module-runner.h"
25 :
26 : namespace v8 {
27 : namespace internal {
28 : namespace wasm {
29 : namespace test_wasm_serialization {
30 :
31 : namespace {
32 : void Cleanup(Isolate* isolate = CcTest::InitIsolateOnce()) {
33 : // By sending a low memory notifications, we will try hard to collect all
34 : // garbage and will therefore also invoke all weak callbacks of actually
35 : // unreachable persistent handles.
36 60 : reinterpret_cast<v8::Isolate*>(isolate)->LowMemoryNotification();
37 : }
38 :
39 : #define EMIT_CODE_WITH_END(f, code) \
40 : do { \
41 : f->EmitCode(code, sizeof(code)); \
42 : f->Emit(kExprEnd); \
43 : } while (false)
44 :
45 : } // namespace
46 :
47 : // Approximate gtest TEST_F style, in case we adopt gtest.
48 : class WasmSerializationTest {
49 : public:
50 30 : WasmSerializationTest() : zone_(&allocator_, ZONE_NAME) {
51 : // Don't call here if we move to gtest.
52 30 : SetUp();
53 30 : }
54 :
55 40 : static void BuildWireBytes(Zone* zone, ZoneBuffer* buffer) {
56 40 : WasmModuleBuilder* builder = new (zone) WasmModuleBuilder(zone);
57 40 : TestSignatures sigs;
58 :
59 40 : WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
60 40 : byte code[] = {WASM_GET_LOCAL(0), kExprI32Const, 1, kExprI32Add};
61 40 : EMIT_CODE_WITH_END(f, code);
62 80 : builder->AddExport(CStrVector(kFunctionName), f);
63 :
64 40 : builder->WriteTo(*buffer);
65 40 : }
66 :
67 5 : void ClearSerializedData() { serialized_bytes_ = {nullptr, 0}; }
68 :
69 : void InvalidateVersion() {
70 : uint32_t* slot = reinterpret_cast<uint32_t*>(
71 10 : const_cast<uint8_t*>(serialized_bytes_.data()) +
72 : SerializedCodeData::kVersionHashOffset);
73 10 : *slot = Version::Hash() + 1;
74 : }
75 :
76 : void InvalidateWireBytes() {
77 5 : memset(const_cast<uint8_t*>(wire_bytes_.data()), 0, wire_bytes_.size() / 2);
78 : }
79 :
80 : void InvalidateLength() {
81 : uint32_t* slot = reinterpret_cast<uint32_t*>(
82 5 : const_cast<uint8_t*>(serialized_bytes_.data()) +
83 : SerializedCodeData::kPayloadLengthOffset);
84 5 : *slot = 0u;
85 : }
86 :
87 30 : v8::MaybeLocal<v8::WasmModuleObject> Deserialize() {
88 : ErrorThrower thrower(current_isolate(), "");
89 : v8::MaybeLocal<v8::WasmModuleObject> deserialized =
90 : v8::WasmModuleObject::DeserializeOrCompile(
91 30 : current_isolate_v8(), serialized_bytes_, wire_bytes_);
92 30 : return deserialized;
93 : }
94 :
95 80 : void DeserializeAndRun() {
96 : ErrorThrower thrower(current_isolate(), "");
97 : v8::Local<v8::WasmModuleObject> deserialized_module;
98 40 : CHECK(Deserialize().ToLocal(&deserialized_module));
99 : Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast(
100 20 : v8::Utils::OpenHandle(*deserialized_module));
101 : {
102 : DisallowHeapAllocation assume_no_gc;
103 : Vector<const byte> deserialized_module_wire_bytes =
104 40 : module_object->native_module()->wire_bytes();
105 20 : CHECK_EQ(deserialized_module_wire_bytes.size(), wire_bytes_.size());
106 20 : CHECK_EQ(memcmp(deserialized_module_wire_bytes.start(),
107 : wire_bytes_.data(), wire_bytes_.size()),
108 : 0);
109 : }
110 : Handle<WasmInstanceObject> instance =
111 : current_isolate()
112 : ->wasm_engine()
113 : ->SyncInstantiate(current_isolate(), &thrower, module_object,
114 : Handle<JSReceiver>::null(),
115 40 : MaybeHandle<JSArrayBuffer>())
116 40 : .ToHandleChecked();
117 : Handle<Object> params[1] = {
118 : Handle<Object>(Smi::FromInt(41), current_isolate())};
119 : int32_t result = testing::CallWasmFunctionForTesting(
120 40 : current_isolate(), instance, &thrower, kFunctionName, 1, params);
121 20 : CHECK_EQ(42, result);
122 20 : }
123 :
124 : Isolate* current_isolate() {
125 : return reinterpret_cast<Isolate*>(current_isolate_v8_);
126 : }
127 :
128 60 : ~WasmSerializationTest() {
129 : // Don't call from here if we move to gtest
130 : TearDown();
131 30 : }
132 :
133 : v8::Isolate* current_isolate_v8() { return current_isolate_v8_; }
134 :
135 : private:
136 : static const char* kFunctionName;
137 :
138 : Zone* zone() { return &zone_; }
139 :
140 90 : void SetUp() {
141 30 : ZoneBuffer buffer(&zone_);
142 30 : WasmSerializationTest::BuildWireBytes(zone(), &buffer);
143 :
144 60 : Isolate* serialization_isolate = CcTest::InitIsolateOnce();
145 : ErrorThrower thrower(serialization_isolate, "");
146 : {
147 : HandleScope scope(serialization_isolate);
148 30 : testing::SetupIsolateForWasmModule(serialization_isolate);
149 :
150 30 : auto enabled_features = WasmFeaturesFromIsolate(serialization_isolate);
151 : MaybeHandle<WasmModuleObject> maybe_module_object =
152 : serialization_isolate->wasm_engine()->SyncCompile(
153 : serialization_isolate, enabled_features, &thrower,
154 60 : ModuleWireBytes(buffer.begin(), buffer.end()));
155 : Handle<WasmModuleObject> module_object =
156 30 : maybe_module_object.ToHandleChecked();
157 :
158 : v8::Local<v8::Object> v8_module_obj =
159 30 : v8::Utils::ToLocal(Handle<JSObject>::cast(module_object));
160 30 : CHECK(v8_module_obj->IsWebAssemblyCompiledModule());
161 :
162 : v8::Local<v8::WasmModuleObject> v8_module_object =
163 : v8_module_obj.As<v8::WasmModuleObject>();
164 : v8::CompiledWasmModule compiled_module =
165 30 : v8_module_object->GetCompiledModule();
166 : v8::MemorySpan<const uint8_t> uncompiled_bytes =
167 30 : compiled_module.GetWireBytesRef();
168 : uint8_t* bytes_copy = zone()->NewArray<uint8_t>(uncompiled_bytes.size());
169 30 : memcpy(bytes_copy, uncompiled_bytes.data(), uncompiled_bytes.size());
170 30 : wire_bytes_ = {bytes_copy, uncompiled_bytes.size()};
171 : // keep alive data_ until the end
172 60 : data_ = compiled_module.Serialize();
173 : }
174 :
175 60 : serialized_bytes_ = {data_.buffer.get(), data_.size};
176 :
177 : v8::Isolate::CreateParams create_params;
178 : create_params.array_buffer_allocator =
179 30 : serialization_isolate->array_buffer_allocator();
180 :
181 30 : current_isolate_v8_ = v8::Isolate::New(create_params);
182 60 : v8::HandleScope new_scope(current_isolate_v8());
183 : v8::Local<v8::Context> deserialization_context =
184 30 : v8::Context::New(current_isolate_v8());
185 30 : deserialization_context->Enter();
186 60 : testing::SetupIsolateForWasmModule(current_isolate());
187 30 : }
188 :
189 30 : void TearDown() {
190 30 : current_isolate_v8()->Dispose();
191 30 : current_isolate_v8_ = nullptr;
192 : }
193 :
194 : v8::internal::AccountingAllocator allocator_;
195 : Zone zone_;
196 : v8::OwnedBuffer data_;
197 : v8::MemorySpan<const uint8_t> wire_bytes_ = {nullptr, 0};
198 : v8::MemorySpan<const uint8_t> serialized_bytes_ = {nullptr, 0};
199 : v8::Isolate* current_isolate_v8_;
200 : };
201 :
202 : const char* WasmSerializationTest::kFunctionName = "increment";
203 :
204 28342 : TEST(DeserializeValidModule) {
205 5 : WasmSerializationTest test;
206 : {
207 5 : HandleScope scope(test.current_isolate());
208 5 : test.DeserializeAndRun();
209 : }
210 5 : Cleanup(test.current_isolate());
211 10 : Cleanup();
212 5 : }
213 :
214 28342 : TEST(DeserializeMismatchingVersion) {
215 5 : WasmSerializationTest test;
216 : {
217 5 : HandleScope scope(test.current_isolate());
218 : test.InvalidateVersion();
219 5 : test.DeserializeAndRun();
220 : }
221 5 : Cleanup(test.current_isolate());
222 10 : Cleanup();
223 5 : }
224 :
225 28342 : TEST(DeserializeNoSerializedData) {
226 5 : WasmSerializationTest test;
227 : {
228 5 : HandleScope scope(test.current_isolate());
229 : test.ClearSerializedData();
230 5 : test.DeserializeAndRun();
231 : }
232 5 : Cleanup(test.current_isolate());
233 10 : Cleanup();
234 5 : }
235 :
236 28342 : TEST(DeserializeInvalidLength) {
237 5 : WasmSerializationTest test;
238 : {
239 5 : HandleScope scope(test.current_isolate());
240 : test.InvalidateLength();
241 5 : test.DeserializeAndRun();
242 : }
243 5 : Cleanup(test.current_isolate());
244 10 : Cleanup();
245 5 : }
246 :
247 28342 : TEST(DeserializeWireBytesAndSerializedDataInvalid) {
248 5 : WasmSerializationTest test;
249 : {
250 5 : HandleScope scope(test.current_isolate());
251 : test.InvalidateVersion();
252 : test.InvalidateWireBytes();
253 5 : test.Deserialize();
254 : }
255 5 : Cleanup(test.current_isolate());
256 10 : Cleanup();
257 5 : }
258 :
259 10 : bool False(v8::Local<v8::Context> context, v8::Local<v8::String> source) {
260 10 : return false;
261 : }
262 :
263 28342 : TEST(BlockWasmCodeGenAtDeserialization) {
264 5 : WasmSerializationTest test;
265 : {
266 5 : HandleScope scope(test.current_isolate());
267 5 : test.current_isolate_v8()->SetAllowCodeGenerationFromStringsCallback(False);
268 5 : v8::MaybeLocal<v8::WasmModuleObject> nothing = test.Deserialize();
269 5 : CHECK(nothing.IsEmpty());
270 : }
271 5 : Cleanup(test.current_isolate());
272 10 : Cleanup();
273 5 : }
274 :
275 : namespace {
276 :
277 10 : void TestTransferrableWasmModules(bool should_share) {
278 10 : i::wasm::WasmEngine::InitializeOncePerProcess();
279 10 : v8::internal::AccountingAllocator allocator;
280 20 : Zone zone(&allocator, ZONE_NAME);
281 :
282 : ZoneBuffer buffer(&zone);
283 10 : WasmSerializationTest::BuildWireBytes(&zone, &buffer);
284 :
285 : v8::Isolate::CreateParams create_params;
286 10 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
287 10 : v8::Isolate* from_isolate = v8::Isolate::New(create_params);
288 10 : std::vector<v8::WasmModuleObject::TransferrableModule> store;
289 : std::shared_ptr<NativeModule> original_native_module;
290 : {
291 10 : v8::HandleScope scope(from_isolate);
292 10 : LocalContext env(from_isolate);
293 :
294 : Isolate* from_i_isolate = reinterpret_cast<Isolate*>(from_isolate);
295 10 : testing::SetupIsolateForWasmModule(from_i_isolate);
296 10 : ErrorThrower thrower(from_i_isolate, "TestTransferrableWasmModules");
297 10 : auto enabled_features = WasmFeaturesFromIsolate(from_i_isolate);
298 : MaybeHandle<WasmModuleObject> maybe_module_object =
299 : from_i_isolate->wasm_engine()->SyncCompile(
300 : from_i_isolate, enabled_features, &thrower,
301 20 : ModuleWireBytes(buffer.begin(), buffer.end()));
302 : Handle<WasmModuleObject> module_object =
303 : maybe_module_object.ToHandleChecked();
304 : v8::Local<v8::WasmModuleObject> v8_module =
305 : v8::Local<v8::WasmModuleObject>::Cast(
306 10 : v8::Utils::ToLocal(Handle<JSObject>::cast(module_object)));
307 20 : store.push_back(v8_module->GetTransferrableModule());
308 30 : original_native_module = module_object->shared_native_module();
309 : }
310 :
311 : {
312 10 : v8::Isolate* to_isolate = v8::Isolate::New(create_params);
313 : {
314 10 : v8::HandleScope scope(to_isolate);
315 10 : LocalContext env(to_isolate);
316 :
317 : v8::MaybeLocal<v8::WasmModuleObject> transferred_module =
318 10 : v8::WasmModuleObject::FromTransferrableModule(to_isolate, store[0]);
319 10 : CHECK(!transferred_module.IsEmpty());
320 : Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast(
321 10 : v8::Utils::OpenHandle(*transferred_module.ToLocalChecked()));
322 : std::shared_ptr<NativeModule> transferred_native_module =
323 10 : module_object->shared_native_module();
324 : bool is_sharing = (original_native_module == transferred_native_module);
325 20 : CHECK_EQ(should_share, is_sharing);
326 : }
327 10 : to_isolate->Dispose();
328 : }
329 : original_native_module.reset();
330 20 : from_isolate->Dispose();
331 10 : }
332 :
333 : } // namespace
334 :
335 28342 : UNINITIALIZED_TEST(TransferrableWasmModulesCloned) {
336 : FlagScope<bool> flag_scope_code(&FLAG_wasm_shared_code, false);
337 5 : TestTransferrableWasmModules(false);
338 5 : }
339 :
340 28342 : UNINITIALIZED_TEST(TransferrableWasmModulesShared) {
341 : FlagScope<bool> flag_scope_engine(&FLAG_wasm_shared_engine, true);
342 : FlagScope<bool> flag_scope_code(&FLAG_wasm_shared_code, true);
343 5 : TestTransferrableWasmModules(true);
344 5 : }
345 :
346 : #undef EMIT_CODE_WITH_END
347 :
348 : } // namespace test_wasm_serialization
349 : } // namespace wasm
350 : } // namespace internal
351 85011 : } // namespace v8
|