1 // Copyright 2021 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 "include/v8-wasm.h"
6 #include "src/base/memory.h"
7 #include "src/base/platform/mutex.h"
8 #include "src/execution/arguments-inl.h"
9 #include "src/execution/frames-inl.h"
10 #include "src/heap/heap-inl.h"
11 #include "src/logging/counters.h"
12 #include "src/objects/smi.h"
13 #include "src/runtime/runtime-utils.h"
14 #include "src/trap-handler/trap-handler.h"
15 #include "src/utils/ostreams.h"
16 #include "src/wasm/memory-tracing.h"
17 #include "src/wasm/module-compiler.h"
18 #include "src/wasm/wasm-code-manager.h"
19 #include "src/wasm/wasm-engine.h"
20 #include "src/wasm/wasm-module.h"
21 #include "src/wasm/wasm-objects-inl.h"
22 #include "src/wasm/wasm-serialization.h"
23
24 namespace v8 {
25 namespace internal {
26
27 namespace {
28 struct WasmCompileControls {
29 uint32_t MaxWasmBufferSize = std::numeric_limits<uint32_t>::max();
30 bool AllowAnySizeForAsync = true;
31 };
32 using WasmCompileControlsMap = std::map<v8::Isolate*, WasmCompileControls>;
33
34 // We need per-isolate controls, because we sometimes run tests in multiple
35 // isolates concurrently. Methods need to hold the accompanying mutex on access.
36 // To avoid upsetting the static initializer count, we lazy initialize this.
37 DEFINE_LAZY_LEAKY_OBJECT_GETTER(WasmCompileControlsMap,
38 GetPerIsolateWasmControls)
39 base::LazyMutex g_PerIsolateWasmControlsMutex = LAZY_MUTEX_INITIALIZER;
40
IsWasmCompileAllowed(v8::Isolate* isolate, v8::Local<v8::Value> value, bool is_async)41 bool IsWasmCompileAllowed(v8::Isolate* isolate, v8::Local<v8::Value> value,
42 bool is_async) {
43 base::MutexGuard guard(g_PerIsolateWasmControlsMutex.Pointer());
44 DCHECK_GT(GetPerIsolateWasmControls()->count(isolate), 0);
45 const WasmCompileControls& ctrls = GetPerIsolateWasmControls()->at(isolate);
46 return (is_async && ctrls.AllowAnySizeForAsync) ||
47 (value->IsArrayBuffer() && value.As<v8::ArrayBuffer>()->ByteLength() <=
48 ctrls.MaxWasmBufferSize) ||
49 (value->IsArrayBufferView() &&
50 value.As<v8::ArrayBufferView>()->ByteLength() <=
51 ctrls.MaxWasmBufferSize);
52 }
53
54 // Use the compile controls for instantiation, too
IsWasmInstantiateAllowed(v8::Isolate* isolate, v8::Local<v8::Value> module_or_bytes, bool is_async)55 bool IsWasmInstantiateAllowed(v8::Isolate* isolate,
56 v8::Local<v8::Value> module_or_bytes,
57 bool is_async) {
58 base::MutexGuard guard(g_PerIsolateWasmControlsMutex.Pointer());
59 DCHECK_GT(GetPerIsolateWasmControls()->count(isolate), 0);
60 const WasmCompileControls& ctrls = GetPerIsolateWasmControls()->at(isolate);
61 if (is_async && ctrls.AllowAnySizeForAsync) return true;
62 if (!module_or_bytes->IsWasmModuleObject()) {
63 return IsWasmCompileAllowed(isolate, module_or_bytes, is_async);
64 }
65 v8::Local<v8::WasmModuleObject> module =
66 v8::Local<v8::WasmModuleObject>::Cast(module_or_bytes);
67 return static_cast<uint32_t>(
68 module->GetCompiledModule().GetWireBytesRef().size()) <=
69 ctrls.MaxWasmBufferSize;
70 }
71
NewRangeException(v8::Isolate* isolate, const char* message)72 v8::Local<v8::Value> NewRangeException(v8::Isolate* isolate,
73 const char* message) {
74 return v8::Exception::RangeError(
75 v8::String::NewFromOneByte(isolate,
76 reinterpret_cast<const uint8_t*>(message))
77 .ToLocalChecked());
78 }
79
ThrowRangeException(v8::Isolate* isolate, const char* message)80 void ThrowRangeException(v8::Isolate* isolate, const char* message) {
81 isolate->ThrowException(NewRangeException(isolate, message));
82 }
83
WasmModuleOverride(const v8::FunctionCallbackInfo<v8::Value>& args)84 bool WasmModuleOverride(const v8::FunctionCallbackInfo<v8::Value>& args) {
85 if (IsWasmCompileAllowed(args.GetIsolate(), args[0], false)) return false;
86 ThrowRangeException(args.GetIsolate(), "Sync compile not allowed");
87 return true;
88 }
89
WasmInstanceOverride(const v8::FunctionCallbackInfo<v8::Value>& args)90 bool WasmInstanceOverride(const v8::FunctionCallbackInfo<v8::Value>& args) {
91 if (IsWasmInstantiateAllowed(args.GetIsolate(), args[0], false)) return false;
92 ThrowRangeException(args.GetIsolate(), "Sync instantiate not allowed");
93 return true;
94 }
95
96 } // namespace
97
98 // Returns a callable object. The object returns the difference of its two
99 // parameters when it is called.
RUNTIME_FUNCTION(Runtime_SetWasmCompileControls)100 RUNTIME_FUNCTION(Runtime_SetWasmCompileControls) {
101 HandleScope scope(isolate);
102 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
103 CHECK_EQ(args.length(), 2);
104 int block_size = args.smi_value_at(0);
105 bool allow_async = Oddball::cast(args[1]).ToBool(isolate);
106 base::MutexGuard guard(g_PerIsolateWasmControlsMutex.Pointer());
107 WasmCompileControls& ctrl = (*GetPerIsolateWasmControls())[v8_isolate];
108 ctrl.AllowAnySizeForAsync = allow_async;
109 ctrl.MaxWasmBufferSize = static_cast<uint32_t>(block_size);
110 v8_isolate->SetWasmModuleCallback(WasmModuleOverride);
111 return ReadOnlyRoots(isolate).undefined_value();
112 }
113
RUNTIME_FUNCTION(Runtime_SetWasmInstantiateControls)114 RUNTIME_FUNCTION(Runtime_SetWasmInstantiateControls) {
115 HandleScope scope(isolate);
116 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
117 CHECK_EQ(args.length(), 0);
118 v8_isolate->SetWasmInstanceCallback(WasmInstanceOverride);
119 return ReadOnlyRoots(isolate).undefined_value();
120 }
121
122 namespace {
123
PrintIndentation(int stack_size)124 void PrintIndentation(int stack_size) {
125 const int max_display = 80;
126 if (stack_size <= max_display) {
127 PrintF("%4d:%*s", stack_size, stack_size, "");
128 } else {
129 PrintF("%4d:%*s", stack_size, max_display, "...");
130 }
131 }
132
WasmStackSize(Isolate* isolate)133 int WasmStackSize(Isolate* isolate) {
134 // TODO(wasm): Fix this for mixed JS/Wasm stacks with both --trace and
135 // --trace-wasm.
136 int n = 0;
137 for (StackTraceFrameIterator it(isolate); !it.done(); it.Advance()) {
138 if (it.is_wasm()) n++;
139 }
140 return n;
141 }
142
143 } // namespace
144
RUNTIME_FUNCTION(Runtime_WasmTraceEnter)145 RUNTIME_FUNCTION(Runtime_WasmTraceEnter) {
146 HandleScope shs(isolate);
147 DCHECK_EQ(0, args.length());
148 PrintIndentation(WasmStackSize(isolate));
149
150 // Find the caller wasm frame.
151 wasm::WasmCodeRefScope wasm_code_ref_scope;
152 StackTraceFrameIterator it(isolate);
153 DCHECK(!it.done());
154 DCHECK(it.is_wasm());
155 WasmFrame* frame = WasmFrame::cast(it.frame());
156
157 // Find the function name.
158 int func_index = frame->function_index();
159 const wasm::WasmModule* module = frame->wasm_instance().module();
160 wasm::ModuleWireBytes wire_bytes =
161 wasm::ModuleWireBytes(frame->native_module()->wire_bytes());
162 wasm::WireBytesRef name_ref =
163 module->lazily_generated_names.LookupFunctionName(wire_bytes, func_index);
164 wasm::WasmName name = wire_bytes.GetNameOrNull(name_ref);
165
166 wasm::WasmCode* code = frame->wasm_code();
167 PrintF(code->is_liftoff() ? "~" : "*");
168
169 if (name.empty()) {
170 PrintF("wasm-function[%d] {\n", func_index);
171 } else {
172 PrintF("wasm-function[%d] \"%.*s\" {\n", func_index, name.length(),
173 name.begin());
174 }
175
176 return ReadOnlyRoots(isolate).undefined_value();
177 }
178
RUNTIME_FUNCTION(Runtime_WasmTraceExit)179 RUNTIME_FUNCTION(Runtime_WasmTraceExit) {
180 HandleScope shs(isolate);
181 DCHECK_EQ(1, args.length());
182 auto value_addr_smi = Smi::cast(args[0]);
183
184 PrintIndentation(WasmStackSize(isolate));
185 PrintF("}");
186
187 // Find the caller wasm frame.
188 wasm::WasmCodeRefScope wasm_code_ref_scope;
189 StackTraceFrameIterator it(isolate);
190 DCHECK(!it.done());
191 DCHECK(it.is_wasm());
192 WasmFrame* frame = WasmFrame::cast(it.frame());
193 int func_index = frame->function_index();
194 const wasm::FunctionSig* sig =
195 frame->wasm_instance().module()->functions[func_index].sig;
196
197 size_t num_returns = sig->return_count();
198 if (num_returns == 1) {
199 wasm::ValueType return_type = sig->GetReturn(0);
200 switch (return_type.kind()) {
201 case wasm::kI32: {
202 int32_t value = base::ReadUnalignedValue<int32_t>(value_addr_smi.ptr());
203 PrintF(" -> %d\n", value);
204 break;
205 }
206 case wasm::kI64: {
207 int64_t value = base::ReadUnalignedValue<int64_t>(value_addr_smi.ptr());
208 PrintF(" -> %" PRId64 "\n", value);
209 break;
210 }
211 case wasm::kF32: {
212 float value = base::ReadUnalignedValue<float>(value_addr_smi.ptr());
213 PrintF(" -> %f\n", value);
214 break;
215 }
216 case wasm::kF64: {
217 double value = base::ReadUnalignedValue<double>(value_addr_smi.ptr());
218 PrintF(" -> %f\n", value);
219 break;
220 }
221 default:
222 PrintF(" -> Unsupported type\n");
223 break;
224 }
225 } else {
226 // TODO(wasm) Handle multiple return values.
227 PrintF("\n");
228 }
229
230 return ReadOnlyRoots(isolate).undefined_value();
231 }
232
RUNTIME_FUNCTION(Runtime_IsAsmWasmCode)233 RUNTIME_FUNCTION(Runtime_IsAsmWasmCode) {
234 SealHandleScope shs(isolate);
235 DCHECK_EQ(1, args.length());
236 auto function = JSFunction::cast(args[0]);
237 if (!function.shared().HasAsmWasmData()) {
238 return ReadOnlyRoots(isolate).false_value();
239 }
240 if (function.shared().HasBuiltinId() &&
241 function.shared().builtin_id() == Builtin::kInstantiateAsmJs) {
242 // Hasn't been compiled yet.
243 return ReadOnlyRoots(isolate).false_value();
244 }
245 return ReadOnlyRoots(isolate).true_value();
246 }
247
248 namespace {
249
DisallowWasmCodegenFromStringsCallback(v8::Local<v8::Context> context, v8::Local<v8::String> source)250 bool DisallowWasmCodegenFromStringsCallback(v8::Local<v8::Context> context,
251 v8::Local<v8::String> source) {
252 return false;
253 }
254
255 } // namespace
256
RUNTIME_FUNCTION(Runtime_DisallowWasmCodegen)257 RUNTIME_FUNCTION(Runtime_DisallowWasmCodegen) {
258 SealHandleScope shs(isolate);
259 DCHECK_EQ(1, args.length());
260 bool flag = Oddball::cast(args[0]).ToBool(isolate);
261 v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
262 v8_isolate->SetAllowWasmCodeGenerationCallback(
263 flag ? DisallowWasmCodegenFromStringsCallback : nullptr);
264 return ReadOnlyRoots(isolate).undefined_value();
265 }
266
RUNTIME_FUNCTION(Runtime_IsWasmCode)267 RUNTIME_FUNCTION(Runtime_IsWasmCode) {
268 SealHandleScope shs(isolate);
269 DCHECK_EQ(1, args.length());
270 auto function = JSFunction::cast(args[0]);
271 CodeT code = function.code();
272 bool is_js_to_wasm = code.kind() == CodeKind::JS_TO_WASM_FUNCTION ||
273 (code.builtin_id() == Builtin::kGenericJSToWasmWrapper);
274 return isolate->heap()->ToBoolean(is_js_to_wasm);
275 }
276
RUNTIME_FUNCTION(Runtime_IsWasmTrapHandlerEnabled)277 RUNTIME_FUNCTION(Runtime_IsWasmTrapHandlerEnabled) {
278 DisallowGarbageCollection no_gc;
279 DCHECK_EQ(0, args.length());
280 return isolate->heap()->ToBoolean(trap_handler::IsTrapHandlerEnabled());
281 }
282
RUNTIME_FUNCTION(Runtime_IsThreadInWasm)283 RUNTIME_FUNCTION(Runtime_IsThreadInWasm) {
284 DisallowGarbageCollection no_gc;
285 DCHECK_EQ(0, args.length());
286 return isolate->heap()->ToBoolean(trap_handler::IsThreadInWasm());
287 }
288
RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount)289 RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) {
290 HandleScope scope(isolate);
291 DCHECK_EQ(0, args.length());
292 size_t trap_count = trap_handler::GetRecoveredTrapCount();
293 return *isolate->factory()->NewNumberFromSize(trap_count);
294 }
295
RUNTIME_FUNCTION(Runtime_GetWasmExceptionTagId)296 RUNTIME_FUNCTION(Runtime_GetWasmExceptionTagId) {
297 HandleScope scope(isolate);
298 DCHECK_EQ(2, args.length());
299 Handle<WasmExceptionPackage> exception = args.at<WasmExceptionPackage>(0);
300 Handle<WasmInstanceObject> instance = args.at<WasmInstanceObject>(1);
301 Handle<Object> tag =
302 WasmExceptionPackage::GetExceptionTag(isolate, exception);
303 CHECK(tag->IsWasmExceptionTag());
304 Handle<FixedArray> tags_table(instance->tags_table(), isolate);
305 for (int index = 0; index < tags_table->length(); ++index) {
306 if (tags_table->get(index) == *tag) return Smi::FromInt(index);
307 }
308 UNREACHABLE();
309 }
310
RUNTIME_FUNCTION(Runtime_GetWasmExceptionValues)311 RUNTIME_FUNCTION(Runtime_GetWasmExceptionValues) {
312 HandleScope scope(isolate);
313 DCHECK_EQ(1, args.length());
314 Handle<WasmExceptionPackage> exception = args.at<WasmExceptionPackage>(0);
315 Handle<Object> values_obj =
316 WasmExceptionPackage::GetExceptionValues(isolate, exception);
317 CHECK(values_obj->IsFixedArray()); // Only called with correct input.
318 Handle<FixedArray> values = Handle<FixedArray>::cast(values_obj);
319 return *isolate->factory()->NewJSArrayWithElements(values);
320 }
321
RUNTIME_FUNCTION(Runtime_SerializeWasmModule)322 RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
323 HandleScope scope(isolate);
324 DCHECK_EQ(1, args.length());
325 Handle<WasmModuleObject> module_obj = args.at<WasmModuleObject>(0);
326
327 wasm::NativeModule* native_module = module_obj->native_module();
328 DCHECK(!native_module->compilation_state()->failed());
329
330 wasm::WasmSerializer wasm_serializer(native_module);
331 size_t byte_length = wasm_serializer.GetSerializedNativeModuleSize();
332
333 Handle<JSArrayBuffer> array_buffer =
334 isolate->factory()
335 ->NewJSArrayBufferAndBackingStore(byte_length,
336 InitializedFlag::kUninitialized)
337 .ToHandleChecked();
338
339 CHECK(wasm_serializer.SerializeNativeModule(
340 {static_cast<uint8_t*>(array_buffer->backing_store()), byte_length}));
341 return *array_buffer;
342 }
343
344 // Take an array buffer and attempt to reconstruct a compiled wasm module.
345 // Return undefined if unsuccessful.
RUNTIME_FUNCTION(Runtime_DeserializeWasmModule)346 RUNTIME_FUNCTION(Runtime_DeserializeWasmModule) {
347 HandleScope scope(isolate);
348 DCHECK_EQ(2, args.length());
349 Handle<JSArrayBuffer> buffer = args.at<JSArrayBuffer>(0);
350 Handle<JSTypedArray> wire_bytes = args.at<JSTypedArray>(1);
351 CHECK(!buffer->was_detached());
352 CHECK(!wire_bytes->WasDetached());
353
354 Handle<JSArrayBuffer> wire_bytes_buffer = wire_bytes->GetBuffer();
355 base::Vector<const uint8_t> wire_bytes_vec{
356 reinterpret_cast<const uint8_t*>(wire_bytes_buffer->backing_store()) +
357 wire_bytes->byte_offset(),
358 wire_bytes->byte_length()};
359 base::Vector<uint8_t> buffer_vec{
360 reinterpret_cast<uint8_t*>(buffer->backing_store()),
361 buffer->byte_length()};
362
363 // Note that {wasm::DeserializeNativeModule} will allocate. We assume the
364 // JSArrayBuffer backing store doesn't get relocated.
365 MaybeHandle<WasmModuleObject> maybe_module_object =
366 wasm::DeserializeNativeModule(isolate, buffer_vec, wire_bytes_vec, {});
367 Handle<WasmModuleObject> module_object;
368 if (!maybe_module_object.ToHandle(&module_object)) {
369 return ReadOnlyRoots(isolate).undefined_value();
370 }
371 return *module_object;
372 }
373
RUNTIME_FUNCTION(Runtime_WasmGetNumberOfInstances)374 RUNTIME_FUNCTION(Runtime_WasmGetNumberOfInstances) {
375 SealHandleScope shs(isolate);
376 DCHECK_EQ(1, args.length());
377 Handle<WasmModuleObject> module_obj = args.at<WasmModuleObject>(0);
378 int instance_count = 0;
379 WeakArrayList weak_instance_list =
380 module_obj->script().wasm_weak_instance_list();
381 for (int i = 0; i < weak_instance_list.length(); ++i) {
382 if (weak_instance_list.Get(i)->IsWeak()) instance_count++;
383 }
384 return Smi::FromInt(instance_count);
385 }
386
RUNTIME_FUNCTION(Runtime_WasmNumCodeSpaces)387 RUNTIME_FUNCTION(Runtime_WasmNumCodeSpaces) {
388 DCHECK_EQ(1, args.length());
389 HandleScope scope(isolate);
390 Handle<JSObject> argument = args.at<JSObject>(0);
391 Handle<WasmModuleObject> module;
392 if (argument->IsWasmInstanceObject()) {
393 module = handle(Handle<WasmInstanceObject>::cast(argument)->module_object(),
394 isolate);
395 } else if (argument->IsWasmModuleObject()) {
396 module = Handle<WasmModuleObject>::cast(argument);
397 }
398 size_t num_spaces =
399 module->native_module()->GetNumberOfCodeSpacesForTesting();
400 return *isolate->factory()->NewNumberFromSize(num_spaces);
401 }
402
RUNTIME_FUNCTION(Runtime_WasmTraceMemory)403 RUNTIME_FUNCTION(Runtime_WasmTraceMemory) {
404 HandleScope scope(isolate);
405 DCHECK_EQ(1, args.length());
406 auto info_addr = Smi::cast(args[0]);
407
408 wasm::MemoryTracingInfo* info =
409 reinterpret_cast<wasm::MemoryTracingInfo*>(info_addr.ptr());
410
411 // Find the caller wasm frame.
412 wasm::WasmCodeRefScope wasm_code_ref_scope;
413 StackTraceFrameIterator it(isolate);
414 DCHECK(!it.done());
415 DCHECK(it.is_wasm());
416 WasmFrame* frame = WasmFrame::cast(it.frame());
417
418 uint8_t* mem_start = reinterpret_cast<uint8_t*>(
419 frame->wasm_instance().memory_object().array_buffer().backing_store());
420 int func_index = frame->function_index();
421 int pos = frame->position();
422 wasm::ExecutionTier tier = frame->wasm_code()->is_liftoff()
423 ? wasm::ExecutionTier::kLiftoff
424 : wasm::ExecutionTier::kTurbofan;
425 wasm::TraceMemoryOperation(tier, info, func_index, pos, mem_start);
426 return ReadOnlyRoots(isolate).undefined_value();
427 }
428
RUNTIME_FUNCTION(Runtime_WasmTierUpFunction)429 RUNTIME_FUNCTION(Runtime_WasmTierUpFunction) {
430 HandleScope scope(isolate);
431 DCHECK_EQ(2, args.length());
432 Handle<WasmInstanceObject> instance = args.at<WasmInstanceObject>(0);
433 int function_index = args.smi_value_at(1);
434 auto* native_module = instance->module_object().native_module();
435 wasm::GetWasmEngine()->CompileFunction(isolate, native_module, function_index,
436 wasm::ExecutionTier::kTurbofan);
437 CHECK(!native_module->compilation_state()->failed());
438 return ReadOnlyRoots(isolate).undefined_value();
439 }
440
RUNTIME_FUNCTION(Runtime_WasmTierDown)441 RUNTIME_FUNCTION(Runtime_WasmTierDown) {
442 HandleScope scope(isolate);
443 DCHECK_EQ(0, args.length());
444 wasm::GetWasmEngine()->TierDownAllModulesPerIsolate(isolate);
445 return ReadOnlyRoots(isolate).undefined_value();
446 }
447
RUNTIME_FUNCTION(Runtime_WasmTierUp)448 RUNTIME_FUNCTION(Runtime_WasmTierUp) {
449 HandleScope scope(isolate);
450 DCHECK_EQ(0, args.length());
451 wasm::GetWasmEngine()->TierUpAllModulesPerIsolate(isolate);
452 return ReadOnlyRoots(isolate).undefined_value();
453 }
454
RUNTIME_FUNCTION(Runtime_IsLiftoffFunction)455 RUNTIME_FUNCTION(Runtime_IsLiftoffFunction) {
456 HandleScope scope(isolate);
457 DCHECK_EQ(1, args.length());
458 Handle<JSFunction> function = args.at<JSFunction>(0);
459 CHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
460 Handle<WasmExportedFunction> exp_fun =
461 Handle<WasmExportedFunction>::cast(function);
462 wasm::NativeModule* native_module =
463 exp_fun->instance().module_object().native_module();
464 uint32_t func_index = exp_fun->function_index();
465 wasm::WasmCodeRefScope code_ref_scope;
466 wasm::WasmCode* code = native_module->GetCode(func_index);
467 return isolate->heap()->ToBoolean(code && code->is_liftoff());
468 }
469
RUNTIME_FUNCTION(Runtime_IsTurboFanFunction)470 RUNTIME_FUNCTION(Runtime_IsTurboFanFunction) {
471 HandleScope scope(isolate);
472 DCHECK_EQ(1, args.length());
473 Handle<JSFunction> function = args.at<JSFunction>(0);
474 CHECK(WasmExportedFunction::IsWasmExportedFunction(*function));
475 Handle<WasmExportedFunction> exp_fun =
476 Handle<WasmExportedFunction>::cast(function);
477 wasm::NativeModule* native_module =
478 exp_fun->instance().module_object().native_module();
479 uint32_t func_index = exp_fun->function_index();
480 wasm::WasmCodeRefScope code_ref_scope;
481 wasm::WasmCode* code = native_module->GetCode(func_index);
482 return isolate->heap()->ToBoolean(code && code->is_turbofan());
483 }
484
RUNTIME_FUNCTION(Runtime_FreezeWasmLazyCompilation)485 RUNTIME_FUNCTION(Runtime_FreezeWasmLazyCompilation) {
486 DCHECK_EQ(1, args.length());
487 DisallowGarbageCollection no_gc;
488 auto instance = WasmInstanceObject::cast(args[0]);
489
490 instance.module_object().native_module()->set_lazy_compile_frozen(true);
491 return ReadOnlyRoots(isolate).undefined_value();
492 }
493
494 } // namespace internal
495 } // namespace v8
496