mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add configurable weights to CodeSize metric
This commit is contained in:
@@ -36,30 +36,71 @@ using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::util;
|
||||
|
||||
size_t CodeSize::codeSize(Statement const& _statement)
|
||||
size_t CodeWeights::costOf(Statement const& _statement) const
|
||||
{
|
||||
CodeSize cs;
|
||||
if (holds_alternative<ExpressionStatement>(_statement))
|
||||
return expressionStatementCost;
|
||||
else if (holds_alternative<Assignment>(_statement))
|
||||
return assignmentCost;
|
||||
else if (holds_alternative<VariableDeclaration>(_statement))
|
||||
return variableDeclarationCost;
|
||||
else if (holds_alternative<FunctionDefinition>(_statement))
|
||||
return functionDefinitionCost;
|
||||
else if (holds_alternative<If>(_statement))
|
||||
return ifCost;
|
||||
else if (holds_alternative<Switch>(_statement))
|
||||
return switchCost + caseCost * std::get<Switch>(_statement).cases.size();
|
||||
else if (holds_alternative<ForLoop>(_statement))
|
||||
return forLoopCost;
|
||||
else if (holds_alternative<Break>(_statement))
|
||||
return breakCost;
|
||||
else if (holds_alternative<Continue>(_statement))
|
||||
return continueCost;
|
||||
else if (holds_alternative<Leave>(_statement))
|
||||
return leaveCost;
|
||||
else if (holds_alternative<Block>(_statement))
|
||||
return blockCost;
|
||||
else
|
||||
yulAssert(false, "If you add a new statement type, you must update CodeWeights.");
|
||||
}
|
||||
|
||||
size_t CodeWeights::costOf(Expression const& _expression) const
|
||||
{
|
||||
if (holds_alternative<FunctionCall>(_expression))
|
||||
return functionCallCost;
|
||||
else if (holds_alternative<Identifier>(_expression))
|
||||
return identifierCost;
|
||||
else if (holds_alternative<Literal>(_expression))
|
||||
return literalCost;
|
||||
else
|
||||
yulAssert(false, "If you add a new expression type, you must update CodeWeights.");
|
||||
}
|
||||
|
||||
|
||||
size_t CodeSize::codeSize(Statement const& _statement, CodeWeights const& _weights)
|
||||
{
|
||||
CodeSize cs(true, _weights);
|
||||
cs.visit(_statement);
|
||||
return cs.m_size;
|
||||
}
|
||||
|
||||
size_t CodeSize::codeSize(Expression const& _expression)
|
||||
size_t CodeSize::codeSize(Expression const& _expression, CodeWeights const& _weights)
|
||||
{
|
||||
CodeSize cs;
|
||||
CodeSize cs(true, _weights);
|
||||
cs.visit(_expression);
|
||||
return cs.m_size;
|
||||
}
|
||||
|
||||
size_t CodeSize::codeSize(Block const& _block)
|
||||
size_t CodeSize::codeSize(Block const& _block, CodeWeights const& _weights)
|
||||
{
|
||||
CodeSize cs;
|
||||
CodeSize cs(true, _weights);
|
||||
cs(_block);
|
||||
return cs.m_size;
|
||||
}
|
||||
|
||||
size_t CodeSize::codeSizeIncludingFunctions(Block const& _block)
|
||||
size_t CodeSize::codeSizeIncludingFunctions(Block const& _block, CodeWeights const& _weights)
|
||||
{
|
||||
CodeSize cs(false);
|
||||
CodeSize cs(false, _weights);
|
||||
cs(_block);
|
||||
return cs.m_size;
|
||||
}
|
||||
@@ -68,32 +109,14 @@ void CodeSize::visit(Statement const& _statement)
|
||||
{
|
||||
if (holds_alternative<FunctionDefinition>(_statement) && m_ignoreFunctions)
|
||||
return;
|
||||
else if (
|
||||
holds_alternative<If>(_statement) ||
|
||||
holds_alternative<Break>(_statement) ||
|
||||
holds_alternative<Continue>(_statement) ||
|
||||
holds_alternative<Leave>(_statement)
|
||||
)
|
||||
m_size += 2;
|
||||
else if (holds_alternative<ForLoop>(_statement))
|
||||
m_size += 3;
|
||||
else if (holds_alternative<Switch>(_statement))
|
||||
m_size += 1 + 2 * std::get<Switch>(_statement).cases.size();
|
||||
else if (!(
|
||||
holds_alternative<Block>(_statement) ||
|
||||
holds_alternative<ExpressionStatement>(_statement) ||
|
||||
holds_alternative<Assignment>(_statement) ||
|
||||
holds_alternative<VariableDeclaration>(_statement)
|
||||
))
|
||||
++m_size;
|
||||
|
||||
m_size += m_weights.costOf(_statement);
|
||||
ASTWalker::visit(_statement);
|
||||
}
|
||||
|
||||
void CodeSize::visit(Expression const& _expression)
|
||||
{
|
||||
if (!holds_alternative<Identifier>(_expression))
|
||||
++m_size;
|
||||
m_size += m_weights.costOf(_expression);
|
||||
ASTWalker::visit(_expression);
|
||||
}
|
||||
|
||||
|
||||
+46
-11
@@ -30,33 +30,67 @@ struct Dialect;
|
||||
struct EVMDialect;
|
||||
|
||||
/**
|
||||
* Metric for the size of code.
|
||||
* More specifically, the number of AST nodes.
|
||||
* Ignores function definitions while traversing the AST by default.
|
||||
* If you want to know the size of a function, you have to invoke this on its body.
|
||||
* Weights to be assigned to specific yul statements and expressions by a metric.
|
||||
*
|
||||
* As an exception, the following AST elements have a cost of zero:
|
||||
* The default values are meant to reflect specifically the number of AST nodes.
|
||||
*
|
||||
* The following AST elements have a default cost of zero (because the cleanup phase would
|
||||
* remove them anyway or they are just wrappers around something else will be counted instead):
|
||||
* - expression statement (only the expression inside has a cost)
|
||||
* - block (only the statements inside have a cost)
|
||||
* - variable references
|
||||
* - variable declarations (only the right hand side has a cost)
|
||||
* - assignments (only the value has a cost)
|
||||
*
|
||||
* As another exception, each statement incurs and additional cost of one
|
||||
* Each statement incurs and additional cost of one
|
||||
* per jump/branch. This means if, break and continue statements have a cost of 2,
|
||||
* switch statements have a cost of 1 plus the number of cases times two,
|
||||
* and for loops cost 3.
|
||||
*/
|
||||
struct CodeWeights
|
||||
{
|
||||
// Statements
|
||||
size_t expressionStatementCost = 0;
|
||||
size_t assignmentCost = 0;
|
||||
size_t variableDeclarationCost = 0;
|
||||
size_t functionDefinitionCost = 1;
|
||||
size_t ifCost = 2;
|
||||
size_t switchCost = 1;
|
||||
size_t caseCost = 2;
|
||||
size_t forLoopCost = 3;
|
||||
size_t breakCost = 2;
|
||||
size_t continueCost = 2;
|
||||
size_t leaveCost = 2;
|
||||
size_t blockCost = 0;
|
||||
|
||||
// Expressions
|
||||
size_t functionCallCost = 1;
|
||||
size_t identifierCost = 0;
|
||||
size_t literalCost = 1;
|
||||
|
||||
size_t costOf(Statement const& _statement) const;
|
||||
size_t costOf(Expression const& _expression) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Metric for the size of code.
|
||||
* Ignores function definitions while traversing the AST by default.
|
||||
* If you want to know the size of a function, you have to invoke this on its body.
|
||||
*
|
||||
* The cost of each statement and expression type is configurable via CodeWeights.
|
||||
*/
|
||||
class CodeSize: public ASTWalker
|
||||
{
|
||||
public:
|
||||
static size_t codeSize(Statement const& _statement);
|
||||
static size_t codeSize(Expression const& _expression);
|
||||
static size_t codeSize(Block const& _block);
|
||||
static size_t codeSizeIncludingFunctions(Block const& _block);
|
||||
static size_t codeSize(Statement const& _statement, CodeWeights const& _weights = {});
|
||||
static size_t codeSize(Expression const& _expression, CodeWeights const& _weights = {});
|
||||
static size_t codeSize(Block const& _block, CodeWeights const& _weights = {});
|
||||
static size_t codeSizeIncludingFunctions(Block const& _block, CodeWeights const& _weights = {});
|
||||
|
||||
private:
|
||||
CodeSize(bool _ignoreFunctions = true): m_ignoreFunctions(_ignoreFunctions) {}
|
||||
CodeSize(bool _ignoreFunctions = true, CodeWeights const& _weights = {}):
|
||||
m_ignoreFunctions(_ignoreFunctions),
|
||||
m_weights(_weights) {}
|
||||
|
||||
void visit(Statement const& _statement) override;
|
||||
void visit(Expression const& _expression) override;
|
||||
@@ -64,6 +98,7 @@ private:
|
||||
private:
|
||||
bool m_ignoreFunctions;
|
||||
size_t m_size = 0;
|
||||
CodeWeights m_weights;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user