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::__anon14468::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::__anon14468::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::__anon14468::IndexedDebugProxy161   static Isolate* GetIsolate(const PropertyCallbackInfo<V>& info) {
162     return reinterpret_cast<Isolate*>(info.GetIsolate());
163   }
164 
165   template <typename V>
GetHolderv8::internal::__anon14468::IndexedDebugProxy166   static Handle<JSObject> GetHolder(const PropertyCallbackInfo<V>& info) {
167     return Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder()));
168   }
169 
GetProviderv8::internal::__anon14468::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::__anon14468::IndexedDebugProxy177   static Handle<Provider> GetProvider(const PropertyCallbackInfo<V>& info) {
178     return GetProvider(GetHolder(info), GetIsolate(info));
179   }
180 
IndexedGetterv8::internal::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::NamedDebugProxy241   static void IndexedEnumerator(const PropertyCallbackInfo<v8::Array>& info) {
242     info.GetReturnValue().Set(v8::Array::New(info.GetIsolate()));
243   }
244 
GetNameTablev8::internal::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::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::__anon14468::FunctionsProxy313   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
314     return static_cast<uint32_t>(instance->module()->functions.size());
315   }
316 
Getv8::internal::__anon14468::FunctionsProxy317   static Handle<Object> Get(Isolate* isolate,
318                             Handle<WasmInstanceObject> instance,
319                             uint32_t index) {
320     return handle(WasmInstanceObject::GetOrCreateWasmInternalFunction(
321                       isolate, instance, index)
322                       ->external(),
323                   isolate);
324   }
325 
GetNamev8::internal::__anon14468::FunctionsProxy326   static Handle<String> GetName(Isolate* isolate,
327                                 Handle<WasmInstanceObject> instance,
328                                 uint32_t index) {
329     return GetWasmFunctionDebugName(isolate, instance, index);
330   }
331 };
332 
333 // This class implements the "globals" proxy.
334 struct GlobalsProxy : NamedDebugProxy<GlobalsProxy, kGlobalsProxy> {
335   static constexpr char const* kClassName = "Globals";
336 
Countv8::internal::__anon14468::GlobalsProxy337   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
338     return static_cast<uint32_t>(instance->module()->globals.size());
339   }
340 
Getv8::internal::__anon14468::GlobalsProxy341   static Handle<Object> Get(Isolate* isolate,
342                             Handle<WasmInstanceObject> instance,
343                             uint32_t index) {
344     Handle<WasmModuleObject> module(instance->module_object(), isolate);
345     return WasmValueObject::New(
346         isolate,
347         WasmInstanceObject::GetGlobalValue(instance,
348                                            instance->module()->globals[index]),
349         module);
350   }
351 
GetNamev8::internal::__anon14468::GlobalsProxy352   static Handle<String> GetName(Isolate* isolate,
353                                 Handle<WasmInstanceObject> instance,
354                                 uint32_t index) {
355     return GetNameOrDefault(
356         isolate,
357         GetNameFromImportsAndExportsOrNull(
358             isolate, instance, wasm::ImportExportKindCode::kExternalGlobal,
359             index),
360         "$global", index);
361   }
362 };
363 
364 // This class implements the "memories" proxy.
365 struct MemoriesProxy : NamedDebugProxy<MemoriesProxy, kMemoriesProxy> {
366   static constexpr char const* kClassName = "Memories";
367 
Countv8::internal::__anon14468::MemoriesProxy368   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
369     return instance->has_memory_object() ? 1 : 0;
370   }
371 
Getv8::internal::__anon14468::MemoriesProxy372   static Handle<Object> Get(Isolate* isolate,
373                             Handle<WasmInstanceObject> instance,
374                             uint32_t index) {
375     return handle(instance->memory_object(), isolate);
376   }
377 
GetNamev8::internal::__anon14468::MemoriesProxy378   static Handle<String> GetName(Isolate* isolate,
379                                 Handle<WasmInstanceObject> instance,
380                                 uint32_t index) {
381     return GetNameOrDefault(
382         isolate,
383         GetNameFromImportsAndExportsOrNull(
384             isolate, instance, wasm::ImportExportKindCode::kExternalMemory,
385             index),
386         "$memory", index);
387   }
388 };
389 
390 // This class implements the "tables" proxy.
391 struct TablesProxy : NamedDebugProxy<TablesProxy, kTablesProxy> {
392   static constexpr char const* kClassName = "Tables";
393 
Countv8::internal::__anon14468::TablesProxy394   static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) {
395     return instance->tables().length();
396   }
397 
Getv8::internal::__anon14468::TablesProxy398   static Handle<Object> Get(Isolate* isolate,
399                             Handle<WasmInstanceObject> instance,
400                             uint32_t index) {
401     return handle(instance->tables().get(index), isolate);
402   }
403 
GetNamev8::internal::__anon14468::TablesProxy404   static Handle<String> GetName(Isolate* isolate,
405                                 Handle<WasmInstanceObject> instance,
406                                 uint32_t index) {
407     return GetNameOrDefault(
408         isolate,
409         GetNameFromImportsAndExportsOrNull(
410             isolate, instance, wasm::ImportExportKindCode::kExternalTable,
411             index),
412         "$table", index);
413   }
414 };
415 
416 // This class implements the "locals" proxy.
417 struct LocalsProxy : NamedDebugProxy<LocalsProxy, kLocalsProxy, FixedArray> {
418   static constexpr char const* kClassName = "Locals";
419 
Createv8::internal::__anon14468::LocalsProxy420   static Handle<JSObject> Create(WasmFrame* frame) {
421     auto isolate = frame->isolate();
422     auto debug_info = frame->native_module()->GetDebugInfo();
423     // TODO(bmeurer): Check if pc is inspectable.
424     int count = debug_info->GetNumLocals(frame->pc());
425     auto function = debug_info->GetFunctionAtAddress(frame->pc());
426     auto values = isolate->factory()->NewFixedArray(count + 2);
427     Handle<WasmModuleObject> module_object(
428         frame->wasm_instance().module_object(), isolate);
429     for (int i = 0; i < count; ++i) {
430       auto value = WasmValueObject::New(
431           isolate,
432           debug_info->GetLocalValue(i, frame->pc(), frame->fp(),
433                                     frame->callee_fp(), isolate),
434           module_object);
435       values->set(i, *value);
436     }
437     values->set(count + 0, frame->wasm_instance().module_object());
438     values->set(count + 1, Smi::FromInt(function.func_index));
439     return NamedDebugProxy::Create(isolate, values);
440   }
441 
Countv8::internal::__anon14468::LocalsProxy442   static uint32_t Count(Isolate* isolate, Handle<FixedArray> values) {
443     return values->length() - 2;
444   }
445 
Getv8::internal::__anon14468::LocalsProxy446   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> values,
447                             uint32_t index) {
448     return handle(values->get(index), isolate);
449   }
450 
GetNamev8::internal::__anon14468::LocalsProxy451   static Handle<String> GetName(Isolate* isolate, Handle<FixedArray> values,
452                                 uint32_t index) {
453     uint32_t count = Count(isolate, values);
454     auto native_module =
455         WasmModuleObject::cast(values->get(count + 0)).native_module();
456     auto function_index = Smi::ToInt(Smi::cast(values->get(count + 1)));
457     wasm::ModuleWireBytes module_wire_bytes(native_module->wire_bytes());
458     auto name_vec = module_wire_bytes.GetNameOrNull(
459         native_module->GetDebugInfo()->GetLocalName(function_index, index));
460     return GetNameOrDefault(
461         isolate,
462         name_vec.empty() ? MaybeHandle<String>()
463                          : isolate->factory()->NewStringFromUtf8(name_vec),
464         "$var", index);
465   }
466 };
467 
468 // This class implements the "stack" proxy (which offers only indexed access).
469 struct StackProxy : IndexedDebugProxy<StackProxy, kStackProxy, FixedArray> {
470   static constexpr char const* kClassName = "Stack";
471 
Createv8::internal::__anon14468::StackProxy472   static Handle<JSObject> Create(WasmFrame* frame) {
473     auto isolate = frame->isolate();
474     auto debug_info =
475         frame->wasm_instance().module_object().native_module()->GetDebugInfo();
476     int count = debug_info->GetStackDepth(frame->pc());
477     auto values = isolate->factory()->NewFixedArray(count);
478     Handle<WasmModuleObject> module_object(
479         frame->wasm_instance().module_object(), isolate);
480     for (int i = 0; i < count; ++i) {
481       auto value = WasmValueObject::New(
482           isolate,
483           debug_info->GetStackValue(i, frame->pc(), frame->fp(),
484                                     frame->callee_fp(), isolate),
485           module_object);
486       values->set(i, *value);
487     }
488     return IndexedDebugProxy::Create(isolate, values);
489   }
490 
Countv8::internal::__anon14468::StackProxy491   static uint32_t Count(Isolate* isolate, Handle<FixedArray> values) {
492     return values->length();
493   }
494 
Getv8::internal::__anon14468::StackProxy495   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> values,
496                             uint32_t index) {
497     return handle(values->get(index), isolate);
498   }
499 };
500 
501 // Creates FixedArray with size |kNumInstanceProxies| as cache on-demand
502 // on the |instance|, stored under the |wasm_debug_proxy_cache_symbol|.
503 // This is used to cache the various instance debug proxies (functions,
504 // globals, tables, and memories) on the WasmInstanceObject.
GetOrCreateInstanceProxyCache( Isolate* isolate, Handle<WasmInstanceObject> instance)505 Handle<FixedArray> GetOrCreateInstanceProxyCache(
506     Isolate* isolate, Handle<WasmInstanceObject> instance) {
507   Handle<Object> cache;
508   Handle<Symbol> symbol = isolate->factory()->wasm_debug_proxy_cache_symbol();
509   if (!Object::GetProperty(isolate, instance, symbol).ToHandle(&cache) ||
510       cache->IsUndefined(isolate)) {
511     cache = isolate->factory()->NewFixedArrayWithHoles(kNumInstanceProxies);
512     Object::SetProperty(isolate, instance, symbol, cache).Check();
513   }
514   return Handle<FixedArray>::cast(cache);
515 }
516 
517 // Creates an instance of the |Proxy| on-demand and caches that on the
518 // |instance|.
519 template <typename Proxy>
GetOrCreateInstanceProxy(Isolate* isolate, Handle<WasmInstanceObject> instance)520 Handle<JSObject> GetOrCreateInstanceProxy(Isolate* isolate,
521                                           Handle<WasmInstanceObject> instance) {
522   STATIC_ASSERT(Proxy::kId < kNumInstanceProxies);
523   Handle<FixedArray> proxies = GetOrCreateInstanceProxyCache(isolate, instance);
524   if (!proxies->is_the_hole(isolate, Proxy::kId)) {
525     return handle(JSObject::cast(proxies->get(Proxy::kId)), isolate);
526   }
527   Handle<JSObject> proxy = Proxy::Create(isolate, instance);
528   proxies->set(Proxy::kId, *proxy);
529   return proxy;
530 }
531 
532 // This class implements the debug proxy for a given Wasm frame. The debug
533 // proxy is used when evaluating JavaScript expressions on a wasm frame via
534 // the inspector |Runtime.evaluateOnCallFrame()| API and enables developers
535 // and extensions to inspect the WebAssembly engine state from JavaScript.
536 // The proxy provides the following interface:
537 //
538 // type WasmValue = {
539 //   type: string;
540 //   value: number | bigint | object | string;
541 // };
542 // type WasmFunction = (... args : WasmValue[]) = > WasmValue;
543 // interface WasmInterface {
544 //   $globalX: WasmValue;
545 //   $varX: WasmValue;
546 //   $funcX(a : WasmValue /*, ...*/) : WasmValue;
547 //   readonly $memoryX : WebAssembly.Memory;
548 //   readonly $tableX : WebAssembly.Table;
549 //
550 //   readonly instance : WebAssembly.Instance;
551 //   readonly module : WebAssembly.Module;
552 //
553 //   readonly memories : {[nameOrIndex:string | number] : WebAssembly.Memory};
554 //   readonly tables : {[nameOrIndex:string | number] : WebAssembly.Table};
555 //   readonly stack : WasmValue[];
556 //   readonly globals : {[nameOrIndex:string | number] : WasmValue};
557 //   readonly locals : {[nameOrIndex:string | number] : WasmValue};
558 //   readonly functions : {[nameOrIndex:string | number] : WasmFunction};
559 // }
560 //
561 // The wasm index spaces memories, tables, stack, globals, locals, and
562 // functions are JSObjects with interceptors that lazily produce values
563 // either by index or by name (except for stack).
564 // Only the names are reported by APIs such as Object.keys() and
565 // Object.getOwnPropertyNames(), since the indices are not meant to be
566 // used interactively by developers (in Chrome DevTools), but are provided
567 // for WebAssembly language extensions. Also note that these JSObjects
568 // all have null prototypes, to not confuse context lookup and to make
569 // their purpose as dictionaries clear.
570 //
571 // See http://doc/1VZOJrU2VsqOZe3IUzbwQWQQSZwgGySsm5119Ust1gUA and
572 // http://bit.ly/devtools-wasm-entities for more details.
573 class ContextProxyPrototype {
574  public:
Create(Isolate* isolate)575   static Handle<JSObject> Create(Isolate* isolate) {
576     auto object_map =
577         GetOrCreateDebugProxyMap(isolate, kContextProxy, &CreateTemplate);
578     return isolate->factory()->NewJSObjectFromMap(object_map);
579   }
580 
581  private:
CreateTemplate(v8::Isolate* isolate)582   static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) {
583     Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate);
584     templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration(
585         &NamedGetter, {}, {}, {}, {}, {}, {}, {},
586         static_cast<v8::PropertyHandlerFlags>(
587             static_cast<unsigned>(
588                 v8::PropertyHandlerFlags::kOnlyInterceptStrings) |
589             static_cast<unsigned>(
590                 v8::PropertyHandlerFlags::kHasNoSideEffect))));
591     return templ;
592   }
593 
GetNamedProperty(Isolate* isolate, Handle<JSObject> receiver, Handle<String> name)594   static MaybeHandle<Object> GetNamedProperty(Isolate* isolate,
595                                               Handle<JSObject> receiver,
596                                               Handle<String> name) {
597     if (name->length() != 0 && name->Get(0) == '$') {
598       const char* kDelegateNames[] = {"memories", "locals", "tables",
599                                       "functions", "globals"};
600       for (auto delegate_name : kDelegateNames) {
601         Handle<Object> delegate;
602         ASSIGN_RETURN_ON_EXCEPTION(
603             isolate, delegate,
604             JSObject::GetProperty(isolate, receiver, delegate_name), Object);
605         if (!delegate->IsUndefined(isolate)) {
606           Handle<Object> value;
607           ASSIGN_RETURN_ON_EXCEPTION(
608               isolate, value, Object::GetProperty(isolate, delegate, name),
609               Object);
610           if (!value->IsUndefined(isolate)) return value;
611         }
612       }
613     }
614     return {};
615   }
616 
NamedGetter(Local<v8::Name> name, const PropertyCallbackInfo<v8::Value>& info)617   static void NamedGetter(Local<v8::Name> name,
618                           const PropertyCallbackInfo<v8::Value>& info) {
619     auto name_string = Handle<String>::cast(Utils::OpenHandle(*name));
620     auto isolate = reinterpret_cast<Isolate*>(info.GetIsolate());
621     auto receiver = Handle<JSObject>::cast(Utils::OpenHandle(*info.This()));
622     Handle<Object> value;
623     if (GetNamedProperty(isolate, receiver, name_string).ToHandle(&value)) {
624       info.GetReturnValue().Set(Utils::ToLocal(value));
625     }
626   }
627 };
628 
629 class ContextProxy {
630  public:
Create(WasmFrame* frame)631   static Handle<JSObject> Create(WasmFrame* frame) {
632     Isolate* isolate = frame->isolate();
633     auto object = isolate->factory()->NewSlowJSObjectWithNullProto();
634     Handle<WasmInstanceObject> instance(frame->wasm_instance(), isolate);
635     JSObject::AddProperty(isolate, object, "instance", instance, FROZEN);
636     Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
637     JSObject::AddProperty(isolate, object, "module", module_object, FROZEN);
638     auto locals = LocalsProxy::Create(frame);
639     JSObject::AddProperty(isolate, object, "locals", locals, FROZEN);
640     auto stack = StackProxy::Create(frame);
641     JSObject::AddProperty(isolate, object, "stack", stack, FROZEN);
642     auto memories = GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance);
643     JSObject::AddProperty(isolate, object, "memories", memories, FROZEN);
644     auto tables = GetOrCreateInstanceProxy<TablesProxy>(isolate, instance);
645     JSObject::AddProperty(isolate, object, "tables", tables, FROZEN);
646     auto globals = GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance);
647     JSObject::AddProperty(isolate, object, "globals", globals, FROZEN);
648     auto functions =
649         GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance);
650     JSObject::AddProperty(isolate, object, "functions", functions, FROZEN);
651     Handle<JSObject> prototype = ContextProxyPrototype::Create(isolate);
652     JSObject::SetPrototype(isolate, object, prototype, false, kDontThrow)
653         .Check();
654     return object;
655   }
656 };
657 
658 class DebugWasmScopeIterator final : public debug::ScopeIterator {
659  public:
DebugWasmScopeIterator(WasmFrame* frame)660   explicit DebugWasmScopeIterator(WasmFrame* frame)
661       : frame_(frame),
662         type_(debug::ScopeIterator::ScopeTypeWasmExpressionStack) {
663     // Skip local scope and expression stack scope if the frame is not
664     // inspectable.
665     if (!frame->is_inspectable()) {
666       type_ = debug::ScopeIterator::ScopeTypeModule;
667     }
668   }
669 
670   bool Done() override { return type_ == ScopeTypeWith; }
671 
672   void Advance() override {
673     DCHECK(!Done());
674     switch (type_) {
675       case ScopeTypeWasmExpressionStack:
676         type_ = debug::ScopeIterator::ScopeTypeLocal;
677         break;
678       case ScopeTypeLocal:
679         type_ = debug::ScopeIterator::ScopeTypeModule;
680         break;
681       case ScopeTypeModule:
682         // We use ScopeTypeWith type as marker for done.
683         type_ = debug::ScopeIterator::ScopeTypeWith;
684         break;
685       default:
686         UNREACHABLE();
687     }
688   }
689 
690   ScopeType GetType() override { return type_; }
691 
692   v8::Local<v8::Object> GetObject() override {
693     Isolate* isolate = frame_->isolate();
694     switch (type_) {
695       case debug::ScopeIterator::ScopeTypeModule: {
696         Handle<WasmInstanceObject> instance(frame_->wasm_instance(), isolate);
697         Handle<JSObject> object =
698             isolate->factory()->NewSlowJSObjectWithNullProto();
699         JSObject::AddProperty(isolate, object, "instance", instance, FROZEN);
700         Handle<JSObject> module_object(instance->module_object(), isolate);
701         JSObject::AddProperty(isolate, object, "module", module_object, FROZEN);
702         if (FunctionsProxy::Count(isolate, instance) != 0) {
703           JSObject::AddProperty(
704               isolate, object, "functions",
705               GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance),
706               FROZEN);
707         }
708         if (GlobalsProxy::Count(isolate, instance) != 0) {
709           JSObject::AddProperty(
710               isolate, object, "globals",
711               GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance),
712               FROZEN);
713         }
714         if (MemoriesProxy::Count(isolate, instance) != 0) {
715           JSObject::AddProperty(
716               isolate, object, "memories",
717               GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance),
718               FROZEN);
719         }
720         if (TablesProxy::Count(isolate, instance) != 0) {
721           JSObject::AddProperty(
722               isolate, object, "tables",
723               GetOrCreateInstanceProxy<TablesProxy>(isolate, instance), FROZEN);
724         }
725         return Utils::ToLocal(object);
726       }
727       case debug::ScopeIterator::ScopeTypeLocal: {
728         return Utils::ToLocal(LocalsProxy::Create(frame_));
729       }
730       case debug::ScopeIterator::ScopeTypeWasmExpressionStack: {
731         auto object = isolate->factory()->NewSlowJSObjectWithNullProto();
732         auto stack = StackProxy::Create(frame_);
733         JSObject::AddProperty(isolate, object, "stack", stack, FROZEN);
734         return Utils::ToLocal(object);
735       }
736       default:
737         UNREACHABLE();
738     }
739   }
740   v8::Local<v8::Value> GetFunctionDebugName() override {
741     return Utils::ToLocal(frame_->isolate()->factory()->empty_string());
742   }
743 
744   int GetScriptId() override { return -1; }
745 
746   bool HasLocationInfo() override { return false; }
747 
748   debug::Location GetStartLocation() override { return {}; }
749 
750   debug::Location GetEndLocation() override { return {}; }
751 
752   bool SetVariableValue(v8::Local<v8::String> name,
753                         v8::Local<v8::Value> value) override {
754     return false;
755   }
756 
757  private:
758   WasmFrame* const frame_;
759   ScopeType type_;
760 };
761 
WasmSimd128ToString(Isolate* isolate, wasm::Simd128 s128)762 Handle<String> WasmSimd128ToString(Isolate* isolate, wasm::Simd128 s128) {
763   // We use the canonical format as described in:
764   // https://github.com/WebAssembly/simd/blob/master/proposals/simd/TextSIMD.md
765   base::EmbeddedVector<char, 50> buffer;
766   auto i32x4 = s128.to_i32x4();
767   SNPrintF(buffer, "i32x4 0x%08X 0x%08X 0x%08X 0x%08X", i32x4.val[0],
768            i32x4.val[1], i32x4.val[2], i32x4.val[3]);
769   return isolate->factory()->NewStringFromAsciiChecked(buffer.data());
770 }
771 
GetRefTypeName(Isolate* isolate, wasm::ValueType type, wasm::NativeModule* module)772 Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type,
773                               wasm::NativeModule* module) {
774   DCHECK(type.is_object_reference());
775   std::ostringstream name;
776   if (type.heap_type().is_generic()) {
777     name << type.name();
778   } else {
779     name << "(ref " << (type.is_nullable() ? "null " : "") << "$";
780     wasm::ModuleWireBytes module_wire_bytes(module->wire_bytes());
781     base::Vector<const char> module_name = module_wire_bytes.GetNameOrNull(
782         module->GetDebugInfo()->GetTypeName(type.ref_index()));
783     if (module_name.empty()) {
784       name << "type" << type.ref_index();
785     } else {
786       name.write(module_name.begin(), module_name.size());
787     }
788     name << ")";
789   }
790   return isolate->factory()->InternalizeString(base::VectorOf(name.str()));
791 }
792 
793 }  // namespace
794 
795 // static
New(Isolate* isolate, Handle<String> type, Handle<Object> value)796 Handle<WasmValueObject> WasmValueObject::New(Isolate* isolate,
797                                              Handle<String> type,
798                                              Handle<Object> value) {
799   auto maps = GetOrCreateDebugMaps(isolate);
800   if (maps->is_the_hole(isolate, kWasmValueMapIndex)) {
801     Handle<Map> map = isolate->factory()->NewMap(
802         WASM_VALUE_OBJECT_TYPE, WasmValueObject::kSize,
803         TERMINAL_FAST_ELEMENTS_KIND, 2);
804     Map::EnsureDescriptorSlack(isolate, map, 2);
805     map->SetConstructor(*isolate->object_function());
806     {  // type
807       Descriptor d = Descriptor::DataField(
808           isolate,
809           isolate->factory()->InternalizeString(base::StaticCharVector("type")),
810           WasmValueObject::kTypeIndex, FROZEN, Representation::Tagged());
811       map->AppendDescriptor(isolate, &d);
812     }
813     {  // value
814       Descriptor d = Descriptor::DataField(
815           isolate,
816           isolate->factory()->InternalizeString(
817               base::StaticCharVector("value")),
818           WasmValueObject::kValueIndex, FROZEN, Representation::Tagged());
819       map->AppendDescriptor(isolate, &d);
820     }
821     map->set_is_extensible(false);
822     maps->set(kWasmValueMapIndex, *map);
823   }
824   Handle<Map> value_map =
825       handle(Map::cast(maps->get(kWasmValueMapIndex)), isolate);
826   Handle<WasmValueObject> object = Handle<WasmValueObject>::cast(
827       isolate->factory()->NewJSObjectFromMap(value_map));
828   object->set_type(*type);
829   object->set_value(*value);
830   return object;
831 }
832 
833 // This class implements a proxy for a single inspectable Wasm struct.
834 struct StructProxy : NamedDebugProxy<StructProxy, kStructProxy, FixedArray> {
835   static constexpr char const* kClassName = "Struct";
836 
837   static const int kObjectIndex = 0;
838   static const int kModuleIndex = 1;
839   static const int kTypeIndexIndex = 2;
840   static const int kLength = 3;
841 
Createv8::internal::StructProxy842   static Handle<JSObject> Create(Isolate* isolate, const wasm::WasmValue& value,
843                                  Handle<WasmModuleObject> module) {
844     Handle<FixedArray> data = isolate->factory()->NewFixedArray(kLength);
845     data->set(kObjectIndex, *value.to_ref());
846     data->set(kModuleIndex, *module);
847     int struct_type_index = value.type().ref_index();
848     data->set(kTypeIndexIndex, Smi::FromInt(struct_type_index));
849     return NamedDebugProxy::Create(isolate, data);
850   }
851 
Countv8::internal::StructProxy852   static uint32_t Count(Isolate* isolate, Handle<FixedArray> data) {
853     return WasmStruct::cast(data->get(kObjectIndex)).type()->field_count();
854   }
855 
Getv8::internal::StructProxy856   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> data,
857                             uint32_t index) {
858     Handle<WasmStruct> obj(WasmStruct::cast(data->get(kObjectIndex)), isolate);
859     Handle<WasmModuleObject> module(
860         WasmModuleObject::cast(data->get(kModuleIndex)), isolate);
861     return WasmValueObject::New(isolate, obj->GetFieldValue(index), module);
862   }
863 
GetNamev8::internal::StructProxy864   static Handle<String> GetName(Isolate* isolate, Handle<FixedArray> data,
865                                 uint32_t index) {
866     wasm::NativeModule* native_module =
867         WasmModuleObject::cast(data->get(kModuleIndex)).native_module();
868     int struct_type_index = Smi::ToInt(Smi::cast(data->get(kTypeIndexIndex)));
869     wasm::ModuleWireBytes module_wire_bytes(native_module->wire_bytes());
870     base::Vector<const char> name_vec = module_wire_bytes.GetNameOrNull(
871         native_module->GetDebugInfo()->GetFieldName(struct_type_index, index));
872     return GetNameOrDefault(
873         isolate,
874         name_vec.empty() ? MaybeHandle<String>()
875                          : isolate->factory()->NewStringFromUtf8(name_vec),
876         "$field", index);
877   }
878 };
879 
880 // This class implements a proxy for a single inspectable Wasm array.
881 struct ArrayProxy : IndexedDebugProxy<ArrayProxy, kArrayProxy, FixedArray> {
882   static constexpr char const* kClassName = "Array";
883 
884   static const int kObjectIndex = 0;
885   static const int kModuleIndex = 1;
886   static const int kLength = 2;
887 
Createv8::internal::ArrayProxy888   static Handle<JSObject> Create(Isolate* isolate, const wasm::WasmValue& value,
889                                  Handle<WasmModuleObject> module) {
890     Handle<FixedArray> data = isolate->factory()->NewFixedArray(kLength);
891     data->set(kObjectIndex, *value.to_ref());
892     data->set(kModuleIndex, *module);
893     Handle<JSObject> proxy = IndexedDebugProxy::Create(
894         isolate, data, false /* leave map extensible */);
895     uint32_t length = WasmArray::cast(*value.to_ref()).length();
896     Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length);
897     Object::SetProperty(isolate, proxy, isolate->factory()->length_string(),
898                         length_obj, StoreOrigin::kNamed,
899                         Just(ShouldThrow::kThrowOnError))
900         .Check();
901     return proxy;
902   }
903 
CreateTemplatev8::internal::ArrayProxy904   static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) {
905     Local<v8::FunctionTemplate> templ =
906         IndexedDebugProxy::CreateTemplate(isolate);
907     templ->InstanceTemplate()->Set(isolate, "length",
908                                    v8::Number::New(isolate, 0));
909     return templ;
910   }
911 
Countv8::internal::ArrayProxy912   static uint32_t Count(Isolate* isolate, Handle<FixedArray> data) {
913     return WasmArray::cast(data->get(kObjectIndex)).length();
914   }
915 
Getv8::internal::ArrayProxy916   static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> data,
917                             uint32_t index) {
918     Handle<WasmArray> array(WasmArray::cast(data->get(kObjectIndex)), isolate);
919     Handle<WasmModuleObject> module(
920         WasmModuleObject::cast(data->get(kModuleIndex)), isolate);
921     return WasmValueObject::New(isolate, array->GetElement(index), module);
922   }
923 };
924 
925 // static
New( Isolate* isolate, const wasm::WasmValue& value, Handle<WasmModuleObject> module_object)926 Handle<WasmValueObject> WasmValueObject::New(
927     Isolate* isolate, const wasm::WasmValue& value,
928     Handle<WasmModuleObject> module_object) {
929   Handle<String> t;
930   Handle<Object> v;
931   switch (value.type().kind()) {
932     case wasm::kI8: {
933       // This can't be reached for most "top-level" things, only via nested
934       // calls for struct/array fields.
935       t = isolate->factory()->InternalizeString(base::StaticCharVector("i8"));
936       v = isolate->factory()->NewNumber(value.to_i8_unchecked());
937       break;
938     }
939     case wasm::kI16: {
940       // This can't be reached for most "top-level" things, only via nested
941       // calls for struct/array fields.
942       t = isolate->factory()->InternalizeString(base::StaticCharVector("i16"));
943       v = isolate->factory()->NewNumber(value.to_i16_unchecked());
944       break;
945     }
946     case wasm::kI32: {
947       t = isolate->factory()->InternalizeString(base::StaticCharVector("i32"));
948       v = isolate->factory()->NewNumberFromInt(value.to_i32_unchecked());
949       break;
950     }
951     case wasm::kI64: {
952       t = isolate->factory()->InternalizeString(base::StaticCharVector("i64"));
953       v = BigInt::FromInt64(isolate, value.to_i64_unchecked());
954       break;
955     }
956     case wasm::kF32: {
957       t = isolate->factory()->InternalizeString(base::StaticCharVector("f32"));
958       v = isolate->factory()->NewNumber(value.to_f32_unchecked());
959       break;
960     }
961     case wasm::kF64: {
962       t = isolate->factory()->InternalizeString(base::StaticCharVector("f64"));
963       v = isolate->factory()->NewNumber(value.to_f64_unchecked());
964       break;
965     }
966     case wasm::kS128: {
967       t = isolate->factory()->InternalizeString(base::StaticCharVector("v128"));
968       v = WasmSimd128ToString(isolate, value.to_s128_unchecked());
969       break;
970     }
971     case wasm::kOptRef:
972     case wasm::kRef: {
973       t = GetRefTypeName(isolate, value.type(), module_object->native_module());
974       Handle<Object> ref = value.to_ref();
975       if (ref->IsWasmStruct()) {
976         v = StructProxy::Create(isolate, value, module_object);
977       } else if (ref->IsWasmArray()) {
978         v = ArrayProxy::Create(isolate, value, module_object);
979       } else if (ref->IsWasmInternalFunction()) {
980         v = handle(Handle<WasmInternalFunction>::cast(ref)->external(),
981                    isolate);
982       } else if (ref->IsJSFunction() || ref->IsSmi() || ref->IsNull() ||
983                  value.type().is_reference_to(wasm::HeapType::kAny)) {
984         v = ref;
985       } else {
986         // Fail gracefully.
987         base::EmbeddedVector<char, 64> error;
988         int len = SNPrintF(error, "unimplemented object type: %d",
989                            HeapObject::cast(*ref).map().instance_type());
990         v = isolate->factory()->InternalizeString(error.SubVector(0, len));
991       }
992       break;
993     }
994     case wasm::kRtt: {
995       // TODO(7748): Expose RTTs to DevTools.
996       t = isolate->factory()->InternalizeString(base::StaticCharVector("rtt"));
997       v = isolate->factory()->InternalizeString(
998           base::StaticCharVector("(unimplemented)"));
999       break;
1000     }
1001     case wasm::kVoid:
1002     case wasm::kBottom:
1003       UNREACHABLE();
1004   }
1005   return New(isolate, t, v);
1006 }
1007 
GetWasmDebugProxy(WasmFrame* frame)1008 Handle<JSObject> GetWasmDebugProxy(WasmFrame* frame) {
1009   return ContextProxy::Create(frame);
1010 }
1011 
GetWasmScopeIterator(WasmFrame* frame)1012 std::unique_ptr<debug::ScopeIterator> GetWasmScopeIterator(WasmFrame* frame) {
1013   return std::make_unique<DebugWasmScopeIterator>(frame);
1014 }
1015 
GetWasmFunctionDebugName(Isolate* isolate, Handle<WasmInstanceObject> instance, uint32_t func_index)1016 Handle<String> GetWasmFunctionDebugName(Isolate* isolate,
1017                                         Handle<WasmInstanceObject> instance,
1018                                         uint32_t func_index) {
1019   Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
1020   MaybeHandle<String> maybe_name = WasmModuleObject::GetFunctionNameOrNull(
1021       isolate, module_object, func_index);
1022   if (module_object->is_asm_js()) {
1023     // In case of asm.js, we use the names from the function declarations.
1024     return maybe_name.ToHandleChecked();
1025   }
1026   if (maybe_name.is_null()) {
1027     maybe_name = GetNameFromImportsAndExportsOrNull(
1028         isolate, instance, wasm::ImportExportKindCode::kExternalFunction,
1029         func_index);
1030   }
1031   return GetNameOrDefault(isolate, maybe_name, "$func", func_index);
1032 }
1033 
AddWasmInstanceObjectInternalProperties( Isolate* isolate, Handle<ArrayList> result, Handle<WasmInstanceObject> instance)1034 Handle<ArrayList> AddWasmInstanceObjectInternalProperties(
1035     Isolate* isolate, Handle<ArrayList> result,
1036     Handle<WasmInstanceObject> instance) {
1037   result = ArrayList::Add(
1038       isolate, result,
1039       isolate->factory()->NewStringFromAsciiChecked("[[Module]]"),
1040       handle(instance->module_object(), isolate));
1041 
1042   if (FunctionsProxy::Count(isolate, instance) != 0) {
1043     result = ArrayList::Add(
1044         isolate, result,
1045         isolate->factory()->NewStringFromAsciiChecked("[[Functions]]"),
1046         GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance));
1047   }
1048 
1049   if (GlobalsProxy::Count(isolate, instance) != 0) {
1050     result = ArrayList::Add(
1051         isolate, result,
1052         isolate->factory()->NewStringFromAsciiChecked("[[Globals]]"),
1053         GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance));
1054   }
1055 
1056   if (MemoriesProxy::Count(isolate, instance) != 0) {
1057     result = ArrayList::Add(
1058         isolate, result,
1059         isolate->factory()->NewStringFromAsciiChecked("[[Memories]]"),
1060         GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance));
1061   }
1062 
1063   if (TablesProxy::Count(isolate, instance) != 0) {
1064     result = ArrayList::Add(
1065         isolate, result,
1066         isolate->factory()->NewStringFromAsciiChecked("[[Tables]]"),
1067         GetOrCreateInstanceProxy<TablesProxy>(isolate, instance));
1068   }
1069 
1070   return result;
1071 }
1072 
AddWasmModuleObjectInternalProperties( Isolate* isolate, Handle<ArrayList> result, Handle<WasmModuleObject> module_object)1073 Handle<ArrayList> AddWasmModuleObjectInternalProperties(
1074     Isolate* isolate, Handle<ArrayList> result,
1075     Handle<WasmModuleObject> module_object) {
1076   result = ArrayList::Add(
1077       isolate, result,
1078       isolate->factory()->NewStringFromStaticChars("[[Exports]]"),
1079       wasm::GetExports(isolate, module_object));
1080   result = ArrayList::Add(
1081       isolate, result,
1082       isolate->factory()->NewStringFromStaticChars("[[Imports]]"),
1083       wasm::GetImports(isolate, module_object));
1084   return result;
1085 }
1086 
AddWasmTableObjectInternalProperties( Isolate* isolate, Handle<ArrayList> result, Handle<WasmTableObject> table)1087 Handle<ArrayList> AddWasmTableObjectInternalProperties(
1088     Isolate* isolate, Handle<ArrayList> result, Handle<WasmTableObject> table) {
1089   int length = table->current_length();
1090   Handle<FixedArray> entries = isolate->factory()->NewFixedArray(length);
1091   for (int i = 0; i < length; ++i) {
1092     Handle<Object> entry = WasmTableObject::Get(isolate, table, i);
1093     if (entry->IsWasmInternalFunction()) {
1094       entry = handle(Handle<WasmInternalFunction>::cast(entry)->external(),
1095                      isolate);
1096     }
1097     entries->set(i, *entry);
1098   }
1099   Handle<JSArray> final_entries = isolate->factory()->NewJSArrayWithElements(
1100       entries, i::PACKED_ELEMENTS, length);
1101   JSObject::SetPrototype(isolate, final_entries,
1102                          isolate->factory()->null_value(), false, kDontThrow)
1103       .Check();
1104   Handle<String> entries_string =
1105       isolate->factory()->NewStringFromStaticChars("[[Entries]]");
1106   result = ArrayList::Add(isolate, result, entries_string, final_entries);
1107   return result;
1108 }
1109 
1110 }  // namespace internal
1111 }  // namespace v8
1112