Merge remote-tracking branch 'origin/develop' into HEAD

This commit is contained in:
chriseth
2020-10-08 14:56:52 +02:00
40 changed files with 723 additions and 327 deletions
+1 -1
View File
@@ -167,7 +167,7 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
// If this is not an ordinary assignment, we write and read at the same time.
bool write = _expression.annotation().willBeWrittenTo;
bool read = !_expression.annotation().willBeWrittenTo || !*_expression.annotation().lValueOfOrdinaryAssignment;
bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment;
if (write)
{
if (!m_currentConstructor)
+56 -1
View File
@@ -1420,7 +1420,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
requireLValue(
*component,
*_tuple.annotation().lValueOfOrdinaryAssignment
_tuple.annotation().lValueOfOrdinaryAssignment
);
types.push_back(type(*component));
}
@@ -1534,6 +1534,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
_operation.annotation().isConstant = false;
_operation.annotation().isPure = !modifying && *_operation.subExpression().annotation().isPure;
_operation.annotation().isLValue = false;
return false;
}
@@ -2178,6 +2179,52 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
m_errorReporter.typeError(errorId, paramArgMap[i]->location(), description);
}
}
TypePointers const& returnParameterTypes = _functionType->returnParameterTypes();
bool isLibraryCall = (_functionType->kind() == FunctionType::Kind::DelegateCall);
bool callRequiresABIEncoding =
// ABIEncode/ABIDecode calls not included because they should have been already validated
// at this point and they have variadic arguments so they need special handling.
_functionType->kind() == FunctionType::Kind::DelegateCall ||
_functionType->kind() == FunctionType::Kind::External ||
_functionType->kind() == FunctionType::Kind::Creation ||
_functionType->kind() == FunctionType::Kind::Event;
if (callRequiresABIEncoding && !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
{
solAssert(!isVariadic, "");
solAssert(parameterTypes.size() == arguments.size(), "");
solAssert(!_functionType->isBareCall(), "");
solAssert(*_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
for (size_t i = 0; i < parameterTypes.size(); ++i)
{
solAssert(parameterTypes[i], "");
if (!typeSupportedByOldABIEncoder(*parameterTypes[i], isLibraryCall))
m_errorReporter.typeError(
2443_error,
paramArgMap[i]->location(),
"The type of this parameter, " + parameterTypes[i]->toString(true) + ", "
"is only supported in ABIEncoderV2. "
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
);
}
for (size_t i = 0; i < returnParameterTypes.size(); ++i)
{
solAssert(returnParameterTypes[i], "");
if (!typeSupportedByOldABIEncoder(*returnParameterTypes[i], isLibraryCall))
m_errorReporter.typeError(
2428_error,
_functionCall.location(),
"The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", "
"is only supported in ABIEncoderV2. "
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
);
}
}
}
bool TypeChecker::visit(FunctionCall const& _functionCall)
@@ -2345,6 +2392,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions)
_functionCallOptions.annotation().isPure = false;
_functionCallOptions.annotation().isConstant = false;
_functionCallOptions.annotation().isLValue = false;
auto expressionFunctionType = dynamic_cast<FunctionType const*>(type(_functionCallOptions.expression()));
if (!expressionFunctionType)
@@ -2477,6 +2525,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
solAssert(!!type, "Type name not resolved.");
_newExpression.annotation().isConstant = false;
_newExpression.annotation().isLValue = false;
if (auto contractName = dynamic_cast<UserDefinedTypeName const*>(&_newExpression.typeName()))
{
@@ -2537,7 +2586,10 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
_newExpression.annotation().isPure = true;
}
else
{
_newExpression.annotation().isPure = false;
m_errorReporter.fatalTypeError(8807_error, _newExpression.location(), "Contract or array type expected.");
}
}
bool TypeChecker::visit(MemberAccess const& _memberAccess)
@@ -2720,6 +2772,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
)
annotation.isPure = *_memberAccess.expression().annotation().isPure;
}
else
annotation.isLValue = false;
}
else if (exprType->category() == Type::Category::Module)
{
@@ -3015,6 +3069,7 @@ vector<Declaration const*> TypeChecker::cleanOverloadedDeclarations(
bool TypeChecker::visit(Identifier const& _identifier)
{
IdentifierAnnotation& annotation = _identifier.annotation();
if (!annotation.referencedDeclaration)
{
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
+2 -1
View File
@@ -253,7 +253,8 @@ struct ExpressionAnnotation: ASTAnnotation
bool willBeWrittenTo = false;
/// Whether the expression is an lvalue that is only assigned.
/// Would be false for --, ++, delete, +=, -=, ....
SetOnce<bool> lValueOfOrdinaryAssignment;
/// Only relevant if isLvalue == true
bool lValueOfOrdinaryAssignment;
/// Types and - if given - names of arguments if the expr. is a function
/// that is called, used for overload resolution
-42
View File
@@ -313,46 +313,4 @@ private:
std::function<void(ASTNode const&)> m_onEndVisit;
};
/**
* Utility class that visits the AST in depth-first order and calls a function on each node and each edge.
* Child nodes are only visited if the node callback of the parent returns true.
* The node callback of a parent is called before any edge or node callback involving the children.
* The edge callbacks of all children are called before the edge callback of the parent.
* This way, the node callback can be used as an initializing callback and the edge callbacks can be
* used to compute a "reduce" function.
*/
class ASTReduce: public ASTConstVisitor
{
public:
/**
* Constructs a new ASTReduce object with the given callback functions.
* @param _onNode called for each node, before its child edges and nodes, should return true to descend deeper
* @param _onEdge called for each edge with (parent, child)
*/
ASTReduce(
std::function<bool(ASTNode const&)> _onNode,
std::function<void(ASTNode const&, ASTNode const&)> _onEdge
): m_onNode(std::move(_onNode)), m_onEdge(std::move(_onEdge))
{
}
protected:
bool visitNode(ASTNode const& _node) override
{
m_parents.push_back(&_node);
return m_onNode(_node);
}
void endVisitNode(ASTNode const& _node) override
{
m_parents.pop_back();
if (!m_parents.empty())
m_onEdge(*m_parents.back(), _node);
}
private:
std::vector<ASTNode const*> m_parents;
std::function<bool(ASTNode const&)> m_onNode;
std::function<void(ASTNode const&, ASTNode const&)> m_onEdge;
};
}
+5 -1
View File
@@ -290,7 +290,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
_context << Instruction::POP << Instruction::SWAP1 << Instruction::POP;
// stack: target_ref target_data_end target_data_pos_updated
utils.clearStorageLoop(targetBaseType);
if (targetBaseType->storageBytes() < 32)
utils.clearStorageLoop(TypeProvider::uint256());
else
utils.clearStorageLoop(targetBaseType);
_context << Instruction::POP;
}
);
@@ -922,6 +925,7 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const
void ArrayUtils::clearStorageLoop(TypePointer _type) const
{
solAssert(_type->storageBytes() >= 32, "");
m_context.callLowLevelFunction(
"$clearStorageLoop_" + _type->identifier(),
2,
-87
View File
@@ -42,93 +42,6 @@ using namespace solidity::evmasm;
using namespace solidity::frontend;
using namespace solidity::langutil;
GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation(
AssemblyItems const& _items,
vector<ASTNode const*> const& _ast
) const
{
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, "");
map<SourceLocation, GasConsumption> particularCosts;
ControlFlowGraph cfg(_items);
for (BasicBlock const& block: cfg.optimisedBlocks())
{
solAssert(!!block.startState, "");
GasMeter meter(block.startState->copy(), m_evmVersion);
auto const end = _items.begin() + static_cast<ptrdiff_t>(block.end);
for (auto iter = _items.begin() + static_cast<ptrdiff_t>(block.begin); iter != end; ++iter)
particularCosts[iter->location()] += meter.estimateMax(*iter);
}
set<ASTNode const*> finestNodes = finestNodesAtLocation(_ast);
ASTGasConsumptionSelfAccumulated gasCosts;
auto onNode = [&](ASTNode const& _node)
{
if (!finestNodes.count(&_node))
return true;
gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.location()];
return true;
};
auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child)
{
gasCosts[&_parent][1] += gasCosts[&_child][1];
};
ASTReduce folder(onNode, onEdge);
for (ASTNode const* ast: _ast)
ast->accept(folder);
return gasCosts;
}
map<ASTNode const*, GasMeter::GasConsumption> GasEstimator::breakToStatementLevel(
ASTGasConsumptionSelfAccumulated const& _gasCosts,
vector<ASTNode const*> const& _roots
)
{
solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, "");
// first pass: statementDepth[node] is the distance from the deepend statement to node
// in direction of the tree root (or undefined if not possible)
map<ASTNode const*, int> statementDepth;
auto onNodeFirstPass = [&](ASTNode const& _node)
{
if (dynamic_cast<Statement const*>(&_node))
statementDepth[&_node] = 0;
return true;
};
auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child)
{
if (statementDepth.count(&_child))
statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1);
};
ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass);
for (ASTNode const* node: _roots)
node->accept(firstPass);
// we use the location of a node if
// - its statement depth is 0 or
// - its statement depth is undefined but the parent's statement depth is at least 1
map<ASTNode const*, GasConsumption> gasCosts;
auto onNodeSecondPass = [&](ASTNode const& _node)
{
return statementDepth.count(&_node);
};
auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child)
{
bool useNode = false;
if (statementDepth.count(&_child))
useNode = statementDepth[&_child] == 0;
else
useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0;
if (useNode)
gasCosts[&_child] = _gasCosts.at(&_child)[1];
};
ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass);
for (ASTNode const* node: _roots)
node->accept(secondPass);
// gasCosts should only contain non-overlapping locations
return gasCosts;
}
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
AssemblyItems const& _items,
string const& _signature
-16
View File
@@ -48,22 +48,6 @@ public:
explicit GasEstimator(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {}
/// Estimates the gas consumption for every assembly item in the given assembly and stores
/// it by source location.
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
ASTGasConsumptionSelfAccumulated structuralEstimation(
evmasm::AssemblyItems const& _items,
std::vector<ASTNode const*> const& _ast
) const;
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
/// the following source locations are part of the mapping:
/// 1. source locations of statements that do not contain other statements
/// 2. maximal source locations that do not overlap locations coming from the first rule
static ASTGasConsumption breakToStatementLevel(
ASTGasConsumptionSelfAccumulated const& _gasCosts,
std::vector<ASTNode const*> const& _roots
);
/// @returns the estimated gas consumption by the (public or external) function with the
/// given signature. If no signature is given, estimates the maximum gas usage.
GasConsumption functionalEstimation(