• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
PrintFToOneByteString(Isolate * isolate,const char * format,Args...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 
WasmValueToValueObject(Isolate * isolate,WasmValue value)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 
GetLocalName(Isolate * isolate,Handle<WasmDebugInfo> debug_info,int func_index,int local_index)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 
StartActivation(Address frame_pointer)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 
FinishActivation(Address frame_pointer,uint32_t activation_id)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 
GetActivationFrameRange(WasmInterpreter::Thread * thread,Address frame_pointer)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 
GetBytes(WasmDebugInfo * debug_info)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.
InterpreterHandle(Isolate * isolate,WasmDebugInfo * debug_info)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 
~InterpreterHandle()148   ~InterpreterHandle() { DCHECK_EQ(0, activations_.size()); }
149 
interpreter()150   WasmInterpreter* interpreter() { return &interpreter_; }
module() const151   const WasmModule* module() const { return module_; }
152 
PrepareStep(StepAction step_action)153   void PrepareStep(StepAction step_action) {
154     next_step_action_ = step_action;
155     last_step_stack_depth_ = CurrentStackDepth();
156   }
157 
ClearStepping()158   void ClearStepping() { next_step_action_ = StepNone; }
159 
CurrentStackDepth()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.
Execute(Handle<WasmInstanceObject> instance_object,Address frame_pointer,uint32_t func_index,Address arg_buffer)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 
ContinueExecution(WasmInterpreter::Thread * thread)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 
GetInstanceObject()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 
NotifyDebugEventListeners(WasmInterpreter::Thread * thread)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 
GetTopPosition(Handle<WasmModuleObject> module_object)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 
GetInterpretedStack(Address frame_pointer)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 
GetInterpretedFrame(Address frame_pointer,int idx)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 
Unwind(Address frame_pointer)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 
NumInterpretedCalls()401   uint64_t NumInterpretedCalls() {
402     DCHECK_EQ(1, interpreter()->GetThreadCount());
403     return interpreter()->GetThread(0)->NumInterpretedCalls();
404   }
405 
GetGlobalScopeObject(InterpretedFrame * frame,Handle<WasmDebugInfo> debug_info)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 
GetLocalScopeObject(InterpretedFrame * frame,Handle<WasmDebugInfo> debug_info)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 
GetScopeDetails(Address frame_pointer,int frame_index,Handle<WasmDebugInfo> debug_info)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 
GetOrCreateInterpreterHandle(Isolate * isolate,Handle<WasmDebugInfo> debug_info)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 
GetInterpreterHandle(WasmDebugInfo * debug_info)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 
GetInterpreterHandleOrNull(WasmDebugInfo * debug_info)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 
GetOrCreateInterpretedFunctions(Isolate * isolate,Handle<WasmDebugInfo> debug_info)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 
New(Handle<WasmInstanceObject> instance)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 
SetupForTesting(Handle<WasmInstanceObject> instance_obj)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 
SetBreakpoint(Handle<WasmDebugInfo> debug_info,int func_index,int offset)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 
RedirectToInterpreter(Handle<WasmDebugInfo> debug_info,Vector<int> func_indexes)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 
PrepareStep(StepAction step_action)642 void WasmDebugInfo::PrepareStep(StepAction step_action) {
643   GetInterpreterHandle(this)->PrepareStep(step_action);
644 }
645 
646 // static
RunInterpreter(Isolate * isolate,Handle<WasmDebugInfo> debug_info,Address frame_pointer,int func_index,Address arg_buffer)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 
GetInterpretedStack(Address frame_pointer)658 std::vector<std::pair<uint32_t, int>> WasmDebugInfo::GetInterpretedStack(
659     Address frame_pointer) {
660   return GetInterpreterHandle(this)->GetInterpretedStack(frame_pointer);
661 }
662 
GetInterpretedFrame(Address frame_pointer,int idx)663 wasm::WasmInterpreter::FramePtr WasmDebugInfo::GetInterpretedFrame(
664     Address frame_pointer, int idx) {
665   return GetInterpreterHandle(this)->GetInterpretedFrame(frame_pointer, idx);
666 }
667 
Unwind(Address frame_pointer)668 void WasmDebugInfo::Unwind(Address frame_pointer) {
669   return GetInterpreterHandle(this)->Unwind(frame_pointer);
670 }
671 
NumInterpretedCalls()672 uint64_t WasmDebugInfo::NumInterpretedCalls() {
673   auto* handle = GetInterpreterHandleOrNull(this);
674   return handle ? handle->NumInterpretedCalls() : 0;
675 }
676 
677 // static
GetScopeDetails(Handle<WasmDebugInfo> debug_info,Address frame_pointer,int frame_index)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
GetGlobalScopeObject(Handle<WasmDebugInfo> debug_info,Address frame_pointer,int frame_index)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
GetLocalScopeObject(Handle<WasmDebugInfo> debug_info,Address frame_pointer,int frame_index)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
GetCWasmEntry(Handle<WasmDebugInfo> debug_info,wasm::FunctionSig * sig)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