wasm-debug.cc 27.4 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
#include <unordered_map>

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 17
#include "src/isolate.h"
#include "src/wasm/module-decoder.h"
18
#include "src/wasm/wasm-code-manager.h"
19 20
#include "src/wasm/wasm-interpreter.h"
#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 26 27
namespace v8 {
namespace internal {
namespace wasm {
28

29 30
namespace {

31 32 33
template <bool internal, typename... Args>
Handle<String> PrintFToOneByteString(Isolate* isolate, const char* format,
                                     Args... args) {
34 35 36
  // Maximum length of a formatted value name ("arg#%d", "local#%d",
  // "global#%d", i32 constants, i64 constants), including null character.
  static constexpr int kMaxStrLen = 21;
37 38 39 40 41 42 43 44 45
  EmbeddedVector<char, kMaxStrLen> value;
  int len = SNPrintF(value, format, args...);
  CHECK(len > 0 && len < value.length());
  Vector<uint8_t> name = Vector<uint8_t>::cast(value.SubVector(0, len));
  return internal
             ? isolate->factory()->InternalizeOneByteString(name)
             : isolate->factory()->NewStringFromOneByte(name).ToHandleChecked();
}

46 47
Handle<Object> WasmValueToValueObject(Isolate* isolate, WasmValue value) {
  switch (value.type()) {
48 49 50 51
    case kWasmI32:
      if (Smi::IsValid(value.to<int32_t>()))
        return handle(Smi::FromInt(value.to<int32_t>()), isolate);
      return PrintFToOneByteString<false>(isolate, "%d", value.to<int32_t>());
52 53 54 55 56 57 58
    case kWasmI64: {
      int64_t i64 = value.to<int64_t>();
      int32_t i32 = static_cast<int32_t>(i64);
      if (i32 == i64 && Smi::IsValid(i32))
        return handle(Smi::FromIntptr(i32), isolate);
      return PrintFToOneByteString<false>(isolate, "%" PRId64, i64);
    }
59 60 61 62 63 64 65 66 67 68
    case kWasmF32:
      return isolate->factory()->NewNumber(value.to<float>());
    case kWasmF64:
      return isolate->factory()->NewNumber(value.to<double>());
    default:
      UNIMPLEMENTED();
      return isolate->factory()->undefined_value();
  }
}

69 70 71 72 73 74
MaybeHandle<String> GetLocalName(Isolate* isolate,
                                 Handle<WasmDebugInfo> debug_info,
                                 int func_index, int local_index) {
  DCHECK_LE(0, func_index);
  DCHECK_LE(0, local_index);
  if (!debug_info->has_locals_names()) {
75 76
    Handle<WasmModuleObject> module_object(
        debug_info->wasm_instance()->module_object(), isolate);
77
    Handle<FixedArray> locals_names = DecodeLocalNames(isolate, module_object);
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
    debug_info->set_locals_names(*locals_names);
  }

  Handle<FixedArray> locals_names(debug_info->locals_names(), isolate);
  if (func_index >= locals_names->length() ||
      locals_names->get(func_index)->IsUndefined(isolate)) {
    return {};
  }

  Handle<FixedArray> func_locals_names(
      FixedArray::cast(locals_names->get(func_index)), isolate);
  if (local_index >= func_locals_names->length() ||
      func_locals_names->get(local_index)->IsUndefined(isolate)) {
    return {};
  }
93
  return handle(String::cast(func_locals_names->get(local_index)), isolate);
94 95
}

96
class InterpreterHandle {
97
  MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(InterpreterHandle);
98
  Isolate* isolate_;
99 100
  const WasmModule* module_;
  WasmInterpreter interpreter_;
101 102
  StepAction next_step_action_ = StepNone;
  int last_step_stack_depth_ = 0;
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
  std::unordered_map<Address, uint32_t> activations_;

  uint32_t StartActivation(Address frame_pointer) {
    WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
    uint32_t activation_id = thread->StartActivation();
    DCHECK_EQ(0, activations_.count(frame_pointer));
    activations_.insert(std::make_pair(frame_pointer, activation_id));
    return activation_id;
  }

  void FinishActivation(Address frame_pointer, uint32_t activation_id) {
    WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
    thread->FinishActivation(activation_id);
    DCHECK_EQ(1, activations_.count(frame_pointer));
    activations_.erase(frame_pointer);
  }

  std::pair<uint32_t, uint32_t> GetActivationFrameRange(
      WasmInterpreter::Thread* thread, Address frame_pointer) {
    DCHECK_EQ(1, activations_.count(frame_pointer));
    uint32_t activation_id = activations_.find(frame_pointer)->second;
    uint32_t num_activations = static_cast<uint32_t>(activations_.size() - 1);
    uint32_t frame_base = thread->ActivationFrameBase(activation_id);
    uint32_t frame_limit = activation_id == num_activations
                               ? thread->GetFrameCount()
                               : thread->ActivationFrameBase(activation_id + 1);
    DCHECK_LE(frame_base, frame_limit);
    DCHECK_LE(frame_limit, thread->GetFrameCount());
    return {frame_base, frame_limit};
  }
133

134
  static ModuleWireBytes GetBytes(WasmDebugInfo debug_info) {
135 136
    // Return raw pointer into heap. The WasmInterpreter will make its own copy
    // of this data anyway, and there is no heap allocation in-between.
137 138
    NativeModule* native_module =
        debug_info->wasm_instance()->module_object()->native_module();
139
    return ModuleWireBytes{native_module->wire_bytes()};
140
  }
141

142
 public:
143
  InterpreterHandle(Isolate* isolate, Handle<WasmDebugInfo> debug_info)
144
      : isolate_(isolate),
145
        module_(debug_info->wasm_instance()->module_object()->module()),
146
        interpreter_(isolate, module_, GetBytes(*debug_info),
147
                     handle(debug_info->wasm_instance(), isolate)) {}
148 149 150

  ~InterpreterHandle() { DCHECK_EQ(0, activations_.size()); }

151
  WasmInterpreter* interpreter() { return &interpreter_; }
152
  const WasmModule* module() const { return module_; }
153

154 155 156 157 158 159 160 161 162 163 164 165
  void PrepareStep(StepAction step_action) {
    next_step_action_ = step_action;
    last_step_stack_depth_ = CurrentStackDepth();
  }

  void ClearStepping() { next_step_action_ = StepNone; }

  int CurrentStackDepth() {
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    return interpreter()->GetThread(0)->GetFrameCount();
  }

166 167 168
  // Returns true if exited regularly, false if a trap/exception occurred and
  // was not handled inside this activation. In the latter case, a pending
  // exception will have been set on the isolate.
169
  bool Execute(Handle<WasmInstanceObject> instance_object,
170
               Address frame_pointer, uint32_t func_index, Address arg_buffer) {
171
    DCHECK_GE(module()->functions.size(), func_index);
172
    FunctionSig* sig = module()->functions[func_index].sig;
173 174
    DCHECK_GE(kMaxInt, sig->parameter_count());
    int num_params = static_cast<int>(sig->parameter_count());
175
    ScopedVector<WasmValue> wasm_args(num_params);
176
    Address arg_buf_ptr = arg_buffer;
177
    for (int i = 0; i < num_params; ++i) {
178 179
      uint32_t param_size = static_cast<uint32_t>(
          ValueTypes::ElementSizeInBytes(sig->GetParam(i)));
180 181 182 183
#define CASE_ARG_TYPE(type, ctype)                                    \
  case type:                                                          \
    DCHECK_EQ(param_size, sizeof(ctype));                             \
    wasm_args[i] = WasmValue(ReadUnalignedValue<ctype>(arg_buf_ptr)); \
184 185 186 187 188 189 190 191 192 193
    break;
      switch (sig->GetParam(i)) {
        CASE_ARG_TYPE(kWasmI32, uint32_t)
        CASE_ARG_TYPE(kWasmI64, uint64_t)
        CASE_ARG_TYPE(kWasmF32, float)
        CASE_ARG_TYPE(kWasmF64, double)
#undef CASE_ARG_TYPE
        default:
          UNREACHABLE();
      }
194
      arg_buf_ptr += param_size;
195 196
    }

197 198
    uint32_t activation_id = StartActivation(frame_pointer);

199
    WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
200
    thread->InitFrame(&module()->functions[func_index], wasm_args.start());
201 202 203 204
    bool finished = false;
    while (!finished) {
      // TODO(clemensh): Add occasional StackChecks.
      WasmInterpreter::State state = ContinueExecution(thread);
205
      switch (state) {
206
        case WasmInterpreter::State::PAUSED:
207
          NotifyDebugEventListeners(thread);
208
          break;
209 210
        case WasmInterpreter::State::FINISHED:
          // Perfect, just break the switch and exit the loop.
211
          finished = true;
212
          break;
213
        case WasmInterpreter::State::TRAPPED: {
214
          MessageTemplate message_id =
215
              WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
216 217
          Handle<Object> exception =
              isolate_->factory()->NewWasmRuntimeError(message_id);
218
          auto result = thread->RaiseException(isolate_, exception);
219 220 221 222 223
          if (result == WasmInterpreter::Thread::HANDLED) break;
          // If no local handler was found, we fall-thru to {STOPPED}.
          DCHECK_EQ(WasmInterpreter::State::STOPPED, thread->state());
          V8_FALLTHROUGH;
        }
224
        case WasmInterpreter::State::STOPPED:
225 226 227
          // An exception happened, and the current activation was unwound
          // without hitting a local exception handler. All that remains to be
          // done is finish the activation and let the exception propagate.
228 229
          DCHECK_EQ(thread->ActivationFrameBase(activation_id),
                    thread->GetFrameCount());
230 231
          DCHECK(isolate_->has_pending_exception());
          FinishActivation(frame_pointer, activation_id);
232 233
          return false;
        // RUNNING should never occur here.
234 235 236 237
        case WasmInterpreter::State::RUNNING:
        default:
          UNREACHABLE();
      }
238
    }
239 240 241 242 243 244

    // Copy back the return value
    DCHECK_GE(kV8MaxWasmFunctionReturns, sig->return_count());
    // TODO(wasm): Handle multi-value returns.
    DCHECK_EQ(1, kV8MaxWasmFunctionReturns);
    if (sig->return_count()) {
245
      WasmValue ret_val = thread->GetReturnValue(0);
246 247 248 249 250
#define CASE_RET_TYPE(type, ctype)                               \
  case type:                                                     \
    DCHECK_EQ(ValueTypes::ElementSizeInBytes(sig->GetReturn(0)), \
              sizeof(ctype));                                    \
    WriteUnalignedValue<ctype>(arg_buffer, ret_val.to<ctype>()); \
251 252 253 254 255 256 257 258 259 260 261
    break;
      switch (sig->GetReturn(0)) {
        CASE_RET_TYPE(kWasmI32, uint32_t)
        CASE_RET_TYPE(kWasmI64, uint64_t)
        CASE_RET_TYPE(kWasmF32, float)
        CASE_RET_TYPE(kWasmF64, double)
#undef CASE_RET_TYPE
        default:
          UNREACHABLE();
      }
    }
262 263 264

    FinishActivation(frame_pointer, activation_id);

265
    return true;
266
  }
267

268 269 270 271 272 273 274 275
  WasmInterpreter::State ContinueExecution(WasmInterpreter::Thread* thread) {
    switch (next_step_action_) {
      case StepNone:
        return thread->Run();
      case StepIn:
        return thread->Step();
      case StepOut:
        thread->AddBreakFlags(WasmInterpreter::BreakFlag::AfterReturn);
276
        return thread->Run();
277 278 279 280 281 282 283 284 285 286 287 288 289
      case StepNext: {
        int stack_depth = thread->GetFrameCount();
        if (stack_depth == last_step_stack_depth_) return thread->Step();
        thread->AddBreakFlags(stack_depth > last_step_stack_depth_
                                  ? WasmInterpreter::BreakFlag::AfterReturn
                                  : WasmInterpreter::BreakFlag::AfterCall);
        return thread->Run();
      }
      default:
        UNREACHABLE();
    }
  }

290 291 292 293 294
  Handle<WasmInstanceObject> GetInstanceObject() {
    StackTraceFrameIterator it(isolate_);
    WasmInterpreterEntryFrame* frame =
        WasmInterpreterEntryFrame::cast(it.frame());
    Handle<WasmInstanceObject> instance_obj(frame->wasm_instance(), isolate_);
295 296
    // Check that this is indeed the instance which is connected to this
    // interpreter.
297
    DCHECK_EQ(this, Managed<InterpreterHandle>::cast(
298
                        instance_obj->debug_info()->interpreter_handle())
299
                        ->raw());
300 301 302
    return instance_obj;
  }

303
  void NotifyDebugEventListeners(WasmInterpreter::Thread* thread) {
304 305 306
    // Enter the debugger.
    DebugScope debug_scope(isolate_->debug());

307
    // Check whether we hit a breakpoint.
308
    if (isolate_->debug()->break_points_active()) {
309 310 311
      Handle<WasmModuleObject> module_object(
          GetInstanceObject()->module_object(), isolate_);
      int position = GetTopPosition(module_object);
312
      Handle<FixedArray> breakpoints;
313
      if (WasmModuleObject::CheckBreakPoints(isolate_, module_object, position)
314
              .ToHandle(&breakpoints)) {
315 316 317
        // We hit one or several breakpoints. Clear stepping, notify the
        // listeners and return.
        ClearStepping();
318
        isolate_->debug()->OnDebugBreak(breakpoints);
319 320
        return;
      }
321 322
    }

323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
    // We did not hit a breakpoint, so maybe this pause is related to stepping.
    bool hit_step = false;
    switch (next_step_action_) {
      case StepNone:
        break;
      case StepIn:
        hit_step = true;
        break;
      case StepOut:
        hit_step = thread->GetFrameCount() < last_step_stack_depth_;
        break;
      case StepNext: {
        hit_step = thread->GetFrameCount() == last_step_stack_depth_;
        break;
      }
      default:
        UNREACHABLE();
340
    }
341 342
    if (!hit_step) return;
    ClearStepping();
343
    isolate_->debug()->OnDebugBreak(isolate_->factory()->empty_fixed_array());
344 345
  }

346
  int GetTopPosition(Handle<WasmModuleObject> module_object) {
347 348 349 350
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
    DCHECK_LT(0, thread->GetFrameCount());

351
    auto frame = thread->GetFrame(thread->GetFrameCount() - 1);
352
    return module_object->GetFunctionOffset(frame->function()->func_index) +
353
           frame->pc();
354 355 356 357 358 359
  }

  std::vector<std::pair<uint32_t, int>> GetInterpretedStack(
      Address frame_pointer) {
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
360 361 362 363 364 365 366

    std::pair<uint32_t, uint32_t> frame_range =
        GetActivationFrameRange(thread, frame_pointer);

    std::vector<std::pair<uint32_t, int>> stack;
    stack.reserve(frame_range.second - frame_range.first);
    for (uint32_t fp = frame_range.first; fp < frame_range.second; ++fp) {
367 368
      auto frame = thread->GetFrame(fp);
      stack.emplace_back(frame->function()->func_index, frame->pc());
369 370 371 372
    }
    return stack;
  }

373 374
  WasmInterpreter::FramePtr GetInterpretedFrame(Address frame_pointer,
                                                int idx) {
375 376
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
377 378 379 380 381 382

    std::pair<uint32_t, uint32_t> frame_range =
        GetActivationFrameRange(thread, frame_pointer);
    DCHECK_LE(0, idx);
    DCHECK_GT(frame_range.second - frame_range.first, idx);

383
    return thread->GetFrame(frame_range.first + idx);
384
  }
385 386 387 388 389

  uint64_t NumInterpretedCalls() {
    DCHECK_EQ(1, interpreter()->GetThreadCount());
    return interpreter()->GetThread(0)->NumInterpretedCalls();
  }
390

391
  Handle<JSObject> GetGlobalScopeObject(InterpretedFrame* frame,
392
                                        Handle<WasmDebugInfo> debug_info) {
393
    Isolate* isolate = isolate_;
394
    Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
395

396
    // TODO(clemensh): Add globals to the global scope.
397 398
    Handle<JSObject> global_scope_object =
        isolate_->factory()->NewJSObjectWithNullProto();
399
    if (instance->has_memory_object()) {
400
      Handle<String> name = isolate_->factory()->InternalizeOneByteString(
401
          StaticCharVector("memory"));
402 403
      Handle<JSArrayBuffer> memory_buffer(
          instance->memory_object()->array_buffer(), isolate_);
404
      Handle<JSTypedArray> uint8_array = isolate_->factory()->NewJSTypedArray(
405
          kExternalUint8Array, memory_buffer, 0, memory_buffer->byte_length());
406 407
      JSObject::SetOwnPropertyIgnoreAttributes(global_scope_object, name,
                                               uint8_array, NONE)
408
          .Assert();
409
    }
410 411 412
    return global_scope_object;
  }

413
  Handle<JSObject> GetLocalScopeObject(InterpretedFrame* frame,
414
                                       Handle<WasmDebugInfo> debug_info) {
415
    Isolate* isolate = isolate_;
416 417 418 419 420 421 422

    Handle<JSObject> local_scope_object =
        isolate_->factory()->NewJSObjectWithNullProto();
    // Fill parameters and locals.
    int num_params = frame->GetParameterCount();
    int num_locals = frame->GetLocalCount();
    DCHECK_LE(num_params, num_locals);
423 424 425 426 427
    if (num_locals > 0) {
      Handle<JSObject> locals_obj =
          isolate_->factory()->NewJSObjectWithNullProto();
      Handle<String> locals_name =
          isolate_->factory()->InternalizeOneByteString(
428
              StaticCharVector("locals"));
429 430 431 432 433 434 435 436 437 438 439 440
      JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, locals_name,
                                               locals_obj, NONE)
          .Assert();
      for (int i = 0; i < num_locals; ++i) {
        MaybeHandle<String> name =
            GetLocalName(isolate, debug_info, frame->function()->func_index, i);
        if (name.is_null()) {
          // Parameters should come before locals in alphabetical ordering, so
          // we name them "args" here.
          const char* label = i < num_params ? "arg#%d" : "local#%d";
          name = PrintFToOneByteString<true>(isolate_, label, i);
        }
441 442
        WasmValue value = frame->GetLocalValue(i);
        Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
443 444 445 446
        JSObject::SetOwnPropertyIgnoreAttributes(
            locals_obj, name.ToHandleChecked(), value_obj, NONE)
            .Assert();
      }
447 448 449 450 451 452 453 454 455
    }

    // Fill stack values.
    int stack_count = frame->GetStackHeight();
    // Use an object without prototype instead of an Array, for nicer displaying
    // in DevTools. For Arrays, the length field and prototype is displayed,
    // which does not make too much sense here.
    Handle<JSObject> stack_obj =
        isolate_->factory()->NewJSObjectWithNullProto();
456
    Handle<String> stack_name = isolate_->factory()->InternalizeOneByteString(
457
        StaticCharVector("stack"));
458 459 460
    JSObject::SetOwnPropertyIgnoreAttributes(local_scope_object, stack_name,
                                             stack_obj, NONE)
        .Assert();
461
    for (int i = 0; i < stack_count; ++i) {
462 463
      WasmValue value = frame->GetStackValue(i);
      Handle<Object> value_obj = WasmValueToValueObject(isolate_, value);
464 465
      JSObject::SetOwnElementIgnoreAttributes(
          stack_obj, static_cast<uint32_t>(i), value_obj, NONE)
466
          .Assert();
467
    }
468 469
    return local_scope_object;
  }
470 471
};

472 473 474 475 476 477 478
}  // namespace

}  // namespace wasm

namespace {

wasm::InterpreterHandle* GetOrCreateInterpreterHandle(
479
    Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
480
  Handle<Object> handle(debug_info->interpreter_handle(), isolate);
481
  if (handle->IsUndefined(isolate)) {
482 483 484 485 486 487
    // Use the maximum stack size to estimate the maximum size of the
    // interpreter. The interpreter keeps its own stack internally, and the size
    // of the stack should dominate the overall size of the interpreter. We
    // multiply by '2' to account for the growing strategy for the backing store
    // of the stack.
    size_t interpreter_size = FLAG_stack_size * KB * 2;
488
    handle = Managed<wasm::InterpreterHandle>::Allocate(
489
        isolate, interpreter_size, isolate, debug_info);
490
    debug_info->set_interpreter_handle(*handle);
491 492
  }

493
  return Handle<Managed<wasm::InterpreterHandle>>::cast(handle)->raw();
494 495
}

496
wasm::InterpreterHandle* GetInterpreterHandle(WasmDebugInfo debug_info) {
497
  Object handle_obj = debug_info->interpreter_handle();
498
  DCHECK(!handle_obj->IsUndefined());
499
  return Managed<wasm::InterpreterHandle>::cast(handle_obj)->raw();
500 501
}

502
wasm::InterpreterHandle* GetInterpreterHandleOrNull(WasmDebugInfo debug_info) {
503
  Object handle_obj = debug_info->interpreter_handle();
504
  if (handle_obj->IsUndefined()) return nullptr;
505
  return Managed<wasm::InterpreterHandle>::cast(handle_obj)->raw();
506 507
}

508
Handle<FixedArray> GetOrCreateInterpretedFunctions(
509
    Isolate* isolate, Handle<WasmDebugInfo> debug_info) {
510
  Handle<FixedArray> arr(debug_info->interpreted_functions(), isolate);
511
  int num_functions = debug_info->wasm_instance()
512 513
                          ->module_object()
                          ->native_module()
514
                          ->num_functions();
515 516 517 518 519 520
  if (arr->length() == 0 && num_functions > 0) {
    arr = isolate->factory()->NewFixedArray(num_functions);
    debug_info->set_interpreted_functions(*arr);
  }
  DCHECK_EQ(num_functions, arr->length());
  return arr;
521 522 523 524
}

}  // namespace

525
Handle<WasmDebugInfo> WasmDebugInfo::New(Handle<WasmInstanceObject> instance) {
526 527
  DCHECK(!instance->has_debug_info());
  Factory* factory = instance->GetIsolate()->factory();
528 529 530
  Handle<WasmDebugInfo> debug_info = Handle<WasmDebugInfo>::cast(
      factory->NewStruct(WASM_DEBUG_INFO_TYPE, TENURED));
  debug_info->set_wasm_instance(*instance);
531
  debug_info->set_interpreted_functions(*factory->empty_fixed_array());
532 533 534 535
  instance->set_debug_info(*debug_info);
  return debug_info;
}

536
wasm::WasmInterpreter* WasmDebugInfo::SetupForTesting(
537
    Handle<WasmInstanceObject> instance_obj) {
538 539
  Handle<WasmDebugInfo> debug_info = WasmDebugInfo::New(instance_obj);
  Isolate* isolate = instance_obj->GetIsolate();
540 541 542 543 544
  // Use the maximum stack size to estimate the maximum size of the interpreter.
  // The interpreter keeps its own stack internally, and the size of the stack
  // should dominate the overall size of the interpreter. We multiply by '2' to
  // account for the growing strategy for the backing store of the stack.
  size_t interpreter_size = FLAG_stack_size * KB * 2;
545
  auto interp_handle = Managed<wasm::InterpreterHandle>::Allocate(
546
      isolate, interpreter_size, isolate, debug_info);
547
  debug_info->set_interpreter_handle(*interp_handle);
548
  auto ret = interp_handle->raw()->interpreter();
549 550
  ret->SetCallIndirectTestMode();
  return ret;
551 552
}

553 554
void WasmDebugInfo::SetBreakpoint(Handle<WasmDebugInfo> debug_info,
                                  int func_index, int offset) {
555
  Isolate* isolate = debug_info->GetIsolate();
556
  auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
557
  RedirectToInterpreter(debug_info, Vector<int>(&func_index, 1));
558
  const wasm::WasmFunction* func = &handle->module()->functions[func_index];
559 560 561 562
  handle->interpreter()->SetBreakpoint(func, offset, true);
}

void WasmDebugInfo::RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,
563
                                          Vector<int> func_indexes) {
564 565 566
  Isolate* isolate = debug_info->GetIsolate();
  // Ensure that the interpreter is instantiated.
  GetOrCreateInterpreterHandle(isolate, debug_info);
567 568
  Handle<FixedArray> interpreted_functions =
      GetOrCreateInterpretedFunctions(isolate, debug_info);
569
  Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
570
  wasm::NativeModule* native_module =
571
      instance->module_object()->native_module();
572
  const wasm::WasmModule* module = instance->module();
573

574
  // We may modify the wasm jump table.
575 576
  wasm::NativeModuleModificationScope native_module_modification_scope(
      native_module);
577

578 579
  for (int func_index : func_indexes) {
    DCHECK_LE(0, func_index);
580
    DCHECK_GT(module->functions.size(), func_index);
581 582
    if (!interpreted_functions->get(func_index)->IsUndefined(isolate)) continue;

583
    wasm::WasmCode* wasm_new_code = compiler::CompileWasmInterpreterEntry(
584 585
        isolate->wasm_engine(), native_module, func_index,
        module->functions[func_index].sig);
586
    native_module->PublishInterpreterEntry(wasm_new_code, func_index);
587
    Handle<Foreign> foreign_holder = isolate->factory()->NewForeign(
588
        wasm_new_code->instruction_start(), TENURED);
589 590
    interpreted_functions->set(func_index, *foreign_holder);
  }
591 592
}

593
void WasmDebugInfo::PrepareStep(StepAction step_action) {
594
  GetInterpreterHandle(*this)->PrepareStep(step_action);
595 596
}

597 598 599 600
// static
bool WasmDebugInfo::RunInterpreter(Isolate* isolate,
                                   Handle<WasmDebugInfo> debug_info,
                                   Address frame_pointer, int func_index,
601
                                   Address arg_buffer) {
602
  DCHECK_LE(0, func_index);
603 604 605 606
  auto* handle = GetOrCreateInterpreterHandle(isolate, debug_info);
  Handle<WasmInstanceObject> instance(debug_info->wasm_instance(), isolate);
  return handle->Execute(instance, frame_pointer,
                         static_cast<uint32_t>(func_index), arg_buffer);
607 608 609 610
}

std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
    Address frame_pointer) {
611
  return GetInterpreterHandle(*this)->GetInterpretedStack(frame_pointer);
612 613
}

614
wasm::WasmInterpreter::FramePtr WasmDebugInfo::GetInterpretedFrame(
615
    Address frame_pointer, int idx) {
616
  return GetInterpreterHandle(*this)->GetInterpretedFrame(frame_pointer, idx);
617
}
618 619

uint64_t WasmDebugInfo::NumInterpretedCalls() {
620
  auto* handle = GetInterpreterHandleOrNull(*this);
621 622
  return handle ? handle->NumInterpretedCalls() : 0;
}
623

624 625 626
// static
Handle<JSObject> WasmDebugInfo::GetGlobalScopeObject(
    Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
627
  auto* interp_handle = GetInterpreterHandle(*debug_info);
628 629 630 631 632 633 634
  auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
  return interp_handle->GetGlobalScopeObject(frame.get(), debug_info);
}

// static
Handle<JSObject> WasmDebugInfo::GetLocalScopeObject(
    Handle<WasmDebugInfo> debug_info, Address frame_pointer, int frame_index) {
635
  auto* interp_handle = GetInterpreterHandle(*debug_info);
636 637 638
  auto frame = interp_handle->GetInterpretedFrame(frame_pointer, frame_index);
  return interp_handle->GetLocalScopeObject(frame.get(), debug_info);
}
639 640 641

// static
Handle<JSFunction> WasmDebugInfo::GetCWasmEntry(
642
    Handle<WasmDebugInfo> debug_info, wasm::FunctionSig* sig) {
643 644 645 646 647 648
  Isolate* isolate = debug_info->GetIsolate();
  DCHECK_EQ(debug_info->has_c_wasm_entries(),
            debug_info->has_c_wasm_entry_map());
  if (!debug_info->has_c_wasm_entries()) {
    auto entries = isolate->factory()->NewFixedArray(4, TENURED);
    debug_info->set_c_wasm_entries(*entries);
649 650
    size_t map_size = 0;  // size estimate not so important here.
    auto managed_map = Managed<wasm::SignatureMap>::Allocate(isolate, map_size);
651 652 653
    debug_info->set_c_wasm_entry_map(*managed_map);
  }
  Handle<FixedArray> entries(debug_info->c_wasm_entries(), isolate);
654
  wasm::SignatureMap* map = debug_info->c_wasm_entry_map()->raw();
655
  int32_t index = map->Find(*sig);
656
  if (index == -1) {
657
    index = static_cast<int32_t>(map->FindOrInsert(*sig));
658 659 660 661 662 663
    if (index == entries->length()) {
      entries = isolate->factory()->CopyFixedArrayAndGrow(
          entries, entries->length(), TENURED);
      debug_info->set_c_wasm_entries(*entries);
    }
    DCHECK(entries->get(index)->IsUndefined(isolate));
664 665
    Handle<Code> new_entry_code =
        compiler::CompileCWasmEntry(isolate, sig).ToHandleChecked();
666 667 668 669 670
    Handle<WasmExportedFunctionData> function_data =
        Handle<WasmExportedFunctionData>::cast(isolate->factory()->NewStruct(
            WASM_EXPORTED_FUNCTION_DATA_TYPE, TENURED));
    function_data->set_wrapper_code(*new_entry_code);
    function_data->set_instance(debug_info->wasm_instance());
671
    function_data->set_jump_table_offset(-1);
672
    function_data->set_function_index(-1);
673
    Handle<String> name = isolate->factory()->InternalizeOneByteString(
674
        StaticCharVector("c-wasm-entry"));
675
    NewFunctionArgs args = NewFunctionArgs::ForWasm(
676
        name, function_data, isolate->sloppy_function_map());
677
    Handle<JSFunction> new_entry = isolate->factory()->NewFunction(args);
678
    new_entry->set_context(debug_info->wasm_instance()->native_context());
679 680
    new_entry->shared()->set_internal_formal_parameter_count(
        compiler::CWasmEntryParameters::kNumParameters);
681 682
    entries->set(index, *new_entry);
  }
683
  return handle(JSFunction::cast(entries->get(index)), isolate);
684
}
685 686 687

}  // namespace internal
}  // namespace v8