| //===-- GoLanguageRuntime.cpp --------------------------------------*- C++ |
| //-*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "GoLanguageRuntime.h" |
| |
| #include "lldb/Breakpoint/BreakpointLocation.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/PluginManager.h" |
| #include "lldb/Core/Scalar.h" |
| #include "lldb/Core/ValueObject.h" |
| #include "lldb/Core/ValueObjectMemory.h" |
| #include "lldb/Symbol/GoASTContext.h" |
| #include "lldb/Symbol/Symbol.h" |
| #include "lldb/Symbol/SymbolFile.h" |
| #include "lldb/Symbol/TypeList.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Target/RegisterContext.h" |
| #include "lldb/Target/SectionLoadList.h" |
| #include "lldb/Target/StopInfo.h" |
| #include "lldb/Target/Target.h" |
| #include "lldb/Target/Thread.h" |
| #include "lldb/Utility/ConstString.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/Status.h" |
| #include "llvm/ADT/Twine.h" |
| |
| #include <vector> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| namespace { |
| ValueObjectSP GetChild(ValueObject &obj, const char *name, |
| bool dereference = true) { |
| ConstString name_const_str(name); |
| ValueObjectSP result = obj.GetChildMemberWithName(name_const_str, true); |
| if (dereference && result && result->IsPointerType()) { |
| Status err; |
| result = result->Dereference(err); |
| if (err.Fail()) |
| result.reset(); |
| } |
| return result; |
| } |
| |
| ConstString ReadString(ValueObject &str, Process *process) { |
| ConstString result; |
| ValueObjectSP data = GetChild(str, "str", false); |
| ValueObjectSP len = GetChild(str, "len"); |
| if (len && data) { |
| Status err; |
| lldb::addr_t addr = data->GetPointerValue(); |
| if (addr == LLDB_INVALID_ADDRESS) |
| return result; |
| uint64_t byte_size = len->GetValueAsUnsigned(0); |
| char *buf = new char[byte_size + 1]; |
| buf[byte_size] = 0; |
| size_t bytes_read = process->ReadMemory(addr, buf, byte_size, err); |
| if (!(err.Fail() || bytes_read != byte_size)) |
| result = ConstString(buf, bytes_read); |
| delete[] buf; |
| } |
| return result; |
| } |
| |
| ConstString ReadTypeName(ValueObjectSP type, Process *process) { |
| if (ValueObjectSP uncommon = GetChild(*type, "x")) { |
| ValueObjectSP name = GetChild(*uncommon, "name"); |
| ValueObjectSP package = GetChild(*uncommon, "pkgpath"); |
| if (name && name->GetPointerValue() != 0 && package && |
| package->GetPointerValue() != 0) { |
| ConstString package_const_str = ReadString(*package, process); |
| ConstString name_const_str = ReadString(*name, process); |
| if (package_const_str.GetLength() == 0) |
| return name_const_str; |
| return ConstString((package_const_str.GetStringRef() + "." + |
| name_const_str.GetStringRef()) |
| .str()); |
| } |
| } |
| ValueObjectSP name = GetChild(*type, "_string"); |
| if (name) |
| return ReadString(*name, process); |
| return ConstString(""); |
| } |
| |
| CompilerType LookupRuntimeType(ValueObjectSP type, ExecutionContext *exe_ctx, |
| bool *is_direct) { |
| uint8_t kind = GetChild(*type, "kind")->GetValueAsUnsigned(0); |
| *is_direct = GoASTContext::IsDirectIface(kind); |
| if (GoASTContext::IsPointerKind(kind)) { |
| CompilerType type_ptr = type->GetCompilerType().GetPointerType(); |
| Status err; |
| ValueObjectSP elem = |
| type->CreateValueObjectFromAddress("elem", type->GetAddressOf() + |
| type->GetByteSize(), |
| *exe_ctx, type_ptr) |
| ->Dereference(err); |
| if (err.Fail()) |
| return CompilerType(); |
| bool tmp_direct; |
| return LookupRuntimeType(elem, exe_ctx, &tmp_direct).GetPointerType(); |
| } |
| Target *target = exe_ctx->GetTargetPtr(); |
| Process *process = exe_ctx->GetProcessPtr(); |
| |
| ConstString const_typename = ReadTypeName(type, process); |
| if (const_typename.GetLength() == 0) |
| return CompilerType(); |
| |
| SymbolContext sc; |
| TypeList type_list; |
| llvm::DenseSet<SymbolFile *> searched_symbol_files; |
| uint32_t num_matches = target->GetImages().FindTypes( |
| sc, const_typename, false, 2, searched_symbol_files, type_list); |
| if (num_matches > 0) { |
| return type_list.GetTypeAtIndex(0)->GetFullCompilerType(); |
| } |
| return CompilerType(); |
| } |
| } |
| |
| bool GoLanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) { |
| return GoASTContext::IsGoInterface(in_value.GetCompilerType()); |
| } |
| |
| bool GoLanguageRuntime::GetDynamicTypeAndAddress( |
| ValueObject &in_value, lldb::DynamicValueType use_dynamic, |
| TypeAndOrName &class_type_or_name, Address &dynamic_address, |
| Value::ValueType &value_type) { |
| value_type = Value::eValueTypeScalar; |
| class_type_or_name.Clear(); |
| if (CouldHaveDynamicValue(in_value)) { |
| Status err; |
| ValueObjectSP iface = in_value.GetStaticValue(); |
| ValueObjectSP data_sp = GetChild(*iface, "data", false); |
| if (!data_sp) |
| return false; |
| |
| if (ValueObjectSP tab = GetChild(*iface, "tab")) |
| iface = tab; |
| ValueObjectSP type = GetChild(*iface, "_type"); |
| if (!type) { |
| return false; |
| } |
| |
| bool direct; |
| ExecutionContext exe_ctx(in_value.GetExecutionContextRef()); |
| CompilerType final_type = LookupRuntimeType(type, &exe_ctx, &direct); |
| if (!final_type) |
| return false; |
| if (direct) { |
| class_type_or_name.SetCompilerType(final_type); |
| } else { |
| // TODO: implement reference types or fix caller to support dynamic types |
| // that aren't pointers |
| // so we don't have to introduce this extra pointer. |
| class_type_or_name.SetCompilerType(final_type.GetPointerType()); |
| } |
| |
| dynamic_address.SetLoadAddress(data_sp->GetPointerValue(), |
| exe_ctx.GetTargetPtr()); |
| |
| return true; |
| } |
| return false; |
| } |
| |
| TypeAndOrName |
| GoLanguageRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name, |
| ValueObject &static_value) { |
| return type_and_or_name; |
| } |
| |
| //------------------------------------------------------------------ |
| // Static Functions |
| //------------------------------------------------------------------ |
| LanguageRuntime * |
| GoLanguageRuntime::CreateInstance(Process *process, |
| lldb::LanguageType language) { |
| if (language == eLanguageTypeGo) |
| return new GoLanguageRuntime(process); |
| else |
| return NULL; |
| } |
| |
| void GoLanguageRuntime::Initialize() { |
| PluginManager::RegisterPlugin(GetPluginNameStatic(), "Go Language Runtime", |
| CreateInstance); |
| } |
| |
| void GoLanguageRuntime::Terminate() { |
| PluginManager::UnregisterPlugin(CreateInstance); |
| } |
| |
| lldb_private::ConstString GoLanguageRuntime::GetPluginNameStatic() { |
| static ConstString g_name("golang"); |
| return g_name; |
| } |
| |
| //------------------------------------------------------------------ |
| // PluginInterface protocol |
| //------------------------------------------------------------------ |
| lldb_private::ConstString GoLanguageRuntime::GetPluginName() { |
| return GetPluginNameStatic(); |
| } |
| |
| uint32_t GoLanguageRuntime::GetPluginVersion() { return 1; } |