1 // Copyright 2021 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/debug/debug-wasm-objects.h"
6 
7 #include "src/api/api-inl.h"
8 #include "src/api/api-natives.h"
9 #include "src/base/strings.h"
10 #include "src/debug/debug-wasm-objects-inl.h"
11 #include "src/execution/frames-inl.h"
12 #include "src/objects/property-descriptor.h"
13 #include "src/wasm/wasm-debug.h"
14 #include "src/wasm/wasm-objects-inl.h"
15 #include "src/wasm/wasm-value.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace {
20 
21 // Helper for unpacking a maybe name that makes a default with an index if
22 // the name is empty. If the name is not empty, it's prefixed with a $.
GetNameOrDefault(Isolate * isolate,MaybeHandle<String> maybe_name,const char * default_name_prefix,uint32_t index)23 Handle<String> GetNameOrDefault(Isolate* isolate,
24                                 MaybeHandle<String> maybe_name,
25                                 const char* default_name_prefix,
26                                 uint32_t index) {
27   Handle<String> name;
28   if (maybe_name.ToHandle(&name)) {
29     name = isolate->factory()
30                ->NewConsString(
31                    isolate->factory()->NewStringFromAsciiChecked("$"), name)
32                .ToHandleChecked();
33     return isolate->factory()->InternalizeString(name);
34   }
35   base::EmbeddedVector<char, 64> value;
36   int len = SNPrintF(value, "%s%u", default_name_prefix, index);
37   return isolate->factory()->InternalizeString(value.SubVector(0, len));
38 }
39 
GetNameFromImportsAndExportsOrNull(Isolate * isolate,Handle<WasmInstanceObject> instance,wasm::ImportExportKindCode kind,uint32_t index)40 MaybeHandle<String> GetNameFromImportsAndExportsOrNull(
41     Isolate* isolate, Handle<WasmInstanceObject> instance,
42     wasm::ImportExportKindCode kind, uint32_t index) {
43   auto debug_info = instance->module_object().native_module()->GetDebugInfo();
44   wasm::ModuleWireBytes wire_bytes(
45       instance->module_object().native_module()->wire_bytes());
46 
47   auto import_name_ref = debug_info->GetImportName(kind, index);
48   if (!import_name_ref.first.is_empty()) {
49     base::ScopedVector<char> name(import_name_ref.first.length() + 1 +
50                                   import_name_ref.second.length());
51     auto name_begin = &name.first(), name_end = name_begin;
52     auto module_name = wire_bytes.GetNameOrNull(import_name_ref.first);
53     name_end = std::copy(module_name.begin(), module_name.end(), name_end);
54     *name_end++ = '.';
55     auto field_name = wire_bytes.GetNameOrNull(import_name_ref.second);
56     name_end = std::copy(field_name.begin(), field_name.end(), name_end);
57     return isolate->factory()->NewStringFromUtf8(
58         base::VectorOf(name_begin, name_end - name_begin));
59   }
60 
61   auto export_name_ref = debug_info->GetExportName(kind, index);
62   if (!export_name_ref.is_empty()) {
63     auto name = wire_bytes.GetNameOrNull(export_name_ref);
64     return isolate->factory()->NewStringFromUtf8(name);
65   }
66 
67   return {};
68 }
69 
70 enum DebugProxyId {
71   kFunctionsProxy,
72   kGlobalsProxy,
73   kMemoriesProxy,
74   kTablesProxy,
75   kLastInstanceProxyId = kTablesProxy,
76 
77   kContextProxy,
78   kLocalsProxy,
79   kStackProxy,
80   kStructProxy,
81   kArrayProxy,
82   kLastProxyId = kArrayProxy,
83 
84   kNumProxies = kLastProxyId + 1,
85   kNumInstanceProxies = kLastInstanceProxyId + 1
86 };
87 
88 constexpr int kWasmValueMapIndex = kNumProxies;
89 constexpr int kNumDebugMaps = kWasmValueMapIndex + 1;
90 
GetOrCreateDebugMaps(Isolate * isolate)91 Handle<FixedArray> GetOrCreateDebugMaps(Isolate* isolate) {
92   Handle<FixedArray> maps = isolate->wasm_debug_maps();
93   if (maps->length() == 0) {
94     maps = isolate->factory()->NewFixedArrayWithHoles(kNumDebugMaps);
95     isolate->native_context()->set_wasm_debug_maps(*maps);
96   }
97   return maps;
98 }
99 
100 // Creates a Map for the given debug proxy |id| using the |create_template_fn|
101 // on-demand and caches this map in the global object. The map is derived from
102 // the FunctionTemplate returned by |create_template_fn| and has its prototype
103 // set to |null| and is marked non-extensible (by default).
104 // TODO(bmeurer): remove the extensibility opt-out and replace it with a proper
105 // way to add non-intercepted named properties.
GetOrCreateDebugProxyMap(Isolate * isolate,DebugProxyId id,v8::Local<v8::FunctionTemplate> (* create_template_fn)(v8::Isolate *),bool make_non_extensible=true)106 Handle<Map> GetOrCreateDebugProxyMap(
107     Isolate* isolate, DebugProxyId id,
108     v8::Local<v8::FunctionTemplate> (*create_template_fn)(v8::Isolate*),
109     bool make_non_extensible = true) {
110   auto maps = GetOrCreateDebugMaps(isolate);
111   CHECK_LE(kNumProxies, maps->length());
112   if (!maps->is_the_hole(isolate, id)) {
113     return handle(Map::cast(maps->get(id)), isolate);
114   }
115   auto tmp = (*create_template_fn)(reinterpret_cast<v8::Isolate*>(isolate));
116   auto fun = ApiNatives::InstantiateFunction(Utils::OpenHandle(*tmp))
117                  .ToHandleChecked();
118   auto map = JSFunction::GetDerivedMap(isolate, fun, fun).ToHandleChecked();
119   Map::SetPrototype(isolate, map, isolate->factory()->null_value());
120   if (make_non_extensible) {
121     map->set_is_extensible(false);
122   }
123   maps->set(id, *map);
124   return map;
125 }
126 
127 // Base class for debug proxies, offers indexed access. The subclasses
128 // need to implement |Count| and |Get| methods appropriately.
129 template <typename T, DebugProxyId id, typename Provider>
130 struct IndexedDebugProxy {
131   static constexpr DebugProxyId kId = id;
132 
Createv8::internal::__anon70e8489b0111::IndexedDebugProxy133   static Handle<JSObject> Create(Isolate* isolate, Handle<Provider> provider,
134                                  bool make_map_non_extensible = true) {
135     auto object_map = GetOrCreateDebugProxyMap(isolate, kId, &T::CreateTemplate,
136                                                make_map_non_extensible);
137     auto object = isolate->factory()->NewJSObjectFromMap(object_map);
138     object->SetEmbedderField(kProviderField, *provider);
139     return object;
140   }
141 
142   enum {
143     kProviderField,
144     kFieldCount,
145   };
146 
CreateTemplatev8::internal::__anon70e8489b0111::IndexedDebugProxy147   static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) {
148     Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
149     templ->SetClassName(
150         v8::String::NewFromUtf8(isolate, T::kClassName).ToLocalChecked());
151     templ->InstanceTemplate()->SetInternalFieldCount(T::kFieldCount);
152     templ->InstanceTemplate()->SetHandler(
153         v8::IndexedPropertyHandlerConfiguration(
154             &T::IndexedGetter, {}, &T::IndexedQuery, {}, &T::IndexedEnumerator,
155             {}, &T::IndexedDescriptor, {},
156             v8::PropertyHandlerFlags::kHasNoSideEffect));
157     return templ;
158   }
159 
160   template <typename V>
GetIsolatev8::internal::__anon70e8489b0111::IndexedDebugProxy161   static Isolate* GetIsolate(const PropertyCallbackInfo<V>& info) {
162     return reinterpret_cast<Isolate*>(info.GetIsolate());
163   }
164 
165   template <typename V>
GetHolderv8::internal::__anon70e8489b0111::IndexedDebugProxy166   static Handle<JSObject> GetHolder(const PropertyCallbackInfo<V>& info) {
167     return Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
168   }
169 
GetProviderv8::internal::__anon70e8489b0111::IndexedDebugProxy170   static Handle<Provider> GetProvider(Handle<JSObject> holder,
171                                       Isolate* isolate) {
172     return handle(Provider::cast(holder->GetEmbedderField(kProviderField)),
173                   isolate);
174   }
175 
176   template <typename V>
GetProviderv8::internal::__anon70e8489b0111::IndexedDebugProxy177   static Handle<Provider> GetProvider(const PropertyCallbackInfo<V>& info) {
178     return GetProvider(GetHolder(info), GetIsolate(info));
179   }
180 
IndexedGetterv8::internal::__anon70e8489b0111::IndexedDebugProxy181   static void IndexedGetter(uint32_t index,
182                             const PropertyCallbackInfo<v8::Value>& info) {
183     auto isolate = GetIsolate(info);
184     auto provider = GetProvider(info);
185     if (index < T::Count(isolate, provider)) {
186       auto value = T::Get(isolate, provider, index);
187       info.GetReturnValue().Set(Utils::ToLocal(value));
188     }
189   }
190 
IndexedDescriptorv8::internal::__anon70e8489b0111::IndexedDebugProxy191   static void IndexedDescriptor(uint32_t index,
192                                 const PropertyCallbackInfo<v8::Value>& info) {
193     auto isolate = GetIsolate(info);
194     auto provider = GetProvider(info);
195     if (index < T::Count(isolate, provider)) {
196       PropertyDescriptor descriptor;
197       descriptor.set_configurable(false);
198       descriptor.set_enumerable(true);
199       descriptor.set_writable(false);
200       descriptor.set_value(T::Get(isolate, provider, index));
201       info.GetReturnValue().Set(Utils::ToLocal(descriptor.ToObject(isolate)));
202     }
203   }
204 
IndexedQueryv8::internal::__anon70e8489b0111::IndexedDebugProxy205   static void IndexedQuery(uint32_t index,
206                            const PropertyCallbackInfo<v8::Integer>& info) {
207     if (index < T::Count(GetIsolate(info), GetProvider(info))) {
208       info.GetReturnValue().Set(Integer::New(
209           info.GetIsolate(),
210           PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly));
211     }
212   }
213 
IndexedEnumeratorv8::internal::__anon70e8489b0111::IndexedDebugProxy214   static void IndexedEnumerator(const PropertyCallbackInfo<v8::Array>& info) {
215     auto isolate = GetIsolate(info);
216     auto count = T::Count(isolate, GetProvider(info));
217     auto indices = isolate->factory()->NewFixedArray(count);
218     for (uint32_t index = 0; index < count; ++index) {
219       indices->set(index, Smi::FromInt(index));
220     }
221     info.GetReturnValue().Set(
222         Utils::ToLocal(isolate->factory()->NewJSArrayWithElements(
223             indices, PACKED_SMI_ELEMENTS)));
224   }
225 };
226 
227 // Extends |IndexedDebugProxy| with named access, where the names are computed
228 // on-demand, and all names are assumed to start with a dollar char ($). This
229 // is important in order to scale to Wasm modules with hundreds of thousands
230 // of functions in them.
231 template <typename T, DebugProxyId id, typename Provider = WasmInstanceObject>
232 struct NamedDebugProxy : IndexedDebugProxy<T, id, Provider> {
CreateTemplatev8::internal::__anon70e8489b0111::NamedDebugProxy233   static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) {
234     auto templ = IndexedDebugProxy<T, id, Provider>::CreateTemplate(isolate);
235     templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
236         &T::NamedGetter, {}, &T::NamedQuery, {}, &T::NamedEnumerator, {},
237         &T::NamedDescriptor, {}, v8::PropertyHandlerFlags::kHasNoSideEffect));
238     return templ;
239   }
240 
IndexedEnumeratorv8::internal::__anon70e8489b0111::NamedDebugProxy241   static void IndexedEnumerator(const PropertyCallbackInfo<v8::Array>& info) {
242     info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
243   }
244 
GetNameTablev8::internal::__anon70e8489b0111::NamedDebugProxy245   static Handle<NameDictionary> GetNameTable(Handle<JSObject> holder,
246                                              Isolate* isolate) {
247     Handle<Symbol> symbol = isolate->factory()->wasm_debug_proxy_names_symbol();
248     Handle<Object> table_or_undefined =
249         JSObject::GetProperty(isolate, holder, symbol).ToHandleChecked();
250     if (!table_or_undefined->IsUndefined(isolate)) {
251       return Handle<NameDictionary>::cast(table_or_undefined);
252     }
253     auto provider = T::GetProvider(holder, isolate);
254     auto count = T::Count(isolate, provider);
255     auto table = NameDictionary::New(isolate, count);
256     for (uint32_t index = 0; index < count; ++index) {
257       HandleScope scope(isolate);
258       auto key = T::GetName(isolate, provider, index);
259       if (table->FindEntry(isolate, key).is_found()) continue;
260       Handle<Smi> value(Smi::FromInt(index), isolate);
261       table = NameDictionary::Add(isolate, table, key, value,
262                                   PropertyDetails::Empty());
263     }
264     Object::SetProperty(isolate, holder, symbol, table).Check();
265     return table;
266   }
267 
268   template <typename V>
FindNamev8::internal::__anon70e8489b0111::NamedDebugProxy269   static base::Optional<uint32_t> FindName(
270       Local<v8::Name> name, const PropertyCallbackInfo<V>& info) {
271     if (!name->IsString()) return {};
272     auto name_str = Utils::OpenHandle(*name.As<v8::String>());
273     if (name_str->length() == 0 || name_str->Get(0) != '$') return {};
274     auto isolate = T::GetIsolate(info);
275     auto table = GetNameTable(T::GetHolder(info), isolate);
276     auto entry = table->FindEntry(isolate, name_str);
277     if (entry.is_found()) return Smi::ToInt(table->ValueAt(entry));
278     return {};
279   }
280 
NamedGetterv8::internal::__anon70e8489b0111::NamedDebugProxy281   static void NamedGetter(Local<v8::Name> name,
282                           const PropertyCallbackInfo<v8::Value>& info) {
283     if (auto index = FindName(name, info)) T::IndexedGetter(*index, info);
284   }
285 
NamedQueryv8::internal::__anon70e8489b0111::NamedDebugProxy286   static void NamedQuery(Local<v8::Name> name,
287                          const PropertyCallbackInfo<v8::Integer>& info) {
288     if (auto index = FindName(name, info)) T::IndexedQuery(*index, info);
289   }
290 
NamedDescriptorv8::internal::__anon70e8489b0111::NamedDebugProxy291   static void NamedDescriptor(Local<v8::Name> name,
292                               const PropertyCallbackInfo<v8::Value>& info) {
293     if (auto index = FindName(name, info)) T::IndexedDescriptor(*index, info);
294   }
295 
NamedEnumeratorv8::internal::__anon70e8489b0111::NamedDebugProxy296   static void NamedEnumerator(const PropertyCallbackInfo<v8::Array>& info) {
297     auto isolate = T::GetIsolate(info);
298     auto table = GetNameTable(T::GetHolder(info), isolate);
299     auto names = NameDictionary::IterationIndices(isolate, table);
300     for (int i = 0; i < names->length(); ++i) {
301       InternalIndex entry(Smi::ToInt(names->get(i)));
302       names->set(i, table->NameAt(entry));
303     }
304     info.GetReturnValue().Set(Utils::ToLocal(
305         isolate->factory()->NewJSArrayWithElements(names, PACKED_ELEMENTS)));
306   }
307 };
308 
309 // This class implements the "functions" proxy.
310 struct FunctionsProxy : NamedDebugProxy<FunctionsProxy, kFunctionsProxy> {
311   static constexpr char const* kClassName = "Functions";
312 
Countv8::internal::__anon70e8489b0111::FunctionsProxy313   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
314     return static_cast<uint32_t>(instance->module()->functions.size());
315   }
316 
Getv8::internal::__anon70e8489b0111::FunctionsProxy317   static Handle<Object> Get(Isolate* isolate,
318                             Handle<WasmInstanceObject> instance,
319                             uint32_t index) {
320     return WasmInstanceObject::GetOrCreateWasmExternalFunction(isolate,
321                                                                instance, index);
322   }
323 
GetNamev8::internal::__anon70e8489b0111::FunctionsProxy324   static Handle<String> GetName(Isolate* isolate,
325                                 Handle<WasmInstanceObject> instance,
326                                 uint32_t index) {
327     return GetWasmFunctionDebugName(isolate, instance, index);
328   }
329 };
330 
331 // This class implements the "globals" proxy.
332 struct GlobalsProxy : NamedDebugProxy<GlobalsProxy, kGlobalsProxy> {
333   static constexpr char const* kClassName = "Globals";
334 
Countv8::internal::__anon70e8489b0111::GlobalsProxy335   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
336     return static_cast<uint32_t>(instance->module()->globals.size());
337   }
338 
Getv8::internal::__anon70e8489b0111::GlobalsProxy339   static Handle<Object> Get(Isolate* isolate,
340                             Handle<WasmInstanceObject> instance,
341                             uint32_t index) {
342     Handle<WasmModuleObject> module(instance->module_object(), isolate);
343     return WasmValueObject::New(
344         isolate,
345         WasmInstanceObject::GetGlobalValue(instance,
346                                            instance->module()->globals[index]),
347         module);
348   }
349 
GetNamev8::internal::__anon70e8489b0111::GlobalsProxy350   static Handle<String> GetName(Isolate* isolate,
351                                 Handle<WasmInstanceObject> instance,
352                                 uint32_t index) {
353     return GetNameOrDefault(
354         isolate,
355         GetNameFromImportsAndExportsOrNull(
356             isolate, instance, wasm::ImportExportKindCode::kExternalGlobal,
357             index),
358         "$global", index);
359   }
360 };
361 
362 // This class implements the "memories" proxy.
363 struct MemoriesProxy : NamedDebugProxy<MemoriesProxy, kMemoriesProxy> {
364   static constexpr char const* kClassName = "Memories";
365 
Countv8::internal::__anon70e8489b0111::MemoriesProxy366   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
367     return instance->has_memory_object() ? 1 : 0;
368   }
369 
Getv8::internal::__anon70e8489b0111::MemoriesProxy370   static Handle<Object> Get(Isolate* isolate,
371                             Handle<WasmInstanceObject> instance,
372                             uint32_t index) {
373     return handle(instance->memory_object(), isolate);
374   }
375 
GetNamev8::internal::__anon70e8489b0111::MemoriesProxy376   static Handle<String> GetName(Isolate* isolate,
377                                 Handle<WasmInstanceObject> instance,
378                                 uint32_t index) {
379     return GetNameOrDefault(
380         isolate,
381         GetNameFromImportsAndExportsOrNull(
382             isolate, instance, wasm::ImportExportKindCode::kExternalMemory,
383             index),
384         "$memory", index);
385   }
386 };
387 
388 // This class implements the "tables" proxy.
389 struct TablesProxy : NamedDebugProxy<TablesProxy, kTablesProxy> {
390   static constexpr char const* kClassName = "Tables";
391 
Countv8::internal::__anon70e8489b0111::TablesProxy392   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
393     return instance->tables().length();
394   }
395 
Getv8::internal::__anon70e8489b0111::TablesProxy396   static Handle<Object> Get(Isolate* isolate,
397                             Handle<WasmInstanceObject> instance,
398                             uint32_t index) {
399     return handle(instance->tables().get(index), isolate);
400   }
401 
GetNamev8::internal::__anon70e8489b0111::TablesProxy402   static Handle<String> GetName(Isolate* isolate,
403                                 Handle<WasmInstanceObject> instance,
404                                 uint32_t index) {
405     return GetNameOrDefault(
406         isolate,
407         GetNameFromImportsAndExportsOrNull(
408             isolate, instance, wasm::ImportExportKindCode::kExternalTable,
409             index),
410         "$table", index);
411   }
412 };
413 
414 // This class implements the "locals" proxy.
415 struct LocalsProxy : NamedDebugProxy<LocalsProxy, kLocalsProxy, FixedArray> {
416   static constexpr char const* kClassName = "Locals";
417 
Createv8::internal::__anon70e8489b0111::LocalsProxy418   static Handle<JSObject> Create(WasmFrame* frame) {
419     auto isolate = frame->isolate();
420     auto debug_info = frame->native_module()->GetDebugInfo();
421     // TODO(bmeurer): Check if pc is inspectable.
422     int count = debug_info->GetNumLocals(frame->pc());
423     auto function = debug_info->GetFunctionAtAddress(frame->pc());
424     auto values = isolate->factory()->NewFixedArray(count + 2);
425     Handle<WasmModuleObject> module_object(
426         frame->wasm_instance().module_object(), isolate);
427     for (int i = 0; i < count; ++i) {
428       auto value = WasmValueObject::New(
429           isolate,
430           debug_info->GetLocalValue(i, frame->pc(), frame->fp(),
431                                     frame->callee_fp(), isolate),
432           module_object);
433       values->set(i, *value);
434     }
435     values->set(count + 0, frame->wasm_instance().module_object());
436     values->set(count + 1, Smi::FromInt(function.func_index));
437     return NamedDebugProxy::Create(isolate, values);
438   }
439 
Countv8::internal::__anon70e8489b0111::LocalsProxy440   static uint32_t Count(Isolate* isolate, Handle<FixedArray> values) {
441     return values->length() - 2;
442   }
443 
Getv8::internal::__anon70e8489b0111::LocalsProxy444   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> values,
445                             uint32_t index) {
446     return handle(values->get(index), isolate);
447   }
448 
GetNamev8::internal::__anon70e8489b0111::LocalsProxy449   static Handle<String> GetName(Isolate* isolate, Handle<FixedArray> values,
450                                 uint32_t index) {
451     uint32_t count = Count(isolate, values);
452     auto native_module =
453         WasmModuleObject::cast(values->get(count + 0)).native_module();
454     auto function_index = Smi::ToInt(Smi::cast(values->get(count + 1)));
455     wasm::ModuleWireBytes module_wire_bytes(native_module->wire_bytes());
456     auto name_vec = module_wire_bytes.GetNameOrNull(
457         native_module->GetDebugInfo()->GetLocalName(function_index, index));
458     return GetNameOrDefault(
459         isolate,
460         name_vec.empty() ? MaybeHandle<String>()
461                          : isolate->factory()->NewStringFromUtf8(name_vec),
462         "$var", index);
463   }
464 };
465 
466 // This class implements the "stack" proxy (which offers only indexed access).
467 struct StackProxy : IndexedDebugProxy<StackProxy, kStackProxy, FixedArray> {
468   static constexpr char const* kClassName = "Stack";
469 
Createv8::internal::__anon70e8489b0111::StackProxy470   static Handle<JSObject> Create(WasmFrame* frame) {
471     auto isolate = frame->isolate();
472     auto debug_info =
473         frame->wasm_instance().module_object().native_module()->GetDebugInfo();
474     int count = debug_info->GetStackDepth(frame->pc());
475     auto values = isolate->factory()->NewFixedArray(count);
476     Handle<WasmModuleObject> module_object(
477         frame->wasm_instance().module_object(), isolate);
478     for (int i = 0; i < count; ++i) {
479       auto value = WasmValueObject::New(
480           isolate,
481           debug_info->GetStackValue(i, frame->pc(), frame->fp(),
482                                     frame->callee_fp(), isolate),
483           module_object);
484       values->set(i, *value);
485     }
486     return IndexedDebugProxy::Create(isolate, values);
487   }
488 
Countv8::internal::__anon70e8489b0111::StackProxy489   static uint32_t Count(Isolate* isolate, Handle<FixedArray> values) {
490     return values->length();
491   }
492 
Getv8::internal::__anon70e8489b0111::StackProxy493   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> values,
494                             uint32_t index) {
495     return handle(values->get(index), isolate);
496   }
497 };
498 
499 // Creates FixedArray with size |kNumInstanceProxies| as cache on-demand
500 // on the |instance|, stored under the |wasm_debug_proxy_cache_symbol|.
501 // This is used to cache the various instance debug proxies (functions,
502 // globals, tables, and memories) on the WasmInstanceObject.
GetOrCreateInstanceProxyCache(Isolate * isolate,Handle<WasmInstanceObject> instance)503 Handle<FixedArray> GetOrCreateInstanceProxyCache(
504     Isolate* isolate, Handle<WasmInstanceObject> instance) {
505   Handle<Object> cache;
506   Handle<Symbol> symbol = isolate->factory()->wasm_debug_proxy_cache_symbol();
507   if (!Object::GetProperty(isolate, instance, symbol).ToHandle(&cache) ||
508       cache->IsUndefined(isolate)) {
509     cache = isolate->factory()->NewFixedArrayWithHoles(kNumInstanceProxies);
510     Object::SetProperty(isolate, instance, symbol, cache).Check();
511   }
512   return Handle<FixedArray>::cast(cache);
513 }
514 
515 // Creates an instance of the |Proxy| on-demand and caches that on the
516 // |instance|.
517 template <typename Proxy>
GetOrCreateInstanceProxy(Isolate * isolate,Handle<WasmInstanceObject> instance)518 Handle<JSObject> GetOrCreateInstanceProxy(Isolate* isolate,
519                                           Handle<WasmInstanceObject> instance) {
520   STATIC_ASSERT(Proxy::kId < kNumInstanceProxies);
521   Handle<FixedArray> proxies = GetOrCreateInstanceProxyCache(isolate, instance);
522   if (!proxies->is_the_hole(isolate, Proxy::kId)) {
523     return handle(JSObject::cast(proxies->get(Proxy::kId)), isolate);
524   }
525   Handle<JSObject> proxy = Proxy::Create(isolate, instance);
526   proxies->set(Proxy::kId, *proxy);
527   return proxy;
528 }
529 
530 // This class implements the debug proxy for a given Wasm frame. The debug
531 // proxy is used when evaluating JavaScript expressions on a wasm frame via
532 // the inspector |Runtime.evaluateOnCallFrame()| API and enables developers
533 // and extensions to inspect the WebAssembly engine state from JavaScript.
534 // The proxy provides the following interface:
535 //
536 // type WasmValue = {
537 //   type: string;
538 //   value: number | bigint | object | string;
539 // };
540 // type WasmFunction = (... args : WasmValue[]) = > WasmValue;
541 // interface WasmInterface {
542 //   $globalX: WasmValue;
543 //   $varX: WasmValue;
544 //   $funcX(a : WasmValue /*, ...*/) : WasmValue;
545 //   readonly $memoryX : WebAssembly.Memory;
546 //   readonly $tableX : WebAssembly.Table;
547 //
548 //   readonly instance : WebAssembly.Instance;
549 //   readonly module : WebAssembly.Module;
550 //
551 //   readonly memories : {[nameOrIndex:string | number] : WebAssembly.Memory};
552 //   readonly tables : {[nameOrIndex:string | number] : WebAssembly.Table};
553 //   readonly stack : WasmValue[];
554 //   readonly globals : {[nameOrIndex:string | number] : WasmValue};
555 //   readonly locals : {[nameOrIndex:string | number] : WasmValue};
556 //   readonly functions : {[nameOrIndex:string | number] : WasmFunction};
557 // }
558 //
559 // The wasm index spaces memories, tables, stack, globals, locals, and
560 // functions are JSObjects with interceptors that lazily produce values
561 // either by index or by name (except for stack).
562 // Only the names are reported by APIs such as Object.keys() and
563 // Object.getOwnPropertyNames(), since the indices are not meant to be
564 // used interactively by developers (in Chrome DevTools), but are provided
565 // for WebAssembly language extensions. Also note that these JSObjects
566 // all have null prototypes, to not confuse context lookup and to make
567 // their purpose as dictionaries clear.
568 //
569 // See http://doc/1VZOJrU2VsqOZe3IUzbwQWQQSZwgGySsm5119Ust1gUA and
570 // http://bit.ly/devtools-wasm-entities for more details.
571 class ContextProxyPrototype {
572  public:
Create(Isolate * isolate)573   static Handle<JSObject> Create(Isolate* isolate) {
574     auto object_map =
575         GetOrCreateDebugProxyMap(isolate, kContextProxy, &CreateTemplate);
576     return isolate->factory()->NewJSObjectFromMap(object_map);
577   }
578 
579  private:
CreateTemplate(v8::Isolate * isolate)580   static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) {
581     Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
582     templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
583         &NamedGetter, {}, {}, {}, {}, {}, {}, {},
584         static_cast<v8::PropertyHandlerFlags>(
585             static_cast<unsigned>(
586                 v8::PropertyHandlerFlags::kOnlyInterceptStrings) |
587             static_cast<unsigned>(
588                 v8::PropertyHandlerFlags::kHasNoSideEffect))));
589     return templ;
590   }
591 
GetNamedProperty(Isolate * isolate,Handle<JSObject> receiver,Handle<String> name)592   static MaybeHandle<Object> GetNamedProperty(Isolate* isolate,
593                                               Handle<JSObject> receiver,
594                                               Handle<String> name) {
595     if (name->length() != 0 && name->Get(0) == '$') {
596       const char* kDelegateNames[] = {"memories", "locals", "tables",
597                                       "functions", "globals"};
598       for (auto delegate_name : kDelegateNames) {
599         Handle<Object> delegate;
600         ASSIGN_RETURN_ON_EXCEPTION(
601             isolate, delegate,
602             JSObject::GetProperty(isolate, receiver, delegate_name), Object);
603         if (!delegate->IsUndefined(isolate)) {
604           Handle<Object> value;
605           ASSIGN_RETURN_ON_EXCEPTION(
606               isolate, value, Object::GetProperty(isolate, delegate, name),
607               Object);
608           if (!value->IsUndefined(isolate)) return value;
609         }
610       }
611     }
612     return {};
613   }
614 
NamedGetter(Local<v8::Name> name,const PropertyCallbackInfo<v8::Value> & info)615   static void NamedGetter(Local<v8::Name> name,
616                           const PropertyCallbackInfo<v8::Value>& info) {
617     auto name_string = Handle<String>::cast(Utils::OpenHandle(*name));
618     auto isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
619     auto receiver = Handle<JSObject>::cast(Utils::OpenHandle(*info.This()));
620     Handle<Object> value;
621     if (GetNamedProperty(isolate, receiver, name_string).ToHandle(&value)) {
622       info.GetReturnValue().Set(Utils::ToLocal(value));
623     }
624   }
625 };
626 
627 class ContextProxy {
628  public:
Create(WasmFrame * frame)629   static Handle<JSObject> Create(WasmFrame* frame) {
630     Isolate* isolate = frame->isolate();
631     auto object = isolate->factory()->NewJSObjectWithNullProto();
632     Handle<WasmInstanceObject> instance(frame->wasm_instance(), isolate);
633     JSObject::AddProperty(isolate, object, "instance", instance, FROZEN);
634     Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
635     JSObject::AddProperty(isolate, object, "module", module_object, FROZEN);
636     auto locals = LocalsProxy::Create(frame);
637     JSObject::AddProperty(isolate, object, "locals", locals, FROZEN);
638     auto stack = StackProxy::Create(frame);
639     JSObject::AddProperty(isolate, object, "stack", stack, FROZEN);
640     auto memories = GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance);
641     JSObject::AddProperty(isolate, object, "memories", memories, FROZEN);
642     auto tables = GetOrCreateInstanceProxy<TablesProxy>(isolate, instance);
643     JSObject::AddProperty(isolate, object, "tables", tables, FROZEN);
644     auto globals = GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance);
645     JSObject::AddProperty(isolate, object, "globals", globals, FROZEN);
646     auto functions =
647         GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance);
648     JSObject::AddProperty(isolate, object, "functions", functions, FROZEN);
649     Handle<JSObject> prototype = ContextProxyPrototype::Create(isolate);
650     JSObject::SetPrototype(object, prototype, false, kDontThrow).Check();
651     return object;
652   }
653 };
654 
655 class DebugWasmScopeIterator final : public debug::ScopeIterator {
656  public:
DebugWasmScopeIterator(WasmFrame * frame)657   explicit DebugWasmScopeIterator(WasmFrame* frame)
658       : frame_(frame),
659         type_(debug::ScopeIterator::ScopeTypeWasmExpressionStack) {
660     // Skip local scope and expression stack scope if the frame is not
661     // inspectable.
662     if (!frame->is_inspectable()) {
663       type_ = debug::ScopeIterator::ScopeTypeModule;
664     }
665   }
666 
Done()667   bool Done() override { return type_ == ScopeTypeWith; }
668 
Advance()669   void Advance() override {
670     DCHECK(!Done());
671     switch (type_) {
672       case ScopeTypeWasmExpressionStack:
673         type_ = debug::ScopeIterator::ScopeTypeLocal;
674         break;
675       case ScopeTypeLocal:
676         type_ = debug::ScopeIterator::ScopeTypeModule;
677         break;
678       case ScopeTypeModule:
679         // We use ScopeTypeWith type as marker for done.
680         type_ = debug::ScopeIterator::ScopeTypeWith;
681         break;
682       default:
683         UNREACHABLE();
684     }
685   }
686 
GetType()687   ScopeType GetType() override { return type_; }
688 
GetObject()689   v8::Local<v8::Object> GetObject() override {
690     Isolate* isolate = frame_->isolate();
691     switch (type_) {
692       case debug::ScopeIterator::ScopeTypeModule: {
693         Handle<WasmInstanceObject> instance(frame_->wasm_instance(), isolate);
694         Handle<JSObject> object =
695             isolate->factory()->NewJSObjectWithNullProto();
696         JSObject::AddProperty(isolate, object, "instance", instance, FROZEN);
697         Handle<JSObject> module_object(instance->module_object(), isolate);
698         JSObject::AddProperty(isolate, object, "module", module_object, FROZEN);
699         if (FunctionsProxy::Count(isolate, instance) != 0) {
700           JSObject::AddProperty(
701               isolate, object, "functions",
702               GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance),
703               FROZEN);
704         }
705         if (GlobalsProxy::Count(isolate, instance) != 0) {
706           JSObject::AddProperty(
707               isolate, object, "globals",
708               GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance),
709               FROZEN);
710         }
711         if (MemoriesProxy::Count(isolate, instance) != 0) {
712           JSObject::AddProperty(
713               isolate, object, "memories",
714               GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance),
715               FROZEN);
716         }
717         if (TablesProxy::Count(isolate, instance) != 0) {
718           JSObject::AddProperty(
719               isolate, object, "tables",
720               GetOrCreateInstanceProxy<TablesProxy>(isolate, instance), FROZEN);
721         }
722         return Utils::ToLocal(object);
723       }
724       case debug::ScopeIterator::ScopeTypeLocal: {
725         return Utils::ToLocal(LocalsProxy::Create(frame_));
726       }
727       case debug::ScopeIterator::ScopeTypeWasmExpressionStack: {
728         auto object = isolate->factory()->NewJSObjectWithNullProto();
729         auto stack = StackProxy::Create(frame_);
730         JSObject::AddProperty(isolate, object, "stack", stack, FROZEN);
731         return Utils::ToLocal(object);
732       }
733       default:
734         UNREACHABLE();
735     }
736   }
GetFunctionDebugName()737   v8::Local<v8::Value> GetFunctionDebugName() override {
738     return Utils::ToLocal(frame_->isolate()->factory()->empty_string());
739   }
740 
GetScriptId()741   int GetScriptId() override { return -1; }
742 
HasLocationInfo()743   bool HasLocationInfo() override { return false; }
744 
GetStartLocation()745   debug::Location GetStartLocation() override { return {}; }
746 
GetEndLocation()747   debug::Location GetEndLocation() override { return {}; }
748 
SetVariableValue(v8::Local<v8::String> name,v8::Local<v8::Value> value)749   bool SetVariableValue(v8::Local<v8::String> name,
750                         v8::Local<v8::Value> value) override {
751     return false;
752   }
753 
754  private:
755   WasmFrame* const frame_;
756   ScopeType type_;
757 };
758 
WasmSimd128ToString(Isolate * isolate,wasm::Simd128 s128)759 Handle<String> WasmSimd128ToString(Isolate* isolate, wasm::Simd128 s128) {
760   // We use the canonical format as described in:
761   // https://github.com/WebAssembly/simd/blob/master/proposals/simd/TextSIMD.md
762   base::EmbeddedVector<char, 50> buffer;
763   auto i32x4 = s128.to_i32x4();
764   SNPrintF(buffer, "i32x4 0x%08X 0x%08X 0x%08X 0x%08X", i32x4.val[0],
765            i32x4.val[1], i32x4.val[2], i32x4.val[3]);
766   return isolate->factory()->NewStringFromAsciiChecked(buffer.data());
767 }
768 
GetRefTypeName(Isolate * isolate,wasm::ValueType type,wasm::NativeModule * module)769 Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
770                               wasm::NativeModule* module) {
771   bool is_nullable = type.kind() == wasm::kOptRef;
772   const char* null_str = is_nullable ? " null" : "";
773   // This length only needs to be enough for generated names like
774   // "(ref null $type12345)". For names coming from the name section,
775   // we'll dynamically allocate an appropriately sized vector.
776   base::EmbeddedVector<char, 32> type_name;
777   size_t len;
778   if (type.heap_type().is_generic()) {
779     const char* generic_name = "";
780     wasm::HeapType::Representation heap_rep = type.heap_representation();
781     switch (heap_rep) {
782       case wasm::HeapType::kFunc:
783         generic_name = "func";
784         break;
785       case wasm::HeapType::kExtern:
786         generic_name = "extern";
787         break;
788       case wasm::HeapType::kEq:
789         generic_name = "eq";
790         break;
791       case wasm::HeapType::kI31:
792         generic_name = "i31";
793         break;
794       case wasm::HeapType::kData:
795         generic_name = "data";
796         break;
797       case wasm::HeapType::kAny:
798         generic_name = "any";
799         break;
800       default:
801         UNREACHABLE();
802     }
803     len = SNPrintF(type_name, "(ref%s %s)", null_str, generic_name);
804   } else {
805     int type_index = type.ref_index();
806     wasm::ModuleWireBytes module_wire_bytes(module->wire_bytes());
807     base::Vector<const char> name_vec = module_wire_bytes.GetNameOrNull(
808         module->GetDebugInfo()->GetTypeName(type_index));
809     if (name_vec.empty()) {
810       len = SNPrintF(type_name, "(ref%s $type%u)", null_str, type_index);
811     } else {
812       size_t required_length =
813           name_vec.size() +       // length of provided name
814           7 +                     // length of "(ref $)"
815           (is_nullable ? 5 : 0);  // length of " null" (optional)
816       base::Vector<char> long_type_name =
817           base::Vector<char>::New(required_length);
818       len = SNPrintF(long_type_name, "(ref%s $", null_str);
819       base::Vector<char> suffix =
820           long_type_name.SubVector(len, long_type_name.size());
821       // StrNCpy requires that there is room for an assumed trailing \0...
822       DCHECK_EQ(suffix.size(), name_vec.size() + 1);
823       base::StrNCpy(suffix, name_vec.data(), name_vec.size());
824       // ...but we actually write ')' into that byte.
825       long_type_name[required_length - 1] = ')';
826       Handle<String> result =
827           isolate->factory()->InternalizeString(long_type_name);
828       long_type_name.Dispose();
829       return result;
830     }
831   }
832   return isolate->factory()->InternalizeString(type_name.SubVector(0, len));
833 }
834 
835 }  // namespace
836 
837 // static
New(Isolate * isolate,Handle<String> type,Handle<Object> value)838 Handle<WasmValueObject> WasmValueObject::New(Isolate* isolate,
839                                              Handle<String> type,
840                                              Handle<Object> value) {
841   auto maps = GetOrCreateDebugMaps(isolate);
842   if (maps->is_the_hole(isolate, kWasmValueMapIndex)) {
843     Handle<Map> map = isolate->factory()->NewMap(
844         WASM_VALUE_OBJECT_TYPE, WasmValueObject::kSize,
845         TERMINAL_FAST_ELEMENTS_KIND, 2);
846     Map::EnsureDescriptorSlack(isolate, map, 2);
847     map->SetConstructor(*isolate->object_function());
848     {  // type
849       Descriptor d = Descriptor::DataField(
850           isolate,
851           isolate->factory()->InternalizeString(base::StaticCharVector("type")),
852           WasmValueObject::kTypeIndex, FROZEN, Representation::Tagged());
853       map->AppendDescriptor(isolate, &d);
854     }
855     {  // value
856       Descriptor d = Descriptor::DataField(
857           isolate,
858           isolate->factory()->InternalizeString(
859               base::StaticCharVector("value")),
860           WasmValueObject::kValueIndex, FROZEN, Representation::Tagged());
861       map->AppendDescriptor(isolate, &d);
862     }
863     map->set_is_extensible(false);
864     maps->set(kWasmValueMapIndex, *map);
865   }
866   Handle<Map> value_map =
867       handle(Map::cast(maps->get(kWasmValueMapIndex)), isolate);
868   Handle<WasmValueObject> object = Handle<WasmValueObject>::cast(
869       isolate->factory()->NewJSObjectFromMap(value_map));
870   object->set_type(*type);
871   object->set_value(*value);
872   return object;
873 }
874 
875 // This class implements a proxy for a single inspectable Wasm struct.
876 struct StructProxy : NamedDebugProxy<StructProxy, kStructProxy, FixedArray> {
877   static constexpr char const* kClassName = "Struct";
878 
879   static const int kObjectIndex = 0;
880   static const int kModuleIndex = 1;
881   static const int kTypeIndexIndex = 2;
882   static const int kLength = 3;
883 
Createv8::internal::StructProxy884   static Handle<JSObject> Create(Isolate* isolate, const wasm::WasmValue& value,
885                                  Handle<WasmModuleObject> module) {
886     Handle<FixedArray> data = isolate->factory()->NewFixedArray(kLength);
887     data->set(kObjectIndex, *value.to_ref());
888     data->set(kModuleIndex, *module);
889     int struct_type_index = value.type().ref_index();
890     data->set(kTypeIndexIndex, Smi::FromInt(struct_type_index));
891     return NamedDebugProxy::Create(isolate, data);
892   }
893 
Countv8::internal::StructProxy894   static uint32_t Count(Isolate* isolate, Handle<FixedArray> data) {
895     return WasmStruct::cast(data->get(kObjectIndex)).type()->field_count();
896   }
897 
Getv8::internal::StructProxy898   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> data,
899                             uint32_t index) {
900     Handle<WasmStruct> obj(WasmStruct::cast(data->get(kObjectIndex)), isolate);
901     Handle<WasmModuleObject> module(
902         WasmModuleObject::cast(data->get(kModuleIndex)), isolate);
903     return WasmValueObject::New(isolate, obj->GetFieldValue(index), module);
904   }
905 
GetNamev8::internal::StructProxy906   static Handle<String> GetName(Isolate* isolate, Handle<FixedArray> data,
907                                 uint32_t index) {
908     wasm::NativeModule* native_module =
909         WasmModuleObject::cast(data->get(kModuleIndex)).native_module();
910     int struct_type_index = Smi::ToInt(Smi::cast(data->get(kTypeIndexIndex)));
911     wasm::ModuleWireBytes module_wire_bytes(native_module->wire_bytes());
912     base::Vector<const char> name_vec = module_wire_bytes.GetNameOrNull(
913         native_module->GetDebugInfo()->GetFieldName(struct_type_index, index));
914     return GetNameOrDefault(
915         isolate,
916         name_vec.empty() ? MaybeHandle<String>()
917                          : isolate->factory()->NewStringFromUtf8(name_vec),
918         "$field", index);
919   }
920 };
921 
922 // This class implements a proxy for a single inspectable Wasm array.
923 struct ArrayProxy : IndexedDebugProxy<ArrayProxy, kArrayProxy, FixedArray> {
924   static constexpr char const* kClassName = "Array";
925 
926   static const int kObjectIndex = 0;
927   static const int kModuleIndex = 1;
928   static const int kLength = 2;
929 
Createv8::internal::ArrayProxy930   static Handle<JSObject> Create(Isolate* isolate, const wasm::WasmValue& value,
931                                  Handle<WasmModuleObject> module) {
932     Handle<FixedArray> data = isolate->factory()->NewFixedArray(kLength);
933     data->set(kObjectIndex, *value.to_ref());
934     data->set(kModuleIndex, *module);
935     Handle<JSObject> proxy = IndexedDebugProxy::Create(
936         isolate, data, false /* leave map extensible */);
937     uint32_t length = WasmArray::cast(*value.to_ref()).length();
938     Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length);
939     Object::SetProperty(isolate, proxy, isolate->factory()->length_string(),
940                         length_obj, StoreOrigin::kNamed,
941                         Just(ShouldThrow::kThrowOnError))
942         .Check();
943     return proxy;
944   }
945 
CreateTemplatev8::internal::ArrayProxy946   static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) {
947     Local<v8::FunctionTemplate> templ =
948         IndexedDebugProxy::CreateTemplate(isolate);
949     templ->InstanceTemplate()->Set(isolate, "length",
950                                    v8::Number::New(isolate, 0));
951     return templ;
952   }
953 
Countv8::internal::ArrayProxy954   static uint32_t Count(Isolate* isolate, Handle<FixedArray> data) {
955     return WasmArray::cast(data->get(kObjectIndex)).length();
956   }
957 
Getv8::internal::ArrayProxy958   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> data,
959                             uint32_t index) {
960     Handle<WasmArray> array(WasmArray::cast(data->get(kObjectIndex)), isolate);
961     Handle<WasmModuleObject> module(
962         WasmModuleObject::cast(data->get(kModuleIndex)), isolate);
963     return WasmValueObject::New(isolate, array->GetElement(index), module);
964   }
965 };
966 
967 // static
New(Isolate * isolate,const wasm::WasmValue & value,Handle<WasmModuleObject> module_object)968 Handle<WasmValueObject> WasmValueObject::New(
969     Isolate* isolate, const wasm::WasmValue& value,
970     Handle<WasmModuleObject> module_object) {
971   Handle<String> t;
972   Handle<Object> v;
973   switch (value.type().kind()) {
974     case wasm::kI8: {
975       // This can't be reached for most "top-level" things, only via nested
976       // calls for struct/array fields.
977       t = isolate->factory()->InternalizeString(base::StaticCharVector("i8"));
978       v = isolate->factory()->NewNumber(value.to_i8_unchecked());
979       break;
980     }
981     case wasm::kI16: {
982       // This can't be reached for most "top-level" things, only via nested
983       // calls for struct/array fields.
984       t = isolate->factory()->InternalizeString(base::StaticCharVector("i16"));
985       v = isolate->factory()->NewNumber(value.to_i16_unchecked());
986       break;
987     }
988     case wasm::kI32: {
989       t = isolate->factory()->InternalizeString(base::StaticCharVector("i32"));
990       v = isolate->factory()->NewNumberFromInt(value.to_i32_unchecked());
991       break;
992     }
993     case wasm::kI64: {
994       t = isolate->factory()->InternalizeString(base::StaticCharVector("i64"));
995       v = BigInt::FromInt64(isolate, value.to_i64_unchecked());
996       break;
997     }
998     case wasm::kF32: {
999       t = isolate->factory()->InternalizeString(base::StaticCharVector("f32"));
1000       v = isolate->factory()->NewNumber(value.to_f32_unchecked());
1001       break;
1002     }
1003     case wasm::kF64: {
1004       t = isolate->factory()->InternalizeString(base::StaticCharVector("f64"));
1005       v = isolate->factory()->NewNumber(value.to_f64_unchecked());
1006       break;
1007     }
1008     case wasm::kS128: {
1009       t = isolate->factory()->InternalizeString(base::StaticCharVector("v128"));
1010       v = WasmSimd128ToString(isolate, value.to_s128_unchecked());
1011       break;
1012     }
1013     case wasm::kOptRef:
1014       if (value.type().is_reference_to(wasm::HeapType::kExtern)) {
1015         t = isolate->factory()->InternalizeString(
1016             base::StaticCharVector("externref"));
1017         v = value.to_ref();
1018         break;
1019       }
1020       V8_FALLTHROUGH;
1021     case wasm::kRef: {
1022       t = GetRefTypeName(isolate, value.type(), module_object->native_module());
1023       Handle<Object> ref = value.to_ref();
1024       if (ref->IsWasmStruct()) {
1025         v = StructProxy::Create(isolate, value, module_object);
1026       } else if (ref->IsWasmArray()) {
1027         v = ArrayProxy::Create(isolate, value, module_object);
1028       } else if (ref->IsJSFunction() || ref->IsSmi() || ref->IsNull()) {
1029         v = ref;
1030       } else {
1031         // Fail gracefully.
1032         base::EmbeddedVector<char, 64> error;
1033         int len = SNPrintF(error, "unimplemented object type: %d",
1034                            HeapObject::cast(*ref).map().instance_type());
1035         v = isolate->factory()->InternalizeString(error.SubVector(0, len));
1036       }
1037       break;
1038     }
1039     case wasm::kRtt:
1040     case wasm::kRttWithDepth: {
1041       // TODO(7748): Expose RTTs to DevTools.
1042       t = isolate->factory()->InternalizeString(base::StaticCharVector("rtt"));
1043       v = isolate->factory()->InternalizeString(
1044           base::StaticCharVector("(unimplemented)"));
1045       break;
1046     }
1047     case wasm::kVoid:
1048     case wasm::kBottom:
1049       UNREACHABLE();
1050   }
1051   return New(isolate, t, v);
1052 }
1053 
GetWasmDebugProxy(WasmFrame * frame)1054 Handle<JSObject> GetWasmDebugProxy(WasmFrame* frame) {
1055   return ContextProxy::Create(frame);
1056 }
1057 
GetWasmScopeIterator(WasmFrame * frame)1058 std::unique_ptr<debug::ScopeIterator> GetWasmScopeIterator(WasmFrame* frame) {
1059   return std::make_unique<DebugWasmScopeIterator>(frame);
1060 }
1061 
GetWasmFunctionDebugName(Isolate * isolate,Handle<WasmInstanceObject> instance,uint32_t func_index)1062 Handle<String> GetWasmFunctionDebugName(Isolate* isolate,
1063                                         Handle<WasmInstanceObject> instance,
1064                                         uint32_t func_index) {
1065   Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
1066   MaybeHandle<String> maybe_name = WasmModuleObject::GetFunctionNameOrNull(
1067       isolate, module_object, func_index);
1068   if (module_object->is_asm_js()) {
1069     // In case of asm.js, we use the names from the function declarations.
1070     return maybe_name.ToHandleChecked();
1071   }
1072   if (maybe_name.is_null()) {
1073     maybe_name = GetNameFromImportsAndExportsOrNull(
1074         isolate, instance, wasm::ImportExportKindCode::kExternalFunction,
1075         func_index);
1076   }
1077   return GetNameOrDefault(isolate, maybe_name, "$func", func_index);
1078 }
1079 
AddWasmInstanceObjectInternalProperties(Isolate * isolate,Handle<ArrayList> result,Handle<WasmInstanceObject> instance)1080 Handle<ArrayList> AddWasmInstanceObjectInternalProperties(
1081     Isolate* isolate, Handle<ArrayList> result,
1082     Handle<WasmInstanceObject> instance) {
1083   result = ArrayList::Add(
1084       isolate, result,
1085       isolate->factory()->NewStringFromAsciiChecked("[[Module]]"),
1086       handle(instance->module_object(), isolate));
1087 
1088   if (FunctionsProxy::Count(isolate, instance) != 0) {
1089     result = ArrayList::Add(
1090         isolate, result,
1091         isolate->factory()->NewStringFromAsciiChecked("[[Functions]]"),
1092         GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance));
1093   }
1094 
1095   if (GlobalsProxy::Count(isolate, instance) != 0) {
1096     result = ArrayList::Add(
1097         isolate, result,
1098         isolate->factory()->NewStringFromAsciiChecked("[[Globals]]"),
1099         GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance));
1100   }
1101 
1102   if (MemoriesProxy::Count(isolate, instance) != 0) {
1103     result = ArrayList::Add(
1104         isolate, result,
1105         isolate->factory()->NewStringFromAsciiChecked("[[Memories]]"),
1106         GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance));
1107   }
1108 
1109   if (TablesProxy::Count(isolate, instance) != 0) {
1110     result = ArrayList::Add(
1111         isolate, result,
1112         isolate->factory()->NewStringFromAsciiChecked("[[Tables]]"),
1113         GetOrCreateInstanceProxy<TablesProxy>(isolate, instance));
1114   }
1115 
1116   return result;
1117 }
1118 
AddWasmModuleObjectInternalProperties(Isolate * isolate,Handle<ArrayList> result,Handle<WasmModuleObject> module_object)1119 Handle<ArrayList> AddWasmModuleObjectInternalProperties(
1120     Isolate* isolate, Handle<ArrayList> result,
1121     Handle<WasmModuleObject> module_object) {
1122   result = ArrayList::Add(
1123       isolate, result,
1124       isolate->factory()->NewStringFromStaticChars("[[Exports]]"),
1125       wasm::GetExports(isolate, module_object));
1126   result = ArrayList::Add(
1127       isolate, result,
1128       isolate->factory()->NewStringFromStaticChars("[[Imports]]"),
1129       wasm::GetImports(isolate, module_object));
1130   return result;
1131 }
1132 
AddWasmTableObjectInternalProperties(Isolate * isolate,Handle<ArrayList> result,Handle<WasmTableObject> table)1133 Handle<ArrayList> AddWasmTableObjectInternalProperties(
1134     Isolate* isolate, Handle<ArrayList> result, Handle<WasmTableObject> table) {
1135   int length = table->current_length();
1136   Handle<FixedArray> entries = isolate->factory()->NewFixedArray(length);
1137   for (int i = 0; i < length; ++i) {
1138     auto entry = WasmTableObject::Get(isolate, table, i);
1139     entries->set(i, *entry);
1140   }
1141   Handle<JSArray> final_entries = isolate->factory()->NewJSArrayWithElements(
1142       entries, i::PACKED_ELEMENTS, length);
1143   JSObject::SetPrototype(final_entries, isolate->factory()->null_value(), false,
1144                          kDontThrow)
1145       .Check();
1146   Handle<String> entries_string =
1147       isolate->factory()->NewStringFromStaticChars("[[Entries]]");
1148   result = ArrayList::Add(isolate, result, entries_string, final_entries);
1149   return result;
1150 }
1151 
1152 }  // namespace internal
1153 }  // namespace v8
1154