mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Introduce typed expressions.
This commit is contained in:
parent
23edc14398
commit
07fdbb309b
@ -404,9 +404,11 @@ string AssignmentStmtGenerator::visit()
|
|||||||
{
|
{
|
||||||
ExpressionGenerator exprGen{state};
|
ExpressionGenerator exprGen{state};
|
||||||
auto lhs = exprGen.randomLValueExpression();
|
auto lhs = exprGen.randomLValueExpression();
|
||||||
|
exprGen.resetNestingDepth();
|
||||||
if (!lhs.has_value())
|
if (!lhs.has_value())
|
||||||
return "\n";
|
return "\n";
|
||||||
auto rhs = exprGen.expression(lhs.value());
|
auto rhs = exprGen.rOrLValueExpression(lhs.value());
|
||||||
|
exprGen.resetNestingDepth();
|
||||||
if (!rhs.has_value())
|
if (!rhs.has_value())
|
||||||
return "\n";
|
return "\n";
|
||||||
auto operation = assignOp(lhs.value().first);
|
auto operation = assignOp(lhs.value().first);
|
||||||
@ -540,33 +542,57 @@ void FunctionGenerator::endVisit()
|
|||||||
mutator->generator<BlockStmtGenerator>()->resetNestingDepth();
|
mutator->generator<BlockStmtGenerator>()->resetNestingDepth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<pair<SolidityTypePtr, string>> ExpressionGenerator::liveVariables()
|
||||||
|
{
|
||||||
|
auto liveVariables = state->currentFunctionState()->inputs |
|
||||||
|
ranges::views::transform([](auto& _item) { return _item; }) |
|
||||||
|
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
||||||
|
liveVariables += state->currentFunctionState()->outputs |
|
||||||
|
ranges::views::transform([](auto& _item) { return _item; }) |
|
||||||
|
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
||||||
|
for (auto const& scope: state->currentFunctionState()->scopes)
|
||||||
|
liveVariables += scope->variables |
|
||||||
|
ranges::views::transform([](auto& _item) { return _item; }) |
|
||||||
|
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
||||||
|
return liveVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<pair<SolidityTypePtr, string>> ExpressionGenerator::liveVariables(
|
||||||
|
pair<SolidityTypePtr, string> _typeName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto liveTypedVariables = state->currentFunctionState()->inputs |
|
||||||
|
ranges::views::filter([&_typeName](auto& _item) {
|
||||||
|
return _item.first.index() == _typeName.first.index() &&
|
||||||
|
_item.second != _typeName.second &&
|
||||||
|
visit(TypeComparator{}, _item.first, _typeName.first);
|
||||||
|
}) |
|
||||||
|
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
||||||
|
liveTypedVariables += state->currentFunctionState()->outputs |
|
||||||
|
ranges::views::filter([&_typeName](auto& _item) {
|
||||||
|
return _item.first.index() == _typeName.first.index() &&
|
||||||
|
_item.second != _typeName.second &&
|
||||||
|
visit(TypeComparator{}, _item.first, _typeName.first);
|
||||||
|
}) |
|
||||||
|
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
||||||
|
for (auto const& scope: state->currentFunctionState()->scopes)
|
||||||
|
liveTypedVariables += scope->variables |
|
||||||
|
ranges::views::filter([&_typeName](auto& _item) {
|
||||||
|
return _item.first.index() == _typeName.first.index() &&
|
||||||
|
_item.second != _typeName.second &&
|
||||||
|
visit(TypeComparator{}, _item.first, _typeName.first);
|
||||||
|
}) |
|
||||||
|
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
||||||
|
return liveTypedVariables;
|
||||||
|
}
|
||||||
|
|
||||||
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::randomLValueExpression()
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::randomLValueExpression()
|
||||||
{
|
{
|
||||||
LValueExpr exprType = static_cast<LValueExpr>(
|
auto liveVars = liveVariables();
|
||||||
state->uRandDist->distributionOneToN(static_cast<size_t>(LValueExpr::TYPEMAX) - 1)
|
if (liveVars.empty())
|
||||||
);
|
return nullopt;
|
||||||
switch (exprType)
|
auto randomLValue = liveVars[state->uRandDist->distributionOneToN(liveVars.size()) - 1];
|
||||||
{
|
return lValueExpression(randomLValue);
|
||||||
case LValueExpr::VARREF:
|
|
||||||
{
|
|
||||||
auto liveVariables = state->currentFunctionState()->inputs |
|
|
||||||
ranges::views::transform([](auto& _item) { return _item; }) |
|
|
||||||
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
|
||||||
liveVariables += state->currentFunctionState()->outputs |
|
|
||||||
ranges::views::transform([](auto& _item) { return _item; }) |
|
|
||||||
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
|
||||||
for (auto const& scope: state->currentFunctionState()->scopes)
|
|
||||||
liveVariables += scope->variables |
|
|
||||||
ranges::views::transform([](auto& _item) { return _item; }) |
|
|
||||||
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
|
||||||
if (liveVariables.empty())
|
|
||||||
return nullopt;
|
|
||||||
else
|
|
||||||
return liveVariables[state->uRandDist->distributionOneToN(liveVariables.size()) - 1];
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
solAssert(false, "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::lValueExpression(
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::lValueExpression(
|
||||||
@ -574,45 +600,395 @@ optional<pair<SolidityTypePtr, string>> ExpressionGenerator::lValueExpression(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Filter non-identical variables of the same type.
|
// Filter non-identical variables of the same type.
|
||||||
auto liveTypedVariables = state->currentFunctionState()->inputs |
|
auto typedLiveVars = liveVariables(_typeName);
|
||||||
ranges::views::filter([&_typeName](auto& _item) {
|
|
||||||
return _item.first.index() == _typeName.first.index() &&
|
if (typedLiveVars.empty())
|
||||||
_item.second != _typeName.second &&
|
|
||||||
visit(TypeComparator{}, _item.first, _typeName.first);
|
|
||||||
}) |
|
|
||||||
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
|
||||||
liveTypedVariables += state->currentFunctionState()->outputs |
|
|
||||||
ranges::views::filter([&_typeName](auto& _item) {
|
|
||||||
return _item.first.index() == _typeName.first.index() &&
|
|
||||||
_item.second != _typeName.second &&
|
|
||||||
visit(TypeComparator{}, _item.first, _typeName.first);
|
|
||||||
}) |
|
|
||||||
ranges::to<vector<pair<SolidityTypePtr, string>>>();
|
|
||||||
if (liveTypedVariables.empty())
|
|
||||||
return nullopt;
|
return nullopt;
|
||||||
else
|
else
|
||||||
return liveTypedVariables[state->uRandDist->distributionOneToN(liveTypedVariables.size()) - 1];
|
return typedLiveVars[state->uRandDist->distributionOneToN(typedLiveVars.size()) - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<SolidityTypePtr, string> ExpressionGenerator::literal(SolidityTypePtr _type)
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::literal(SolidityTypePtr _type)
|
||||||
{
|
{
|
||||||
string literalValue = visit(LiteralGenerator{state}, _type);
|
bool functionType = holds_alternative<shared_ptr<FunctionType>>(_type);
|
||||||
return pair(_type, literalValue);
|
bool contractType = holds_alternative<shared_ptr<ContractType>>(_type);
|
||||||
}
|
// TODO: Generate literals for contract and function types.
|
||||||
|
if (functionType || contractType)
|
||||||
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::expression(pair<SolidityTypePtr, string> _typeName)
|
return nullopt;
|
||||||
{
|
else
|
||||||
auto varRef = lValueExpression(_typeName);
|
|
||||||
if (!varRef.has_value())
|
|
||||||
{
|
{
|
||||||
// TODO: Generate literals for contract and function types.
|
string literalValue = visit(LiteralGenerator{state}, _type);
|
||||||
if (!(holds_alternative<shared_ptr<FunctionType>>(_typeName.first) || holds_alternative<shared_ptr<ContractType>>(_typeName.first)))
|
return pair(_type, literalValue);
|
||||||
return literal(_typeName.first);
|
}
|
||||||
else
|
}
|
||||||
return nullopt;
|
|
||||||
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::rLValueOrLiteral(
|
||||||
|
pair<SolidityTypePtr, string>& _typeName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
optional<pair<SolidityTypePtr, string>> rLValue;
|
||||||
|
// Try to obtain an RLValue failing which a typed literal.
|
||||||
|
rLValue = rOrLValueExpression(_typeName);
|
||||||
|
if (!rLValue.has_value())
|
||||||
|
rLValue = literal(_typeName.first);
|
||||||
|
return rLValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::unaryExpression(
|
||||||
|
pair<SolidityTypePtr, string>& _typeName,
|
||||||
|
string const& _op
|
||||||
|
)
|
||||||
|
{
|
||||||
|
optional<pair<SolidityTypePtr, string>> rLValue = rLValueOrLiteral(_typeName);
|
||||||
|
pair<SolidityTypePtr, string> result;
|
||||||
|
if (rLValue.has_value())
|
||||||
|
{
|
||||||
|
result = rLValue.value();
|
||||||
|
result.second = _op + "(" + result.second + ")";
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return varRef.value();
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::binaryExpression(
|
||||||
|
pair<SolidityTypePtr, string>& _typeName,
|
||||||
|
string const& _op
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto left = rLValueOrLiteral(_typeName);
|
||||||
|
auto right = rLValueOrLiteral(_typeName);
|
||||||
|
if (left.has_value() && right.has_value())
|
||||||
|
{
|
||||||
|
auto leftResult = left.value();
|
||||||
|
auto rightResult = right.value();
|
||||||
|
leftResult.second = "(" +
|
||||||
|
leftResult.second +
|
||||||
|
" " +
|
||||||
|
_op +
|
||||||
|
" " +
|
||||||
|
rightResult.second +
|
||||||
|
")";
|
||||||
|
return leftResult;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::incDecOperation(
|
||||||
|
pair<SolidityTypePtr, string>& _typeName,
|
||||||
|
string const& _op,
|
||||||
|
bool _prefixOp
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!holds_alternative<shared_ptr<IntegerType>>(_typeName.first))
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
auto lValue = lValueExpression(_typeName);
|
||||||
|
if (!lValue.has_value())
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
auto lResult = lValue.value();
|
||||||
|
if (_prefixOp)
|
||||||
|
lResult.second = _op + lResult.second;
|
||||||
|
else
|
||||||
|
lResult.second += _op;
|
||||||
|
return lResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::rOrLValueExpression(pair<SolidityTypePtr, string> _typeName)
|
||||||
|
{
|
||||||
|
RLValueExpr exprType = static_cast<RLValueExpr>(
|
||||||
|
state->uRandDist->distributionOneToN(static_cast<size_t>(RLValueExpr::RLMAX) - 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (deeplyNested())
|
||||||
|
return literal(_typeName.first);
|
||||||
|
|
||||||
|
incrementNestingDepth();
|
||||||
|
|
||||||
|
string op;
|
||||||
|
switch (exprType)
|
||||||
|
{
|
||||||
|
case RLValueExpr::VARREF:
|
||||||
|
return lValueExpression(_typeName);
|
||||||
|
case RLValueExpr::PINC:
|
||||||
|
return incDecOperation(_typeName, "++", true);
|
||||||
|
case RLValueExpr::PDEC:
|
||||||
|
return incDecOperation(_typeName, "--", true);
|
||||||
|
case RLValueExpr::SINC:
|
||||||
|
return incDecOperation(_typeName, "++", false);
|
||||||
|
case RLValueExpr::SDEC:
|
||||||
|
return incDecOperation(_typeName, "--", false);
|
||||||
|
case RLValueExpr::NOT:
|
||||||
|
{
|
||||||
|
// Logical not may only be applied to expressions of boolean type.
|
||||||
|
if (!holds_alternative<shared_ptr<BoolType>>(_typeName.first))
|
||||||
|
return nullopt;
|
||||||
|
op = "!";
|
||||||
|
return unaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::BITNOT:
|
||||||
|
{
|
||||||
|
// Bitwise not may only be applied to integer types.
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
op = "~";
|
||||||
|
return unaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::USUB:
|
||||||
|
{
|
||||||
|
// Unary sub may only be applied to signed integer types
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
bool signedType = get<shared_ptr<IntegerType>>(_typeName.first)->signedType;
|
||||||
|
if (!signedType)
|
||||||
|
return nullopt;
|
||||||
|
op = "-";
|
||||||
|
return unaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::EXP:
|
||||||
|
{
|
||||||
|
// Exponentiation may only be applied to unsigned integer types
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
bool signedType = get<shared_ptr<IntegerType>>(_typeName.first)->signedType;
|
||||||
|
if (signedType)
|
||||||
|
return nullopt;
|
||||||
|
op = "**";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
// Arithmetic ops only be applied to integer types
|
||||||
|
case RLValueExpr::MUL:
|
||||||
|
{
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
op = "*";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::DIV:
|
||||||
|
{
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
op = "/";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::MOD:
|
||||||
|
{
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
op = "%";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::ADD:
|
||||||
|
{
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
op = "+";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::BSUB:
|
||||||
|
{
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
op = "-";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::SHL:
|
||||||
|
{
|
||||||
|
// Left shift may only be applied to unsigned integer types.
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
bool signedType = get<shared_ptr<IntegerType>>(_typeName.first)->signedType;
|
||||||
|
if (signedType)
|
||||||
|
return nullopt;
|
||||||
|
op = "<<";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::SHR:
|
||||||
|
{
|
||||||
|
// Left shift may only be applied to unsigned integer types.
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
if (!integerType)
|
||||||
|
return nullopt;
|
||||||
|
bool signedType = get<shared_ptr<IntegerType>>(_typeName.first)->signedType;
|
||||||
|
if (signedType)
|
||||||
|
return nullopt;
|
||||||
|
op = ">>";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::BITAND:
|
||||||
|
{
|
||||||
|
// Bitwise ops may only be applied to integer and fixed bytes types.
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
bool fixedBytesType = holds_alternative<shared_ptr<FixedBytesType>>(_typeName.first);
|
||||||
|
if (!(integerType || fixedBytesType))
|
||||||
|
return nullopt;
|
||||||
|
op = "&";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::BITOR:
|
||||||
|
{
|
||||||
|
// Bitwise ops may only be applied to integer and fixed bytes types.
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
bool fixedBytesType = holds_alternative<shared_ptr<FixedBytesType>>(_typeName.first);
|
||||||
|
if (!(integerType || fixedBytesType))
|
||||||
|
return nullopt;
|
||||||
|
op = "|";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::BITXOR:
|
||||||
|
{
|
||||||
|
// Bitwise ops may only be applied to integer and fixed bytes types.
|
||||||
|
bool integerType = holds_alternative<shared_ptr<IntegerType>>(_typeName.first);
|
||||||
|
bool fixedBytesType = holds_alternative<shared_ptr<FixedBytesType>>(_typeName.first);
|
||||||
|
if (!(integerType || fixedBytesType))
|
||||||
|
return nullopt;
|
||||||
|
op = "^";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::LT:
|
||||||
|
{
|
||||||
|
// Comparison ops may be applied only if LHS type is boolean.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
// Types being compared could be integer, fixed bytes, address, or contract.
|
||||||
|
auto operandType = TypeProvider{state}.type();
|
||||||
|
bool opFunctionType = holds_alternative<shared_ptr<FunctionType>>(operandType);
|
||||||
|
bool opBoolType = holds_alternative<shared_ptr<BoolType>>(operandType);
|
||||||
|
bool opBytesType = holds_alternative<shared_ptr<BytesType>>(operandType);
|
||||||
|
if (opFunctionType || opBoolType || opBytesType)
|
||||||
|
return nullopt;
|
||||||
|
op = "<";
|
||||||
|
pair<SolidityTypePtr, string> operandTypeName = {operandType, {}};
|
||||||
|
return binaryExpression(operandTypeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::GT:
|
||||||
|
{
|
||||||
|
// Comparison ops may be applied only if LHS type is boolean.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
// Types being compared could be integer, fixed bytes, address, or contract.
|
||||||
|
auto operandType = TypeProvider{state}.type();
|
||||||
|
bool opFunctionType = holds_alternative<shared_ptr<FunctionType>>(operandType);
|
||||||
|
bool opBoolType = holds_alternative<shared_ptr<BoolType>>(operandType);
|
||||||
|
bool opBytesType = holds_alternative<shared_ptr<BytesType>>(operandType);
|
||||||
|
if (opFunctionType || opBoolType || opBytesType)
|
||||||
|
return nullopt;
|
||||||
|
op = ">";
|
||||||
|
pair<SolidityTypePtr, string> operandTypeName = {operandType, {}};
|
||||||
|
return binaryExpression(operandTypeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::LTE:
|
||||||
|
{
|
||||||
|
// Comparison ops may be applied only if LHS type is boolean.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
// Types being compared could be integer, fixed bytes, address, or contract.
|
||||||
|
auto operandType = TypeProvider{state}.type();
|
||||||
|
bool opFunctionType = holds_alternative<shared_ptr<FunctionType>>(operandType);
|
||||||
|
bool opBoolType = holds_alternative<shared_ptr<BoolType>>(operandType);
|
||||||
|
bool opBytesType = holds_alternative<shared_ptr<BytesType>>(operandType);
|
||||||
|
if (opFunctionType || opBoolType || opBytesType)
|
||||||
|
return nullopt;
|
||||||
|
op = "<=";
|
||||||
|
pair<SolidityTypePtr, string> operandTypeName = {operandType, {}};
|
||||||
|
return binaryExpression(operandTypeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::GTE:
|
||||||
|
{
|
||||||
|
// Comparison ops may be applied only if LHS type is boolean.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
// Types being compared could be integer, fixed bytes, address, or contract.
|
||||||
|
auto operandType = TypeProvider{state}.type();
|
||||||
|
bool opFunctionType = holds_alternative<shared_ptr<FunctionType>>(operandType);
|
||||||
|
bool opBoolType = holds_alternative<shared_ptr<BoolType>>(operandType);
|
||||||
|
bool opBytesType = holds_alternative<shared_ptr<BytesType>>(operandType);
|
||||||
|
if (opFunctionType || opBoolType || opBytesType)
|
||||||
|
return nullopt;
|
||||||
|
op = ">=";
|
||||||
|
pair<SolidityTypePtr, string> operandTypeName = {operandType, {}};
|
||||||
|
return binaryExpression(operandTypeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::EQ:
|
||||||
|
{
|
||||||
|
// Comparison ops may be applied only if LHS type is boolean.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
// Types being compared could be integer, fixed bytes, address, or contract.
|
||||||
|
auto operandType = TypeProvider{state}.type();
|
||||||
|
bool opFunctionType = holds_alternative<shared_ptr<FunctionType>>(operandType);
|
||||||
|
bool opBoolType = holds_alternative<shared_ptr<BoolType>>(operandType);
|
||||||
|
bool opBytesType = holds_alternative<shared_ptr<BytesType>>(operandType);
|
||||||
|
if (opFunctionType || opBoolType || opBytesType)
|
||||||
|
return nullopt;
|
||||||
|
op = "==";
|
||||||
|
pair<SolidityTypePtr, string> operandTypeName = {operandType, {}};
|
||||||
|
return binaryExpression(operandTypeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::NEQ:
|
||||||
|
{
|
||||||
|
// Comparison ops may be applied only if LHS type is boolean.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
// Types being compared could be integer, fixed bytes, address, or contract.
|
||||||
|
auto operandType = TypeProvider{state}.type();
|
||||||
|
bool opFunctionType = holds_alternative<shared_ptr<FunctionType>>(operandType);
|
||||||
|
bool opBoolType = holds_alternative<shared_ptr<BoolType>>(operandType);
|
||||||
|
bool opBytesType = holds_alternative<shared_ptr<BytesType>>(operandType);
|
||||||
|
if (opFunctionType || opBoolType || opBytesType)
|
||||||
|
return nullopt;
|
||||||
|
op = "!=";
|
||||||
|
pair<SolidityTypePtr, string> operandTypeName = {operandType, {}};
|
||||||
|
return binaryExpression(operandTypeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::AND:
|
||||||
|
{
|
||||||
|
// Logical ops may be applied only to boolean types.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
op = "&&";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::OR:
|
||||||
|
{
|
||||||
|
// Logical ops may be applied only to boolean types.
|
||||||
|
bool boolType = holds_alternative<shared_ptr<BoolType>>(_typeName.first);
|
||||||
|
if (!boolType)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
op = "||";
|
||||||
|
return binaryExpression(_typeName, op);
|
||||||
|
}
|
||||||
|
case RLValueExpr::LIT:
|
||||||
|
return literal(_typeName.first);
|
||||||
|
default:
|
||||||
|
solAssert(false, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string LiteralGenerator::operator()(shared_ptr<AddressType> const&)
|
string LiteralGenerator::operator()(shared_ptr<AddressType> const&)
|
||||||
@ -684,7 +1060,7 @@ optional<SolidityTypePtr> TypeProvider::type(SolidityTypePtr _type)
|
|||||||
{
|
{
|
||||||
vector<SolidityTypePtr> matchingTypes = state->currentFunctionState()->inputs |
|
vector<SolidityTypePtr> matchingTypes = state->currentFunctionState()->inputs |
|
||||||
ranges::views::filter([&_type](auto& _item) {
|
ranges::views::filter([&_type](auto& _item) {
|
||||||
return _item.first == _type;
|
return _item.first >= _type;
|
||||||
}) |
|
}) |
|
||||||
ranges::views::transform([](auto& _item) { return _item.first; }) |
|
ranges::views::transform([](auto& _item) { return _item.first; }) |
|
||||||
ranges::to<vector<SolidityTypePtr>>();
|
ranges::to<vector<SolidityTypePtr>>();
|
||||||
@ -744,6 +1120,7 @@ string FunctionCallGenerator::lhs(vector<pair<SolidityTypePtr, string>> _functio
|
|||||||
auto assignToVars = _functionReturnTypeNames |
|
auto assignToVars = _functionReturnTypeNames |
|
||||||
ranges::views::transform([&exprGen](auto const& _item) -> pair<bool, optional<pair<SolidityTypePtr, string>>> {
|
ranges::views::transform([&exprGen](auto const& _item) -> pair<bool, optional<pair<SolidityTypePtr, string>>> {
|
||||||
auto e = exprGen.lValueExpression(_item);
|
auto e = exprGen.lValueExpression(_item);
|
||||||
|
exprGen.resetNestingDepth();
|
||||||
if (e.has_value())
|
if (e.has_value())
|
||||||
return {true, e.value()};
|
return {true, e.value()};
|
||||||
else
|
else
|
||||||
@ -792,12 +1169,14 @@ optional<string> FunctionCallGenerator::rhs(vector<pair<SolidityTypePtr, string>
|
|||||||
auto inputArguments = _functionInputTypeNames |
|
auto inputArguments = _functionInputTypeNames |
|
||||||
ranges::views::transform([&exprGen](auto const& _item) -> pair<bool, optional<pair<SolidityTypePtr, string>>>
|
ranges::views::transform([&exprGen](auto const& _item) -> pair<bool, optional<pair<SolidityTypePtr, string>>>
|
||||||
{
|
{
|
||||||
auto e = exprGen.expression(_item);
|
auto e = exprGen.rOrLValueExpression(_item);
|
||||||
|
exprGen.resetNestingDepth();
|
||||||
if (e.has_value())
|
if (e.has_value())
|
||||||
return {true, e.value()};
|
return {true, e.value()};
|
||||||
else
|
else
|
||||||
return {false, nullopt};
|
return {false, nullopt};
|
||||||
});
|
}) |
|
||||||
|
ranges::to<vector<pair<bool, optional<pair<SolidityTypePtr, string>>>>>();
|
||||||
bool inputArgsValid = ranges::all_of(
|
bool inputArgsValid = ranges::all_of(
|
||||||
inputArguments,
|
inputArguments,
|
||||||
[](auto const& _item) -> bool { return _item.first; }
|
[](auto const& _item) -> bool { return _item.first; }
|
||||||
@ -806,7 +1185,10 @@ optional<string> FunctionCallGenerator::rhs(vector<pair<SolidityTypePtr, string>
|
|||||||
if (inputArgsValid)
|
if (inputArgsValid)
|
||||||
{
|
{
|
||||||
auto vars = inputArguments |
|
auto vars = inputArguments |
|
||||||
ranges::views::transform([](auto const& _item) { return _item.second.value().second; }) |
|
ranges::views::transform([](auto const& _item) {
|
||||||
|
solAssert(_item.second.has_value(), "");
|
||||||
|
return _item.second.value().second;
|
||||||
|
}) |
|
||||||
ranges::to<vector<string>>();
|
ranges::to<vector<string>>();
|
||||||
callStmtRhs << boost::algorithm::join(vars, ",");
|
callStmtRhs << boost::algorithm::join(vars, ",");
|
||||||
return callStmtRhs.str();
|
return callStmtRhs.str();
|
||||||
|
@ -165,10 +165,10 @@ public:
|
|||||||
signedType(_signed),
|
signedType(_signed),
|
||||||
numBits(static_cast<size_t>(_bits) * 8)
|
numBits(static_cast<size_t>(_bits) * 8)
|
||||||
{}
|
{}
|
||||||
bool operator==(IntegerType const& _rhs)
|
bool operator>=(IntegerType const& _rhs)
|
||||||
{
|
{
|
||||||
return this->signedType == _rhs.signedType &&
|
return this->signedType == _rhs.signedType &&
|
||||||
this->numBits == _rhs.numBits;
|
this->numBits >= _rhs.numBits;
|
||||||
}
|
}
|
||||||
std::string toString() override
|
std::string toString() override
|
||||||
{
|
{
|
||||||
@ -185,7 +185,7 @@ public:
|
|||||||
{
|
{
|
||||||
return "bool";
|
return "bool";
|
||||||
}
|
}
|
||||||
bool operator==(BoolType const&)
|
bool operator>=(BoolType const&)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ public:
|
|||||||
{
|
{
|
||||||
return "address";
|
return "address";
|
||||||
}
|
}
|
||||||
bool operator==(AddressType const&)
|
bool operator>=(AddressType const&)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -246,7 +246,7 @@ public:
|
|||||||
FixedBytesType(Bytes _width): numBytes(static_cast<size_t>(_width))
|
FixedBytesType(Bytes _width): numBytes(static_cast<size_t>(_width))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool operator==(FixedBytesType const& _rhs)
|
bool operator>=(FixedBytesType const& _rhs)
|
||||||
{
|
{
|
||||||
return this->numBytes == _rhs.numBytes;
|
return this->numBytes == _rhs.numBytes;
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ public:
|
|||||||
{
|
{
|
||||||
return "bytes memory";
|
return "bytes memory";
|
||||||
}
|
}
|
||||||
bool operator==(BytesType const&)
|
bool operator>=(BytesType const&)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -283,7 +283,7 @@ public:
|
|||||||
{
|
{
|
||||||
return contractName;
|
return contractName;
|
||||||
}
|
}
|
||||||
bool operator==(ContractType const& _rhs)
|
bool operator>=(ContractType const& _rhs)
|
||||||
{
|
{
|
||||||
return _rhs.name() == this->name();
|
return _rhs.name() == this->name();
|
||||||
}
|
}
|
||||||
@ -319,7 +319,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string toString() override;
|
std::string toString() override;
|
||||||
bool operator==(FunctionType const& _rhs)
|
bool operator>=(FunctionType const& _rhs)
|
||||||
{
|
{
|
||||||
if (_rhs.inputs.size() != this->inputs.size() || _rhs.outputs.size() != this->outputs.size())
|
if (_rhs.inputs.size() != this->inputs.size() || _rhs.outputs.size() != this->outputs.size())
|
||||||
return false;
|
return false;
|
||||||
@ -451,13 +451,13 @@ struct FunctionState
|
|||||||
}
|
}
|
||||||
void addInput(SolidityTypePtr _input)
|
void addInput(SolidityTypePtr _input)
|
||||||
{
|
{
|
||||||
inputs.emplace_back(_input, "i" + std::to_string(numInputs++));
|
|
||||||
type->addInput(_input);
|
type->addInput(_input);
|
||||||
|
inputs.emplace_back(_input, "i" + std::to_string(numInputs++));
|
||||||
}
|
}
|
||||||
void addOutput(SolidityTypePtr _output)
|
void addOutput(SolidityTypePtr _output)
|
||||||
{
|
{
|
||||||
outputs.emplace_back(_output, "o" + std::to_string(numOutpus++));
|
|
||||||
type->addOutput(_output);
|
type->addOutput(_output);
|
||||||
|
outputs.emplace_back(_output, "o" + std::to_string(numOutpus++));
|
||||||
}
|
}
|
||||||
void addLocal(SolidityTypePtr _local)
|
void addLocal(SolidityTypePtr _local)
|
||||||
{
|
{
|
||||||
@ -714,7 +714,7 @@ struct TypeComparator
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
bool operator()(T _i1, T _i2)
|
bool operator()(T _i1, T _i2)
|
||||||
{
|
{
|
||||||
return *_i1 == *_i2;
|
return *_i1 >= *_i2;
|
||||||
}
|
}
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
bool operator()(T1 _i1, T2 _i2)
|
bool operator()(T1 _i1, T2 _i2)
|
||||||
@ -745,22 +745,84 @@ struct ExpressionGenerator
|
|||||||
ExpressionGenerator(std::shared_ptr<TestState> _state): state(std::move(_state))
|
ExpressionGenerator(std::shared_ptr<TestState> _state): state(std::move(_state))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
enum class LValueExpr: size_t
|
enum class RLValueExpr: size_t
|
||||||
{
|
{
|
||||||
VARREF = 1,
|
VARREF = 1,
|
||||||
TYPEMAX
|
PINC,
|
||||||
|
PDEC,
|
||||||
|
SINC,
|
||||||
|
SDEC,
|
||||||
|
NOT,
|
||||||
|
BITNOT,
|
||||||
|
USUB,
|
||||||
|
EXP,
|
||||||
|
MUL,
|
||||||
|
DIV,
|
||||||
|
MOD,
|
||||||
|
ADD,
|
||||||
|
BSUB,
|
||||||
|
SHL,
|
||||||
|
SHR,
|
||||||
|
BITAND,
|
||||||
|
BITXOR,
|
||||||
|
BITOR,
|
||||||
|
LT,
|
||||||
|
GT,
|
||||||
|
LTE,
|
||||||
|
GTE,
|
||||||
|
EQ,
|
||||||
|
NEQ,
|
||||||
|
AND,
|
||||||
|
OR,
|
||||||
|
LIT,
|
||||||
|
RLMAX
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::pair<SolidityTypePtr, std::string>> expression(
|
std::optional<std::pair<SolidityTypePtr, std::string>> rOrLValueExpression(
|
||||||
std::pair<SolidityTypePtr, std::string> _typeName
|
std::pair<SolidityTypePtr, std::string> _typeName
|
||||||
);
|
);
|
||||||
std::pair<SolidityTypePtr, std::string> literal(SolidityTypePtr _type);
|
std::optional<std::pair<SolidityTypePtr, std::string>> literal(SolidityTypePtr _type);
|
||||||
std::optional<std::pair<SolidityTypePtr, std::string>> randomLValueExpression();
|
std::optional<std::pair<SolidityTypePtr, std::string>> randomLValueExpression();
|
||||||
std::optional<std::pair<SolidityTypePtr, std::string>> lValueExpression(
|
std::optional<std::pair<SolidityTypePtr, std::string>> lValueExpression(
|
||||||
std::pair<SolidityTypePtr, std::string> _typeName
|
std::pair<SolidityTypePtr, std::string> _typeName
|
||||||
);
|
);
|
||||||
|
std::vector<std::pair<SolidityTypePtr, std::string>> liveVariables();
|
||||||
|
std::vector<std::pair<SolidityTypePtr, std::string>> liveVariables(
|
||||||
|
std::pair<SolidityTypePtr, std::string> _typeName
|
||||||
|
);
|
||||||
|
std::optional<std::pair<SolidityTypePtr, std::string>> unaryExpression(
|
||||||
|
std::pair<SolidityTypePtr, std::string>& _typeName,
|
||||||
|
std::string const& _op
|
||||||
|
);
|
||||||
|
std::optional<std::pair<SolidityTypePtr, std::string>> binaryExpression(
|
||||||
|
std::pair<SolidityTypePtr, std::string>& _typeName,
|
||||||
|
std::string const& _op
|
||||||
|
);
|
||||||
|
std::optional<std::pair<SolidityTypePtr, std::string>> incDecOperation(
|
||||||
|
std::pair<SolidityTypePtr, std::string>& _typeName,
|
||||||
|
std::string const& _op,
|
||||||
|
bool _prefixOp
|
||||||
|
);
|
||||||
|
std::optional<std::pair<SolidityTypePtr, std::string>> rLValueOrLiteral(
|
||||||
|
std::pair<SolidityTypePtr, std::string>& _typeName
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
void incrementNestingDepth()
|
||||||
|
{
|
||||||
|
nestingDepth++;
|
||||||
|
}
|
||||||
|
void resetNestingDepth()
|
||||||
|
{
|
||||||
|
nestingDepth = 0;
|
||||||
|
}
|
||||||
|
bool deeplyNested()
|
||||||
|
{
|
||||||
|
return nestingDepth > s_maxNestingDepth;
|
||||||
|
}
|
||||||
std::shared_ptr<TestState> state;
|
std::shared_ptr<TestState> state;
|
||||||
|
unsigned nestingDepth;
|
||||||
|
static constexpr unsigned s_maxNestingDepth = 30;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GeneratorBase
|
struct GeneratorBase
|
||||||
|
Loading…
Reference in New Issue
Block a user