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