Skip to content

Commit 8996cfa

Browse files
committed
[ObjC] Improve naming of arguments to Objective-C methods
Detect and remove common prefixes used on selectors so that their argument names are more natural. For instance, a selector of `initWithURL:withStagedURL`: now ends up with arguments named `URL` and `stagedURL`, rather than `initWithURL` and `withStagedURL`.
1 parent ee11cbb commit 8996cfa

File tree

1 file changed

+41
-3
lines changed

1 file changed

+41
-3
lines changed

objectivec/objc.cpp

+41-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,41 @@
33

44
using namespace BinaryNinja;
55

6+
namespace {
7+
8+
// Given a selector component such as `initWithPath' and a prefix of `initWith`, returns `path`.
9+
std::optional<std::string> SelectorComponentWithoutPrefix(std::string_view prefix, std::string_view component)
10+
{
11+
if (component.size() <= prefix.size() || component.rfind(prefix.data(), 0) != 0
12+
|| !isupper(component[prefix.size()]))
13+
{
14+
return std::nullopt;
15+
}
16+
17+
std::string result(component.substr(prefix.size()));
18+
19+
// Lowercase the first character if the second character is not also uppercase.
20+
// This ensures we leave initialisms such as `URL` alone.
21+
if (result.size() > 1 && islower(result[1]))
22+
result[0] = tolower(result[0]);
23+
24+
return result;
25+
}
26+
27+
std::string ArgumentNameFromSelectorComponent(std::string component)
28+
{
29+
// TODO: Handle other common patterns such as <do some action>With<arg>: and <do some action>For<arg>:
30+
for (const auto& prefix : {"initWith", "with", "and", "using", "set", "read", "to", "for"})
31+
{
32+
if (auto argumentName = SelectorComponentWithoutPrefix(prefix, component); argumentName.has_value())
33+
return std::move(*argumentName);
34+
}
35+
36+
return component;
37+
}
38+
39+
} // namespace
40+
641
Ref<Metadata> ObjCProcessor::SerializeMethod(uint64_t loc, const Method& method)
742
{
843
std::map<std::string, Ref<Metadata>> methodMeta;
@@ -1099,10 +1134,13 @@ bool ObjCProcessor::ApplyMethodType(Class& cls, Method& method, bool isInstanceM
10991134

11001135
for (size_t i = 3; i < typeTokens.size(); i++)
11011136
{
1102-
std::string suffix;
1137+
std::string name;
1138+
if (selectorTokens.size() > i - 3)
1139+
name = ArgumentNameFromSelectorComponent(selectorTokens[i - 3]);
1140+
else
1141+
name = "arg";
11031142

1104-
params.push_back({selectorTokens.size() > i - 3 ? selectorTokens[i - 3] : "arg",
1105-
typeForQualifiedNameOrType(typeTokens[i]), true, BinaryNinja::Variable()});
1143+
params.push_back({std::move(name), typeForQualifiedNameOrType(typeTokens[i]), true, BinaryNinja::Variable()});
11061144
}
11071145

11081146
auto funcType = BinaryNinja::Type::FunctionType(retType, cc, params);

0 commit comments

Comments
 (0)