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