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