Changed the ABIs and ClangFunction to take a

llvm::ArrayRef of arguments rather than taking
a fixed number of possibly-NULL pointers to
arguments.

Also changed ClangFunction::GetThreadPlanToCallFunction
to take the address of the argument struct by value
instead of by reference, since it doesn't actually
modify the value passed into it.

llvm-svn: 194232
This commit is contained in:
Sean Callanan 2013-11-08 01:14:26 +00:00
parent 949ec546c4
commit a464f3d43a
15 changed files with 105 additions and 376 deletions

View File

@ -270,8 +270,8 @@ public:
/// @param[in] func_addr
/// The address of the function in the target process.
///
/// @param[in] args_addr_ref
/// The value of the void* parameter.
/// @param[in] args_addr
/// The address of the argument struct.
///
/// @param[in] errors
/// The stream to write errors to.
@ -287,7 +287,7 @@ public:
//------------------------------------------------------------------
ThreadPlan *
GetThreadPlanToCallFunction (ExecutionContext &exe_ctx,
lldb::addr_t &args_addr_ref,
lldb::addr_t args_addr,
const EvaluateExpressionOptions &options,
Stream &errors);

View File

@ -18,6 +18,8 @@
#include "lldb/Core/PluginInterface.h"
#include "lldb/lldb-private.h"
#include "llvm/ADT/ArrayRef.h"
namespace lldb_private {
class ABI :
@ -35,12 +37,7 @@ public:
lldb::addr_t sp,
lldb::addr_t functionAddress,
lldb::addr_t returnAddress,
lldb::addr_t *arg1_ptr = NULL,
lldb::addr_t *arg2_ptr = NULL,
lldb::addr_t *arg3_ptr = NULL,
lldb::addr_t *arg4_ptr = NULL,
lldb::addr_t *arg5_ptr = NULL,
lldb::addr_t *arg6_ptr = NULL) const = 0;
llvm::ArrayRef<lldb::addr_t> args) const = 0;
virtual bool
GetArgumentValues (Thread &thread,

View File

@ -18,6 +18,8 @@
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h"
#include "llvm/ADT/ArrayRef.h"
namespace lldb_private {
class ThreadPlanCallFunction : public ThreadPlan
@ -29,21 +31,8 @@ public:
ThreadPlanCallFunction (Thread &thread,
const Address &function,
const ClangASTType &return_type,
lldb::addr_t arg,
const EvaluateExpressionOptions &options,
lldb::addr_t *this_arg = 0,
lldb::addr_t *cmd_arg = 0);
ThreadPlanCallFunction (Thread &thread,
const Address &function,
const ClangASTType &return_type,
const EvaluateExpressionOptions &options,
lldb::addr_t *arg1_ptr = NULL,
lldb::addr_t *arg2_ptr = NULL,
lldb::addr_t *arg3_ptr = NULL,
lldb::addr_t *arg4_ptr = NULL,
lldb::addr_t *arg5_ptr = NULL,
lldb::addr_t *arg6_ptr = NULL);
llvm::ArrayRef<lldb::addr_t> args,
const EvaluateExpressionOptions &options);
virtual
~ThreadPlanCallFunction ();

View File

@ -20,6 +20,8 @@
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallFunction.h"
#include "llvm/ADT/ArrayRef.h"
namespace lldb_private {
class ThreadPlanCallUserExpression : public ThreadPlanCallFunction
@ -27,10 +29,8 @@ class ThreadPlanCallUserExpression : public ThreadPlanCallFunction
public:
ThreadPlanCallUserExpression (Thread &thread,
Address &function,
lldb::addr_t arg,
llvm::ArrayRef<lldb::addr_t> args,
const EvaluateExpressionOptions &options,
lldb::addr_t *this_arg,
lldb::addr_t *cmd_arg,
ClangUserExpression::ClangUserExpressionSP &user_expression_sp);
virtual

View File

@ -394,7 +394,7 @@ ClangFunction::InsertFunction (ExecutionContext &exe_ctx, lldb::addr_t &args_add
ThreadPlan *
ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx,
lldb::addr_t &args_addr,
lldb::addr_t args_addr,
const EvaluateExpressionOptions &options,
Stream &errors)
{
@ -414,13 +414,14 @@ ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx,
// Okay, now run the function:
Address wrapper_address (m_jit_start_addr);
lldb::addr_t args = { args_addr };
ThreadPlan *new_plan = new ThreadPlanCallFunction (*thread,
wrapper_address,
ClangASTType(),
args_addr,
options,
0,
0);
args,
options);
new_plan->SetIsMasterPlan(true);
new_plan->SetOkayToDiscard (false);
return new_plan;

View File

@ -824,12 +824,21 @@ ClangUserExpression::Execute (Stream &error_stream,
else
{
Address wrapper_address (m_jit_start_addr);
llvm::SmallVector <lldb::addr_t, 3> args;
if (m_needs_object_ptr) {
args.push_back(object_ptr);
if (m_objectivec)
args.push_back(cmd_ptr);
}
args.push_back(struct_address);
lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(),
wrapper_address,
struct_address,
args,
options,
(m_needs_object_ptr ? &object_ptr : NULL),
((m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL),
shared_ptr_to_me));
if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream))

View File

@ -198,12 +198,7 @@ ABIMacOSX_arm::PrepareTrivialCall (Thread &thread,
addr_t sp,
addr_t function_addr,
addr_t return_addr,
addr_t *arg1_ptr,
addr_t *arg2_ptr,
addr_t *arg3_ptr,
addr_t *arg4_ptr,
addr_t *arg5_ptr,
addr_t *arg6_ptr) const
llvm::ArrayRef<addr_t> args) const
{
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
if (!reg_ctx)
@ -215,50 +210,45 @@ ABIMacOSX_arm::PrepareTrivialCall (Thread &thread,
RegisterValue reg_value;
if (arg1_ptr)
const char *reg_names[] = { "r0", "r1", "r2", "r3" };
llvm::ArrayRef<addr_t>::iterator ai = args.begin(), ae = args.end();
for (size_t i = 0; i < (sizeof(reg_names) / sizeof(reg_names[0])); ++i)
{
reg_value.SetUInt32(*arg1_ptr);
if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r0"), reg_value))
if (ai == ae)
break;
reg_value.SetUInt32(*ai);
if (!reg_ctx->WriteRegister(reg_ctx->GetRegisterInfoByName(reg_names[i]), reg_value))
return false;
if (arg2_ptr)
++ai;
}
if (ai != ae)
{
// Spill onto the stack
size_t num_stack_regs = ae - ai;
sp -= (num_stack_regs * 4);
// Keep the stack 8 byte aligned, not that we need to
sp &= ~(8ull-1ull);
// just using arg1 to get the right size
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
addr_t arg_pos = sp;
for (; ai != ae; ++ai)
{
reg_value.SetUInt32(*arg2_ptr);
if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r1"), reg_value))
reg_value.SetUInt32(*ai);
if (reg_ctx->WriteRegisterValueToMemory(reg_info, arg_pos, reg_info->byte_size, reg_value).Fail())
return false;
if (arg3_ptr)
{
reg_value.SetUInt32(*arg3_ptr);
if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r2"), reg_value))
return false;
if (arg4_ptr)
{
reg_value.SetUInt32(*arg4_ptr);
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3");
if (!reg_ctx->WriteRegister (reg_info, reg_value))
return false;
if (arg5_ptr)
{
// Keep the stack 8 byte aligned, not that we need to
sp -= 8;
sp &= ~(8ull-1ull);
reg_value.SetUInt32(*arg5_ptr);
if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp, reg_info->byte_size, reg_value).Fail())
return false;
if (arg6_ptr)
{
reg_value.SetUInt32(*arg6_ptr);
if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp + 4, reg_info->byte_size, reg_value).Fail())
return false;
}
}
}
}
arg_pos += reg_info->byte_size;
}
}
TargetSP target_sp (thread.CalculateTarget());
Address so_addr;

View File

@ -30,12 +30,7 @@ public:
lldb::addr_t sp,
lldb::addr_t func_addr,
lldb::addr_t returnAddress,
lldb::addr_t *arg1_ptr = NULL,
lldb::addr_t *arg2_ptr = NULL,
lldb::addr_t *arg3_ptr = NULL,
lldb::addr_t *arg4_ptr = NULL,
lldb::addr_t *arg5_ptr = NULL,
lldb::addr_t *arg6_ptr = NULL) const;
llvm::ArrayRef<lldb::addr_t> args) const;
virtual bool
GetArgumentValues (lldb_private::Thread &thread,

View File

@ -260,12 +260,7 @@ ABIMacOSX_i386::PrepareTrivialCall (Thread &thread,
addr_t sp,
addr_t func_addr,
addr_t return_addr,
addr_t *arg1_ptr,
addr_t *arg2_ptr,
addr_t *arg3_ptr,
addr_t *arg4_ptr,
addr_t *arg5_ptr,
addr_t *arg6_ptr) const
llvm::ArrayRef<addr_t> args) const
{
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
if (!reg_ctx)
@ -287,114 +282,25 @@ ABIMacOSX_i386::PrepareTrivialCall (Thread &thread,
RegisterValue reg_value;
// Write any arguments onto the stack
if (arg1_ptr)
{
sp -= 4;
if (arg2_ptr)
{
sp -= 4;
if (arg3_ptr)
{
sp -= 4;
if (arg4_ptr)
{
sp -= 4;
if (arg5_ptr)
{
sp -= 4;
if (arg6_ptr)
{
sp -= 4;
}
}
}
}
}
}
sp -= 4 * args.size();
// Align the SP
sp &= ~(16ull-1ull); // 16-byte alignment
if (arg1_ptr)
addr_t arg_pos = sp;
for (addr_t arg : args)
{
reg_value.SetUInt32(*arg1_ptr);
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
sp,
reg_info_32->byte_size,
reg_value.SetUInt32(arg);
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
arg_pos,
reg_info_32->byte_size,
reg_value);
if (error.Fail())
return false;
if (arg2_ptr)
{
reg_value.SetUInt32(*arg2_ptr);
// The register info used to write memory just needs to have the correct
// size of a 32 bit register, the actual register it pertains to is not
// important, just the size needs to be correct. Here we use "eax"...
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
sp + 4,
reg_info_32->byte_size,
reg_value);
if (error.Fail())
return false;
if (arg3_ptr)
{
reg_value.SetUInt32(*arg3_ptr);
// The register info used to write memory just needs to have the correct
// size of a 32 bit register, the actual register it pertains to is not
// important, just the size needs to be correct. Here we use "eax"...
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
sp + 8,
reg_info_32->byte_size,
reg_value);
if (error.Fail())
return false;
if (arg4_ptr)
{
reg_value.SetUInt32(*arg4_ptr);
// The register info used to write memory just needs to have the correct
// size of a 32 bit register, the actual register it pertains to is not
// important, just the size needs to be correct. Here we use "eax"...
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
sp + 12,
reg_info_32->byte_size,
reg_value);
if (error.Fail())
return false;
if (arg5_ptr)
{
reg_value.SetUInt32(*arg5_ptr);
// The register info used to write memory just needs to have the correct
// size of a 32 bit register, the actual register it pertains to is not
// important, just the size needs to be correct. Here we use "eax"...
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
sp + 16,
reg_info_32->byte_size,
reg_value);
if (error.Fail())
return false;
if (arg6_ptr)
{
reg_value.SetUInt32(*arg6_ptr);
// The register info used to write memory just needs to have the correct
// size of a 32 bit register, the actual register it pertains to is not
// important, just the size needs to be correct. Here we use "eax"...
error = reg_ctx->WriteRegisterValueToMemory (reg_info_32,
sp + 20,
reg_info_32->byte_size,
reg_value);
if (error.Fail())
return false;
}
}
}
}
}
arg_pos += 4;
}
// The return address is pushed onto the stack (yes after we just set the
// alignment above!).
sp -= 4;

View File

@ -33,12 +33,7 @@ public:
lldb::addr_t sp,
lldb::addr_t func_addr,
lldb::addr_t return_addr,
lldb::addr_t *arg1_ptr = NULL,
lldb::addr_t *arg2_ptr = NULL,
lldb::addr_t *arg3_ptr = NULL,
lldb::addr_t *arg4_ptr = NULL,
lldb::addr_t *arg5_ptr = NULL,
lldb::addr_t *arg6_ptr = NULL) const;
llvm::ArrayRef<lldb::addr_t> args) const;
virtual bool
PrepareNormalCall (lldb_private::Thread &thread,

View File

@ -303,12 +303,7 @@ ABISysV_x86_64::PrepareTrivialCall (Thread &thread,
addr_t sp,
addr_t func_addr,
addr_t return_addr,
addr_t *arg1_ptr,
addr_t *arg2_ptr,
addr_t *arg3_ptr,
addr_t *arg4_ptr,
addr_t *arg5_ptr,
addr_t *arg6_ptr) const
llvm::ArrayRef<addr_t> args) const
{
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
@ -321,28 +316,8 @@ ABISysV_x86_64::PrepareTrivialCall (Thread &thread,
(uint64_t)func_addr,
(uint64_t)return_addr);
if (arg1_ptr)
{
s.Printf (", arg1 = 0x%" PRIx64, (uint64_t)*arg1_ptr);
if (arg2_ptr)
{
s.Printf (", arg2 = 0x%" PRIx64, (uint64_t)*arg2_ptr);
if (arg3_ptr)
{
s.Printf (", arg3 = 0x%" PRIx64, (uint64_t)*arg3_ptr);
if (arg4_ptr)
{
s.Printf (", arg4 = 0x%" PRIx64, (uint64_t)*arg4_ptr);
if (arg5_ptr)
{
s.Printf (", arg5 = 0x%" PRIx64, (uint64_t)*arg5_ptr);
if (arg6_ptr)
s.Printf (", arg6 = 0x%" PRIx64, (uint64_t)*arg6_ptr);
}
}
}
}
}
for (int i = 0; i < args.size(); ++i)
s.Printf (", arg%d = 0x%" PRIx64, i + 1, args[i]);
s.PutCString (")");
log->PutCString(s.GetString().c_str());
}
@ -352,62 +327,19 @@ ABISysV_x86_64::PrepareTrivialCall (Thread &thread,
return false;
const RegisterInfo *reg_info = NULL;
if (arg1_ptr)
if (args.size() > 6) // TODO handle more than 6 arguments
return false;
for (int i = 0; i < args.size(); ++i)
{
reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1);
reg_info = reg_ctx->GetRegisterInfo(eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
if (log)
log->Printf("About to write arg1 (0x%" PRIx64 ") into %s", (uint64_t)*arg1_ptr, reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg1_ptr))
log->Printf("About to write arg%d (0x%" PRIx64 ") into %s", i + 1, args[i], reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned(reg_info, args[i]))
return false;
if (arg2_ptr)
{
reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG2);
if (log)
log->Printf("About to write arg2 (0x%" PRIx64 ") into %s", (uint64_t)*arg2_ptr, reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg2_ptr))
return false;
if (arg3_ptr)
{
reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG3);
if (log)
log->Printf("About to write arg3 (0x%" PRIx64 ") into %s", (uint64_t)*arg3_ptr, reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg3_ptr))
return false;
if (arg4_ptr)
{
reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG4);
if (log)
log->Printf("About to write arg4 (0x%" PRIx64 ") into %s", (uint64_t)*arg4_ptr, reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg4_ptr))
return false;
if (arg5_ptr)
{
reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG5);
if (log)
log->Printf("About to write arg5 (0x%" PRIx64 ") into %s", (uint64_t)*arg5_ptr, reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg5_ptr))
return false;
if (arg6_ptr)
{
reg_info = reg_ctx->GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG6);
if (log)
log->Printf("About to write arg6 (0x%" PRIx64 ") into %s", (uint64_t)*arg6_ptr, reg_info->name);
if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg6_ptr))
return false;
}
}
}
}
}
}
// First, align the SP
if (log)

View File

@ -34,12 +34,7 @@ public:
lldb::addr_t sp,
lldb::addr_t functionAddress,
lldb::addr_t returnAddress,
lldb::addr_t *arg1_ptr = NULL,
lldb::addr_t *arg2_ptr = NULL,
lldb::addr_t *arg3_ptr = NULL,
lldb::addr_t *arg4_ptr = NULL,
lldb::addr_t *arg5_ptr = NULL,
lldb::addr_t *arg6_ptr = NULL) const;
llvm::ArrayRef<lldb::addr_t> args) const;
virtual bool
GetArgumentValues (lldb_private::Thread &thread,

View File

@ -90,17 +90,13 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
{
ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
ClangASTType clang_void_ptr_type = clang_ast_context->GetBasicType(eBasicTypeVoid).GetPointerType();
lldb::addr_t args[] = { addr, length, prot_arg, flags_arg, fd, offset };
ThreadPlanCallFunction *call_function_thread_plan
= new ThreadPlanCallFunction (*thread,
mmap_range.GetBaseAddress(),
clang_void_ptr_type,
options,
&addr,
&length,
&prot_arg,
&flags_arg,
&fd,
&offset);
args,
options);
lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan);
if (call_plan_sp)
{
@ -178,12 +174,12 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
AddressRange munmap_range;
if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, munmap_range))
{
lldb::addr_t args[] = { addr, length };
lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread,
munmap_range.GetBaseAddress(),
ClangASTType(),
options,
&addr,
&length));
args,
options));
if (call_plan_sp)
{
StreamFile error_strm;
@ -232,6 +228,7 @@ bool lldb_private::InferiorCall(Process *process, const Address *address, addr_t
= new ThreadPlanCallFunction (*thread,
*address,
clang_void_ptr_type,
llvm::ArrayRef<addr_t>(),
options);
lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan);
if (call_plan_sp)

View File

@ -126,10 +126,8 @@ ThreadPlanCallFunction::ConstructorSetup (Thread &thread,
ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
const Address &function,
const ClangASTType &return_type,
addr_t arg,
const EvaluateExpressionOptions &options,
addr_t *this_arg,
addr_t *cmd_arg) :
llvm::ArrayRef<addr_t> args,
const EvaluateExpressionOptions &options) :
ThreadPlan (ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion),
m_valid (false),
m_stop_other_threads (options.GetStopOthers()),
@ -150,87 +148,14 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
lldb::addr_t function_load_addr;
if (!ConstructorSetup (thread, abi, start_load_addr, function_load_addr))
return;
if (this_arg && cmd_arg)
{
if (!abi->PrepareTrivialCall (thread,
m_function_sp,
function_load_addr,
start_load_addr,
this_arg,
cmd_arg,
&arg))
return;
}
else if (this_arg)
{
if (!abi->PrepareTrivialCall (thread,
m_function_sp,
function_load_addr,
start_load_addr,
this_arg,
&arg))
return;
}
else
{
if (!abi->PrepareTrivialCall (thread,
m_function_sp,
function_load_addr,
start_load_addr,
&arg))
return;
}
ReportRegisterState ("Function call was set up. Register state was:");
m_valid = true;
}
ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
const Address &function,
const ClangASTType &return_type,
const EvaluateExpressionOptions &options,
addr_t *arg1_ptr,
addr_t *arg2_ptr,
addr_t *arg3_ptr,
addr_t *arg4_ptr,
addr_t *arg5_ptr,
addr_t *arg6_ptr) :
ThreadPlan (ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion),
m_valid (false),
m_stop_other_threads (options.GetStopOthers()),
m_unwind_on_error (options.DoesUnwindOnError()),
m_ignore_breakpoints (options.DoesIgnoreBreakpoints()),
m_debug_execution (options.GetDebug()),
m_trap_exceptions (options.GetTrapExceptions()),
m_function_addr (function),
m_function_sp (0),
m_return_type (return_type),
m_takedown_done (false),
m_stop_address (LLDB_INVALID_ADDRESS)
{
lldb::addr_t start_load_addr;
ABI *abi;
lldb::addr_t function_load_addr;
if (!ConstructorSetup (thread, abi, start_load_addr, function_load_addr))
if (!abi->PrepareTrivialCall(thread,
m_function_sp,
function_load_addr,
start_load_addr,
args))
return;
if (!abi->PrepareTrivialCall (thread,
m_function_sp,
function_load_addr,
start_load_addr,
arg1_ptr,
arg2_ptr,
arg3_ptr,
arg4_ptr,
arg5_ptr,
arg6_ptr))
{
return;
}
ReportRegisterState ("Function call was set up. Register state was:");
m_valid = true;

View File

@ -38,12 +38,10 @@ using namespace lldb_private;
ThreadPlanCallUserExpression::ThreadPlanCallUserExpression (Thread &thread,
Address &function,
lldb::addr_t arg,
llvm::ArrayRef<lldb::addr_t> args,
const EvaluateExpressionOptions &options,
lldb::addr_t *this_arg,
lldb::addr_t *cmd_arg,
ClangUserExpression::ClangUserExpressionSP &user_expression_sp) :
ThreadPlanCallFunction (thread, function, ClangASTType(), arg, options, this_arg, cmd_arg),
ThreadPlanCallFunction (thread, function, ClangASTType(), args, options),
m_user_expression_sp (user_expression_sp)
{
// User expressions are generally "User generated" so we should set them up to stop when done.