1 // Copyright 2016 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 <unordered_map> 6 7 #include "src/assembler-inl.h" 8 #include "src/assert-scope.h" 9 #include "src/base/optional.h" 10 #include "src/compiler/wasm-compiler.h" 11 #include "src/debug/debug-scopes.h" 12 #include "src/debug/debug.h" 13 #include "src/frames-inl.h" 14 #include "src/heap/factory.h" 15 #include "src/identity-map.h" 16 #include "src/isolate.h" 17 #include "src/wasm/module-decoder.h" 18 #include "src/wasm/wasm-code-manager.h" 19 #include "src/wasm/wasm-interpreter.h" 20 #include "src/wasm/wasm-limits.h" 21 #include "src/wasm/wasm-module.h" 22 #include "src/wasm/wasm-objects-inl.h" 23 #include "src/zone/accounting-allocator.h" 24 25 namespace v8 { 26 namespace internal { 27 namespace wasm { 28 29 namespace { 30 31 template <bool internal, typename... Args> 32 Handle<String> PrintFToOneByteString(Isolate* isolate, const char* format, 33 Args... args) { 34 // Maximum length of a formatted value name ("param#%d", "local#%d", 35 // "global#%d"). 36 constexpr int kMaxStrLen = 18; 37 EmbeddedVector<char, kMaxStrLen> value; 38 int len = SNPrintF(value, format, args...); 39 CHECK(len > 0 && len < value.length()); 40 Vector<uint8_t> name = Vector<uint8_t>::cast(value.SubVector(0, len)); 41 return internal 42 ? isolate->factory()->InternalizeOneByteString(name) 43 : isolate->factory()->NewStringFromOneByte(name).ToHandleChecked(); 44 } 45 46 Handle<Object> WasmValueToValueObject(Isolate* isolate, WasmValue value) { 47 switch (value.type()) { 48 case kWasmI32: 49 if (Smi::IsValid(value.to<int32_t>())) 50 return handle(Smi::FromInt(value.to<int32_t>()), isolate); 51 return PrintFToOneByteString<false>(isolate, "%d", value.to<int32_t>()); 52 case kWasmI64: 53 if (Smi::IsValid(value.to<int64_t>())) 54 return handle(Smi::FromIntptr(value.to<int64_t>()), isolate); 55 return PrintFToOneByteString<false>(isolate, "%" PRId64, 56 value.to<int64_t>()); 57 case kWasmF32: 58 return isolate->factory()->NewNumber(value.to<float>()); 59 case kWasmF64: 60 return isolate->factory()->NewNumber(value.to<double>()); 61 default: 62 UNIMPLEMENTED(); 63 return isolate->factory()->undefined_value(); 64 } 65 } 66 67 MaybeHandle<String> GetLocalName(Isolate* isolate, 68 Handle<WasmDebugInfo> debug_info, 69 int func_index, int local_index) { 70 DCHECK_LE(0, func_index); 71 DCHECK_LE(0, local_index); 72 if (!debug_info->has_locals_names()) { 73 Handle<WasmModuleObject> module_object( 74 debug_info->wasm_instance()->module_object(), isolate); 75 Handle<FixedArray> locals_names = DecodeLocalNames(isolate, module_object); 76 debug_info->set_locals_names(*locals_names); 77 } 78 79 Handle<FixedArray> locals_names(debug_info->locals_names(), isolate); 80 if (func_index >= locals_names->length() || 81 locals_names->get(func_index)->IsUndefined(isolate)) { 82 return {}; 83 } 84 85 Handle<FixedArray> func_locals_names( 86 FixedArray::cast(locals_names->get(func_index)), isolate); 87 if (local_index >= func_locals_names->length() || 88 func_locals_names->get(local_index)->IsUndefined(isolate)) { 89 return {}; 90 } 91 return handle(String::cast(func_locals_names->get(local_index)), isolate); 92 } 93 94 class InterpreterHandle { 95 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(InterpreterHandle); 96 Isolate* isolate_; 97 const WasmModule* module_; 98 WasmInterpreter interpreter_; 99 StepAction next_step_action_ = StepNone; 100 int last_step_stack_depth_ = 0; 101 std::unordered_map<Address, uint32_t> activations_; 102 103 uint32_t StartActivation(Address frame_pointer) { 104 WasmInterpreter::Thread* thread = interpreter_.GetThread(0); 105 uint32_t activation_id = thread->StartActivation(); 106 DCHECK_EQ(0, activations_.count(frame_pointer)); 107 activations_.insert(std::make_pair(frame_pointer, activation_id)); 108 return activation_id; 109 } 110 111 void FinishActivation(Address frame_pointer, uint32_t activation_id) { 112 WasmInterpreter::Thread* thread = interpreter_.GetThread(0); 113 thread->FinishActivation(activation_id); 114 DCHECK_EQ(1, activations_.count(frame_pointer)); 115 activations_.erase(frame_pointer); 116 } 117 118 std::pair<uint32_t, uint32_t> GetActivationFrameRange( 119 WasmInterpreter::Thread* thread, Address frame_pointer) { 120 DCHECK_EQ(1, activations_.count(frame_pointer)); 121 uint32_t activation_id = activations_.find(frame_pointer)->second; 122 uint32_t num_activations = static_cast<uint32_t>(activations_.size() - 1); 123 uint32_t frame_base = thread->ActivationFrameBase(activation_id); 124 uint32_t frame_limit = activation_id == num_activations 125 ? thread->GetFrameCount() 126 : thread->ActivationFrameBase(activation_id + 1); 127 DCHECK_LE(frame_base, frame_limit); 128 DCHECK_LE(frame_limit, thread->GetFrameCount()); 129 return {frame_base, frame_limit}; 130 } 131 132 static Vector<const byte> GetBytes(WasmDebugInfo* debug_info) { 133 // Return raw pointer into heap. The WasmInterpreter will make its own copy 134 // of this data anyway, and there is no heap allocation in-between. 135 NativeModule* native_module = 136 debug_info->wasm_instance()->module_object()->native_module(); 137 return native_module->wire_bytes(); 138 } 139 140 public: 141 // TODO(wasm): properly handlify this constructor. 142 InterpreterHandle(Isolate* isolate, WasmDebugInfo* debug_info) 143 : isolate_(isolate), 144 module_(debug_info->wasm_instance()->module_object()->module()), 145 interpreter_(isolate, module_, GetBytes(debug_info), 146 handle(debug_info->wasm_instance(), isolate)) {} 147 148 ~InterpreterHandle() { DCHECK_EQ(0, activations_.size()); } 149 150 WasmInterpreter* interpreter() { return &interpreter_; } 151 const WasmModule* module() const { return module_; } 152 153 void PrepareStep(StepAction step_action) { 154 next_step_action_ = step_action; 155 last_step_stack_depth_ = CurrentStackDepth(); 156 } 157 158 void ClearStepping() { next_step_action_ = StepNone; } 159 160 int CurrentStackDepth() { 161 DCHECK_EQ(1, interpreter()->GetThreadCount()); 162 return interpreter()->GetThread(0)->GetFrameCount(); 163 } 164 165 // Returns true if exited regularly, false if a trap/exception occurred and 166 // was not handled inside this activation. In the latter case, a pending 167 // exception will have been set on the isolate. 168 bool Execute(Handle<WasmInstanceObject> instance_object, 169 Address frame_pointer, uint32_t func_index, Address arg_buffer) { 170 DCHECK_GE(module()->functions.size(), func_index); 171 FunctionSig* sig = module()->functions[func_index].sig; 172 DCHECK_GE(kMaxInt, sig->parameter_count()); 173 int num_params = static_cast<int>(sig->parameter_count()); 174 ScopedVector<WasmValue> wasm_args(num_params); 175 Address arg_buf_ptr = arg_buffer; 176 for (int i = 0; i < num_params; ++i) { 177 uint32_t param_size = static_cast<uint32_t>( 178 ValueTypes::ElementSizeInBytes(sig->GetParam(i))); 179 #define CASE_ARG_TYPE(type, ctype) \ 180 case type: \ 181 DCHECK_EQ(param_size, sizeof(ctype)); \ 182 wasm_args[i] = WasmValue(ReadUnalignedValue<ctype>(arg_buf_ptr)); \ 183 break; 184 switch (sig->GetParam(i)) { 185 CASE_ARG_TYPE(kWasmI32, uint32_t) 186 CASE_ARG_TYPE(kWasmI64, uint64_t) 187 CASE_ARG_TYPE(kWasmF32, float) 188 CASE_ARG_TYPE(kWasmF64, double) 189 #undef CASE_ARG_TYPE 190 default: 191 UNREACHABLE(); 192 } 193 arg_buf_ptr += param_size; 194 } 195 196 uint32_t activation_id = StartActivation(frame_pointer); 197 198 WasmInterpreter::Thread* thread = interpreter_.GetThread(0); 199 thread->InitFrame(&module()->functions[func_index], wasm_args.start()); 200 bool finished = false; 201 while (!finished) { 202 // TODO(clemensh): Add occasional StackChecks. 203 WasmInterpreter::State state = ContinueExecution(thread); 204 switch (state) { 205 case WasmInterpreter::State::PAUSED: 206 NotifyDebugEventListeners(thread); 207 break; 208 case WasmInterpreter::State::FINISHED: 209 // Perfect, just break the switch and exit the loop. 210 finished = true; 211 break; 212 case WasmInterpreter::State::TRAPPED: { 213 int message_id = 214 WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason()); 215 Handle<Object> exception = isolate_->factory()->NewWasmRuntimeError( 216 static_cast<MessageTemplate::Template>(message_id)); 217 isolate_->Throw(*exception); 218 // Handle this exception. Return without trying to read back the 219 // return value. 220 auto result = thread->HandleException(isolate_); 221 return result == WasmInterpreter::Thread::HANDLED; 222 } break; 223 case WasmInterpreter::State::STOPPED: 224 // An exception happened, and the current activation was unwound. 225 DCHECK_EQ(thread->ActivationFrameBase(activation_id), 226 thread->GetFrameCount()); 227 return false; 228 // RUNNING should never occur here. 229 case WasmInterpreter::State::RUNNING: 230 default: 231 UNREACHABLE(); 232 } 233 } 234 235 // Copy back the return value 236 DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count()); 237 // TODO(wasm): Handle multi-value returns. 238 DCHECK_EQ(1, kV8MaxWasmFunctionReturns); 239 if (sig->return_count()) { 240 WasmValue ret_val = thread->GetReturnValue(0); 241 #define CASE_RET_TYPE(type, ctype) \ 242 case type: \ 243 DCHECK_EQ(ValueTypes::ElementSizeInBytes(sig->GetReturn(0)), \ 244 sizeof(ctype)); \ 245 WriteUnalignedValue<ctype>(arg_buffer, ret_val.to<ctype>()); \ 246 break; 247 switch (sig->GetReturn(0)) { 248 CASE_RET_TYPE(kWasmI32, uint32_t) 249 CASE_RET_TYPE(kWasmI64, uint64_t) 250 CASE_RET_TYPE(kWasmF32, float) 251 CASE_RET_TYPE(kWasmF64, double) 252 #undef CASE_RET_TYPE 253 default: 254 UNREACHABLE(); 255 } 256 } 257 258 FinishActivation(frame_pointer, activation_id); 259 260 return true; 261 } 262 263 WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) { 264 switch (next_step_action_) { 265 case StepNone: 266 return thread->Run(); 267 case StepIn: 268 return thread->Step(); 269 case StepOut: 270 thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn); 271 return thread->Run(); 272 case StepNext: { 273 int stack_depth = thread->GetFrameCount(); 274 if (stack_depth == last_step_stack_depth_) return thread->Step(); 275 thread->AddBreakFlags(stack_depth > last_step_stack_depth_ 276 ? WasmInterpreter::BreakFlag::AfterReturn 277 : WasmInterpreter::BreakFlag::AfterCall); 278 return thread->Run(); 279 } 280 default: 281 UNREACHABLE(); 282 } 283 } 284 285 Handle<WasmInstanceObject> GetInstanceObject() { 286 StackTraceFrameIterator it(isolate_); 287 WasmInterpreterEntryFrame* frame = 288 WasmInterpreterEntryFrame::cast(it.frame()); 289 Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_); 290 // Check that this is indeed the instance which is connected to this 291 // interpreter. 292 DCHECK_EQ(this, Managed<InterpreterHandle>::cast( 293 instance_obj->debug_info()->interpreter_handle()) 294 ->raw()); 295 return instance_obj; 296 } 297 298 void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) { 299 // Enter the debugger. 300 DebugScope debug_scope(isolate_->debug()); 301 302 // Check whether we hit a breakpoint. 303 if (isolate_->debug()->break_points_active()) { 304 Handle<WasmModuleObject> module_object( 305 GetInstanceObject()->module_object(), isolate_); 306 int position = GetTopPosition(module_object); 307 Handle<FixedArray> breakpoints; 308 if (WasmModuleObject::CheckBreakPoints(isolate_, module_object, position) 309 .ToHandle(&breakpoints)) { 310 // We hit one or several breakpoints. Clear stepping, notify the 311 // listeners and return. 312 ClearStepping(); 313 isolate_->debug()->OnDebugBreak(breakpoints); 314 return; 315 } 316 } 317 318 // We did not hit a breakpoint, so maybe this pause is related to stepping. 319 bool hit_step = false; 320 switch (next_step_action_) { 321 case StepNone: 322 break; 323 case StepIn: 324 hit_step = true; 325 break; 326 case StepOut: 327 hit_step = thread->GetFrameCount() < last_step_stack_depth_; 328 break; 329 case StepNext: { 330 hit_step = thread->GetFrameCount() == last_step_stack_depth_; 331 break; 332 } 333 default: 334 UNREACHABLE(); 335 } 336 if (!hit_step) return; 337 ClearStepping(); 338 isolate_->debug()->OnDebugBreak(isolate_->factory()->empty_fixed_array()); 339 } 340 341 int GetTopPosition(Handle<WasmModuleObject> module_object) { 342 DCHECK_EQ(1, interpreter()->GetThreadCount()); 343 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); 344 DCHECK_LT(0, thread->GetFrameCount()); 345 346 auto frame = thread->GetFrame(thread->GetFrameCount() - 1); 347 return module_object->GetFunctionOffset(frame->function()->func_index) + 348 frame->pc(); 349 } 350 351 std::vector<std::pair<uint32_t, int>> GetInterpretedStack( 352 Address frame_pointer) { 353 DCHECK_EQ(1, interpreter()->GetThreadCount()); 354 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); 355 356 std::pair<uint32_t, uint32_t> frame_range = 357 GetActivationFrameRange(thread, frame_pointer); 358 359 std::vector<std::pair<uint32_t, int>> stack; 360 stack.reserve(frame_range.second - frame_range.first); 361 for (uint32_t fp = frame_range.first; fp < frame_range.second; ++fp) { 362 auto frame = thread->GetFrame(fp); 363 stack.emplace_back(frame->function()->func_index, frame->pc()); 364 } 365 return stack; 366 } 367 368 WasmInterpreter::FramePtr GetInterpretedFrame(Address frame_pointer, 369 int idx) { 370 DCHECK_EQ(1, interpreter()->GetThreadCount()); 371 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); 372 373 std::pair<uint32_t, uint32_t> frame_range = 374 GetActivationFrameRange(thread, frame_pointer); 375 DCHECK_LE(0, idx); 376 DCHECK_GT(frame_range.second - frame_range.first, idx); 377 378 return thread->GetFrame(frame_range.first + idx); 379 } 380 381 void Unwind(Address frame_pointer) { 382 // Find the current activation. 383 DCHECK_EQ(1, activations_.count(frame_pointer)); 384 // Activations must be properly stacked: 385 DCHECK_EQ(activations_.size() - 1, activations_[frame_pointer]); 386 uint32_t activation_id = static_cast<uint32_t>(activations_.size() - 1); 387 388 // Unwind the frames of the current activation if not already unwound. 389 WasmInterpreter::Thread* thread = interpreter()->GetThread(0); 390 if (static_cast<uint32_t>(thread->GetFrameCount()) > 391 thread->ActivationFrameBase(activation_id)) { 392 using ExceptionResult = WasmInterpreter::Thread::ExceptionHandlingResult; 393 ExceptionResult result = thread->HandleException(isolate_); 394 // TODO(wasm): Handle exceptions caught in wasm land. 395 CHECK_EQ(ExceptionResult::UNWOUND, result); 396 } 397 398 FinishActivation(frame_pointer, activation_id); 399 } 400 401 uint64_t NumInterpretedCalls() { 402 DCHECK_EQ(1, interpreter()->GetThreadCount()); 403 return interpreter()->GetThread(0)->NumInterpretedCalls(); 404 } 405 406 Handle<JSObject> GetGlobalScopeObject(InterpretedFrame* frame, 407 Handle<WasmDebugInfo> debug_info) { 408 Isolate* isolate = isolate_; 409 Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); 410 411 // TODO(clemensh): Add globals to the global scope. 412 Handle<JSObject> global_scope_object = 413 isolate_->factory()->NewJSObjectWithNullProto(); 414 if (instance->has_memory_object()) { 415 Handle<String> name = isolate_->factory()->InternalizeOneByteString( 416 STATIC_CHAR_VECTOR("memory")); 417 Handle<JSArrayBuffer> memory_buffer( 418 instance->memory_object()->array_buffer(), isolate_); 419 uint32_t byte_length; 420 CHECK(memory_buffer->byte_length()->ToUint32(&byte_length)); 421 Handle<JSTypedArray> uint8_array = isolate_->factory()->NewJSTypedArray( 422 kExternalUint8Array, memory_buffer, 0, byte_length); 423 JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, name, 424 uint8_array, NONE) 425 .Assert(); 426 } 427 return global_scope_object; 428 } 429 430 Handle<JSObject> GetLocalScopeObject(InterpretedFrame* frame, 431 Handle<WasmDebugInfo> debug_info) { 432 Isolate* isolate = isolate_; 433 434 Handle<JSObject> local_scope_object = 435 isolate_->factory()->NewJSObjectWithNullProto(); 436 // Fill parameters and locals. 437 int num_params = frame->GetParameterCount(); 438 int num_locals = frame->GetLocalCount(); 439 DCHECK_LE(num_params, num_locals); 440 if (num_locals > 0) { 441 Handle<JSObject> locals_obj = 442 isolate_->factory()->NewJSObjectWithNullProto(); 443 Handle<String> locals_name = 444 isolate_->factory()->InternalizeOneByteString( 445 STATIC_CHAR_VECTOR("locals")); 446 JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, locals_name, 447 locals_obj, NONE) 448 .Assert(); 449 for (int i = 0; i < num_locals; ++i) { 450 MaybeHandle<String> name = 451 GetLocalName(isolate, debug_info, frame->function()->func_index, i); 452 if (name.is_null()) { 453 // Parameters should come before locals in alphabetical ordering, so 454 // we name them "args" here. 455 const char* label = i < num_params ? "arg#%d" : "local#%d"; 456 name = PrintFToOneByteString<true>(isolate_, label, i); 457 } 458 WasmValue value = frame->GetLocalValue(i); 459 Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); 460 JSObject::SetOwnPropertyIgnoreAttributes( 461 locals_obj, name.ToHandleChecked(), value_obj, NONE) 462 .Assert(); 463 } 464 } 465 466 // Fill stack values. 467 int stack_count = frame->GetStackHeight(); 468 // Use an object without prototype instead of an Array, for nicer displaying 469 // in DevTools. For Arrays, the length field and prototype is displayed, 470 // which does not make too much sense here. 471 Handle<JSObject> stack_obj = 472 isolate_->factory()->NewJSObjectWithNullProto(); 473 Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString( 474 STATIC_CHAR_VECTOR("stack")); 475 JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name, 476 stack_obj, NONE) 477 .Assert(); 478 for (int i = 0; i < stack_count; ++i) { 479 WasmValue value = frame->GetStackValue(i); 480 Handle<Object> value_obj = WasmValueToValueObject(isolate_, value); 481 JSObject::SetOwnElementIgnoreAttributes( 482 stack_obj, static_cast<uint32_t>(i), value_obj, NONE) 483 .Assert(); 484 } 485 return local_scope_object; 486 } 487 488 Handle<JSArray> GetScopeDetails(Address frame_pointer, int frame_index, 489 Handle<WasmDebugInfo> debug_info) { 490 auto frame = GetInterpretedFrame(frame_pointer, frame_index); 491 492 Handle<FixedArray> global_scope = 493 isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize); 494 global_scope->set(ScopeIterator::kScopeDetailsTypeIndex, 495 Smi::FromInt(ScopeIterator::ScopeTypeGlobal)); 496 Handle<JSObject> global_scope_object = 497 GetGlobalScopeObject(frame.get(), debug_info); 498 global_scope->set(ScopeIterator::kScopeDetailsObjectIndex, 499 *global_scope_object); 500 501 Handle<FixedArray> local_scope = 502 isolate_->factory()->NewFixedArray(ScopeIterator::kScopeDetailsSize); 503 local_scope->set(ScopeIterator::kScopeDetailsTypeIndex, 504 Smi::FromInt(ScopeIterator::ScopeTypeLocal)); 505 Handle<JSObject> local_scope_object = 506 GetLocalScopeObject(frame.get(), debug_info); 507 local_scope->set(ScopeIterator::kScopeDetailsObjectIndex, 508 *local_scope_object); 509 510 Handle<JSArray> global_jsarr = 511 isolate_->factory()->NewJSArrayWithElements(global_scope); 512 Handle<JSArray> local_jsarr = 513 isolate_->factory()->NewJSArrayWithElements(local_scope); 514 Handle<FixedArray> all_scopes = isolate_->factory()->NewFixedArray(2); 515 all_scopes->set(0, *global_jsarr); 516 all_scopes->set(1, *local_jsarr); 517 return isolate_->factory()->NewJSArrayWithElements(all_scopes); 518 } 519 }; 520 521 } // namespace 522 523 } // namespace wasm 524 525 namespace { 526 527 wasm::InterpreterHandle* GetOrCreateInterpreterHandle( 528 Isolate* isolate, Handle<WasmDebugInfo> debug_info) { 529 Handle<Object> handle(debug_info->interpreter_handle(), isolate); 530 if (handle->IsUndefined(isolate)) { 531 // Use the maximum stack size to estimate the maximum size of the 532 // interpreter. The interpreter keeps its own stack internally, and the size 533 // of the stack should dominate the overall size of the interpreter. We 534 // multiply by '2' to account for the growing strategy for the backing store 535 // of the stack. 536 size_t interpreter_size = FLAG_stack_size * KB * 2; 537 handle = Managed<wasm::InterpreterHandle>::Allocate( 538 isolate, interpreter_size, isolate, *debug_info); 539 debug_info->set_interpreter_handle(*handle); 540 } 541 542 return Handle<Managed<wasm::InterpreterHandle>>::cast(handle)->raw(); 543 } 544 545 wasm::InterpreterHandle* GetInterpreterHandle(WasmDebugInfo* debug_info) { 546 Object* handle_obj = debug_info->interpreter_handle(); 547 DCHECK(!handle_obj->IsUndefined()); 548 return Managed<wasm::InterpreterHandle>::cast(handle_obj)->raw(); 549 } 550 551 wasm::InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo* debug_info) { 552 Object* handle_obj = debug_info->interpreter_handle(); 553 if (handle_obj->IsUndefined()) return nullptr; 554 return Managed<wasm::InterpreterHandle>::cast(handle_obj)->raw(); 555 } 556 557 Handle<FixedArray> GetOrCreateInterpretedFunctions( 558 Isolate* isolate, Handle<WasmDebugInfo> debug_info) { 559 Handle<Object> obj(debug_info->interpreted_functions(), isolate); 560 if (!obj->IsUndefined(isolate)) return Handle<FixedArray>::cast(obj); 561 562 int num_functions = debug_info->wasm_instance() 563 ->module_object() 564 ->native_module() 565 ->num_functions(); 566 Handle<FixedArray> new_arr = isolate->factory()->NewFixedArray(num_functions); 567 debug_info->set_interpreted_functions(*new_arr); 568 return new_arr; 569 } 570 571 } // namespace 572 573 Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) { 574 DCHECK(!instance->has_debug_info()); 575 Factory* factory = instance->GetIsolate()->factory(); 576 Handle<WasmDebugInfo> debug_info = Handle<WasmDebugInfo>::cast( 577 factory->NewStruct(WASM_DEBUG_INFO_TYPE, TENURED)); 578 debug_info->set_wasm_instance(*instance); 579 instance->set_debug_info(*debug_info); 580 return debug_info; 581 } 582 583 wasm::WasmInterpreter* WasmDebugInfo::SetupForTesting( 584 Handle<WasmInstanceObject> instance_obj) { 585 Handle<WasmDebugInfo> debug_info = WasmDebugInfo::New(instance_obj); 586 Isolate* isolate = instance_obj->GetIsolate(); 587 // Use the maximum stack size to estimate the maximum size of the interpreter. 588 // The interpreter keeps its own stack internally, and the size of the stack 589 // should dominate the overall size of the interpreter. We multiply by '2' to 590 // account for the growing strategy for the backing store of the stack. 591 size_t interpreter_size = FLAG_stack_size * KB * 2; 592 auto interp_handle = Managed<wasm::InterpreterHandle>::Allocate( 593 isolate, interpreter_size, isolate, *debug_info); 594 debug_info->set_interpreter_handle(*interp_handle); 595 auto ret = interp_handle->raw()->interpreter(); 596 ret->SetCallIndirectTestMode(); 597 return ret; 598 } 599 600 void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info, 601 int func_index, int offset) { 602 Isolate* isolate = debug_info->GetIsolate(); 603 auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info); 604 RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1)); 605 const wasm::WasmFunction* func = &handle->module()->functions[func_index]; 606 handle->interpreter()->SetBreakpoint(func, offset, true); 607 } 608 609 void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info, 610 Vector<int> func_indexes) { 611 Isolate* isolate = debug_info->GetIsolate(); 612 // Ensure that the interpreter is instantiated. 613 GetOrCreateInterpreterHandle(isolate, debug_info); 614 Handle<FixedArray> interpreted_functions = 615 GetOrCreateInterpretedFunctions(isolate, debug_info); 616 Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); 617 wasm::NativeModule* native_module = 618 instance->module_object()->native_module(); 619 const wasm::WasmModule* module = instance->module(); 620 621 // We may modify js wrappers, as well as wasm functions. Hence the 2 622 // modification scopes. 623 CodeSpaceMemoryModificationScope modification_scope(isolate->heap()); 624 wasm::NativeModuleModificationScope native_module_modification_scope( 625 native_module); 626 627 for (int func_index : func_indexes) { 628 DCHECK_LE(0, func_index); 629 DCHECK_GT(module->functions.size(), func_index); 630 if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) continue; 631 632 MaybeHandle<Code> new_code = compiler::CompileWasmInterpreterEntry( 633 isolate, func_index, module->functions[func_index].sig); 634 const wasm::WasmCode* wasm_new_code = native_module->AddInterpreterEntry( 635 new_code.ToHandleChecked(), func_index); 636 Handle<Foreign> foreign_holder = isolate->factory()->NewForeign( 637 wasm_new_code->instruction_start(), TENURED); 638 interpreted_functions->set(func_index, *foreign_holder); 639 } 640 } 641 642 void WasmDebugInfo::PrepareStep(StepAction step_action) { 643 GetInterpreterHandle(this)->PrepareStep(step_action); 644 } 645 646 // static 647 bool WasmDebugInfo::RunInterpreter(Isolate* isolate, 648 Handle<WasmDebugInfo> debug_info, 649 Address frame_pointer, int func_index, 650 Address arg_buffer) { 651 DCHECK_LE(0, func_index); 652 auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info); 653 Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate); 654 return handle->Execute(instance, frame_pointer, 655 static_cast<uint32_t>(func_index), arg_buffer); 656 } 657 658 std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack( 659 Address frame_pointer) { 660 return GetInterpreterHandle(this)->GetInterpretedStack(frame_pointer); 661 } 662 663 wasm::WasmInterpreter::FramePtr WasmDebugInfo::GetInterpretedFrame( 664 Address frame_pointer, int idx) { 665 return GetInterpreterHandle(this)->GetInterpretedFrame(frame_pointer, idx); 666 } 667 668 void WasmDebugInfo::Unwind(Address frame_pointer) { 669 return GetInterpreterHandle(this)->Unwind(frame_pointer); 670 } 671 672 uint64_t WasmDebugInfo::NumInterpretedCalls() { 673 auto* handle = GetInterpreterHandleOrNull(this); 674 return handle ? handle->NumInterpretedCalls() : 0; 675 } 676 677 // static 678 Handle<JSObject> WasmDebugInfo::GetScopeDetails( 679 Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) { 680 auto* interp_handle = GetInterpreterHandle(*debug_info); 681 return interp_handle->GetScopeDetails(frame_pointer, frame_index, debug_info); 682 } 683 684 // static 685 Handle<JSObject> WasmDebugInfo::GetGlobalScopeObject( 686 Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) { 687 auto* interp_handle = GetInterpreterHandle(*debug_info); 688 auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index); 689 return interp_handle->GetGlobalScopeObject(frame.get(), debug_info); 690 } 691 692 // static 693 Handle<JSObject> WasmDebugInfo::GetLocalScopeObject( 694 Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) { 695 auto* interp_handle = GetInterpreterHandle(*debug_info); 696 auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index); 697 return interp_handle->GetLocalScopeObject(frame.get(), debug_info); 698 } 699 700 // static 701 Handle<JSFunction> WasmDebugInfo::GetCWasmEntry( 702 Handle<WasmDebugInfo> debug_info, wasm::FunctionSig* sig) { 703 Isolate* isolate = debug_info->GetIsolate(); 704 DCHECK_EQ(debug_info->has_c_wasm_entries(), 705 debug_info->has_c_wasm_entry_map()); 706 if (!debug_info->has_c_wasm_entries()) { 707 auto entries = isolate->factory()->NewFixedArray(4, TENURED); 708 debug_info->set_c_wasm_entries(*entries); 709 size_t map_size = 0; // size estimate not so important here. 710 auto managed_map = Managed<wasm::SignatureMap>::Allocate(isolate, map_size); 711 debug_info->set_c_wasm_entry_map(*managed_map); 712 } 713 Handle<FixedArray> entries(debug_info->c_wasm_entries(), isolate); 714 wasm::SignatureMap* map = debug_info->c_wasm_entry_map()->raw(); 715 int32_t index = map->Find(*sig); 716 if (index == -1) { 717 index = static_cast<int32_t>(map->FindOrInsert(*sig)); 718 if (index == entries->length()) { 719 entries = isolate->factory()->CopyFixedArrayAndGrow( 720 entries, entries->length(), TENURED); 721 debug_info->set_c_wasm_entries(*entries); 722 } 723 DCHECK(entries->get(index)->IsUndefined(isolate)); 724 Handle<Code> new_entry_code = 725 compiler::CompileCWasmEntry(isolate, sig).ToHandleChecked(); 726 Handle<WasmExportedFunctionData> function_data = 727 Handle<WasmExportedFunctionData>::cast(isolate->factory()->NewStruct( 728 WASM_EXPORTED_FUNCTION_DATA_TYPE, TENURED)); 729 function_data->set_wrapper_code(*new_entry_code); 730 function_data->set_instance(debug_info->wasm_instance()); 731 function_data->set_jump_table_offset(-1); 732 function_data->set_function_index(-1); 733 Handle<String> name = isolate->factory()->InternalizeOneByteString( 734 STATIC_CHAR_VECTOR("c-wasm-entry")); 735 NewFunctionArgs args = NewFunctionArgs::ForWasm( 736 name, function_data, isolate->sloppy_function_map()); 737 Handle<JSFunction> new_entry = isolate->factory()->NewFunction(args); 738 new_entry->set_context(debug_info->wasm_instance()->native_context()); 739 new_entry->shared()->set_internal_formal_parameter_count( 740 compiler::CWasmEntryParameters::kNumParameters); 741 entries->set(index, *new_entry); 742 } 743 return handle(JSFunction::cast(entries->get(index)), isolate); 744 } 745 746 } // namespace internal 747 } // namespace v8 748