Introduce typed expressions.

This commit is contained in:
Bhargava Shastry 2021-05-10 11:33:32 +02:00
parent 23edc14398
commit 07fdbb309b
2 changed files with 520 additions and 76 deletions

View File

@ -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,15 +542,8 @@ void FunctionGenerator::endVisit()
mutator->generator<BlockStmtGenerator>()->resetNestingDepth(); mutator->generator<BlockStmtGenerator>()->resetNestingDepth();
} }
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::randomLValueExpression() vector<pair<SolidityTypePtr, string>> ExpressionGenerator::liveVariables()
{ {
LValueExpr exprType = static_cast<LValueExpr>(
state->uRandDist->distributionOneToN(static_cast<size_t>(LValueExpr::TYPEMAX) - 1)
);
switch (exprType)
{
case LValueExpr::VARREF:
{
auto liveVariables = state->currentFunctionState()->inputs | auto liveVariables = state->currentFunctionState()->inputs |
ranges::views::transform([](auto& _item) { return _item; }) | ranges::views::transform([](auto& _item) { return _item; }) |
ranges::to<vector<pair<SolidityTypePtr, string>>>(); ranges::to<vector<pair<SolidityTypePtr, string>>>();
@ -559,21 +554,13 @@ optional<pair<SolidityTypePtr, string>> ExpressionGenerator::randomLValueExpress
liveVariables += scope->variables | liveVariables += scope->variables |
ranges::views::transform([](auto& _item) { return _item; }) | ranges::views::transform([](auto& _item) { return _item; }) |
ranges::to<vector<pair<SolidityTypePtr, string>>>(); ranges::to<vector<pair<SolidityTypePtr, string>>>();
if (liveVariables.empty()) return liveVariables;
return nullopt;
else
return liveVariables[state->uRandDist->distributionOneToN(liveVariables.size()) - 1];
}
default:
solAssert(false, "");
}
} }
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::lValueExpression( vector<pair<SolidityTypePtr, string>> ExpressionGenerator::liveVariables(
pair<SolidityTypePtr, string> _typeName pair<SolidityTypePtr, string> _typeName
) )
{ {
// Filter non-identical variables of the same type.
auto liveTypedVariables = state->currentFunctionState()->inputs | auto liveTypedVariables = state->currentFunctionState()->inputs |
ranges::views::filter([&_typeName](auto& _item) { ranges::views::filter([&_typeName](auto& _item) {
return _item.first.index() == _typeName.first.index() && return _item.first.index() == _typeName.first.index() &&
@ -588,31 +575,420 @@ optional<pair<SolidityTypePtr, string>> ExpressionGenerator::lValueExpression(
visit(TypeComparator{}, _item.first, _typeName.first); visit(TypeComparator{}, _item.first, _typeName.first);
}) | }) |
ranges::to<vector<pair<SolidityTypePtr, string>>>(); ranges::to<vector<pair<SolidityTypePtr, string>>>();
if (liveTypedVariables.empty()) for (auto const& scope: state->currentFunctionState()->scopes)
return nullopt; liveTypedVariables += scope->variables |
else ranges::views::filter([&_typeName](auto& _item) {
return liveTypedVariables[state->uRandDist->distributionOneToN(liveTypedVariables.size()) - 1]; 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;
} }
pair<SolidityTypePtr, string> ExpressionGenerator::literal(SolidityTypePtr _type) optional<pair<SolidityTypePtr, string>> ExpressionGenerator::randomLValueExpression()
{ {
auto liveVars = liveVariables();
if (liveVars.empty())
return nullopt;
auto randomLValue = liveVars[state->uRandDist->distributionOneToN(liveVars.size()) - 1];
return lValueExpression(randomLValue);
}
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::lValueExpression(
pair<SolidityTypePtr, string> _typeName
)
{
// Filter non-identical variables of the same type.
auto typedLiveVars = liveVariables(_typeName);
if (typedLiveVars.empty())
return nullopt;
else
return typedLiveVars[state->uRandDist->distributionOneToN(typedLiveVars.size()) - 1];
}
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::literal(SolidityTypePtr _type)
{
bool functionType = holds_alternative<shared_ptr<FunctionType>>(_type);
bool contractType = holds_alternative<shared_ptr<ContractType>>(_type);
// TODO: Generate literals for contract and function types.
if (functionType || contractType)
return nullopt;
else
{
string literalValue = visit(LiteralGenerator{state}, _type); string literalValue = visit(LiteralGenerator{state}, _type);
return pair(_type, literalValue); return pair(_type, literalValue);
}
} }
optional<pair<SolidityTypePtr, string>> ExpressionGenerator::expression(pair<SolidityTypePtr, string> _typeName) optional<pair<SolidityTypePtr, string>> ExpressionGenerator::rLValueOrLiteral(
pair<SolidityTypePtr, string>& _typeName
)
{ {
auto varRef = lValueExpression(_typeName); optional<pair<SolidityTypePtr, string>> rLValue;
if (!varRef.has_value()) // 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())
{ {
// TODO: Generate literals for contract and function types. result = rLValue.value();
if (!(holds_alternative<shared_ptr<FunctionType>>(_typeName.first) || holds_alternative<shared_ptr<ContractType>>(_typeName.first))) result.second = _op + "(" + result.second + ")";
return literal(_typeName.first); return result;
else
return nullopt;
} }
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();

View File

@ -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