Skip to content

Commit 3e24f6a

Browse files
committed
[WARP] Improved fetch dialog UX
- Respects views fetch batch size and allowed tags - Saves and loads from the view settings instead of qt settings
1 parent aafb1e7 commit 3e24f6a

File tree

7 files changed

+58
-83
lines changed

7 files changed

+58
-83
lines changed

plugins/warp/ui/matches.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,6 @@ WarpCurrentFunctionWidget::WarpCurrentFunctionWidget()
110110
void WarpCurrentFunctionWidget::SetFetcher(std::shared_ptr<WarpFetcher> fetcher)
111111
{
112112
m_fetcher = fetcher;
113-
// TODO: We need to remove the completion callback from the previously set fetcher.
114-
m_fetcher->AddCompletionCallback([this]() {
115-
// TODO: This is a little bit underspecified, we may end up updating more than strictly necessary.
116-
// Once this function has been fetched, we need to update the matches for this widget.
117-
UpdateMatches();
118-
return KeepCallback;
119-
});
120113
}
121114

122115
void WarpCurrentFunctionWidget::SetCurrentFunction(FunctionRef current)
@@ -136,7 +129,8 @@ void WarpCurrentFunctionWidget::SetCurrentFunction(FunctionRef current)
136129
{
137130
BinaryNinja::WorkerPriorityEnqueue([this]() {
138131
BinaryNinja::Ref bgTask = new BinaryNinja::BackgroundTask("Fetching WARP Functions...", true);
139-
m_fetcher->FetchPendingFunctions();
132+
const auto allowedTags = GetAllowedTagsFromView(m_current->GetView());
133+
m_fetcher->FetchPendingFunctions(allowedTags);
140134
bgTask->Finish();
141135
});
142136
}

plugins/warp/ui/plugin.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,22 @@ WarpSidebarWidget::WarpSidebarWidget(BinaryViewRef data) : SidebarWidget("WARP")
163163
tabWidget->addTab(matchedFrame, "Matched Functions");
164164
tabWidget->addTab(containerFrame, "Containers");
165165

166+
layout->addWidget(tabWidget);
167+
this->setLayout(layout);
168+
166169
// Do a full update if analysis has been done, otherwise we may persist old data and not have new data.
167170
m_analysisEvent = new AnalysisCompletionEvent(m_data, [this]() {
168171
ExecuteOnMainThread([this]() {
169172
Update();
170173
});
171174
});
172175

173-
std::shared_ptr<WarpFetcher> fetcher = WarpFetcher::Global();
176+
const std::shared_ptr<WarpFetcher> fetcher = WarpFetcher::Global();
174177
fetcher->AddCompletionCallback([this]() {
175178
Update();
176179
return KeepCallback;
177180
});
178181

179-
layout->addWidget(tabWidget);
180-
this->setLayout(layout);
181-
182182
// NOTE: This fetcher is shared with the fetch dialog that is constructed on initialization of this plugin.
183183
m_currentFunctionWidget->SetFetcher(fetcher);
184184
}

plugins/warp/ui/shared/fetchdialog.cpp

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "action.h"
99
#include "fetcher.h"
10+
#include "misc.h"
1011

1112
using namespace BinaryNinja;
1213

@@ -35,8 +36,6 @@ WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv,
3536
for (const auto &c: m_containers)
3637
m_containerCombo->addItem(QString::fromStdString(c->GetName()));
3738

38-
// TODO: Need to add tooltip to explain that a source must have atleast one of these tags to be considered.
39-
4039
// Tags editor
4140
m_tagsList = new QListWidget(this);
4241
m_addTagBtn = new QPushButton(this);
@@ -67,13 +66,13 @@ WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv,
6766
m_tagsList->setToolTip("A source must have atleast ONE of these tags to be considered");
6867

6968
// Defaults from processor tags
70-
for (const auto &t: m_fetchProcessor->GetTags())
69+
for (const auto &t: GetAllowedTagsFromView(m_bv))
7170
AddListItem(m_tagsList, QString::fromStdString(t));
7271

7372
// Batch size and matcher checkbox
7473
m_batchSize = new QSpinBox(this);
7574
m_batchSize->setRange(10, 1000);
76-
m_batchSize->setValue(100);
75+
m_batchSize->setValue(GetBatchSizeFromView(m_bv));
7776
m_batchSize->setToolTip("Number of functions to fetch in each batch");
7877

7978
m_rerunMatcher = new QCheckBox("Re-run matcher after fetch", this);
@@ -84,17 +83,14 @@ WarpFetchDialog::WarpFetchDialog(BinaryViewRef bv,
8483
m_clearProcessed->setChecked(false);
8584

8685
form->addRow(new QLabel("Container: "), m_containerCombo);
87-
// TODO: Need to plumb this through to the fetcher, and also likely have a blacklisted or whitelist mode for this dialog.
88-
// TODO: Alos wan to prefill the list of sources from the view/global settings.
89-
// form->addRow(new QLabel("Allowed Sources: "), srcWrapper);
9086
form->addRow(new QLabel("Allowed Tags: "), tagWrapper);
9187
form->addRow(new QLabel("Batch Size: "), m_batchSize);
9288
form->addRow(m_rerunMatcher);
9389
form->addRow(m_clearProcessed);
9490

9591
auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
9692
connect(buttons, &QDialogButtonBox::accepted, this, &WarpFetchDialog::onAccept);
97-
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
93+
connect(buttons, &QDialogButtonBox::rejected, this, &WarpFetchDialog::onReject);
9894

9995
auto root = new QVBoxLayout(this);
10096
root->addLayout(form);
@@ -149,12 +145,12 @@ void WarpFetchDialog::onAccept()
149145
if (idx > 0) // 0 == All Containers
150146
containerIndex = static_cast<size_t>(idx - 1);
151147

152-
auto tags = collectTags();
153148
const auto batch = static_cast<size_t>(m_batchSize->value());
154149
const bool rerun = m_rerunMatcher->isChecked();
155150

156-
// Persist tags to the shared processor for consistency across navigation
157-
m_fetchProcessor->SetTags(tags);
151+
const auto tags = collectTags();
152+
// Persist tags to the view settings.
153+
SetTagsToView(m_bv, tags);
158154

159155
if (m_clearProcessed->isChecked())
160156
m_fetchProcessor->ClearProcessed();
@@ -165,8 +161,16 @@ void WarpFetchDialog::onAccept()
165161
accept();
166162
}
167163

164+
void WarpFetchDialog::onReject()
165+
{
166+
const auto tags = collectTags();
167+
// Persist tags to the view settings.
168+
SetTagsToView(m_bv, tags);
169+
reject();
170+
}
171+
168172
void WarpFetchDialog::runBatchedFetch(const std::optional<size_t> &containerIndex,
169-
const std::vector<Warp::SourceTag> &tags,
173+
const std::vector<Warp::SourceTag> &allowedTags,
170174
size_t batchSize,
171175
bool rerunMatcher)
172176
{
@@ -186,7 +190,7 @@ void WarpFetchDialog::runBatchedFetch(const std::optional<size_t> &containerInde
186190
auto bv = m_bv;
187191

188192
// TODO: Too many captures in this thing lol.
189-
WorkerInteractiveEnqueue([fetcher, bv, funcs = std::move(funcs), batchSize, rerunMatcher, task]() mutable {
193+
WorkerInteractiveEnqueue([fetcher, bv, funcs = std::move(funcs), batchSize, rerunMatcher, task, allowedTags]() mutable {
190194
size_t processed = 0;
191195
size_t batchIndex = 0;
192196

@@ -198,7 +202,7 @@ void WarpFetchDialog::runBatchedFetch(const std::optional<size_t> &containerInde
198202
for (size_t i = 0; i < thisBatchCount; ++i)
199203
fetcher->AddPendingFunction(funcs[processed + i]);
200204

201-
fetcher->FetchPendingFunctions();
205+
fetcher->FetchPendingFunctions(allowedTags);
202206

203207
++batchIndex;
204208
processed += thisBatchCount;
@@ -219,8 +223,6 @@ void RegisterWarpFetchFunctionsCommand()
219223
{
220224
// Register a UI action and bind it globally. Add it to the Tools menu.
221225
const QString actionName = "WARP\\Fetch";
222-
223-
// TODO: Because we register this in every widget this will happen, this is bad behavior!
224226
if (!UIAction::isActionRegistered(actionName))
225227
UIAction::registerAction(actionName);
226228

plugins/warp/ui/shared/fetchdialog.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ private slots:
4545

4646
void onAccept();
4747

48+
void onReject();
49+
4850
private:
4951
void populateContainers();
5052

5153
std::vector<Warp::SourceTag> collectTags() const;
5254

5355
void runBatchedFetch(const std::optional<size_t> &containerIndex,
54-
const std::vector<Warp::SourceTag> &tags,
56+
const std::vector<Warp::SourceTag> &allowedTags,
5557
size_t batchSize,
5658
bool rerunMatcher);
5759
};

plugins/warp/ui/shared/fetcher.cpp

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,8 @@
11
#include "fetcher.h"
22

3-
#include <QSettings>
4-
53
WarpFetcher::WarpFetcher()
64
{
75
m_logger = new BinaryNinja::Logger("WARP Fetcher");
8-
QSettings qtSettings;
9-
const QString key = "warp/allowedTags";
10-
11-
QStringList tags = qtSettings.value(key).toStringList();
12-
if (tags.isEmpty()) {
13-
tags = QStringList{ "official", "trusted" };
14-
qtSettings.setValue(key, tags);
15-
qtSettings.sync();
16-
}
17-
18-
std::vector<Warp::SourceTag> initialTags;
19-
initialTags.reserve(tags.size());
20-
for (const auto& t : tags)
21-
initialTags.emplace_back(t.trimmed().toStdString());
22-
23-
SetTags(initialTags);
246
}
257

268
void WarpFetcher::AddPendingFunction(const FunctionRef &func)
@@ -47,7 +29,7 @@ void WarpFetcher::ExecuteCompletionCallback()
4729
std::lock_guard<std::mutex> lock(m_requestMutex);
4830
m_completionCallbacks.erase(
4931
std::ranges::remove_if(m_completionCallbacks,
50-
[](const auto &cb) { return cb() != RemoveCallback; }).begin(),
32+
[](const auto &cb) { return cb() == RemoveCallback; }).begin(),
5133
m_completionCallbacks.end());
5234
});
5335
}
@@ -58,7 +40,7 @@ std::shared_ptr<WarpFetcher> WarpFetcher::Global()
5840
return global;
5941
}
6042

61-
void WarpFetcher::FetchPendingFunctions()
43+
void WarpFetcher::FetchPendingFunctions(const std::vector<Warp::SourceTag>& allowedTags)
6244
{
6345
m_requestInProgress = true;
6446
const auto requests = FlushPendingFunctions();
@@ -82,13 +64,12 @@ void WarpFetcher::FetchPendingFunctions()
8264
platformMappedGuids[platform].push_back(guid.value());
8365
}
8466

85-
const auto tags = GetTags();
8667
for (const auto &[platform, guids] : platformMappedGuids)
8768
{
8869
m_logger->LogDebugF("Fetching {} functions for platform {}", guids.size(), platform->GetName());
8970
auto target = Warp::Target::FromPlatform(*platform);
9071
for (const auto &container: Warp::Container::All())
91-
container->FetchFunctions(*target, guids, tags);
72+
container->FetchFunctions(*target, guids, allowedTags);
9273

9374
std::lock_guard<std::mutex> lock(m_requestMutex);
9475
for (const auto &guid: guids)

plugins/warp/ui/shared/fetcher.h

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include <unordered_set>
66
#include <vector>
77
#include <functional>
8-
#include <QSettings>
98

109
#include "warp.h"
1110
#include "binaryninjaapi.h"
@@ -24,7 +23,6 @@ class WarpFetcher
2423

2524
std::mutex m_requestMutex;
2625
std::vector<FunctionRef> m_pendingRequests;
27-
// TODO: Easy way to clear this if user wants to refetch.
2826
std::unordered_set<Warp::FunctionGUID> m_processedGuids;
2927

3028
// List of callbacks to call when done fetching data, assume that others are using this as well.
@@ -38,35 +36,6 @@ class WarpFetcher
3836

3937
std::atomic<bool> m_requestInProgress = false;
4038

41-
// Set the allowed source tags, sources with none of these tags will not be fetched from.
42-
void SetTags(const std::vector<Warp::SourceTag> &tags)
43-
{
44-
std::lock_guard<std::mutex> lock(m_requestMutex);
45-
// TODO: This is kinda a hack, the fetcher instance should not sync through qt settings!
46-
QStringList qtTags = {};
47-
for (const auto& t : tags)
48-
qtTags.append(QString::fromStdString(t));
49-
QSettings().setValue("warp/allowedTags", qtTags);
50-
}
51-
52-
std::vector<Warp::SourceTag> GetTags() const
53-
{
54-
std::lock_guard<std::mutex> lock(const_cast<std::mutex &>(m_requestMutex));
55-
// TODO: This is kinda a hack, the fetcher instance should not sync through qt settings!
56-
QSettings qtSettings;
57-
QStringList tags = qtSettings.value("warp/allowedTags").toStringList();
58-
if (tags.isEmpty()) {
59-
// The default tags to allow.
60-
tags = QStringList{ "official", "trusted" };
61-
qtSettings.setValue("warp/allowedTags", tags);
62-
qtSettings.sync();
63-
}
64-
std::vector<Warp::SourceTag> initialTags = {};
65-
for (const auto& t : tags)
66-
initialTags.emplace_back(t.trimmed().toStdString());
67-
return initialTags;
68-
}
69-
7039
void AddCompletionCallback(std::function<WarpFetchCompletionStatus()> cb)
7140
{
7241
std::lock_guard<std::mutex> lock(m_requestMutex);
@@ -75,7 +44,7 @@ class WarpFetcher
7544

7645
void AddPendingFunction(const FunctionRef &func);
7746

78-
void FetchPendingFunctions();
47+
void FetchPendingFunctions(const std::vector<Warp::SourceTag>& allowedTags);
7948

8049
void ClearProcessed();
8150
private:

plugins/warp/ui/shared/misc.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,30 @@ struct ParsedQuery
112112
return it.value();
113113
}
114114
};
115+
116+
constexpr const char* ALLOWED_TAGS_SETTING = "warp.fetcher.allowedSourceTags";
117+
constexpr const char* BATCH_SIZE_SETTING = "warp.fetcher.fetchBatchSize";
118+
119+
inline std::vector<Warp::SourceTag> GetAllowedTagsFromView(const BinaryViewRef& view)
120+
{
121+
auto settings = BinaryNinja::Settings::Instance();
122+
if (!settings->Contains(ALLOWED_TAGS_SETTING))
123+
return {};
124+
return settings->Get<std::vector<std::string>>(ALLOWED_TAGS_SETTING, view);
125+
}
126+
127+
inline void SetTagsToView(const BinaryViewRef& view, const std::vector<Warp::SourceTag>& tags)
128+
{
129+
auto settings = BinaryNinja::Settings::Instance();
130+
if (!settings->Contains(ALLOWED_TAGS_SETTING))
131+
return;
132+
settings->Set(ALLOWED_TAGS_SETTING, tags, view);
133+
}
134+
135+
inline int GetBatchSizeFromView(const BinaryViewRef& view)
136+
{
137+
auto settings = BinaryNinja::Settings::Instance();
138+
if (!settings->Contains(BATCH_SIZE_SETTING))
139+
return 100;
140+
return settings->Get<uint64_t>(BATCH_SIZE_SETTING, view);
141+
}

0 commit comments

Comments
 (0)