| // Copyright 2021 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/debug/debug-wasm-objects.h" |
| |
| #include "src/api/api-inl.h" |
| #include "src/api/api-natives.h" |
| #include "src/base/strings.h" |
| #include "src/common/globals.h" |
| #include "src/debug/debug-wasm-objects-inl.h" |
| #include "src/execution/frames-inl.h" |
| #include "src/objects/allocation-site.h" |
| #include "src/objects/property-descriptor.h" |
| #include "src/wasm/names-provider.h" |
| #include "src/wasm/string-builder.h" |
| #include "src/wasm/wasm-debug.h" |
| #include "src/wasm/wasm-objects-inl.h" |
| #include "src/wasm/wasm-value.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace { |
| |
| using StringBuilder = wasm::StringBuilder; |
| Handle<String> ToInternalString(StringBuilder& sb, Isolate* isolate) { |
| return isolate->factory()->InternalizeString( |
| base::VectorOf(sb.start(), sb.length())); |
| } |
| |
| enum DebugProxyId { |
| kFunctionsProxy, |
| kGlobalsProxy, |
| kMemoriesProxy, |
| kTablesProxy, |
| kLastInstanceProxyId = kTablesProxy, |
| |
| kContextProxy, |
| kLocalsProxy, |
| kStackProxy, |
| kStructProxy, |
| kArrayProxy, |
| kLastProxyId = kArrayProxy, |
| |
| kNumProxies = kLastProxyId + 1, |
| kNumInstanceProxies = kLastInstanceProxyId + 1 |
| }; |
| |
| constexpr int kWasmValueMapIndex = kNumProxies; |
| constexpr int kNumDebugMaps = kWasmValueMapIndex + 1; |
| |
| Handle<FixedArray> GetOrCreateDebugMaps(Isolate* isolate) { |
| Handle<FixedArray> maps = isolate->wasm_debug_maps(); |
| if (maps->length() == 0) { |
| maps = isolate->factory()->NewFixedArrayWithHoles(kNumDebugMaps); |
| isolate->native_context()->set_wasm_debug_maps(*maps); |
| } |
| return maps; |
| } |
| |
| // Creates a Map for the given debug proxy |id| using the |create_template_fn| |
| // on-demand and caches this map in the global object. The map is derived from |
| // the FunctionTemplate returned by |create_template_fn| and has its prototype |
| // set to |null| and is marked non-extensible (by default). |
| // TODO(bmeurer): remove the extensibility opt-out and replace it with a proper |
| // way to add non-intercepted named properties. |
| Handle<Map> GetOrCreateDebugProxyMap( |
| Isolate* isolate, DebugProxyId id, |
| v8::Local<v8::FunctionTemplate> (*create_template_fn)(v8::Isolate*), |
| bool make_non_extensible = true) { |
| auto maps = GetOrCreateDebugMaps(isolate); |
| CHECK_LE(kNumProxies, maps->length()); |
| if (!maps->is_the_hole(isolate, id)) { |
| return handle(Map::cast(maps->get(id)), isolate); |
| } |
| auto tmp = (*create_template_fn)(reinterpret_cast<v8::Isolate*>(isolate)); |
| auto fun = ApiNatives::InstantiateFunction(isolate, Utils::OpenHandle(*tmp)) |
| .ToHandleChecked(); |
| auto map = JSFunction::GetDerivedMap(isolate, fun, fun).ToHandleChecked(); |
| Map::SetPrototype(isolate, map, isolate->factory()->null_value()); |
| if (make_non_extensible) { |
| map->set_is_extensible(false); |
| } |
| maps->set(id, *map); |
| return map; |
| } |
| |
| // Base class for debug proxies, offers indexed access. The subclasses |
| // need to implement |Count| and |Get| methods appropriately. |
| template <typename T, DebugProxyId id, typename Provider> |
| struct IndexedDebugProxy { |
| static constexpr DebugProxyId kId = id; |
| |
| static Handle<JSObject> Create(Isolate* isolate, Handle<Provider> provider, |
| bool make_map_non_extensible = true) { |
| auto object_map = GetOrCreateDebugProxyMap(isolate, kId, &T::CreateTemplate, |
| make_map_non_extensible); |
| auto object = isolate->factory()->NewFastOrSlowJSObjectFromMap( |
| object_map, 0, AllocationType::kYoung, |
| DirectHandle<AllocationSite>::null(), NewJSObjectType::kAPIWrapper); |
| object->SetEmbedderField(kProviderField, *provider); |
| return object; |
| } |
| |
| enum { |
| kProviderField, |
| kFieldCount, |
| }; |
| |
| static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) { |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->SetClassName( |
| v8::String::NewFromUtf8(isolate, T::kClassName).ToLocalChecked()); |
| templ->InstanceTemplate()->SetInternalFieldCount(T::kFieldCount); |
| templ->InstanceTemplate()->SetHandler( |
| v8::IndexedPropertyHandlerConfiguration( |
| &T::IndexedGetter, {}, &T::IndexedQuery, {}, &T::IndexedEnumerator, |
| {}, &T::IndexedDescriptor, {}, |
| v8::PropertyHandlerFlags::kHasNoSideEffect)); |
| return templ; |
| } |
| |
| template <typename V> |
| static Isolate* GetIsolate(const PropertyCallbackInfo<V>& info) { |
| return reinterpret_cast<Isolate*>(info.GetIsolate()); |
| } |
| |
| template <typename V> |
| static Handle<JSObject> GetHolder(const PropertyCallbackInfo<V>& info) { |
| return Handle<JSObject>::cast(Utils::OpenHandle(*info.Holder())); |
| } |
| |
| static Handle<Provider> GetProvider(Handle<JSObject> holder, |
| Isolate* isolate) { |
| return handle(Provider::cast(holder->GetEmbedderField(kProviderField)), |
| isolate); |
| } |
| |
| template <typename V> |
| static Handle<Provider> GetProvider(const PropertyCallbackInfo<V>& info) { |
| return GetProvider(GetHolder(info), GetIsolate(info)); |
| } |
| |
| static v8::Intercepted IndexedGetter( |
| uint32_t index, const PropertyCallbackInfo<v8::Value>& info) { |
| auto isolate = GetIsolate(info); |
| auto provider = GetProvider(info); |
| if (index < T::Count(isolate, provider)) { |
| auto value = T::Get(isolate, provider, index); |
| info.GetReturnValue().Set(Utils::ToLocal(value)); |
| return v8::Intercepted::kYes; |
| } |
| return v8::Intercepted::kNo; |
| } |
| |
| static v8::Intercepted IndexedDescriptor( |
| uint32_t index, const PropertyCallbackInfo<v8::Value>& info) { |
| auto isolate = GetIsolate(info); |
| auto provider = GetProvider(info); |
| if (index < T::Count(isolate, provider)) { |
| PropertyDescriptor descriptor; |
| descriptor.set_configurable(false); |
| descriptor.set_enumerable(true); |
| descriptor.set_writable(false); |
| descriptor.set_value(T::Get(isolate, provider, index)); |
| info.GetReturnValue().Set(Utils::ToLocal(descriptor.ToObject(isolate))); |
| return v8::Intercepted::kYes; |
| } |
| return v8::Intercepted::kNo; |
| } |
| |
| static v8::Intercepted IndexedQuery( |
| uint32_t index, const PropertyCallbackInfo<v8::Integer>& info) { |
| if (index < T::Count(GetIsolate(info), GetProvider(info))) { |
| info.GetReturnValue().Set(Integer::New( |
| info.GetIsolate(), |
| PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly)); |
| return v8::Intercepted::kYes; |
| } |
| return v8::Intercepted::kNo; |
| } |
| |
| static void IndexedEnumerator(const PropertyCallbackInfo<v8::Array>& info) { |
| auto isolate = GetIsolate(info); |
| auto count = T::Count(isolate, GetProvider(info)); |
| auto indices = isolate->factory()->NewFixedArray(count); |
| for (uint32_t index = 0; index < count; ++index) { |
| indices->set(index, Smi::FromInt(index)); |
| } |
| info.GetReturnValue().Set( |
| Utils::ToLocal(isolate->factory()->NewJSArrayWithElements( |
| indices, PACKED_SMI_ELEMENTS))); |
| } |
| }; |
| |
| // Extends |IndexedDebugProxy| with named access, where the names are computed |
| // on-demand, and all names are assumed to start with a dollar char ($). This |
| // is important in order to scale to Wasm modules with hundreds of thousands |
| // of functions in them. |
| template <typename T, DebugProxyId id, typename Provider = WasmInstanceObject> |
| struct NamedDebugProxy : IndexedDebugProxy<T, id, Provider> { |
| static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) { |
| auto templ = IndexedDebugProxy<T, id, Provider>::CreateTemplate(isolate); |
| templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| &T::NamedGetter, {}, &T::NamedQuery, {}, &T::NamedEnumerator, {}, |
| &T::NamedDescriptor, {}, v8::PropertyHandlerFlags::kHasNoSideEffect)); |
| return templ; |
| } |
| |
| static void IndexedEnumerator(const PropertyCallbackInfo<v8::Array>& info) { |
| info.GetReturnValue().Set(v8::Array::New(info.GetIsolate())); |
| } |
| |
| static Handle<NameDictionary> GetNameTable(Handle<JSObject> holder, |
| Isolate* isolate) { |
| Handle<Symbol> symbol = isolate->factory()->wasm_debug_proxy_names_symbol(); |
| Handle<Object> table_or_undefined = |
| JSObject::GetProperty(isolate, holder, symbol).ToHandleChecked(); |
| if (!IsUndefined(*table_or_undefined, isolate)) { |
| return Handle<NameDictionary>::cast(table_or_undefined); |
| } |
| auto provider = T::GetProvider(holder, isolate); |
| auto count = T::Count(isolate, provider); |
| auto table = NameDictionary::New(isolate, count); |
| for (uint32_t index = 0; index < count; ++index) { |
| HandleScope scope(isolate); |
| auto key = T::GetName(isolate, provider, index); |
| if (table->FindEntry(isolate, key).is_found()) continue; |
| Handle<Smi> value(Smi::FromInt(index), isolate); |
| table = NameDictionary::Add(isolate, table, key, value, |
| PropertyDetails::Empty()); |
| } |
| Object::SetProperty(isolate, holder, symbol, table).Check(); |
| return table; |
| } |
| |
| template <typename V> |
| static base::Optional<uint32_t> FindName( |
| Local<v8::Name> name, const PropertyCallbackInfo<V>& info) { |
| if (!name->IsString()) return {}; |
| auto name_str = Utils::OpenHandle(*name.As<v8::String>()); |
| if (name_str->length() == 0 || name_str->Get(0) != '$') return {}; |
| auto isolate = T::GetIsolate(info); |
| auto table = GetNameTable(T::GetHolder(info), isolate); |
| auto entry = table->FindEntry(isolate, name_str); |
| if (entry.is_found()) return Smi::ToInt(table->ValueAt(entry)); |
| return {}; |
| } |
| |
| static v8::Intercepted NamedGetter( |
| Local<v8::Name> name, const PropertyCallbackInfo<v8::Value>& info) { |
| if (auto index = FindName(name, info)) { |
| return T::IndexedGetter(*index, info); |
| } |
| return v8::Intercepted::kNo; |
| } |
| |
| static v8::Intercepted NamedQuery( |
| Local<v8::Name> name, const PropertyCallbackInfo<v8::Integer>& info) { |
| if (auto index = FindName(name, info)) { |
| return T::IndexedQuery(*index, info); |
| } |
| return v8::Intercepted::kNo; |
| } |
| |
| static v8::Intercepted NamedDescriptor( |
| Local<v8::Name> name, const PropertyCallbackInfo<v8::Value>& info) { |
| if (auto index = FindName(name, info)) { |
| return T::IndexedDescriptor(*index, info); |
| } |
| return v8::Intercepted::kNo; |
| } |
| |
| static void NamedEnumerator(const PropertyCallbackInfo<v8::Array>& info) { |
| auto isolate = T::GetIsolate(info); |
| auto table = GetNameTable(T::GetHolder(info), isolate); |
| auto names = NameDictionary::IterationIndices(isolate, table); |
| for (int i = 0; i < names->length(); ++i) { |
| InternalIndex entry(Smi::ToInt(names->get(i))); |
| names->set(i, table->NameAt(entry)); |
| } |
| info.GetReturnValue().Set(Utils::ToLocal( |
| isolate->factory()->NewJSArrayWithElements(names, PACKED_ELEMENTS))); |
| } |
| }; |
| |
| // This class implements the "functions" proxy. |
| struct FunctionsProxy : NamedDebugProxy<FunctionsProxy, kFunctionsProxy> { |
| static constexpr char const* kClassName = "Functions"; |
| |
| static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) { |
| return static_cast<uint32_t>(instance->module()->functions.size()); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| Handle<WasmTrustedInstanceData> trusted_data{ |
| instance->trusted_data(isolate), isolate}; |
| Handle<WasmFuncRef> func_ref = WasmTrustedInstanceData::GetOrCreateFuncRef( |
| isolate, trusted_data, index); |
| Handle<WasmInternalFunction> internal_function{func_ref->internal(isolate), |
| isolate}; |
| return WasmInternalFunction::GetOrCreateExternal(internal_function); |
| } |
| |
| static Handle<String> GetName(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| return GetWasmFunctionDebugName(isolate, instance, index); |
| } |
| }; |
| |
| // This class implements the "globals" proxy. |
| struct GlobalsProxy : NamedDebugProxy<GlobalsProxy, kGlobalsProxy> { |
| static constexpr char const* kClassName = "Globals"; |
| |
| static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) { |
| return static_cast<uint32_t>(instance->module()->globals.size()); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| Handle<WasmModuleObject> module(instance->module_object(), isolate); |
| return WasmValueObject::New( |
| isolate, |
| instance->trusted_data(isolate)->GetGlobalValue( |
| isolate, instance->module()->globals[index]), |
| module); |
| } |
| |
| static Handle<String> GetName(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| wasm::NamesProvider* names = |
| instance->module_object()->native_module()->GetNamesProvider(); |
| StringBuilder sb; |
| names->PrintGlobalName(sb, index); |
| return ToInternalString(sb, isolate); |
| } |
| }; |
| |
| // This class implements the "memories" proxy. |
| struct MemoriesProxy : NamedDebugProxy<MemoriesProxy, kMemoriesProxy> { |
| static constexpr char const* kClassName = "Memories"; |
| |
| static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) { |
| return instance->trusted_data(isolate)->memory_objects()->length(); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| return handle(instance->trusted_data(isolate)->memory_object(index), |
| isolate); |
| } |
| |
| static Handle<String> GetName(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| wasm::NamesProvider* names = |
| instance->module_object()->native_module()->GetNamesProvider(); |
| StringBuilder sb; |
| names->PrintMemoryName(sb, index); |
| return ToInternalString(sb, isolate); |
| } |
| }; |
| |
| // This class implements the "tables" proxy. |
| struct TablesProxy : NamedDebugProxy<TablesProxy, kTablesProxy> { |
| static constexpr char const* kClassName = "Tables"; |
| |
| static uint32_t Count(Isolate* isolate, Handle<WasmInstanceObject> instance) { |
| return instance->trusted_data(isolate)->tables()->length(); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| return handle(instance->trusted_data(isolate)->tables()->get(index), |
| isolate); |
| } |
| |
| static Handle<String> GetName(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t index) { |
| wasm::NamesProvider* names = |
| instance->module_object()->native_module()->GetNamesProvider(); |
| StringBuilder sb; |
| names->PrintTableName(sb, index); |
| return ToInternalString(sb, isolate); |
| } |
| }; |
| |
| // This class implements the "locals" proxy. |
| struct LocalsProxy : NamedDebugProxy<LocalsProxy, kLocalsProxy, FixedArray> { |
| static constexpr char const* kClassName = "Locals"; |
| |
| static Handle<JSObject> Create(WasmFrame* frame) { |
| auto isolate = frame->isolate(); |
| auto debug_info = frame->native_module()->GetDebugInfo(); |
| // TODO(bmeurer): Check if pc is inspectable. |
| int count = debug_info->GetNumLocals(frame->pc(), isolate); |
| auto function = debug_info->GetFunctionAtAddress(frame->pc(), isolate); |
| auto values = isolate->factory()->NewFixedArray(count + 2); |
| Handle<WasmModuleObject> module_object( |
| frame->wasm_instance()->module_object(), isolate); |
| for (int i = 0; i < count; ++i) { |
| auto value = WasmValueObject::New( |
| isolate, |
| debug_info->GetLocalValue(i, frame->pc(), frame->fp(), |
| frame->callee_fp(), isolate), |
| module_object); |
| values->set(i, *value); |
| } |
| values->set(count + 0, frame->wasm_instance()->module_object()); |
| values->set(count + 1, Smi::FromInt(function.func_index)); |
| return NamedDebugProxy::Create(isolate, values); |
| } |
| |
| static uint32_t Count(Isolate* isolate, Handle<FixedArray> values) { |
| return values->length() - 2; |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> values, |
| uint32_t index) { |
| return handle(values->get(index), isolate); |
| } |
| |
| static Handle<String> GetName(Isolate* isolate, Handle<FixedArray> values, |
| uint32_t index) { |
| uint32_t count = Count(isolate, values); |
| auto native_module = |
| WasmModuleObject::cast(values->get(count + 0))->native_module(); |
| auto function_index = Smi::ToInt(Smi::cast(values->get(count + 1))); |
| wasm::NamesProvider* names = native_module->GetNamesProvider(); |
| StringBuilder sb; |
| names->PrintLocalName(sb, function_index, index); |
| return ToInternalString(sb, isolate); |
| } |
| }; |
| |
| // This class implements the "stack" proxy (which offers only indexed access). |
| struct StackProxy : IndexedDebugProxy<StackProxy, kStackProxy, FixedArray> { |
| static constexpr char const* kClassName = "Stack"; |
| |
| static Handle<JSObject> Create(WasmFrame* frame) { |
| auto isolate = frame->isolate(); |
| auto debug_info = frame->wasm_instance() |
| ->module_object() |
| ->native_module() |
| ->GetDebugInfo(); |
| int count = debug_info->GetStackDepth(frame->pc(), isolate); |
| auto values = isolate->factory()->NewFixedArray(count); |
| Handle<WasmModuleObject> module_object( |
| frame->wasm_instance()->module_object(), isolate); |
| for (int i = 0; i < count; ++i) { |
| auto value = WasmValueObject::New( |
| isolate, |
| debug_info->GetStackValue(i, frame->pc(), frame->fp(), |
| frame->callee_fp(), isolate), |
| module_object); |
| values->set(i, *value); |
| } |
| return IndexedDebugProxy::Create(isolate, values); |
| } |
| |
| static uint32_t Count(Isolate* isolate, Handle<FixedArray> values) { |
| return values->length(); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> values, |
| uint32_t index) { |
| return handle(values->get(index), isolate); |
| } |
| }; |
| |
| // Creates FixedArray with size |kNumInstanceProxies| as cache on-demand |
| // on the |instance|, stored under the |wasm_debug_proxy_cache_symbol|. |
| // This is used to cache the various instance debug proxies (functions, |
| // globals, tables, and memories) on the WasmInstanceObject. |
| Handle<FixedArray> GetOrCreateInstanceProxyCache( |
| Isolate* isolate, Handle<WasmInstanceObject> instance) { |
| Handle<Object> cache; |
| Handle<Symbol> symbol = isolate->factory()->wasm_debug_proxy_cache_symbol(); |
| if (!Object::GetProperty(isolate, instance, symbol).ToHandle(&cache) || |
| IsUndefined(*cache, isolate)) { |
| cache = isolate->factory()->NewFixedArrayWithHoles(kNumInstanceProxies); |
| Object::SetProperty(isolate, instance, symbol, cache).Check(); |
| } |
| return Handle<FixedArray>::cast(cache); |
| } |
| |
| // Creates an instance of the |Proxy| on-demand and caches that on the |
| // |instance|. |
| template <typename Proxy> |
| Handle<JSObject> GetOrCreateInstanceProxy(Isolate* isolate, |
| Handle<WasmInstanceObject> instance) { |
| static_assert(Proxy::kId < kNumInstanceProxies); |
| Handle<FixedArray> proxies = GetOrCreateInstanceProxyCache(isolate, instance); |
| if (!proxies->is_the_hole(isolate, Proxy::kId)) { |
| return handle(JSObject::cast(proxies->get(Proxy::kId)), isolate); |
| } |
| Handle<JSObject> proxy = Proxy::Create(isolate, instance); |
| proxies->set(Proxy::kId, *proxy); |
| return proxy; |
| } |
| |
| // This class implements the debug proxy for a given Wasm frame. The debug |
| // proxy is used when evaluating JavaScript expressions on a wasm frame via |
| // the inspector |Runtime.evaluateOnCallFrame()| API and enables developers |
| // and extensions to inspect the WebAssembly engine state from JavaScript. |
| // The proxy provides the following interface: |
| // |
| // type WasmValue = { |
| // type: string; |
| // value: number | bigint | object | string; |
| // }; |
| // type WasmFunction = (... args : WasmValue[]) = > WasmValue; |
| // interface WasmInterface { |
| // $globalX: WasmValue; |
| // $varX: WasmValue; |
| // $funcX(a : WasmValue /*, ...*/) : WasmValue; |
| // readonly $memoryX : WebAssembly.Memory; |
| // readonly $tableX : WebAssembly.Table; |
| // |
| // readonly instance : WebAssembly.Instance; |
| // readonly module : WebAssembly.Module; |
| // |
| // readonly memories : {[nameOrIndex:string | number] : WebAssembly.Memory}; |
| // readonly tables : {[nameOrIndex:string | number] : WebAssembly.Table}; |
| // readonly stack : WasmValue[]; |
| // readonly globals : {[nameOrIndex:string | number] : WasmValue}; |
| // readonly locals : {[nameOrIndex:string | number] : WasmValue}; |
| // readonly functions : {[nameOrIndex:string | number] : WasmFunction}; |
| // } |
| // |
| // The wasm index spaces memories, tables, stack, globals, locals, and |
| // functions are JSObjects with interceptors that lazily produce values |
| // either by index or by name (except for stack). |
| // Only the names are reported by APIs such as Object.keys() and |
| // Object.getOwnPropertyNames(), since the indices are not meant to be |
| // used interactively by developers (in Chrome DevTools), but are provided |
| // for WebAssembly language extensions. Also note that these JSObjects |
| // all have null prototypes, to not confuse context lookup and to make |
| // their purpose as dictionaries clear. |
| // |
| // See http://doc/1VZOJrU2VsqOZe3IUzbwQWQQSZwgGySsm5119Ust1gUA and |
| // http://bit.ly/devtools-wasm-entities for more details. |
| class ContextProxyPrototype { |
| public: |
| static Handle<JSObject> Create(Isolate* isolate) { |
| auto object_map = |
| GetOrCreateDebugProxyMap(isolate, kContextProxy, &CreateTemplate); |
| return isolate->factory()->NewJSObjectFromMap( |
| object_map, AllocationType::kYoung, |
| DirectHandle<AllocationSite>::null(), NewJSObjectType::kAPIWrapper); |
| } |
| |
| private: |
| static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) { |
| Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); |
| templ->InstanceTemplate()->SetHandler(v8::NamedPropertyHandlerConfiguration( |
| &NamedGetter, {}, {}, {}, {}, {}, {}, {}, |
| static_cast<v8::PropertyHandlerFlags>( |
| static_cast<unsigned>( |
| v8::PropertyHandlerFlags::kOnlyInterceptStrings) | |
| static_cast<unsigned>( |
| v8::PropertyHandlerFlags::kHasNoSideEffect)))); |
| return templ; |
| } |
| |
| static MaybeHandle<Object> GetNamedProperty(Isolate* isolate, |
| Handle<JSObject> receiver, |
| Handle<String> name) { |
| if (name->length() != 0 && name->Get(0) == '$') { |
| const char* kDelegateNames[] = {"memories", "locals", "tables", |
| "functions", "globals"}; |
| for (auto delegate_name : kDelegateNames) { |
| Handle<Object> delegate; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, delegate, |
| JSObject::GetProperty(isolate, receiver, delegate_name), Object); |
| if (!IsUndefined(*delegate, isolate)) { |
| Handle<Object> value; |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, value, Object::GetProperty(isolate, delegate, name), |
| Object); |
| if (!IsUndefined(*value, isolate)) return value; |
| } |
| } |
| } |
| return {}; |
| } |
| |
| static v8::Intercepted NamedGetter( |
| Local<v8::Name> name, const PropertyCallbackInfo<v8::Value>& info) { |
| auto name_string = Handle<String>::cast(Utils::OpenHandle(*name)); |
| auto isolate = reinterpret_cast<Isolate*>(info.GetIsolate()); |
| auto receiver = Handle<JSObject>::cast(Utils::OpenHandle(*info.This())); |
| Handle<Object> value; |
| if (GetNamedProperty(isolate, receiver, name_string).ToHandle(&value)) { |
| info.GetReturnValue().Set(Utils::ToLocal(value)); |
| return v8::Intercepted::kYes; |
| } |
| return v8::Intercepted::kNo; |
| } |
| }; |
| |
| class ContextProxy { |
| public: |
| static Handle<JSObject> Create(WasmFrame* frame) { |
| Isolate* isolate = frame->isolate(); |
| auto object = isolate->factory()->NewSlowJSObjectWithNullProto(); |
| Handle<WasmInstanceObject> instance(frame->wasm_instance(), isolate); |
| JSObject::AddProperty(isolate, object, "instance", instance, FROZEN); |
| Handle<WasmModuleObject> module_object(instance->module_object(), isolate); |
| JSObject::AddProperty(isolate, object, "module", module_object, FROZEN); |
| auto locals = LocalsProxy::Create(frame); |
| JSObject::AddProperty(isolate, object, "locals", locals, FROZEN); |
| auto stack = StackProxy::Create(frame); |
| JSObject::AddProperty(isolate, object, "stack", stack, FROZEN); |
| auto memories = GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance); |
| JSObject::AddProperty(isolate, object, "memories", memories, FROZEN); |
| auto tables = GetOrCreateInstanceProxy<TablesProxy>(isolate, instance); |
| JSObject::AddProperty(isolate, object, "tables", tables, FROZEN); |
| auto globals = GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance); |
| JSObject::AddProperty(isolate, object, "globals", globals, FROZEN); |
| auto functions = |
| GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance); |
| JSObject::AddProperty(isolate, object, "functions", functions, FROZEN); |
| Handle<JSObject> prototype = ContextProxyPrototype::Create(isolate); |
| JSObject::SetPrototype(isolate, object, prototype, false, kDontThrow) |
| .Check(); |
| return object; |
| } |
| }; |
| |
| class DebugWasmScopeIterator final : public debug::ScopeIterator { |
| public: |
| explicit DebugWasmScopeIterator(WasmFrame* frame) |
| : frame_(frame), |
| type_(debug::ScopeIterator::ScopeTypeWasmExpressionStack) { |
| // Skip local scope and expression stack scope if the frame is not |
| // inspectable. |
| if (!frame->is_inspectable()) { |
| type_ = debug::ScopeIterator::ScopeTypeModule; |
| } |
| } |
| |
| bool Done() override { return type_ == ScopeTypeWith; } |
| |
| void Advance() override { |
| DCHECK(!Done()); |
| switch (type_) { |
| case ScopeTypeWasmExpressionStack: |
| type_ = debug::ScopeIterator::ScopeTypeLocal; |
| break; |
| case ScopeTypeLocal: |
| type_ = debug::ScopeIterator::ScopeTypeModule; |
| break; |
| case ScopeTypeModule: |
| // We use ScopeTypeWith type as marker for done. |
| type_ = debug::ScopeIterator::ScopeTypeWith; |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| ScopeType GetType() override { return type_; } |
| |
| v8::Local<v8::Object> GetObject() override { |
| Isolate* isolate = frame_->isolate(); |
| switch (type_) { |
| case debug::ScopeIterator::ScopeTypeModule: { |
| Handle<WasmInstanceObject> instance{frame_->wasm_instance(), isolate}; |
| Handle<JSObject> object = |
| isolate->factory()->NewSlowJSObjectWithNullProto(); |
| JSObject::AddProperty(isolate, object, "instance", instance, FROZEN); |
| Handle<JSObject> module_object(instance->module_object(), isolate); |
| JSObject::AddProperty(isolate, object, "module", module_object, FROZEN); |
| if (FunctionsProxy::Count(isolate, instance) != 0) { |
| JSObject::AddProperty( |
| isolate, object, "functions", |
| GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance), |
| FROZEN); |
| } |
| if (GlobalsProxy::Count(isolate, instance) != 0) { |
| JSObject::AddProperty( |
| isolate, object, "globals", |
| GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance), |
| FROZEN); |
| } |
| if (MemoriesProxy::Count(isolate, instance) != 0) { |
| JSObject::AddProperty( |
| isolate, object, "memories", |
| GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance), |
| FROZEN); |
| } |
| if (TablesProxy::Count(isolate, instance) != 0) { |
| JSObject::AddProperty( |
| isolate, object, "tables", |
| GetOrCreateInstanceProxy<TablesProxy>(isolate, instance), FROZEN); |
| } |
| return Utils::ToLocal(object); |
| } |
| case debug::ScopeIterator::ScopeTypeLocal: { |
| return Utils::ToLocal(LocalsProxy::Create(frame_)); |
| } |
| case debug::ScopeIterator::ScopeTypeWasmExpressionStack: { |
| auto object = isolate->factory()->NewSlowJSObjectWithNullProto(); |
| auto stack = StackProxy::Create(frame_); |
| JSObject::AddProperty(isolate, object, "stack", stack, FROZEN); |
| return Utils::ToLocal(object); |
| } |
| default: |
| UNREACHABLE(); |
| } |
| } |
| v8::Local<v8::Value> GetFunctionDebugName() override { |
| return Utils::ToLocal(frame_->isolate()->factory()->empty_string()); |
| } |
| |
| int GetScriptId() override { return -1; } |
| |
| bool HasLocationInfo() override { return false; } |
| |
| debug::Location GetStartLocation() override { return {}; } |
| |
| debug::Location GetEndLocation() override { return {}; } |
| |
| bool SetVariableValue(v8::Local<v8::String> name, |
| v8::Local<v8::Value> value) override { |
| return false; |
| } |
| |
| private: |
| WasmFrame* const frame_; |
| ScopeType type_; |
| }; |
| |
| Handle<String> WasmSimd128ToString(Isolate* isolate, wasm::Simd128 s128) { |
| // We use the canonical format as described in: |
| // https://github.com/WebAssembly/simd/blob/master/proposals/simd/TextSIMD.md |
| base::EmbeddedVector<char, 50> buffer; |
| auto i32x4 = s128.to_i32x4(); |
| SNPrintF(buffer, "i32x4 0x%08X 0x%08X 0x%08X 0x%08X", i32x4.val[0], |
| i32x4.val[1], i32x4.val[2], i32x4.val[3]); |
| return isolate->factory()->NewStringFromAsciiChecked(buffer.data()); |
| } |
| |
| Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type, |
| wasm::NativeModule* module) { |
| DCHECK(type.is_object_reference()); |
| StringBuilder name; |
| module->GetNamesProvider()->PrintValueType(name, type); |
| return ToInternalString(name, isolate); |
| } |
| |
| // Returns the type name for the given value. Uses the module object for |
| // providing user-defined type names if available, otherwise falls back |
| // to numbers for indexed types. |
| Handle<String> GetRefTypeName(Isolate* isolate, wasm::ValueType type, |
| Handle<WasmModuleObject> module_object) { |
| if (!module_object.is_null()) { |
| return GetRefTypeName(isolate, type, module_object->native_module()); |
| } |
| std::string name = type.name(); |
| return isolate->factory()->InternalizeString({name.data(), name.length()}); |
| } |
| |
| } // namespace |
| |
| // static |
| Handle<WasmValueObject> WasmValueObject::New(Isolate* isolate, |
| Handle<String> type, |
| Handle<Object> value) { |
| auto maps = GetOrCreateDebugMaps(isolate); |
| if (maps->is_the_hole(isolate, kWasmValueMapIndex)) { |
| Handle<Map> map = isolate->factory()->NewContextfulMapForCurrentContext( |
| WASM_VALUE_OBJECT_TYPE, WasmValueObject::kSize, |
| TERMINAL_FAST_ELEMENTS_KIND, 2); |
| Map::EnsureDescriptorSlack(isolate, map, 2); |
| map->SetConstructor(*isolate->object_function()); |
| { // type |
| Descriptor d = Descriptor::DataField( |
| isolate, |
| isolate->factory()->InternalizeString(base::StaticCharVector("type")), |
| WasmValueObject::kTypeIndex, FROZEN, Representation::Tagged()); |
| map->AppendDescriptor(isolate, &d); |
| } |
| { // value |
| Descriptor d = Descriptor::DataField( |
| isolate, |
| isolate->factory()->InternalizeString( |
| base::StaticCharVector("value")), |
| WasmValueObject::kValueIndex, FROZEN, Representation::Tagged()); |
| map->AppendDescriptor(isolate, &d); |
| } |
| map->set_is_extensible(false); |
| maps->set(kWasmValueMapIndex, *map); |
| } |
| Handle<Map> value_map = |
| handle(Map::cast(maps->get(kWasmValueMapIndex)), isolate); |
| Handle<WasmValueObject> object = Handle<WasmValueObject>::cast( |
| isolate->factory()->NewJSObjectFromMap(value_map)); |
| object->set_type(*type); |
| object->set_value(*value); |
| return object; |
| } |
| |
| // This class implements a proxy for a single inspectable Wasm struct. |
| struct StructProxy : NamedDebugProxy<StructProxy, kStructProxy, FixedArray> { |
| static constexpr char const* kClassName = "Struct"; |
| |
| static const int kObjectIndex = 0; |
| static const int kModuleIndex = 1; |
| static const int kTypeIndexIndex = 2; |
| static const int kLength = 3; |
| |
| static Handle<JSObject> Create(Isolate* isolate, Handle<WasmStruct> value, |
| Handle<WasmModuleObject> module) { |
| Handle<FixedArray> data = isolate->factory()->NewFixedArray(kLength); |
| data->set(kObjectIndex, *value); |
| data->set(kModuleIndex, *module); |
| int struct_type_index = value->map()->wasm_type_info()->type_index(); |
| data->set(kTypeIndexIndex, Smi::FromInt(struct_type_index)); |
| return NamedDebugProxy::Create(isolate, data); |
| } |
| |
| static uint32_t Count(Isolate* isolate, Handle<FixedArray> data) { |
| return WasmStruct::cast(data->get(kObjectIndex))->type()->field_count(); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> data, |
| uint32_t index) { |
| Handle<WasmStruct> obj(WasmStruct::cast(data->get(kObjectIndex)), isolate); |
| Handle<WasmModuleObject> module( |
| WasmModuleObject::cast(data->get(kModuleIndex)), isolate); |
| return WasmValueObject::New(isolate, obj->GetFieldValue(index), module); |
| } |
| |
| static Handle<String> GetName(Isolate* isolate, Handle<FixedArray> data, |
| uint32_t index) { |
| wasm::NativeModule* native_module = |
| WasmModuleObject::cast(data->get(kModuleIndex))->native_module(); |
| int struct_type_index = Smi::ToInt(Smi::cast(data->get(kTypeIndexIndex))); |
| wasm::NamesProvider* names = native_module->GetNamesProvider(); |
| StringBuilder sb; |
| names->PrintFieldName(sb, struct_type_index, index); |
| return ToInternalString(sb, isolate); |
| } |
| }; |
| |
| // This class implements a proxy for a single inspectable Wasm array. |
| struct ArrayProxy : IndexedDebugProxy<ArrayProxy, kArrayProxy, FixedArray> { |
| static constexpr char const* kClassName = "Array"; |
| |
| static const int kObjectIndex = 0; |
| static const int kModuleIndex = 1; |
| static const int kLength = 2; |
| |
| static Handle<JSObject> Create(Isolate* isolate, Handle<WasmArray> value, |
| Handle<WasmModuleObject> module) { |
| Handle<FixedArray> data = isolate->factory()->NewFixedArray(kLength); |
| data->set(kObjectIndex, *value); |
| data->set(kModuleIndex, *module); |
| Handle<JSObject> proxy = IndexedDebugProxy::Create( |
| isolate, data, false /* leave map extensible */); |
| uint32_t length = value->length(); |
| Handle<Object> length_obj = isolate->factory()->NewNumberFromUint(length); |
| Object::SetProperty(isolate, proxy, isolate->factory()->length_string(), |
| length_obj, StoreOrigin::kNamed, |
| Just(ShouldThrow::kThrowOnError)) |
| .Check(); |
| return proxy; |
| } |
| |
| static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Isolate* isolate) { |
| Local<v8::FunctionTemplate> templ = |
| IndexedDebugProxy::CreateTemplate(isolate); |
| templ->InstanceTemplate()->Set(isolate, "length", |
| v8::Number::New(isolate, 0)); |
| return templ; |
| } |
| |
| static uint32_t Count(Isolate* isolate, Handle<FixedArray> data) { |
| return WasmArray::cast(data->get(kObjectIndex))->length(); |
| } |
| |
| static Handle<Object> Get(Isolate* isolate, Handle<FixedArray> data, |
| uint32_t index) { |
| Handle<WasmArray> array(WasmArray::cast(data->get(kObjectIndex)), isolate); |
| Handle<WasmModuleObject> module( |
| WasmModuleObject::cast(data->get(kModuleIndex)), isolate); |
| return WasmValueObject::New(isolate, array->GetElement(index), module); |
| } |
| }; |
| |
| // static |
| Handle<WasmValueObject> WasmValueObject::New( |
| Isolate* isolate, const wasm::WasmValue& value, |
| Handle<WasmModuleObject> module_object) { |
| Handle<String> t; |
| Handle<Object> v; |
| switch (value.type().kind()) { |
| case wasm::kI8: { |
| // This can't be reached for most "top-level" things, only via nested |
| // calls for struct/array fields. |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("i8")); |
| v = isolate->factory()->NewNumber(value.to_i8_unchecked()); |
| break; |
| } |
| case wasm::kI16: { |
| // This can't be reached for most "top-level" things, only via nested |
| // calls for struct/array fields. |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("i16")); |
| v = isolate->factory()->NewNumber(value.to_i16_unchecked()); |
| break; |
| } |
| case wasm::kI32: { |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("i32")); |
| v = isolate->factory()->NewNumberFromInt(value.to_i32_unchecked()); |
| break; |
| } |
| case wasm::kI64: { |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("i64")); |
| v = BigInt::FromInt64(isolate, value.to_i64_unchecked()); |
| break; |
| } |
| case wasm::kF32: { |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("f32")); |
| v = isolate->factory()->NewNumber(value.to_f32_unchecked()); |
| break; |
| } |
| case wasm::kF64: { |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("f64")); |
| v = isolate->factory()->NewNumber(value.to_f64_unchecked()); |
| break; |
| } |
| case wasm::kS128: { |
| t = isolate->factory()->InternalizeString(base::StaticCharVector("v128")); |
| v = WasmSimd128ToString(isolate, value.to_s128_unchecked()); |
| break; |
| } |
| case wasm::kRefNull: |
| case wasm::kRef: { |
| Handle<Object> ref = value.to_ref(); |
| if (value.type().heap_type().representation() == wasm::HeapType::kExn) { |
| t = isolate->factory()->InternalizeString( |
| base::StaticCharVector("exnref")); |
| v = ref; |
| } else if (IsWasmStruct(*ref)) { |
| Tagged<WasmTypeInfo> type_info = |
| HeapObject::cast(*ref)->map()->wasm_type_info(); |
| wasm::ValueType type = wasm::ValueType::FromIndex( |
| wasm::ValueKind::kRef, type_info->type_index()); |
| // The cast is safe; structs always have the instance defined. |
| Handle<WasmModuleObject> module( |
| WasmInstanceObject::cast(type_info->instance())->module_object(), |
| isolate); |
| t = GetRefTypeName(isolate, type, module->native_module()); |
| v = StructProxy::Create(isolate, Handle<WasmStruct>::cast(ref), module); |
| } else if (IsWasmArray(*ref)) { |
| Tagged<WasmTypeInfo> type_info = |
| HeapObject::cast(*ref)->map()->wasm_type_info(); |
| wasm::ValueType type = wasm::ValueType::FromIndex( |
| wasm::ValueKind::kRef, type_info->type_index()); |
| // The cast is safe; arrays always have the instance defined. |
| Handle<WasmModuleObject> module( |
| WasmInstanceObject::cast(type_info->instance())->module_object(), |
| isolate); |
| t = GetRefTypeName(isolate, type, module->native_module()); |
| v = ArrayProxy::Create(isolate, Handle<WasmArray>::cast(ref), module); |
| } else if (IsWasmFuncRef(*ref)) { |
| Handle<WasmInternalFunction> internal_fct{ |
| WasmFuncRef::cast(*ref)->internal(isolate), isolate}; |
| v = WasmInternalFunction::GetOrCreateExternal(internal_fct); |
| // If the module is not provided by the caller, retrieve it from the |
| // instance object. If the function was created in JavaScript using |
| // `new WebAssembly.Function(...)`, a module for name resolution is not |
| // available. |
| if (module_object.is_null() && |
| IsWasmTrustedInstanceData(internal_fct->ref())) { |
| module_object = |
| handle(WasmTrustedInstanceData::cast(internal_fct->ref()) |
| ->module_object(), |
| isolate); |
| } |
| t = GetRefTypeName(isolate, value.type(), module_object); |
| } else if (IsWasmNull(*ref)) { |
| // TODO(manoskouk): Is this value correct? |
| v = isolate->factory()->null_value(); |
| t = GetRefTypeName(isolate, value.type(), module_object); |
| } else if (IsJSFunction(*ref) || IsSmi(*ref) || IsNull(*ref) || |
| IsString(*ref) || |
| value.type().is_reference_to(wasm::HeapType::kExtern) || |
| value.type().is_reference_to(wasm::HeapType::kAny)) { |
| t = GetRefTypeName(isolate, value.type(), module_object); |
| v = ref; |
| } else { |
| // Fail gracefully. |
| base::EmbeddedVector<char, 64> error; |
| int len = SNPrintF(error, "unimplemented object type: %d", |
| HeapObject::cast(*ref)->map()->instance_type()); |
| t = GetRefTypeName(isolate, value.type(), module_object); |
| v = isolate->factory()->InternalizeString(error.SubVector(0, len)); |
| } |
| break; |
| } |
| case wasm::kRtt: |
| case wasm::kVoid: |
| case wasm::kBottom: |
| UNREACHABLE(); |
| } |
| return New(isolate, t, v); |
| } |
| |
| Handle<JSObject> GetWasmDebugProxy(WasmFrame* frame) { |
| return ContextProxy::Create(frame); |
| } |
| |
| std::unique_ptr<debug::ScopeIterator> GetWasmScopeIterator(WasmFrame* frame) { |
| return std::make_unique<DebugWasmScopeIterator>(frame); |
| } |
| |
| Handle<String> GetWasmFunctionDebugName(Isolate* isolate, |
| Handle<WasmInstanceObject> instance, |
| uint32_t func_index) { |
| wasm::NativeModule* native_module = |
| instance->module_object()->native_module(); |
| wasm::NamesProvider* names = native_module->GetNamesProvider(); |
| StringBuilder sb; |
| wasm::NamesProvider::FunctionNamesBehavior behavior = |
| is_asmjs_module(native_module->module()) |
| ? wasm::NamesProvider::kWasmInternal |
| : wasm::NamesProvider::kDevTools; |
| names->PrintFunctionName(sb, func_index, behavior); |
| return ToInternalString(sb, isolate); |
| } |
| |
| Handle<ArrayList> AddWasmInstanceObjectInternalProperties( |
| Isolate* isolate, Handle<ArrayList> result, |
| Handle<WasmInstanceObject> instance) { |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromAsciiChecked("[[Module]]"), |
| handle(instance->module_object(), isolate)); |
| |
| if (FunctionsProxy::Count(isolate, instance) != 0) { |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromAsciiChecked("[[Functions]]"), |
| GetOrCreateInstanceProxy<FunctionsProxy>(isolate, instance)); |
| } |
| |
| if (GlobalsProxy::Count(isolate, instance) != 0) { |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromAsciiChecked("[[Globals]]"), |
| GetOrCreateInstanceProxy<GlobalsProxy>(isolate, instance)); |
| } |
| |
| if (MemoriesProxy::Count(isolate, instance) != 0) { |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromAsciiChecked("[[Memories]]"), |
| GetOrCreateInstanceProxy<MemoriesProxy>(isolate, instance)); |
| } |
| |
| if (TablesProxy::Count(isolate, instance) != 0) { |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromAsciiChecked("[[Tables]]"), |
| GetOrCreateInstanceProxy<TablesProxy>(isolate, instance)); |
| } |
| |
| return result; |
| } |
| |
| Handle<ArrayList> AddWasmModuleObjectInternalProperties( |
| Isolate* isolate, Handle<ArrayList> result, |
| Handle<WasmModuleObject> module_object) { |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromStaticChars("[[Exports]]"), |
| wasm::GetExports(isolate, module_object)); |
| result = ArrayList::Add( |
| isolate, result, |
| isolate->factory()->NewStringFromStaticChars("[[Imports]]"), |
| wasm::GetImports(isolate, module_object)); |
| return result; |
| } |
| |
| Handle<ArrayList> AddWasmTableObjectInternalProperties( |
| Isolate* isolate, Handle<ArrayList> result, Handle<WasmTableObject> table) { |
| int length = table->current_length(); |
| Handle<FixedArray> entries = isolate->factory()->NewFixedArray(length); |
| for (int i = 0; i < length; ++i) { |
| Handle<Object> entry = WasmTableObject::Get(isolate, table, i); |
| wasm::WasmValue wasm_value(entry, table->type()); |
| Handle<WasmModuleObject> module; |
| if (IsWasmInstanceObject(table->instance())) { |
| module = Handle<WasmModuleObject>( |
| WasmInstanceObject::cast(table->instance())->module_object(), |
| isolate); |
| } |
| Handle<Object> debug_value = |
| WasmValueObject::New(isolate, wasm_value, module); |
| entries->set(i, *debug_value); |
| } |
| Handle<JSArray> final_entries = isolate->factory()->NewJSArrayWithElements( |
| entries, i::PACKED_ELEMENTS, length); |
| JSObject::SetPrototype(isolate, final_entries, |
| isolate->factory()->null_value(), false, kDontThrow) |
| .Check(); |
| Handle<String> entries_string = |
| isolate->factory()->NewStringFromStaticChars("[[Entries]]"); |
| result = ArrayList::Add(isolate, result, entries_string, final_entries); |
| return result; |
| } |
| |
| } // namespace internal |
| } // namespace v8 |