Skip to content

Commit 897dd35

Browse files
committed
[lldb/Interpreter] Add requirements to Scripted Interface abstract methods
This patch adds new requirements to the Scripted Interface abstract method checker to check the minimum number of argument for abstract methods. This check is done when creating the interface object so the object is not created if the user implementation doesn't match the abstract method requirement. Signed-off-by: Med Ismail Bennani <[email protected]>
1 parent 3793264 commit 897dd35

File tree

7 files changed

+158
-54
lines changed

7 files changed

+158
-54
lines changed

lldb/include/lldb/Interpreter/Interfaces/ScriptedInterface.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,22 @@ class ScriptedInterface {
3131
return m_object_instance_sp;
3232
}
3333

34-
virtual llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const = 0;
34+
struct AbstractMethodRequirement {
35+
llvm::StringLiteral name;
36+
size_t min_arg_count = 0;
37+
};
38+
39+
virtual llvm::SmallVector<AbstractMethodRequirement>
40+
GetAbstractMethodRequirements() const = 0;
41+
42+
llvm::SmallVector<llvm::StringLiteral> const GetAbstractMethods() const {
43+
llvm::SmallVector<llvm::StringLiteral> abstract_methods;
44+
llvm::transform(GetAbstractMethodRequirements(), abstract_methods.begin(),
45+
[](const AbstractMethodRequirement &requirement) {
46+
return requirement.name;
47+
});
48+
return abstract_methods;
49+
}
3550

3651
template <typename Ret>
3752
static Ret ErrorWithMessage(llvm::StringRef caller_name,

lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/OperatingSystemPythonInterface.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ class OperatingSystemPythonInterface
3131
StructuredData::DictionarySP args_sp,
3232
StructuredData::Generic *script_obj = nullptr) override;
3333

34-
llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
35-
return llvm::SmallVector<llvm::StringLiteral>({"get_thread_info"});
34+
llvm::SmallVector<AbstractMethodRequirement>
35+
GetAbstractMethodRequirements() const override {
36+
return llvm::SmallVector<AbstractMethodRequirement>({{"get_thread_info"}});
3637
}
3738

3839
StructuredData::DictionarySP CreateThread(lldb::tid_t tid,

lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPlatformPythonInterface.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ class ScriptedPlatformPythonInterface : public ScriptedPlatformInterface,
2929
StructuredData::DictionarySP args_sp,
3030
StructuredData::Generic *script_obj = nullptr) override;
3131

32-
llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
33-
return llvm::SmallVector<llvm::StringLiteral>(
34-
{"list_processes", "attach_to_process", "launch_process",
35-
"kill_process"});
32+
llvm::SmallVector<AbstractMethodRequirement>
33+
GetAbstractMethodRequirements() const override {
34+
return llvm::SmallVector<AbstractMethodRequirement>(
35+
{{"list_processes"},
36+
{"attach_to_process", 2},
37+
{"launch_process", 2},
38+
{"kill_process", 2}});
3639
}
3740

3841
StructuredData::DictionarySP ListProcesses() override;

lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedProcessPythonInterface.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
3131
StructuredData::DictionarySP args_sp,
3232
StructuredData::Generic *script_obj = nullptr) override;
3333

34-
llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
35-
return llvm::SmallVector<llvm::StringLiteral>(
36-
{"read_memory_at_address", "is_alive", "get_scripted_thread_plugin"});
34+
llvm::SmallVector<AbstractMethodRequirement>
35+
GetAbstractMethodRequirements() const override {
36+
return llvm::SmallVector<AbstractMethodRequirement>(
37+
{{"read_memory_at_address", 4},
38+
{"is_alive"},
39+
{"get_scripted_thread_plugin"}});
3740
}
3841

3942
StructuredData::DictionarySP GetCapabilities() override;

lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h

Lines changed: 120 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -36,37 +36,78 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
3636
eNotImplemented,
3737
eNotAllocated,
3838
eNotCallable,
39+
eUnknownArgumentCount,
40+
eInvalidArgumentCount,
3941
eValid
4042
};
4143

42-
llvm::Expected<std::map<llvm::StringLiteral, AbstractMethodCheckerCases>>
44+
struct AbstrackMethodCheckerPayload {
45+
46+
struct InvalidArgumentCountPayload {
47+
InvalidArgumentCountPayload(size_t required, size_t actual)
48+
: required_argument_count(required), actual_argument_count(actual) {}
49+
50+
size_t required_argument_count;
51+
size_t actual_argument_count;
52+
};
53+
54+
AbstractMethodCheckerCases checker_case;
55+
std::variant<std::monostate, InvalidArgumentCountPayload> payload;
56+
};
57+
58+
llvm::Expected<std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload>>
4359
CheckAbstractMethodImplementation(
4460
const python::PythonDictionary &class_dict) const {
4561

4662
using namespace python;
4763

48-
std::map<llvm::StringLiteral, AbstractMethodCheckerCases> checker;
49-
#define SET_ERROR_AND_CONTINUE(method_name, error) \
64+
std::map<llvm::StringLiteral, AbstrackMethodCheckerPayload> checker;
65+
#define SET_CASE_AND_CONTINUE(method_name, case) \
5066
{ \
51-
checker[method_name] = error; \
67+
checker[method_name] = {case, {}}; \
5268
continue; \
5369
}
5470

55-
for (const llvm::StringLiteral &method_name : GetAbstractMethods()) {
71+
for (const AbstractMethodRequirement &requirement :
72+
GetAbstractMethodRequirements()) {
73+
llvm::StringLiteral method_name = requirement.name;
5674
if (!class_dict.HasKey(method_name))
57-
SET_ERROR_AND_CONTINUE(method_name,
58-
AbstractMethodCheckerCases::eNotImplemented)
75+
SET_CASE_AND_CONTINUE(method_name,
76+
AbstractMethodCheckerCases::eNotImplemented)
5977
auto callable_or_err = class_dict.GetItem(method_name);
60-
if (!callable_or_err)
61-
SET_ERROR_AND_CONTINUE(method_name,
62-
AbstractMethodCheckerCases::eNotAllocated)
63-
if (!PythonCallable::Check(callable_or_err.get().get()))
64-
SET_ERROR_AND_CONTINUE(method_name,
65-
AbstractMethodCheckerCases::eNotCallable)
66-
checker[method_name] = AbstractMethodCheckerCases::eValid;
78+
if (!callable_or_err) {
79+
llvm::consumeError(callable_or_err.takeError());
80+
SET_CASE_AND_CONTINUE(method_name,
81+
AbstractMethodCheckerCases::eNotAllocated)
82+
}
83+
84+
PythonCallable callable = callable_or_err->AsType<PythonCallable>();
85+
if (!callable)
86+
SET_CASE_AND_CONTINUE(method_name,
87+
AbstractMethodCheckerCases::eNotCallable)
88+
89+
if (!requirement.min_arg_count)
90+
SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid)
91+
92+
auto arg_info_or_err = callable.GetArgInfo();
93+
if (!arg_info_or_err) {
94+
llvm::consumeError(arg_info_or_err.takeError());
95+
SET_CASE_AND_CONTINUE(method_name,
96+
AbstractMethodCheckerCases::eUnknownArgumentCount)
97+
}
98+
99+
PythonCallable::ArgInfo arg_info = *arg_info_or_err;
100+
if (requirement.min_arg_count <= arg_info.max_positional_args) {
101+
SET_CASE_AND_CONTINUE(method_name, AbstractMethodCheckerCases::eValid)
102+
} else {
103+
checker[method_name] = {
104+
AbstractMethodCheckerCases::eInvalidArgumentCount,
105+
AbstrackMethodCheckerPayload::InvalidArgumentCountPayload(
106+
requirement.min_arg_count, arg_info.max_positional_args)};
107+
}
67108
}
68109

69-
#undef HANDLE_ERROR
110+
#undef SET_CASE_AND_CONTINUE
70111

71112
return checker;
72113
}
@@ -78,8 +119,11 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
78119
using namespace python;
79120
using Locker = ScriptInterpreterPythonImpl::Locker;
80121

81-
auto create_error = [](std::string message) {
82-
return llvm::createStringError(llvm::inconvertibleErrorCode(), message);
122+
Log *log = GetLog(LLDBLog::Script);
123+
auto create_error = [](llvm::StringLiteral format, auto &&...ts) {
124+
return llvm::createStringError(
125+
llvm::formatv(format.data(), std::forward<decltype(ts)>(ts)...)
126+
.str());
83127
};
84128

85129
bool has_class_name = !class_name.empty();
@@ -107,16 +151,15 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
107151
PythonModule::MainModule().ResolveName<python::PythonDictionary>(
108152
m_interpreter.GetDictionaryName());
109153
if (!dict.IsAllocated())
110-
return create_error(
111-
llvm::formatv("Could not find interpreter dictionary: %s",
112-
m_interpreter.GetDictionaryName()));
154+
return create_error("Could not find interpreter dictionary: {0}",
155+
m_interpreter.GetDictionaryName());
113156

114157
auto init =
115158
PythonObject::ResolveNameWithDictionary<python::PythonCallable>(
116159
class_name, dict);
117160
if (!init.IsAllocated())
118-
return create_error(llvm::formatv("Could not find script class: {0}",
119-
class_name.data()));
161+
return create_error("Could not find script class: {0}",
162+
class_name.data());
120163

121164
std::tuple<Args...> original_args = std::forward_as_tuple(args...);
122165
auto transformed_args = TransformArgs(original_args);
@@ -186,36 +229,73 @@ class ScriptedPythonInterface : virtual public ScriptedInterface {
186229
if (!checker_or_err)
187230
return checker_or_err.takeError();
188231

232+
llvm::Error abstract_method_errors = llvm::Error::success();
189233
for (const auto &method_checker : *checker_or_err)
190-
switch (method_checker.second) {
234+
switch (method_checker.second.checker_case) {
191235
case AbstractMethodCheckerCases::eNotImplemented:
192-
LLDB_LOG(GetLog(LLDBLog::Script),
193-
"Abstract method {0}.{1} not implemented.",
194-
obj_class_name.GetString(), method_checker.first);
236+
abstract_method_errors = llvm::joinErrors(
237+
std::move(abstract_method_errors),
238+
std::move(create_error("Abstract method {0}.{1} not implemented.",
239+
obj_class_name.GetString(),
240+
method_checker.first)));
195241
break;
196242
case AbstractMethodCheckerCases::eNotAllocated:
197-
LLDB_LOG(GetLog(LLDBLog::Script),
198-
"Abstract method {0}.{1} not allocated.",
199-
obj_class_name.GetString(), method_checker.first);
243+
abstract_method_errors = llvm::joinErrors(
244+
std::move(abstract_method_errors),
245+
std::move(create_error("Abstract method {0}.{1} not allocated.",
246+
obj_class_name.GetString(),
247+
method_checker.first)));
200248
break;
201249
case AbstractMethodCheckerCases::eNotCallable:
202-
LLDB_LOG(GetLog(LLDBLog::Script),
203-
"Abstract method {0}.{1} not callable.",
204-
obj_class_name.GetString(), method_checker.first);
250+
abstract_method_errors = llvm::joinErrors(
251+
std::move(abstract_method_errors),
252+
std::move(create_error("Abstract method {0}.{1} not callable.",
253+
obj_class_name.GetString(),
254+
method_checker.first)));
255+
break;
256+
case AbstractMethodCheckerCases::eUnknownArgumentCount:
257+
abstract_method_errors = llvm::joinErrors(
258+
std::move(abstract_method_errors),
259+
std::move(create_error(
260+
"Abstract method {0}.{1} has unknown argument count.",
261+
obj_class_name.GetString(), method_checker.first)));
205262
break;
263+
case AbstractMethodCheckerCases::eInvalidArgumentCount: {
264+
auto &payload_variant = method_checker.second.payload;
265+
if (!std::holds_alternative<
266+
AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>(
267+
payload_variant)) {
268+
abstract_method_errors = llvm::joinErrors(
269+
std::move(abstract_method_errors),
270+
std::move(create_error(
271+
"Abstract method {0}.{1} has unexpected argument count.",
272+
obj_class_name.GetString(), method_checker.first)));
273+
} else {
274+
auto payload = std::get<
275+
AbstrackMethodCheckerPayload::InvalidArgumentCountPayload>(
276+
payload_variant);
277+
abstract_method_errors = llvm::joinErrors(
278+
std::move(abstract_method_errors),
279+
std::move(
280+
create_error("Abstract method {0}.{1} has unexpected "
281+
"argument count (expected {2} but has {3}).",
282+
obj_class_name.GetString(), method_checker.first,
283+
payload.required_argument_count,
284+
payload.actual_argument_count)));
285+
}
286+
} break;
206287
case AbstractMethodCheckerCases::eValid:
207-
LLDB_LOG(GetLog(LLDBLog::Script),
208-
"Abstract method {0}.{1} implemented & valid.",
288+
LLDB_LOG(log, "Abstract method {0}.{1} implemented & valid.",
209289
obj_class_name.GetString(), method_checker.first);
210290
break;
211291
}
212292

213-
for (const auto &method_checker : *checker_or_err)
214-
if (method_checker.second != AbstractMethodCheckerCases::eValid)
215-
return create_error(
216-
llvm::formatv("Abstract method {0}.{1} missing. Enable lldb "
217-
"script log for more details.",
218-
obj_class_name.GetString(), method_checker.first));
293+
if (abstract_method_errors) {
294+
Status error = Status::FromError(std::move(abstract_method_errors));
295+
LLDB_LOG(log, "Abstract method error in {0}:\n{1}", class_name,
296+
error.AsCString());
297+
return error.ToError();
298+
}
219299

220300
m_object_instance_sp = StructuredData::GenericSP(
221301
new StructuredPythonObject(std::move(result)));

lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPlanPythonInterface.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class ScriptedThreadPlanPythonInterface : public ScriptedThreadPlanInterface,
3030
lldb::ThreadPlanSP thread_plan_sp,
3131
const StructuredDataImpl &args_sp) override;
3232

33-
llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
33+
llvm::SmallVector<AbstractMethodRequirement>
34+
GetAbstractMethodRequirements() const override {
3435
return {};
3536
}
3637

lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ class ScriptedThreadPythonInterface : public ScriptedThreadInterface,
2828
StructuredData::DictionarySP args_sp,
2929
StructuredData::Generic *script_obj = nullptr) override;
3030

31-
llvm::SmallVector<llvm::StringLiteral> GetAbstractMethods() const override {
32-
return llvm::SmallVector<llvm::StringLiteral>(
33-
{"get_stop_reason", "get_register_context"});
31+
llvm::SmallVector<AbstractMethodRequirement>
32+
GetAbstractMethodRequirements() const override {
33+
return llvm::SmallVector<AbstractMethodRequirement>(
34+
{{"get_stop_reason"}, {"get_register_context"}});
3435
}
3536

3637
lldb::tid_t GetThreadID() override;

0 commit comments

Comments
 (0)