callers had to do this by hand and we ended up never actually adding initial arguments and then reusing them by passing in the struct address separately, so the distinction wasn't needed. llvm-svn: 252108
674 lines
21 KiB
C++
674 lines
21 KiB
C++
//===-- ClangUserExpression.cpp ---------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include <stdio.h>
|
|
#if HAVE_SYS_TYPES_H
|
|
# include <sys/types.h>
|
|
#endif
|
|
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <map>
|
|
|
|
#include "ClangUserExpression.h"
|
|
|
|
#include "ASTResultSynthesizer.h"
|
|
#include "ClangExpressionDeclMap.h"
|
|
#include "ClangExpressionParser.h"
|
|
#include "ClangModulesDeclVendor.h"
|
|
#include "ClangPersistentVariables.h"
|
|
|
|
#include "lldb/Core/ConstString.h"
|
|
#include "lldb/Core/Log.h"
|
|
#include "lldb/Core/Module.h"
|
|
#include "lldb/Core/StreamFile.h"
|
|
#include "lldb/Core/StreamString.h"
|
|
#include "lldb/Core/ValueObjectConstResult.h"
|
|
#include "lldb/Expression/ExpressionSourceCode.h"
|
|
#include "lldb/Expression/IRExecutionUnit.h"
|
|
#include "lldb/Expression/IRInterpreter.h"
|
|
#include "lldb/Expression/Materializer.h"
|
|
#include "lldb/Host/HostInfo.h"
|
|
#include "lldb/Symbol/Block.h"
|
|
#include "lldb/Symbol/ClangASTContext.h"
|
|
#include "lldb/Symbol/Function.h"
|
|
#include "lldb/Symbol/ObjectFile.h"
|
|
#include "lldb/Symbol/SymbolVendor.h"
|
|
#include "lldb/Symbol/Type.h"
|
|
#include "lldb/Symbol/ClangExternalASTSourceCommon.h"
|
|
#include "lldb/Symbol/VariableList.h"
|
|
#include "lldb/Target/ExecutionContext.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/StackFrame.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Target/ThreadPlan.h"
|
|
#include "lldb/Target/ThreadPlanCallUserExpression.h"
|
|
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
|
|
using namespace lldb_private;
|
|
|
|
ClangUserExpression::ClangUserExpression (ExecutionContextScope &exe_scope,
|
|
const char *expr,
|
|
const char *expr_prefix,
|
|
lldb::LanguageType language,
|
|
ResultType desired_type,
|
|
const EvaluateExpressionOptions &options) :
|
|
LLVMUserExpression (exe_scope, expr, expr_prefix, language, desired_type, options),
|
|
m_type_system_helper(*m_target_wp.lock().get())
|
|
{
|
|
switch (m_language)
|
|
{
|
|
case lldb::eLanguageTypeC_plus_plus:
|
|
m_allow_cxx = true;
|
|
break;
|
|
case lldb::eLanguageTypeObjC:
|
|
m_allow_objc = true;
|
|
break;
|
|
case lldb::eLanguageTypeObjC_plus_plus:
|
|
default:
|
|
m_allow_cxx = true;
|
|
m_allow_objc = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ClangUserExpression::~ClangUserExpression ()
|
|
{
|
|
}
|
|
|
|
void
|
|
ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &err)
|
|
{
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
|
|
|
|
if (log)
|
|
log->Printf("ClangUserExpression::ScanContext()");
|
|
|
|
m_target = exe_ctx.GetTargetPtr();
|
|
|
|
if (!(m_allow_cxx || m_allow_objc))
|
|
{
|
|
if (log)
|
|
log->Printf(" [CUE::SC] Settings inhibit C++ and Objective-C");
|
|
return;
|
|
}
|
|
|
|
StackFrame *frame = exe_ctx.GetFramePtr();
|
|
if (frame == NULL)
|
|
{
|
|
if (log)
|
|
log->Printf(" [CUE::SC] Null stack frame");
|
|
return;
|
|
}
|
|
|
|
SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction | lldb::eSymbolContextBlock);
|
|
|
|
if (!sym_ctx.function)
|
|
{
|
|
if (log)
|
|
log->Printf(" [CUE::SC] Null function");
|
|
return;
|
|
}
|
|
|
|
// Find the block that defines the function represented by "sym_ctx"
|
|
Block *function_block = sym_ctx.GetFunctionBlock();
|
|
|
|
if (!function_block)
|
|
{
|
|
if (log)
|
|
log->Printf(" [CUE::SC] Null function block");
|
|
return;
|
|
}
|
|
|
|
CompilerDeclContext decl_context = function_block->GetDeclContext();
|
|
|
|
if (!decl_context)
|
|
{
|
|
if (log)
|
|
log->Printf(" [CUE::SC] Null decl context");
|
|
return;
|
|
}
|
|
|
|
if (clang::CXXMethodDecl *method_decl = ClangASTContext::DeclContextGetAsCXXMethodDecl(decl_context))
|
|
{
|
|
if (m_allow_cxx && method_decl->isInstance())
|
|
{
|
|
if (m_enforce_valid_object)
|
|
{
|
|
lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true));
|
|
|
|
const char *thisErrorString = "Stopped in a C++ method, but 'this' isn't available; pretending we are in a generic context";
|
|
|
|
if (!variable_list_sp)
|
|
{
|
|
err.SetErrorString(thisErrorString);
|
|
return;
|
|
}
|
|
|
|
lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this")));
|
|
|
|
if (!this_var_sp ||
|
|
!this_var_sp->IsInScope(frame) ||
|
|
!this_var_sp->LocationIsValidForFrame (frame))
|
|
{
|
|
err.SetErrorString(thisErrorString);
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_in_cplusplus_method = true;
|
|
m_needs_object_ptr = true;
|
|
}
|
|
}
|
|
else if (clang::ObjCMethodDecl *method_decl = ClangASTContext::DeclContextGetAsObjCMethodDecl(decl_context))
|
|
{
|
|
if (m_allow_objc)
|
|
{
|
|
if (m_enforce_valid_object)
|
|
{
|
|
lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true));
|
|
|
|
const char *selfErrorString = "Stopped in an Objective-C method, but 'self' isn't available; pretending we are in a generic context";
|
|
|
|
if (!variable_list_sp)
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
|
|
lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self"));
|
|
|
|
if (!self_variable_sp ||
|
|
!self_variable_sp->IsInScope(frame) ||
|
|
!self_variable_sp->LocationIsValidForFrame (frame))
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_in_objectivec_method = true;
|
|
m_needs_object_ptr = true;
|
|
|
|
if (!method_decl->isInstanceMethod())
|
|
m_in_static_method = true;
|
|
}
|
|
}
|
|
else if (clang::FunctionDecl *function_decl = ClangASTContext::DeclContextGetAsFunctionDecl(decl_context))
|
|
{
|
|
// We might also have a function that said in the debug information that it captured an
|
|
// object pointer. The best way to deal with getting to the ivars at present is by pretending
|
|
// that this is a method of a class in whatever runtime the debug info says the object pointer
|
|
// belongs to. Do that here.
|
|
|
|
ClangASTMetadata *metadata = ClangASTContext::DeclContextGetMetaData (decl_context, function_decl);
|
|
if (metadata && metadata->HasObjectPtr())
|
|
{
|
|
lldb::LanguageType language = metadata->GetObjectPtrLanguage();
|
|
if (language == lldb::eLanguageTypeC_plus_plus)
|
|
{
|
|
if (m_enforce_valid_object)
|
|
{
|
|
lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true));
|
|
|
|
const char *thisErrorString = "Stopped in a context claiming to capture a C++ object pointer, but 'this' isn't available; pretending we are in a generic context";
|
|
|
|
if (!variable_list_sp)
|
|
{
|
|
err.SetErrorString(thisErrorString);
|
|
return;
|
|
}
|
|
|
|
lldb::VariableSP this_var_sp (variable_list_sp->FindVariable(ConstString("this")));
|
|
|
|
if (!this_var_sp ||
|
|
!this_var_sp->IsInScope(frame) ||
|
|
!this_var_sp->LocationIsValidForFrame (frame))
|
|
{
|
|
err.SetErrorString(thisErrorString);
|
|
return;
|
|
}
|
|
}
|
|
|
|
m_in_cplusplus_method = true;
|
|
m_needs_object_ptr = true;
|
|
}
|
|
else if (language == lldb::eLanguageTypeObjC)
|
|
{
|
|
if (m_enforce_valid_object)
|
|
{
|
|
lldb::VariableListSP variable_list_sp (function_block->GetBlockVariableList (true));
|
|
|
|
const char *selfErrorString = "Stopped in a context claiming to capture an Objective-C object pointer, but 'self' isn't available; pretending we are in a generic context";
|
|
|
|
if (!variable_list_sp)
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
|
|
lldb::VariableSP self_variable_sp = variable_list_sp->FindVariable(ConstString("self"));
|
|
|
|
if (!self_variable_sp ||
|
|
!self_variable_sp->IsInScope(frame) ||
|
|
!self_variable_sp->LocationIsValidForFrame (frame))
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
|
|
Type *self_type = self_variable_sp->GetType();
|
|
|
|
if (!self_type)
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
|
|
CompilerType self_clang_type = self_type->GetForwardCompilerType ();
|
|
|
|
if (!self_clang_type)
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
|
|
if (ClangASTContext::IsObjCClassType(self_clang_type))
|
|
{
|
|
return;
|
|
}
|
|
else if (ClangASTContext::IsObjCObjectPointerType(self_clang_type))
|
|
{
|
|
m_in_objectivec_method = true;
|
|
m_needs_object_ptr = true;
|
|
}
|
|
else
|
|
{
|
|
err.SetErrorString(selfErrorString);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_in_objectivec_method = true;
|
|
m_needs_object_ptr = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a really nasty hack, meant to fix Objective-C expressions of the form
|
|
// (int)[myArray count]. Right now, because the type information for count is
|
|
// not available, [myArray count] returns id, which can't be directly cast to
|
|
// int without causing a clang error.
|
|
static void
|
|
ApplyObjcCastHack(std::string &expr)
|
|
{
|
|
#define OBJC_CAST_HACK_FROM "(int)["
|
|
#define OBJC_CAST_HACK_TO "(int)(long long)["
|
|
|
|
size_t from_offset;
|
|
|
|
while ((from_offset = expr.find(OBJC_CAST_HACK_FROM)) != expr.npos)
|
|
expr.replace(from_offset, sizeof(OBJC_CAST_HACK_FROM) - 1, OBJC_CAST_HACK_TO);
|
|
|
|
#undef OBJC_CAST_HACK_TO
|
|
#undef OBJC_CAST_HACK_FROM
|
|
}
|
|
|
|
bool
|
|
ClangUserExpression::Parse (Stream &error_stream,
|
|
ExecutionContext &exe_ctx,
|
|
lldb_private::ExecutionPolicy execution_policy,
|
|
bool keep_result_in_memory,
|
|
bool generate_debug_info)
|
|
{
|
|
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
|
|
|
|
Error err;
|
|
|
|
InstallContext(exe_ctx);
|
|
|
|
if (Target *target = exe_ctx.GetTargetPtr())
|
|
{
|
|
if (PersistentExpressionState *persistent_state = target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC))
|
|
{
|
|
m_result_delegate.RegisterPersistentState(persistent_state);
|
|
}
|
|
else
|
|
{
|
|
error_stream.PutCString ("error: couldn't start parsing (no persistent data)");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error_stream.PutCString ("error: couldn't start parsing (no target)");
|
|
return false;
|
|
}
|
|
|
|
ScanContext(exe_ctx, err);
|
|
|
|
if (!err.Success())
|
|
{
|
|
error_stream.Printf("warning: %s\n", err.AsCString());
|
|
}
|
|
|
|
StreamString m_transformed_stream;
|
|
|
|
////////////////////////////////////
|
|
// Generate the expression
|
|
//
|
|
|
|
ApplyObjcCastHack(m_expr_text);
|
|
//ApplyUnicharHack(m_expr_text);
|
|
|
|
std::string prefix = m_expr_prefix;
|
|
|
|
if (ClangModulesDeclVendor *decl_vendor = m_target->GetClangModulesDeclVendor())
|
|
{
|
|
const ClangModulesDeclVendor::ModuleVector &hand_imported_modules = llvm::cast<ClangPersistentVariables>(m_target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC))->GetHandLoadedClangModules();
|
|
ClangModulesDeclVendor::ModuleVector modules_for_macros;
|
|
|
|
for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules)
|
|
{
|
|
modules_for_macros.push_back(module);
|
|
}
|
|
|
|
if (m_target->GetEnableAutoImportClangModules())
|
|
{
|
|
if (StackFrame *frame = exe_ctx.GetFramePtr())
|
|
{
|
|
if (Block *block = frame->GetFrameBlock())
|
|
{
|
|
SymbolContext sc;
|
|
|
|
block->CalculateSymbolContext(&sc);
|
|
|
|
if (sc.comp_unit)
|
|
{
|
|
StreamString error_stream;
|
|
|
|
decl_vendor->AddModulesForCompileUnit(*sc.comp_unit, modules_for_macros, error_stream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<ExpressionSourceCode> source_code (ExpressionSourceCode::CreateWrapped(prefix.c_str(), m_expr_text.c_str()));
|
|
|
|
lldb::LanguageType lang_type;
|
|
|
|
if (m_in_cplusplus_method)
|
|
lang_type = lldb::eLanguageTypeC_plus_plus;
|
|
else if (m_in_objectivec_method)
|
|
lang_type = lldb::eLanguageTypeObjC;
|
|
else
|
|
lang_type = lldb::eLanguageTypeC;
|
|
|
|
if (!source_code->GetText(m_transformed_text, lang_type, m_const_object, m_in_static_method, exe_ctx))
|
|
{
|
|
error_stream.PutCString ("error: couldn't construct expression body");
|
|
return false;
|
|
}
|
|
|
|
if (log)
|
|
log->Printf("Parsing the following code:\n%s", m_transformed_text.c_str());
|
|
|
|
////////////////////////////////////
|
|
// Set up the target and compiler
|
|
//
|
|
|
|
Target *target = exe_ctx.GetTargetPtr();
|
|
|
|
if (!target)
|
|
{
|
|
error_stream.PutCString ("error: invalid target\n");
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////
|
|
// Parse the expression
|
|
//
|
|
|
|
m_materializer_ap.reset(new Materializer());
|
|
|
|
ResetDeclMap(exe_ctx, m_result_delegate, keep_result_in_memory);
|
|
|
|
class OnExit
|
|
{
|
|
public:
|
|
typedef std::function <void (void)> Callback;
|
|
|
|
OnExit (Callback const &callback) :
|
|
m_callback(callback)
|
|
{
|
|
}
|
|
|
|
~OnExit ()
|
|
{
|
|
m_callback();
|
|
}
|
|
private:
|
|
Callback m_callback;
|
|
};
|
|
|
|
OnExit on_exit([this]() { ResetDeclMap(); });
|
|
|
|
if (!DeclMap()->WillParse(exe_ctx, m_materializer_ap.get()))
|
|
{
|
|
error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n");
|
|
|
|
ResetDeclMap(); // We are being careful here in the case of breakpoint conditions.
|
|
|
|
return false;
|
|
}
|
|
|
|
Process *process = exe_ctx.GetProcessPtr();
|
|
ExecutionContextScope *exe_scope = process;
|
|
|
|
if (!exe_scope)
|
|
exe_scope = exe_ctx.GetTargetPtr();
|
|
|
|
ClangExpressionParser parser(exe_scope, *this, generate_debug_info);
|
|
|
|
unsigned num_errors = parser.Parse (error_stream);
|
|
|
|
if (num_errors)
|
|
{
|
|
error_stream.Printf ("error: %d errors parsing expression\n", num_errors);
|
|
|
|
ResetDeclMap(); // We are being careful here in the case of breakpoint conditions.
|
|
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Prepare the output of the parser for execution, evaluating it statically if possible
|
|
//
|
|
|
|
Error jit_error = parser.PrepareForExecution (m_jit_start_addr,
|
|
m_jit_end_addr,
|
|
m_execution_unit_sp,
|
|
exe_ctx,
|
|
m_can_interpret,
|
|
execution_policy);
|
|
|
|
if (generate_debug_info)
|
|
{
|
|
lldb::ModuleSP jit_module_sp ( m_execution_unit_sp->GetJITModule());
|
|
|
|
if (jit_module_sp)
|
|
{
|
|
ConstString const_func_name(FunctionName());
|
|
FileSpec jit_file;
|
|
jit_file.GetFilename() = const_func_name;
|
|
jit_module_sp->SetFileSpecAndObjectName (jit_file, ConstString());
|
|
m_jit_module_wp = jit_module_sp;
|
|
target->GetImages().Append(jit_module_sp);
|
|
}
|
|
// lldb_private::ObjectFile *jit_obj_file = jit_module_sp->GetObjectFile();
|
|
// StreamFile strm (stdout, false);
|
|
// if (jit_obj_file)
|
|
// {
|
|
// jit_obj_file->GetSectionList();
|
|
// jit_obj_file->GetSymtab();
|
|
// jit_obj_file->Dump(&strm);
|
|
// }
|
|
// lldb_private::SymbolVendor *jit_sym_vendor = jit_module_sp->GetSymbolVendor();
|
|
// if (jit_sym_vendor)
|
|
// {
|
|
// lldb_private::SymbolContextList sc_list;
|
|
// jit_sym_vendor->FindFunctions(const_func_name, NULL, lldb::eFunctionNameTypeFull, true, false, sc_list);
|
|
// sc_list.Dump(&strm, target);
|
|
// jit_sym_vendor->Dump(&strm);
|
|
// }
|
|
}
|
|
|
|
ResetDeclMap(); // Make this go away since we don't need any of its state after parsing. This also gets rid of any ClangASTImporter::Minions.
|
|
|
|
if (jit_error.Success())
|
|
{
|
|
if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS)
|
|
m_jit_process_wp = lldb::ProcessWP(process->shared_from_this());
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
const char *error_cstr = jit_error.AsCString();
|
|
if (error_cstr && error_cstr[0])
|
|
error_stream.Printf ("error: %s\n", error_cstr);
|
|
else
|
|
error_stream.Printf ("error: expression can't be interpreted or run\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
ClangUserExpression::AddArguments (ExecutionContext &exe_ctx,
|
|
std::vector<lldb::addr_t> &args,
|
|
lldb::addr_t struct_address,
|
|
Stream &error_stream)
|
|
{
|
|
lldb::addr_t object_ptr = LLDB_INVALID_ADDRESS;
|
|
lldb::addr_t cmd_ptr = LLDB_INVALID_ADDRESS;
|
|
|
|
if (m_needs_object_ptr)
|
|
{
|
|
lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP();
|
|
if (!frame_sp)
|
|
return true;
|
|
|
|
ConstString object_name;
|
|
|
|
if (m_in_cplusplus_method)
|
|
{
|
|
object_name.SetCString("this");
|
|
}
|
|
else if (m_in_objectivec_method)
|
|
{
|
|
object_name.SetCString("self");
|
|
}
|
|
else
|
|
{
|
|
error_stream.Printf("Need object pointer but don't know the language\n");
|
|
return false;
|
|
}
|
|
|
|
Error object_ptr_error;
|
|
|
|
object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error);
|
|
|
|
if (!object_ptr_error.Success())
|
|
{
|
|
error_stream.Printf("warning: couldn't get required object pointer (substituting NULL): %s\n", object_ptr_error.AsCString());
|
|
object_ptr = 0;
|
|
}
|
|
|
|
if (m_in_objectivec_method)
|
|
{
|
|
ConstString cmd_name("_cmd");
|
|
|
|
cmd_ptr = GetObjectPointer(frame_sp, cmd_name, object_ptr_error);
|
|
|
|
if (!object_ptr_error.Success())
|
|
{
|
|
error_stream.Printf("warning: couldn't get cmd pointer (substituting NULL): %s\n", object_ptr_error.AsCString());
|
|
cmd_ptr = 0;
|
|
}
|
|
}
|
|
if (object_ptr)
|
|
args.push_back(object_ptr);
|
|
|
|
if (m_in_objectivec_method)
|
|
args.push_back(cmd_ptr);
|
|
|
|
args.push_back(struct_address);
|
|
}
|
|
else
|
|
{
|
|
args.push_back(struct_address);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
lldb::ExpressionVariableSP
|
|
ClangUserExpression::GetResultAfterDematerialization(ExecutionContextScope *exe_scope)
|
|
{
|
|
return m_result_delegate.GetVariable();
|
|
}
|
|
|
|
void
|
|
ClangUserExpression::ClangUserExpressionHelper::ResetDeclMap(ExecutionContext &exe_ctx, Materializer::PersistentVariableDelegate &delegate, bool keep_result_in_memory)
|
|
{
|
|
m_expr_decl_map_up.reset(new ClangExpressionDeclMap(keep_result_in_memory, &delegate, exe_ctx));
|
|
}
|
|
|
|
clang::ASTConsumer *
|
|
ClangUserExpression::ClangUserExpressionHelper::ASTTransformer (clang::ASTConsumer *passthrough)
|
|
{
|
|
m_result_synthesizer_up.reset(new ASTResultSynthesizer(passthrough,
|
|
m_target));
|
|
|
|
return m_result_synthesizer_up.get();
|
|
}
|
|
|
|
ClangUserExpression::ResultDelegate::ResultDelegate()
|
|
{
|
|
}
|
|
|
|
ConstString
|
|
ClangUserExpression::ResultDelegate::GetName()
|
|
{
|
|
return m_persistent_state->GetNextPersistentVariableName();
|
|
}
|
|
|
|
void
|
|
ClangUserExpression::ResultDelegate::DidDematerialize(lldb::ExpressionVariableSP &variable)
|
|
{
|
|
m_variable = variable;
|
|
}
|
|
|
|
void
|
|
ClangUserExpression::ResultDelegate::RegisterPersistentState(PersistentExpressionState *persistent_state)
|
|
{
|
|
m_persistent_state = persistent_state;
|
|
}
|
|
|
|
lldb::ExpressionVariableSP &
|
|
ClangUserExpression::ResultDelegate::GetVariable()
|
|
{
|
|
return m_variable;
|
|
}
|
|
|