//===-- GoUserExpression.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include #if HAVE_SYS_TYPES_H #include #endif // C++ Includes #include #include #include #include // Other libraries and framework includes #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" // Project includes #include "GoUserExpression.h" #include "lldb/Core/Module.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObjectConstResult.h" #include "lldb/Core/ValueObjectRegister.h" #include "lldb/Expression/DiagnosticManager.h" #include "lldb/Expression/ExpressionVariable.h" #include "lldb/Symbol/GoASTContext.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanCallUserExpression.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/DataEncoder.h" #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/lldb-private.h" #include "Plugins/ExpressionParser/Go/GoAST.h" #include "Plugins/ExpressionParser/Go/GoParser.h" using namespace lldb_private; using namespace lldb; class GoUserExpression::GoInterpreter { public: GoInterpreter(ExecutionContext &exe_ctx, const char *expr) : m_exe_ctx(exe_ctx), m_frame(exe_ctx.GetFrameSP()), m_parser(expr) { if (m_frame) { const SymbolContext &ctx = m_frame->GetSymbolContext(eSymbolContextFunction); ConstString fname = ctx.GetFunctionName(); if (fname.GetLength() > 0) { size_t dot = fname.GetStringRef().find('.'); if (dot != llvm::StringRef::npos) m_package = llvm::StringRef(fname.AsCString(), dot); } } } void set_use_dynamic(DynamicValueType use_dynamic) { m_use_dynamic = use_dynamic; } bool Parse(); lldb::ValueObjectSP Evaluate(ExecutionContext &exe_ctx); lldb::ValueObjectSP EvaluateStatement(const GoASTStmt *s); lldb::ValueObjectSP EvaluateExpr(const GoASTExpr *e); ValueObjectSP VisitBadExpr(const GoASTBadExpr *e) { m_parser.GetError(m_error); return nullptr; } ValueObjectSP VisitParenExpr(const GoASTParenExpr *e); ValueObjectSP VisitIdent(const GoASTIdent *e); ValueObjectSP VisitStarExpr(const GoASTStarExpr *e); ValueObjectSP VisitSelectorExpr(const GoASTSelectorExpr *e); ValueObjectSP VisitBasicLit(const GoASTBasicLit *e); ValueObjectSP VisitIndexExpr(const GoASTIndexExpr *e); ValueObjectSP VisitUnaryExpr(const GoASTUnaryExpr *e); ValueObjectSP VisitCallExpr(const GoASTCallExpr *e); ValueObjectSP VisitTypeAssertExpr(const GoASTTypeAssertExpr *e) { return NotImplemented(e); } ValueObjectSP VisitBinaryExpr(const GoASTBinaryExpr *e) { return NotImplemented(e); } ValueObjectSP VisitArrayType(const GoASTArrayType *e) { return NotImplemented(e); } ValueObjectSP VisitChanType(const GoASTChanType *e) { return NotImplemented(e); } ValueObjectSP VisitCompositeLit(const GoASTCompositeLit *e) { return NotImplemented(e); } ValueObjectSP VisitEllipsis(const GoASTEllipsis *e) { return NotImplemented(e); } ValueObjectSP VisitFuncType(const GoASTFuncType *e) { return NotImplemented(e); } ValueObjectSP VisitFuncLit(const GoASTFuncLit *e) { return NotImplemented(e); } ValueObjectSP VisitInterfaceType(const GoASTInterfaceType *e) { return NotImplemented(e); } ValueObjectSP VisitKeyValueExpr(const GoASTKeyValueExpr *e) { return NotImplemented(e); } ValueObjectSP VisitMapType(const GoASTMapType *e) { return NotImplemented(e); } ValueObjectSP VisitSliceExpr(const GoASTSliceExpr *e) { return NotImplemented(e); } ValueObjectSP VisitStructType(const GoASTStructType *e) { return NotImplemented(e); } CompilerType EvaluateType(const GoASTExpr *e); Status &error() { return m_error; } private: std::nullptr_t NotImplemented(const GoASTExpr *e) { m_error.SetErrorStringWithFormat("%s node not implemented", e->GetKindName()); return nullptr; } ExecutionContext m_exe_ctx; lldb::StackFrameSP m_frame; GoParser m_parser; DynamicValueType m_use_dynamic; Status m_error; llvm::StringRef m_package; std::vector> m_statements; }; VariableSP FindGlobalVariable(TargetSP target, llvm::Twine name) { ConstString fullname(name.str()); VariableList variable_list; const bool append = true; if (!target) { return nullptr; } const uint32_t match_count = target->GetImages().FindGlobalVariables( fullname, append, 1, variable_list); if (match_count == 1) { return variable_list.GetVariableAtIndex(0); } return nullptr; } CompilerType LookupType(TargetSP target, ConstString name) { if (!target) return CompilerType(); SymbolContext sc; TypeList type_list; llvm::DenseSet searched_symbol_files; uint32_t num_matches = target->GetImages().FindTypes( sc, name, false, 2, searched_symbol_files, type_list); if (num_matches > 0) { return type_list.GetTypeAtIndex(0)->GetFullCompilerType(); } return CompilerType(); } GoUserExpression::GoUserExpression(ExecutionContextScope &exe_scope, llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, ResultType desired_type, const EvaluateExpressionOptions &options) : UserExpression(exe_scope, expr, prefix, language, desired_type, options) { } bool GoUserExpression::Parse(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, lldb_private::ExecutionPolicy execution_policy, bool keep_result_in_memory, bool generate_debug_info) { InstallContext(exe_ctx); m_interpreter.reset(new GoInterpreter(exe_ctx, GetUserText())); if (m_interpreter->Parse()) return true; const char *error_cstr = m_interpreter->error().AsCString(); if (error_cstr && error_cstr[0]) diagnostic_manager.PutString(eDiagnosticSeverityError, error_cstr); else diagnostic_manager.Printf(eDiagnosticSeverityError, "expression can't be interpreted or run"); return false; } lldb::ExpressionResults GoUserExpression::DoExecute(DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, const EvaluateExpressionOptions &options, lldb::UserExpressionSP &shared_ptr_to_me, lldb::ExpressionVariableSP &result) { Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); lldb_private::ExecutionPolicy execution_policy = options.GetExecutionPolicy(); lldb::ExpressionResults execution_results = lldb::eExpressionSetupError; Process *process = exe_ctx.GetProcessPtr(); Target *target = exe_ctx.GetTargetPtr(); if (target == nullptr || process == nullptr || process->GetState() != lldb::eStateStopped) { if (execution_policy == eExecutionPolicyAlways) { if (log) log->Printf("== [GoUserExpression::Evaluate] Expression may not run, " "but is not constant =="); diagnostic_manager.PutString(eDiagnosticSeverityError, "expression needed to run but couldn't"); return execution_results; } } m_interpreter->set_use_dynamic(options.GetUseDynamic()); ValueObjectSP result_val_sp = m_interpreter->Evaluate(exe_ctx); Status err = m_interpreter->error(); m_interpreter.reset(); if (!result_val_sp) { const char *error_cstr = err.AsCString(); if (error_cstr && error_cstr[0]) diagnostic_manager.PutString(eDiagnosticSeverityError, error_cstr); else diagnostic_manager.PutString(eDiagnosticSeverityError, "expression can't be interpreted or run"); return lldb::eExpressionDiscarded; } result.reset(new ExpressionVariable(ExpressionVariable::eKindGo)); result->m_live_sp = result->m_frozen_sp = result_val_sp; result->m_flags |= ExpressionVariable::EVIsProgramReference; PersistentExpressionState *pv = target->GetPersistentExpressionStateForLanguage(eLanguageTypeGo); if (pv != nullptr) { result->SetName(pv->GetNextPersistentVariableName()); pv->AddVariable(result); } return lldb::eExpressionCompleted; } bool GoUserExpression::GoInterpreter::Parse() { for (std::unique_ptr stmt(m_parser.Statement()); stmt; stmt.reset(m_parser.Statement())) { if (m_parser.Failed()) break; m_statements.emplace_back(std::move(stmt)); } if (m_parser.Failed() || !m_parser.AtEOF()) m_parser.GetError(m_error); return m_error.Success(); } ValueObjectSP GoUserExpression::GoInterpreter::Evaluate(ExecutionContext &exe_ctx) { m_exe_ctx = exe_ctx; ValueObjectSP result; for (const std::unique_ptr &stmt : m_statements) { result = EvaluateStatement(stmt.get()); if (m_error.Fail()) return nullptr; } return result; } ValueObjectSP GoUserExpression::GoInterpreter::EvaluateStatement( const lldb_private::GoASTStmt *stmt) { ValueObjectSP result; switch (stmt->GetKind()) { case GoASTNode::eBlockStmt: { const GoASTBlockStmt *block = llvm::cast(stmt); for (size_t i = 0; i < block->NumList(); ++i) result = EvaluateStatement(block->GetList(i)); break; } case GoASTNode::eBadStmt: m_parser.GetError(m_error); break; case GoASTNode::eExprStmt: { const GoASTExprStmt *expr = llvm::cast(stmt); return EvaluateExpr(expr->GetX()); } default: m_error.SetErrorStringWithFormat("%s node not supported", stmt->GetKindName()); } return result; } ValueObjectSP GoUserExpression::GoInterpreter::EvaluateExpr( const lldb_private::GoASTExpr *e) { if (e) return e->Visit(this); return ValueObjectSP(); } ValueObjectSP GoUserExpression::GoInterpreter::VisitParenExpr( const lldb_private::GoASTParenExpr *e) { return EvaluateExpr(e->GetX()); } ValueObjectSP GoUserExpression::GoInterpreter::VisitIdent(const GoASTIdent *e) { ValueObjectSP val; if (m_frame) { VariableSP var_sp; std::string varname = e->GetName().m_value.str(); if (varname.size() > 1 && varname[0] == '$') { RegisterContextSP reg_ctx_sp = m_frame->GetRegisterContext(); const RegisterInfo *reg = reg_ctx_sp->GetRegisterInfoByName(varname.c_str() + 1); if (reg) { std::string type; switch (reg->encoding) { case lldb::eEncodingSint: type.append("int"); break; case lldb::eEncodingUint: type.append("uint"); break; case lldb::eEncodingIEEE754: type.append("float"); break; default: m_error.SetErrorString("Invalid register encoding"); return nullptr; } switch (reg->byte_size) { case 8: type.append("64"); break; case 4: type.append("32"); break; case 2: type.append("16"); break; case 1: type.append("8"); break; default: m_error.SetErrorString("Invalid register size"); return nullptr; } ValueObjectSP regVal = ValueObjectRegister::Create( m_frame.get(), reg_ctx_sp, reg->kinds[eRegisterKindLLDB]); CompilerType goType = LookupType(m_frame->CalculateTarget(), ConstString(type)); if (regVal) { regVal = regVal->Cast(goType); return regVal; } } m_error.SetErrorString("Invalid register name"); return nullptr; } VariableListSP var_list_sp(m_frame->GetInScopeVariableList(false)); if (var_list_sp) { var_sp = var_list_sp->FindVariable(ConstString(varname)); if (var_sp) val = m_frame->GetValueObjectForFrameVariable(var_sp, m_use_dynamic); else { // When a variable is on the heap instead of the stack, go records a // variable // '&x' instead of 'x'. var_sp = var_list_sp->FindVariable(ConstString("&" + varname)); if (var_sp) { val = m_frame->GetValueObjectForFrameVariable(var_sp, m_use_dynamic); if (val) val = val->Dereference(m_error); if (m_error.Fail()) return nullptr; } } } if (!val) { m_error.Clear(); TargetSP target = m_frame->CalculateTarget(); if (!target) { m_error.SetErrorString("No target"); return nullptr; } var_sp = FindGlobalVariable(target, m_package + "." + e->GetName().m_value); if (var_sp) return m_frame->TrackGlobalVariable(var_sp, m_use_dynamic); } } if (!val) m_error.SetErrorStringWithFormat("Unknown variable %s", e->GetName().m_value.str().c_str()); return val; } ValueObjectSP GoUserExpression::GoInterpreter::VisitStarExpr(const GoASTStarExpr *e) { ValueObjectSP target = EvaluateExpr(e->GetX()); if (!target) return nullptr; return target->Dereference(m_error); } ValueObjectSP GoUserExpression::GoInterpreter::VisitSelectorExpr( const lldb_private::GoASTSelectorExpr *e) { ValueObjectSP target = EvaluateExpr(e->GetX()); if (target) { if (target->GetCompilerType().IsPointerType()) { target = target->Dereference(m_error); if (m_error.Fail()) return nullptr; } ConstString field(e->GetSel()->GetName().m_value); ValueObjectSP result = target->GetChildMemberWithName(field, true); if (!result) m_error.SetErrorStringWithFormat("Unknown child %s", field.AsCString()); return result; } if (const GoASTIdent *package = llvm::dyn_cast(e->GetX())) { if (VariableSP global = FindGlobalVariable( m_exe_ctx.GetTargetSP(), package->GetName().m_value + "." + e->GetSel()->GetName().m_value)) { if (m_frame) { m_error.Clear(); return m_frame->GetValueObjectForFrameVariable(global, m_use_dynamic); } } } if (const GoASTBasicLit *packageLit = llvm::dyn_cast(e->GetX())) { if (packageLit->GetValue().m_type == GoLexer::LIT_STRING) { std::string value = packageLit->GetValue().m_value.str(); value = value.substr(1, value.size() - 2); if (VariableSP global = FindGlobalVariable( m_exe_ctx.GetTargetSP(), value + "." + e->GetSel()->GetName().m_value)) { if (m_frame) { m_error.Clear(); return m_frame->TrackGlobalVariable(global, m_use_dynamic); } } } } // EvaluateExpr should have already set m_error. return target; } ValueObjectSP GoUserExpression::GoInterpreter::VisitBasicLit( const lldb_private::GoASTBasicLit *e) { std::string value = e->GetValue().m_value.str(); if (e->GetValue().m_type != GoLexer::LIT_INTEGER) { m_error.SetErrorStringWithFormat("Unsupported literal %s", value.c_str()); return nullptr; } errno = 0; int64_t intvalue = strtol(value.c_str(), nullptr, 0); if (errno != 0) { m_error.SetErrorToErrno(); return nullptr; } DataBufferSP buf(new DataBufferHeap(sizeof(intvalue), 0)); TargetSP target = m_exe_ctx.GetTargetSP(); if (!target) { m_error.SetErrorString("No target"); return nullptr; } ByteOrder order = target->GetArchitecture().GetByteOrder(); uint8_t addr_size = target->GetArchitecture().GetAddressByteSize(); DataEncoder enc(buf, order, addr_size); enc.PutU64(0, static_cast(intvalue)); DataExtractor data(buf, order, addr_size); CompilerType type = LookupType(target, ConstString("int64")); return ValueObject::CreateValueObjectFromData(llvm::StringRef(), data, m_exe_ctx, type); } ValueObjectSP GoUserExpression::GoInterpreter::VisitIndexExpr( const lldb_private::GoASTIndexExpr *e) { ValueObjectSP target = EvaluateExpr(e->GetX()); if (!target) return nullptr; ValueObjectSP index = EvaluateExpr(e->GetIndex()); if (!index) return nullptr; bool is_signed; if (!index->GetCompilerType().IsIntegerType(is_signed)) { m_error.SetErrorString("Unsupported index"); return nullptr; } size_t idx; if (is_signed) idx = index->GetValueAsSigned(0); else idx = index->GetValueAsUnsigned(0); if (GoASTContext::IsGoSlice(target->GetCompilerType())) { target = target->GetStaticValue(); ValueObjectSP cap = target->GetChildMemberWithName(ConstString("cap"), true); if (cap) { uint64_t capval = cap->GetValueAsUnsigned(0); if (idx >= capval) { m_error.SetErrorStringWithFormat("Invalid index %" PRIu64 " , cap = %" PRIu64, uint64_t(idx), capval); return nullptr; } } target = target->GetChildMemberWithName(ConstString("array"), true); if (target && m_use_dynamic != eNoDynamicValues) { ValueObjectSP dynamic = target->GetDynamicValue(m_use_dynamic); if (dynamic) target = dynamic; } if (!target) return nullptr; return target->GetSyntheticArrayMember(idx, true); } return target->GetChildAtIndex(idx, true); } ValueObjectSP GoUserExpression::GoInterpreter::VisitUnaryExpr(const GoASTUnaryExpr *e) { ValueObjectSP x = EvaluateExpr(e->GetX()); if (!x) return nullptr; switch (e->GetOp()) { case GoLexer::OP_AMP: { CompilerType type = x->GetCompilerType().GetPointerType(); uint64_t address = x->GetAddressOf(); return ValueObject::CreateValueObjectFromAddress(llvm::StringRef(), address, m_exe_ctx, type); } case GoLexer::OP_PLUS: return x; default: m_error.SetErrorStringWithFormat( "Operator %s not supported", GoLexer::LookupToken(e->GetOp()).str().c_str()); return nullptr; } } CompilerType GoUserExpression::GoInterpreter::EvaluateType(const GoASTExpr *e) { TargetSP target = m_exe_ctx.GetTargetSP(); if (auto *id = llvm::dyn_cast(e)) { CompilerType result = LookupType(target, ConstString(id->GetName().m_value)); if (result.IsValid()) return result; std::string fullname = (m_package + "." + id->GetName().m_value).str(); result = LookupType(target, ConstString(fullname)); if (!result) m_error.SetErrorStringWithFormat("Unknown type %s", fullname.c_str()); return result; } if (auto *sel = llvm::dyn_cast(e)) { std::string package; if (auto *pkg_node = llvm::dyn_cast(sel->GetX())) { package = pkg_node->GetName().m_value.str(); } else if (auto *str_node = llvm::dyn_cast(sel->GetX())) { if (str_node->GetValue().m_type == GoLexer::LIT_STRING) { package = str_node->GetValue().m_value.substr(1).str(); package.resize(package.length() - 1); } } if (package.empty()) { m_error.SetErrorStringWithFormat("Invalid %s in type expression", sel->GetX()->GetKindName()); return CompilerType(); } std::string fullname = (package + "." + sel->GetSel()->GetName().m_value).str(); CompilerType result = LookupType(target, ConstString(fullname)); if (!result) m_error.SetErrorStringWithFormat("Unknown type %s", fullname.c_str()); return result; } if (auto *star = llvm::dyn_cast(e)) { CompilerType elem = EvaluateType(star->GetX()); return elem.GetPointerType(); } if (auto *paren = llvm::dyn_cast(e)) return EvaluateType(paren->GetX()); if (auto *array = llvm::dyn_cast(e)) { CompilerType elem = EvaluateType(array->GetElt()); } m_error.SetErrorStringWithFormat("Invalid %s in type expression", e->GetKindName()); return CompilerType(); } ValueObjectSP GoUserExpression::GoInterpreter::VisitCallExpr( const lldb_private::GoASTCallExpr *e) { ValueObjectSP x = EvaluateExpr(e->GetFun()); if (x || e->NumArgs() != 1) { m_error.SetErrorStringWithFormat("Code execution not supported"); return nullptr; } m_error.Clear(); CompilerType type = EvaluateType(e->GetFun()); if (!type) { return nullptr; } ValueObjectSP value = EvaluateExpr(e->GetArgs(0)); if (!value) return nullptr; // TODO: Handle special conversions return value->Cast(type); } GoPersistentExpressionState::GoPersistentExpressionState() : PersistentExpressionState(eKindGo) {} ConstString GoPersistentExpressionState::GetNextPersistentVariableName() { char name_cstr[256]; // We can't use the same variable format as clang. ::snprintf(name_cstr, sizeof(name_cstr), "$go%u", m_next_persistent_variable_id++); ConstString name(name_cstr); return name; } void GoPersistentExpressionState::RemovePersistentVariable( lldb::ExpressionVariableSP variable) { RemoveVariable(variable); const char *name = variable->GetName().AsCString(); if (*(name++) != '$') return; if (*(name++) != 'g') return; if (*(name++) != 'o') return; if (strtoul(name, nullptr, 0) == m_next_persistent_variable_id - 1) m_next_persistent_variable_id--; }