solidity/libsolidity/ast/AST.cpp
chriseth 5ca3abd7cb
Merge pull request #8106 from random-internet-cat/move-contract-kind
Move ContractKind from ContractDefinition to file-scope
2020-01-08 13:06:26 +01:00

765 lines
21 KiB
C++

/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity abstract syntax tree.
*/
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/AST_accept.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolutil/Keccak256.h>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#include <functional>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
class IDDispenser
{
public:
static size_t next() { return ++instance(); }
static void reset() { instance() = 0; }
private:
static size_t& instance()
{
static IDDispenser dispenser;
return dispenser.id;
}
size_t id = 0;
};
ASTNode::ASTNode(SourceLocation const& _location):
m_id(IDDispenser::next()),
m_location(_location)
{
}
void ASTNode::resetID()
{
IDDispenser::reset();
}
ASTAnnotation& ASTNode::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ASTAnnotation>();
return *m_annotation;
}
SourceUnitAnnotation& SourceUnit::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<SourceUnitAnnotation>();
return dynamic_cast<SourceUnitAnnotation&>(*m_annotation);
}
set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<SourceUnit const*> _skipList) const
{
set<SourceUnit const*> sourceUnits;
for (ImportDirective const* importDirective: filteredNodes<ImportDirective>(nodes()))
{
auto const& sourceUnit = importDirective->annotation().sourceUnit;
if (!_skipList.count(sourceUnit))
{
_skipList.insert(sourceUnit);
sourceUnits.insert(sourceUnit);
if (_recurse)
sourceUnits += sourceUnit->referencedSourceUnits(true, _skipList);
}
}
return sourceUnits;
}
ImportAnnotation& ImportDirective::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ImportAnnotation>();
return dynamic_cast<ImportAnnotation&>(*m_annotation);
}
TypePointer ImportDirective::type() const
{
solAssert(!!annotation().sourceUnit, "");
return TypeProvider::module(*annotation().sourceUnit);
}
vector<VariableDeclaration const*> ContractDefinition::stateVariablesIncludingInherited() const
{
vector<VariableDeclaration const*> stateVars;
for (auto const& contract: annotation().linearizedBaseContracts)
for (auto var: contract->stateVariables())
if (*contract == *this || var->isVisibleInDerivedContracts())
stateVars.push_back(var);
return stateVars;
}
map<util::FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
{
auto exportedFunctionList = interfaceFunctionList();
map<util::FixedHash<4>, FunctionTypePointer> exportedFunctions;
for (auto const& it: exportedFunctionList)
exportedFunctions.insert(it);
solAssert(
exportedFunctionList.size() == exportedFunctions.size(),
"Hash collision at Function Definition Hash calculation"
);
return exportedFunctions;
}
FunctionDefinition const* ContractDefinition::constructor() const
{
for (FunctionDefinition const* f: definedFunctions())
if (f->isConstructor())
return f;
return nullptr;
}
bool ContractDefinition::constructorIsPublic() const
{
FunctionDefinition const* f = constructor();
return !f || f->isPublic();
}
bool ContractDefinition::canBeDeployed() const
{
return constructorIsPublic() && !abstract() && !isInterface();
}
FunctionDefinition const* ContractDefinition::fallbackFunction() const
{
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isFallback())
return f;
return nullptr;
}
FunctionDefinition const* ContractDefinition::receiveFunction() const
{
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isReceive())
return f;
return nullptr;
}
vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
{
if (!m_interfaceEvents)
{
set<string> eventsSeen;
m_interfaceEvents = make_unique<vector<EventDefinition const*>>();
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (EventDefinition const* e: contract->events())
{
/// NOTE: this requires the "internal" version of an Event,
/// though here internal strictly refers to visibility,
/// and not to function encoding (jump vs. call)
auto const& function = e->functionType(true);
solAssert(function, "");
string eventSignature = function->externalSignature();
if (eventsSeen.count(eventSignature) == 0)
{
eventsSeen.insert(eventSignature);
m_interfaceEvents->push_back(e);
}
}
}
return *m_interfaceEvents;
}
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList() const
{
if (!m_interfaceFunctionList)
{
set<string> signaturesSeen;
m_interfaceFunctionList = make_unique<vector<pair<util::FixedHash<4>, FunctionTypePointer>>>();
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
{
vector<FunctionTypePointer> functions;
for (FunctionDefinition const* f: contract->definedFunctions())
if (f->isPartOfExternalInterface())
functions.push_back(TypeProvider::function(*f, false));
for (VariableDeclaration const* v: contract->stateVariables())
if (v->isPartOfExternalInterface())
functions.push_back(TypeProvider::function(*v));
for (FunctionTypePointer const& fun: functions)
{
if (!fun->interfaceFunctionType())
// Fails hopefully because we already registered the error
continue;
string functionSignature = fun->externalSignature();
if (signaturesSeen.count(functionSignature) == 0)
{
signaturesSeen.insert(functionSignature);
util::FixedHash<4> hash(util::keccak256(functionSignature));
m_interfaceFunctionList->emplace_back(hash, fun);
}
}
}
}
return *m_interfaceFunctionList;
}
vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
{
if (!m_inheritableMembers)
{
m_inheritableMembers = make_unique<vector<Declaration const*>>();
auto addInheritableMember = [&](Declaration const* _decl)
{
solAssert(_decl, "addInheritableMember got a nullpointer.");
if (_decl->isVisibleInDerivedContracts())
m_inheritableMembers->push_back(_decl);
};
for (FunctionDefinition const* f: definedFunctions())
addInheritableMember(f);
for (VariableDeclaration const* v: stateVariables())
addInheritableMember(v);
for (StructDefinition const* s: definedStructs())
addInheritableMember(s);
for (EnumDefinition const* e: definedEnums())
addInheritableMember(e);
for (EventDefinition const* e: events())
addInheritableMember(e);
}
return *m_inheritableMembers;
}
TypePointer ContractDefinition::type() const
{
return TypeProvider::typeType(TypeProvider::contract(*this));
}
ContractDefinitionAnnotation& ContractDefinition::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ContractDefinitionAnnotation>();
return dynamic_cast<ContractDefinitionAnnotation&>(*m_annotation);
}
TypeNameAnnotation& TypeName::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<TypeNameAnnotation>();
return dynamic_cast<TypeNameAnnotation&>(*m_annotation);
}
TypePointer StructDefinition::type() const
{
return TypeProvider::typeType(TypeProvider::structType(*this, DataLocation::Storage));
}
TypeDeclarationAnnotation& StructDefinition::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<TypeDeclarationAnnotation>();
return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
}
TypePointer EnumValue::type() const
{
auto parentDef = dynamic_cast<EnumDefinition const*>(scope());
solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
return TypeProvider::enumType(*parentDef);
}
TypePointer EnumDefinition::type() const
{
return TypeProvider::typeType(TypeProvider::enumType(*this));
}
TypeDeclarationAnnotation& EnumDefinition::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<TypeDeclarationAnnotation>();
return dynamic_cast<TypeDeclarationAnnotation&>(*m_annotation);
}
ContractKind FunctionDefinition::inContractKind() const
{
auto contractDef = dynamic_cast<ContractDefinition const*>(scope());
solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set.");
return contractDef->contractKind();
}
CallableDeclarationAnnotation& CallableDeclaration::annotation() const
{
solAssert(
m_annotation,
"CallableDeclarationAnnotation is an abstract base, need to call annotation on the concrete class first."
);
return dynamic_cast<CallableDeclarationAnnotation&>(*m_annotation);
}
FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
{
if (_internal)
{
switch (visibility())
{
case Visibility::Default:
solAssert(false, "visibility() should not return Default");
case Visibility::Private:
case Visibility::Internal:
case Visibility::Public:
return TypeProvider::function(*this, _internal);
case Visibility::External:
return {};
}
}
else
{
switch (visibility())
{
case Visibility::Default:
solAssert(false, "visibility() should not return Default");
case Visibility::Private:
case Visibility::Internal:
return {};
case Visibility::Public:
case Visibility::External:
return TypeProvider::function(*this, _internal);
}
}
// To make the compiler happy
return {};
}
TypePointer FunctionDefinition::type() const
{
solAssert(visibility() != Visibility::External, "");
return TypeProvider::function(*this);
}
string FunctionDefinition::externalSignature() const
{
return TypeProvider::function(*this)->externalSignature();
}
string FunctionDefinition::externalIdentifierHex() const
{
return TypeProvider::function(*this)->externalIdentifierHex();
}
FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<FunctionDefinitionAnnotation>();
return dynamic_cast<FunctionDefinitionAnnotation&>(*m_annotation);
}
TypePointer ModifierDefinition::type() const
{
return TypeProvider::modifier(*this);
}
ModifierDefinitionAnnotation& ModifierDefinition::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ModifierDefinitionAnnotation>();
return dynamic_cast<ModifierDefinitionAnnotation&>(*m_annotation);
}
TypePointer EventDefinition::type() const
{
return TypeProvider::function(*this);
}
FunctionTypePointer EventDefinition::functionType(bool _internal) const
{
if (_internal)
return TypeProvider::function(*this);
else
return nullptr;
}
EventDefinitionAnnotation& EventDefinition::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<EventDefinitionAnnotation>();
return dynamic_cast<EventDefinitionAnnotation&>(*m_annotation);
}
UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<UserDefinedTypeNameAnnotation>();
return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
}
SourceUnit const& Scopable::sourceUnit() const
{
ASTNode const* s = scope();
solAssert(s, "");
// will not always be a declaration
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
s = dynamic_cast<Scopable const*>(s)->scope();
return dynamic_cast<SourceUnit const&>(*s);
}
CallableDeclaration const* Scopable::functionOrModifierDefinition() const
{
ASTNode const* s = scope();
solAssert(s, "");
while (dynamic_cast<Scopable const*>(s))
{
if (auto funDef = dynamic_cast<FunctionDefinition const*>(s))
return funDef;
if (auto modDef = dynamic_cast<ModifierDefinition const*>(s))
return modDef;
s = dynamic_cast<Scopable const*>(s)->scope();
}
return nullptr;
}
string Scopable::sourceUnitName() const
{
return sourceUnit().annotation().path;
}
DeclarationAnnotation& Declaration::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<DeclarationAnnotation>();
return dynamic_cast<DeclarationAnnotation&>(*m_annotation);
}
bool VariableDeclaration::isLValue() const
{
// Constant declared variables are Read-Only
if (m_isConstant)
return false;
// External function arguments of reference type are Read-Only
if (isExternalCallableParameter() && dynamic_cast<ReferenceType const*>(type()))
return false;
return true;
}
bool VariableDeclaration::isLocalVariable() const
{
auto s = scope();
return
dynamic_cast<FunctionTypeName const*>(s) ||
dynamic_cast<CallableDeclaration const*>(s) ||
dynamic_cast<Block const*>(s) ||
dynamic_cast<TryCatchClause const*>(s) ||
dynamic_cast<ForStatement const*>(s);
}
bool VariableDeclaration::isCallableOrCatchParameter() const
{
if (isReturnParameter() || isTryCatchParameter())
return true;
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
parameters = &funTypeName->parameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
parameters = &callable->parameters();
if (parameters)
for (auto const& variable: *parameters)
if (variable.get() == this)
return true;
return false;
}
bool VariableDeclaration::isLocalOrReturn() const
{
return isReturnParameter() || (isLocalVariable() && !isCallableOrCatchParameter());
}
bool VariableDeclaration::isReturnParameter() const
{
vector<ASTPointer<VariableDeclaration>> const* returnParameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
returnParameters = &funTypeName->returnParameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->returnParameterList())
returnParameters = &callable->returnParameterList()->parameters();
if (returnParameters)
for (auto const& variable: *returnParameters)
if (variable.get() == this)
return true;
return false;
}
bool VariableDeclaration::isTryCatchParameter() const
{
return dynamic_cast<TryCatchClause const*>(scope());
}
bool VariableDeclaration::isExternalCallableParameter() const
{
if (!isCallableOrCatchParameter())
return false;
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->visibility() == Visibility::External)
return !isReturnParameter();
return false;
}
bool VariableDeclaration::isInternalCallableParameter() const
{
if (!isCallableOrCatchParameter())
return false;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
return funTypeName->visibility() == Visibility::Internal;
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
return callable->visibility() <= Visibility::Internal;
return false;
}
bool VariableDeclaration::isLibraryFunctionParameter() const
{
if (!isCallableOrCatchParameter())
return false;
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
else
return false;
}
bool VariableDeclaration::isEventParameter() const
{
return dynamic_cast<EventDefinition const*>(scope()) != nullptr;
}
bool VariableDeclaration::hasReferenceOrMappingType() const
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
Type const* type = typeName()->annotation().type;
return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type);
}
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
{
using Location = VariableDeclaration::Location;
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
return set<Location>{ Location::Unspecified };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
{
set<Location> locations{ Location::CallData };
if (isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isCallableOrCatchParameter())
{
set<Location> locations{ Location::Memory };
if (isInternalCallableParameter() || isLibraryFunctionParameter() || isTryCatchParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isLocalVariable())
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
if (typeName()->annotation().type->category() == Type::Category::Mapping)
return set<Location>{ Location::Storage };
else
// TODO: add Location::Calldata once implemented for local variables.
return set<Location>{ Location::Memory, Location::Storage };
}
else
// Struct members etc.
return set<Location>{ Location::Unspecified };
}
string VariableDeclaration::externalIdentifierHex() const
{
solAssert(isStateVariable() && isPublic(), "Can only be called for public state variables");
return TypeProvider::function(*this)->externalIdentifierHex();
}
TypePointer VariableDeclaration::type() const
{
return annotation().type;
}
FunctionTypePointer VariableDeclaration::functionType(bool _internal) const
{
if (_internal)
return nullptr;
switch (visibility())
{
case Visibility::Default:
solAssert(false, "visibility() should not return Default");
case Visibility::Private:
case Visibility::Internal:
return nullptr;
case Visibility::Public:
case Visibility::External:
return TypeProvider::function(*this);
}
// To make the compiler happy
return nullptr;
}
VariableDeclarationAnnotation& VariableDeclaration::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<VariableDeclarationAnnotation>();
return dynamic_cast<VariableDeclarationAnnotation&>(*m_annotation);
}
StatementAnnotation& Statement::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<StatementAnnotation>();
return dynamic_cast<StatementAnnotation&>(*m_annotation);
}
InlineAssemblyAnnotation& InlineAssembly::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<InlineAssemblyAnnotation>();
return dynamic_cast<InlineAssemblyAnnotation&>(*m_annotation);
}
BlockAnnotation& Block::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<BlockAnnotation>();
return dynamic_cast<BlockAnnotation&>(*m_annotation);
}
TryCatchClauseAnnotation& TryCatchClause::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<TryCatchClauseAnnotation>();
return dynamic_cast<TryCatchClauseAnnotation&>(*m_annotation);
}
ForStatementAnnotation& ForStatement::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ForStatementAnnotation>();
return dynamic_cast<ForStatementAnnotation&>(*m_annotation);
}
ReturnAnnotation& Return::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ReturnAnnotation>();
return dynamic_cast<ReturnAnnotation&>(*m_annotation);
}
ExpressionAnnotation& Expression::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<ExpressionAnnotation>();
return dynamic_cast<ExpressionAnnotation&>(*m_annotation);
}
MemberAccessAnnotation& MemberAccess::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<MemberAccessAnnotation>();
return dynamic_cast<MemberAccessAnnotation&>(*m_annotation);
}
BinaryOperationAnnotation& BinaryOperation::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<BinaryOperationAnnotation>();
return dynamic_cast<BinaryOperationAnnotation&>(*m_annotation);
}
FunctionCallAnnotation& FunctionCall::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<FunctionCallAnnotation>();
return dynamic_cast<FunctionCallAnnotation&>(*m_annotation);
}
IdentifierAnnotation& Identifier::annotation() const
{
if (!m_annotation)
m_annotation = make_unique<IdentifierAnnotation>();
return dynamic_cast<IdentifierAnnotation&>(*m_annotation);
}
ASTString Literal::valueWithoutUnderscores() const
{
return boost::erase_all_copy(value(), "_");
}
bool Literal::isHexNumber() const
{
if (token() != Token::Number)
return false;
return boost::starts_with(value(), "0x");
}
bool Literal::looksLikeAddress() const
{
if (subDenomination() != SubDenomination::None)
return false;
if (!isHexNumber())
return false;
return abs(int(valueWithoutUnderscores().length()) - 42) <= 1;
}
bool Literal::passesAddressChecksum() const
{
solAssert(isHexNumber(), "Expected hex number");
return util::passesAddressChecksum(valueWithoutUnderscores(), true);
}
string Literal::getChecksummedAddress() const
{
solAssert(isHexNumber(), "Expected hex number");
/// Pad literal to be a proper hex address.
string address = valueWithoutUnderscores().substr(2);
if (address.length() > 40)
return string();
address.insert(address.begin(), 40 - address.size(), '0');
return util::getChecksummedAddress(address);
}