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 24namespace v8 { 25namespace internal { 26 27namespace { 28struct WasmCompileControls { 29 uint32_t MaxWasmBufferSize = std::numeric_limits<uint32_t>::max(); 30 bool AllowAnySizeForAsync = true; 31}; 32using 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. 37DEFINE_LAZY_LEAKY_OBJECT_GETTER(WasmCompileControlsMap, 38 GetPerIsolateWasmControls) 39base::LazyMutex g_PerIsolateWasmControlsMutex = LAZY_MUTEX_INITIALIZER; 40 41bool 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 55bool 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 72v8::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 80void ThrowRangeException(v8::Isolate* isolate, const char* message) { 81 isolate->ThrowException(NewRangeException(isolate, message)); 82} 83 84bool 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 90bool 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. 100RUNTIME_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 114RUNTIME_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 122namespace { 123 124void 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 133int 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 145RUNTIME_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 179RUNTIME_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 233RUNTIME_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 248namespace { 249 250bool DisallowWasmCodegenFromStringsCallback(v8::Local<v8::Context> context, 251 v8::Local<v8::String> source) { 252 return false; 253} 254 255} // namespace 256 257RUNTIME_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 267RUNTIME_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 277RUNTIME_FUNCTION(Runtime_IsWasmTrapHandlerEnabled) { 278 DisallowGarbageCollection no_gc; 279 DCHECK_EQ(0, args.length()); 280 return isolate->heap()->ToBoolean(trap_handler::IsTrapHandlerEnabled()); 281} 282 283RUNTIME_FUNCTION(Runtime_IsThreadInWasm) { 284 DisallowGarbageCollection no_gc; 285 DCHECK_EQ(0, args.length()); 286 return isolate->heap()->ToBoolean(trap_handler::IsThreadInWasm()); 287} 288 289RUNTIME_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 296RUNTIME_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 311RUNTIME_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 322RUNTIME_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. 346RUNTIME_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 374RUNTIME_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 387RUNTIME_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 403RUNTIME_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 429RUNTIME_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 441RUNTIME_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 448RUNTIME_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 455RUNTIME_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 470RUNTIME_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 485RUNTIME_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