Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions binaryninjaapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -13537,6 +13537,32 @@ namespace BinaryNinja {
\return The HLIL for this Function if it's available.
*/
Ref<HighLevelILFunction> GetHighLevelILIfAvailable() const;

/*! Compute a code complexity score for this function.

Several independent metrics are available, each isolating a different factor that
contributes to how hard a function is to read, test, or reason about: code length,
the number/density of branches, the diversity of instruction types used, or the
nesting depth of its control flow. A weighted composite of all of them is also
available. Two further metrics ("fan_out", "transitive") look past the function's own
body at what it calls, since a thin dispatcher function can look trivial on every
purely intraprocedural metric while still requiring a lot of downstream code to be
read to understand what it actually does. See \c GetComplexityMetricNames for the
full list of valid names.

\param metric Name of the metric to compute. Defaults to "composite".
\return The computed complexity score. Higher means more complex. Scores are only
meaningful to compare between functions when computed with the same metric.
\throws std::invalid_argument if `metric` is not a recognized metric name.
*/
double GetComplexity(const std::string& metric = "composite") const;

/*! Names accepted by \c GetComplexity

\return The list of valid complexity metric names
*/
static std::vector<std::string> GetComplexityMetricNames();

Ref<LanguageRepresentationFunction> GetLanguageRepresentation(const std::string& language = "Pseudo C") const;
Ref<LanguageRepresentationFunction> GetLanguageRepresentationIfAvailable(
const std::string& language = "Pseudo C") const;
Expand Down
11 changes: 11 additions & 0 deletions binaryninjacore.h
Original file line number Diff line number Diff line change
Expand Up @@ -6025,6 +6025,17 @@ extern "C"
BINARYNINJACOREAPI BNLanguageRepresentationFunction* BNGetFunctionLanguageRepresentationIfAvailable(
BNFunction* func, const char* language);

// Computes a code complexity score for `func` using the named metric (e.g. "composite",
// "cyclomatic", "transitive", ...) - see BNGetFunctionComplexityMetricNames for the full,
// current list. Returns 0.0 (and logs) for an unrecognized metric name rather than raising
// across the C ABI boundary; callers are expected to validate `metric` against
// BNGetFunctionComplexityMetricNames themselves first if they want to surface that as their
// own idiomatic error.
BINARYNINJACOREAPI double BNGetFunctionComplexity(BNFunction* func, const char* metric);
// Names accepted by BNGetFunctionComplexity's `metric` parameter. Free the result with
// BNFreeStringList.
BINARYNINJACOREAPI char** BNGetFunctionComplexityMetricNames(size_t* count);

BINARYNINJACOREAPI BNDataBuffer* BNGetConstantData(BNFunction* func, BNRegisterValueType state, uint64_t value,
size_t size, BNBuiltinType* builtin);

Expand Down
35 changes: 35 additions & 0 deletions function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "binaryninjaapi.h"
#include "mediumlevelilinstruction.h"
#include "highlevelilinstruction.h"
#include <algorithm>
#include <cstring>

#include "ffi.h"
Expand Down Expand Up @@ -739,6 +740,40 @@ Ref<LanguageRepresentationFunction> Function::GetLanguageRepresentationIfAvailab
}


double Function::GetComplexity(const string& metric) const
{
// Validated here, client-side, rather than relying on the core: BNGetFunctionComplexity can't
// let a C++ exception cross the C ABI boundary (fatal for other language bindings, e.g. Rust),
// so it just logs and returns 0.0 for an unrecognized name instead of throwing - which would
// silently violate this method's own documented `\throws std::invalid_argument` contract if
// nothing here caught it first.
vector<string> validMetrics = GetComplexityMetricNames();
if (find(validMetrics.begin(), validMetrics.end(), metric) == validMetrics.end())
{
string valid;
for (size_t i = 0; i < validMetrics.size(); i++)
valid += (i == 0 ? "" : ", ") + validMetrics[i];
throw std::invalid_argument("Unknown complexity metric \"" + metric + "\"; valid options are: " + valid);
}

return BNGetFunctionComplexity(m_object, metric.c_str());
}


vector<string> Function::GetComplexityMetricNames()
{
size_t count = 0;
char** names = BNGetFunctionComplexityMetricNames(&count);
vector<string> result;
result.reserve(count);
for (size_t i = 0; i < count; i++)
result.push_back(names[i]);

BNFreeStringList(names, count);
return result;
}


Ref<Type> Function::GetType() const
{
return new Type(BNGetFunctionType(m_object));
Expand Down
Loading
Loading