mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-02 15:26:29 +00:00
Add the ability to set python breakpoint commands from the SBBreakpoint & SBBreakpointLocation API's.
You can either provide the function name, or function body text. Also propagate the compilation error up from where it is checked so we can report compilation errors. <rdar://problem/9898371> llvm-svn: 205380
This commit is contained in:
parent
3c2b13b258
commit
d80102e420
@ -117,7 +117,13 @@ public:
|
||||
|
||||
void
|
||||
SetCallback (BreakpointHitCallback callback, void *baton);
|
||||
|
||||
|
||||
void
|
||||
SetScriptCallbackFunction (const char *callback_function_name);
|
||||
|
||||
SBError
|
||||
SetScriptCallbackBody (const char *script_body_text);
|
||||
|
||||
size_t
|
||||
GetNumResolvedLocations() const;
|
||||
|
||||
|
@ -58,6 +58,12 @@ public:
|
||||
const char *
|
||||
GetCondition ();
|
||||
|
||||
void
|
||||
SetScriptCallbackFunction (const char *callback_function_name);
|
||||
|
||||
SBError
|
||||
SetScriptCallbackBody (const char *script_body_text);
|
||||
|
||||
void
|
||||
SetThreadID (lldb::tid_t sb_thread_id);
|
||||
|
||||
|
@ -77,6 +77,8 @@ protected:
|
||||
friend class SBTarget;
|
||||
friend class SBValue;
|
||||
friend class SBWatchpoint;
|
||||
friend class SBBreakpoint;
|
||||
friend class SBBreakpointLocation;
|
||||
|
||||
lldb_private::Error *
|
||||
get();
|
||||
|
@ -254,16 +254,20 @@ public:
|
||||
return error;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
virtual Error
|
||||
ExportFunctionDefinitionToInterpreter (StringList &function_def)
|
||||
{
|
||||
return false;
|
||||
Error error;
|
||||
error.SetErrorString("not implemented");
|
||||
return error;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
virtual Error
|
||||
GenerateBreakpointCommandCallbackData (StringList &input, std::string& output)
|
||||
{
|
||||
return false;
|
||||
Error error;
|
||||
error.SetErrorString("not implemented");
|
||||
return error;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
@ -359,10 +363,12 @@ public:
|
||||
return lldb::ScriptInterpreterObjectSP();
|
||||
}
|
||||
|
||||
virtual bool
|
||||
virtual Error
|
||||
GenerateFunction(const char *signature, const StringList &input)
|
||||
{
|
||||
return false;
|
||||
Error error;
|
||||
error.SetErrorString("unimplemented");
|
||||
return error;
|
||||
}
|
||||
|
||||
virtual void
|
||||
@ -373,10 +379,20 @@ public:
|
||||
CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
|
||||
CommandReturnObject &result);
|
||||
|
||||
/// Set the specified text as the callback for the breakpoint.
|
||||
virtual Error
|
||||
SetBreakpointCommandCallback (BreakpointOptions *bp_options,
|
||||
const char *callback_text)
|
||||
{
|
||||
Error error;
|
||||
error.SetErrorString("unimplemented");
|
||||
return error;
|
||||
}
|
||||
|
||||
/// Set a one-liner as the callback for the breakpoint.
|
||||
virtual void
|
||||
SetBreakpointCommandCallback (BreakpointOptions *bp_options,
|
||||
const char *oneliner)
|
||||
SetBreakpointCommandCallbackFunction (BreakpointOptions *bp_options,
|
||||
const char *function_name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
ExecuteMultipleLines (const char *in_string,
|
||||
const ExecuteScriptOptions &options = ExecuteScriptOptions());
|
||||
|
||||
bool
|
||||
Error
|
||||
ExportFunctionDefinitionToInterpreter (StringList &function_def);
|
||||
|
||||
bool
|
||||
@ -130,10 +130,10 @@ public:
|
||||
lldb_private::CommandReturnObject& cmd_retobj,
|
||||
Error& error);
|
||||
|
||||
bool
|
||||
Error
|
||||
GenerateFunction(const char *signature, const StringList &input);
|
||||
|
||||
bool
|
||||
Error
|
||||
GenerateBreakpointCommandCallbackData (StringList &input, std::string& output);
|
||||
|
||||
bool
|
||||
@ -227,10 +227,14 @@ public:
|
||||
CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
|
||||
CommandReturnObject &result);
|
||||
|
||||
/// Set a Python one-liner as the callback for the breakpoint.
|
||||
void
|
||||
/// Set the callback body text into the callback for the breakpoint.
|
||||
Error
|
||||
SetBreakpointCommandCallback (BreakpointOptions *bp_options,
|
||||
const char *oneliner);
|
||||
const char *callback_body);
|
||||
|
||||
void
|
||||
SetBreakpointCommandCallbackFunction (BreakpointOptions *bp_options,
|
||||
const char *function_name);
|
||||
|
||||
/// Set a one-liner as the callback for the watchpoint.
|
||||
void
|
||||
|
@ -177,9 +177,29 @@ public:
|
||||
const char *
|
||||
GetQueueName () const;
|
||||
|
||||
%feature("docstring", "
|
||||
//------------------------------------------------------------------
|
||||
/// Set the name of the script function to be called when the breakpoint is hit.
|
||||
//------------------------------------------------------------------
|
||||
") SetScriptCallbackFunction;
|
||||
void
|
||||
SetCallback (BreakpointHitCallback callback, void *baton);
|
||||
SetScriptCallbackFunction (const char *callback_function_name);
|
||||
|
||||
%feature("docstring", "
|
||||
//------------------------------------------------------------------
|
||||
/// Provide the body for the script function to be called when the breakpoint is hit.
|
||||
/// The body will be wrapped in a function, which be passed two arguments:
|
||||
/// 'frame' - which holds the bottom-most SBFrame of the thread that hit the breakpoint
|
||||
/// 'bpno' - which is the SBBreakpointLocation to which the callback was attached.
|
||||
///
|
||||
/// The error parameter is currently ignored, but will at some point hold the Python
|
||||
/// compilation diagnostics.
|
||||
/// Returns true if the body compiles successfully, false if not.
|
||||
//------------------------------------------------------------------
|
||||
") SetScriptCallbackBody;
|
||||
SBError
|
||||
SetScriptCallbackBody (const char *script_body_text);
|
||||
|
||||
size_t
|
||||
GetNumResolvedLocations() const;
|
||||
|
||||
|
@ -70,6 +70,29 @@ public:
|
||||
const char *
|
||||
GetCondition ();
|
||||
|
||||
%feature("docstring", "
|
||||
//------------------------------------------------------------------
|
||||
/// Set the callback to the given Python function name.
|
||||
//------------------------------------------------------------------
|
||||
") SetScriptCallbackFunction;
|
||||
void
|
||||
SetScriptCallbackFunction (const char *callback_function_name);
|
||||
|
||||
%feature("docstring", "
|
||||
//------------------------------------------------------------------
|
||||
/// Provide the body for the script function to be called when the breakpoint location is hit.
|
||||
/// The body will be wrapped in a function, which be passed two arguments:
|
||||
/// 'frame' - which holds the bottom-most SBFrame of the thread that hit the breakpoint
|
||||
/// 'bpno' - which is the SBBreakpointLocation to which the callback was attached.
|
||||
///
|
||||
/// The error parameter is currently ignored, but will at some point hold the Python
|
||||
/// compilation diagnostics.
|
||||
/// Returns true if the body compiles successfully, false if not.
|
||||
//------------------------------------------------------------------
|
||||
") SetScriptCallbackBody;
|
||||
SBError
|
||||
SetScriptCallbackBody (const char *script_body_text);
|
||||
|
||||
void
|
||||
SetThreadID (lldb::tid_t sb_thread_id);
|
||||
|
||||
|
@ -19,9 +19,12 @@
|
||||
#include "lldb/Breakpoint/BreakpointLocation.h"
|
||||
#include "lldb/Breakpoint/StoppointCallbackContext.h"
|
||||
#include "lldb/Core/Address.h"
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/Log.h"
|
||||
#include "lldb/Core/Stream.h"
|
||||
#include "lldb/Core/StreamFile.h"
|
||||
#include "lldb/Interpreter/CommandInterpreter.h"
|
||||
#include "lldb/Interpreter/ScriptInterpreter.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
#include "lldb/Target/SectionLoadList.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
@ -579,6 +582,45 @@ SBBreakpoint::SetCallback (BreakpointHitCallback callback, void *baton)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SBBreakpoint::SetScriptCallbackFunction (const char *callback_function_name)
|
||||
{
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
|
||||
|
||||
if (log)
|
||||
log->Printf ("SBBreakpoint(%p)::SetScriptCallbackFunction (callback=%s)", m_opaque_sp.get(), callback_function_name);
|
||||
|
||||
if (m_opaque_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
|
||||
BreakpointOptions *bp_options = m_opaque_sp->GetOptions();
|
||||
m_opaque_sp->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter()->SetBreakpointCommandCallbackFunction (bp_options,
|
||||
callback_function_name);
|
||||
}
|
||||
}
|
||||
|
||||
SBError
|
||||
SBBreakpoint::SetScriptCallbackBody (const char *callback_body_text)
|
||||
{
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
|
||||
|
||||
if (log)
|
||||
log->Printf ("SBBreakpoint(%p)::SetScriptCallbackBody: callback body:\n%s)", m_opaque_sp.get(), callback_body_text);
|
||||
|
||||
SBError sb_error;
|
||||
if (m_opaque_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex());
|
||||
BreakpointOptions *bp_options = m_opaque_sp->GetOptions();
|
||||
Error error = m_opaque_sp->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
|
||||
callback_body_text);
|
||||
sb_error.SetError(error);
|
||||
}
|
||||
else
|
||||
sb_error.SetErrorString("invalid breakpoint");
|
||||
|
||||
return sb_error;
|
||||
}
|
||||
|
||||
lldb_private::Breakpoint *
|
||||
SBBreakpoint::operator->() const
|
||||
|
@ -17,10 +17,13 @@
|
||||
#include "lldb/lldb-defines.h"
|
||||
#include "lldb/Breakpoint/Breakpoint.h"
|
||||
#include "lldb/Breakpoint/BreakpointLocation.h"
|
||||
#include "lldb/Target/ThreadSpec.h"
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/Log.h"
|
||||
#include "lldb/Core/Stream.h"
|
||||
#include "lldb/Core/StreamFile.h"
|
||||
#include "lldb/Interpreter/CommandInterpreter.h"
|
||||
#include "lldb/Interpreter/ScriptInterpreter.h"
|
||||
#include "lldb/Target/ThreadSpec.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/ThreadSpec.h"
|
||||
|
||||
@ -159,6 +162,47 @@ SBBreakpointLocation::GetCondition ()
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
SBBreakpointLocation::SetScriptCallbackFunction (const char *callback_function_name)
|
||||
{
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
|
||||
|
||||
if (log)
|
||||
log->Printf ("SBBreakpointLocation(%p)::SetScriptCallbackFunction (callback=%s)", m_opaque_sp.get(), callback_function_name);
|
||||
|
||||
if (m_opaque_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex());
|
||||
BreakpointOptions *bp_options = m_opaque_sp->GetLocationOptions();
|
||||
m_opaque_sp->GetBreakpoint().GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter()->SetBreakpointCommandCallbackFunction (bp_options,
|
||||
callback_function_name);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
SBError
|
||||
SBBreakpointLocation::SetScriptCallbackBody (const char *callback_body_text)
|
||||
{
|
||||
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API));
|
||||
|
||||
if (log)
|
||||
log->Printf ("SBBreakpoint(%p)::SetScriptCallbackBody: callback body:\n%s)", m_opaque_sp.get(), callback_body_text);
|
||||
|
||||
SBError sb_error;
|
||||
if (m_opaque_sp)
|
||||
{
|
||||
Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex());
|
||||
BreakpointOptions *bp_options = m_opaque_sp->GetLocationOptions();
|
||||
Error error = m_opaque_sp->GetBreakpoint().GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
|
||||
callback_body_text);
|
||||
sb_error.SetError(error);
|
||||
}
|
||||
else
|
||||
sb_error.SetErrorString("invalid breakpoint");
|
||||
|
||||
return sb_error;
|
||||
}
|
||||
|
||||
void
|
||||
SBBreakpointLocation::SetThreadID (tid_t thread_id)
|
||||
{
|
||||
|
@ -504,16 +504,10 @@ protected:
|
||||
m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
|
||||
m_options.m_one_liner.c_str());
|
||||
}
|
||||
// Special handling for using a Python function by name
|
||||
// instead of extending the breakpoint callback data structures, we just automatize
|
||||
// what the user would do manually: make their breakpoint command be a function call
|
||||
else if (m_options.m_function_name.size())
|
||||
{
|
||||
std::string oneliner("return ");
|
||||
oneliner += m_options.m_function_name;
|
||||
oneliner += "(frame, bp_loc, internal_dict)";
|
||||
m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
|
||||
oneliner.c_str());
|
||||
m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallbackFunction (bp_options,
|
||||
m_options.m_function_name.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -254,7 +254,7 @@ ScriptInterpreterPython::IOHandlerInputComplete (IOHandler &io_handler, std::str
|
||||
{
|
||||
data_ap->user_source.SplitIntoLines(data);
|
||||
|
||||
if (GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source))
|
||||
if (GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source).Success())
|
||||
{
|
||||
BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
|
||||
bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp);
|
||||
@ -1083,27 +1083,39 @@ ScriptInterpreterPython::CollectDataForWatchpointCommandCallback (WatchpointOpti
|
||||
m_interpreter.GetPythonCommandsFromIOHandler (" ", *this, true, wp_options);
|
||||
}
|
||||
|
||||
// Set a Python one-liner as the callback for the breakpoint.
|
||||
void
|
||||
ScriptInterpreterPython::SetBreakpointCommandCallbackFunction (BreakpointOptions *bp_options,
|
||||
const char *function_name)
|
||||
{
|
||||
// For now just cons up a oneliner that calls the provided function.
|
||||
std::string oneliner("return ");
|
||||
oneliner += function_name;
|
||||
oneliner += "(frame, bp_loc, internal_dict)";
|
||||
m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
|
||||
oneliner.c_str());
|
||||
}
|
||||
|
||||
// Set a Python one-liner as the callback for the breakpoint.
|
||||
Error
|
||||
ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_options,
|
||||
const char *oneliner)
|
||||
const char *command_body_text)
|
||||
{
|
||||
std::unique_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
|
||||
|
||||
// It's necessary to set both user_source and script_source to the oneliner.
|
||||
// The former is used to generate callback description (as in breakpoint command list)
|
||||
// while the latter is used for Python to interpret during the actual callback.
|
||||
|
||||
data_ap->user_source.AppendString (oneliner);
|
||||
data_ap->script_source.assign (oneliner);
|
||||
|
||||
if (GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source))
|
||||
// Split the command_body_text into lines, and pass that to GenerateBreakpointCommandCallbackData. That will
|
||||
// wrap the body in an auto-generated function, and return the function name in script_source. That is what
|
||||
// the callback will actually invoke.
|
||||
|
||||
data_ap->user_source.SplitIntoLines(command_body_text);
|
||||
Error error = GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source);
|
||||
if (error.Success())
|
||||
{
|
||||
BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
|
||||
bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp);
|
||||
return error;
|
||||
}
|
||||
|
||||
return;
|
||||
else
|
||||
return error;
|
||||
}
|
||||
|
||||
// Set a Python one-liner as the callback for the watchpoint.
|
||||
@ -1129,24 +1141,32 @@ ScriptInterpreterPython::SetWatchpointCommandCallback (WatchpointOptions *wp_opt
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
Error
|
||||
ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def)
|
||||
{
|
||||
// Convert StringList to one long, newline delimited, const char *.
|
||||
std::string function_def_string(function_def.CopyList());
|
||||
|
||||
return ExecuteMultipleLines (function_def_string.c_str(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false)).Success();
|
||||
Error error = ExecuteMultipleLines (function_def_string.c_str(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false));
|
||||
return error;
|
||||
}
|
||||
|
||||
bool
|
||||
Error
|
||||
ScriptInterpreterPython::GenerateFunction(const char *signature, const StringList &input)
|
||||
{
|
||||
Error error;
|
||||
int num_lines = input.GetSize ();
|
||||
if (num_lines == 0)
|
||||
return false;
|
||||
{
|
||||
error.SetErrorString ("No input data.");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!signature || *signature == 0)
|
||||
return false;
|
||||
{
|
||||
error.SetErrorString("No output function name.");
|
||||
return error;
|
||||
}
|
||||
|
||||
StreamString sstr;
|
||||
StringList auto_generated_function;
|
||||
@ -1173,11 +1193,9 @@ ScriptInterpreterPython::GenerateFunction(const char *signature, const StringLis
|
||||
|
||||
// Verify that the results are valid Python.
|
||||
|
||||
if (!ExportFunctionDefinitionToInterpreter (auto_generated_function))
|
||||
return false;
|
||||
error = ExportFunctionDefinitionToInterpreter (auto_generated_function);
|
||||
|
||||
return true;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1197,7 +1215,7 @@ ScriptInterpreterPython::GenerateTypeScriptFunction (StringList &user_input, std
|
||||
std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_type_print_func", num_created_functions, name_token));
|
||||
sstr.Printf ("def %s (valobj, internal_dict):", auto_generated_function_name.c_str());
|
||||
|
||||
if (!GenerateFunction(sstr.GetData(), user_input))
|
||||
if (!GenerateFunction(sstr.GetData(), user_input).Success())
|
||||
return false;
|
||||
|
||||
// Store the name of the auto-generated function to be called.
|
||||
@ -1220,7 +1238,7 @@ ScriptInterpreterPython::GenerateScriptAliasFunction (StringList &user_input, st
|
||||
|
||||
sstr.Printf ("def %s (debugger, args, result, internal_dict):", auto_generated_function_name.c_str());
|
||||
|
||||
if (!GenerateFunction(sstr.GetData(),user_input))
|
||||
if (!GenerateFunction(sstr.GetData(),user_input).Success())
|
||||
return false;
|
||||
|
||||
// Store the name of the auto-generated function to be called.
|
||||
@ -1266,7 +1284,7 @@ ScriptInterpreterPython::GenerateTypeSynthClass (StringList &user_input, std::st
|
||||
// Verify that the results are valid Python.
|
||||
// (even though the method is ExportFunctionDefinitionToInterpreter, a class will actually be exported)
|
||||
// (TODO: rename that method to ExportDefinitionToInterpreter)
|
||||
if (!ExportFunctionDefinitionToInterpreter (auto_generated_class))
|
||||
if (!ExportFunctionDefinitionToInterpreter (auto_generated_class).Success())
|
||||
return false;
|
||||
|
||||
// Store the name of the auto-generated class
|
||||
@ -1668,25 +1686,29 @@ ScriptInterpreterPython::GenerateTypeSynthClass (const char* oneliner, std::stri
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Error
|
||||
ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (StringList &user_input, std::string& output)
|
||||
{
|
||||
static uint32_t num_created_functions = 0;
|
||||
user_input.RemoveBlankLines ();
|
||||
StreamString sstr;
|
||||
|
||||
Error error;
|
||||
if (user_input.GetSize() == 0)
|
||||
return false;
|
||||
{
|
||||
error.SetErrorString("No input data.");
|
||||
return error;
|
||||
}
|
||||
|
||||
std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_bp_callback_func_",num_created_functions));
|
||||
sstr.Printf ("def %s (frame, bp_loc, internal_dict):", auto_generated_function_name.c_str());
|
||||
|
||||
if (!GenerateFunction(sstr.GetData(), user_input))
|
||||
return false;
|
||||
error = GenerateFunction(sstr.GetData(), user_input);
|
||||
if (!error.Success())
|
||||
return error;
|
||||
|
||||
// Store the name of the auto-generated function to be called.
|
||||
output.assign(auto_generated_function_name);
|
||||
return true;
|
||||
return error;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1702,7 +1724,7 @@ ScriptInterpreterPython::GenerateWatchpointCommandCallbackData (StringList &user
|
||||
std::string auto_generated_function_name(GenerateUniqueName("lldb_autogen_python_wp_callback_func_",num_created_functions));
|
||||
sstr.Printf ("def %s (frame, wp, internal_dict):", auto_generated_function_name.c_str());
|
||||
|
||||
if (!GenerateFunction(sstr.GetData(), user_input))
|
||||
if (!GenerateFunction(sstr.GetData(), user_input).Success())
|
||||
return false;
|
||||
|
||||
// Store the name of the auto-generated function to be called.
|
||||
|
Loading…
x
Reference in New Issue
Block a user