aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/GlobalContext.cpp4
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h4
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp184
-rw-r--r--libsolidity/analysis/ReferencesResolver.h8
-rw-r--r--libsolidity/analysis/TypeChecker.cpp22
-rw-r--r--libsolidity/analysis/TypeChecker.h1
-rw-r--r--libsolidity/ast/AST.h28
-rw-r--r--libsolidity/ast/ASTForward.h1
-rw-r--r--libsolidity/ast/ASTVisitor.h4
-rw-r--r--libsolidity/ast/AST_accept.h22
-rw-r--r--libsolidity/ast/Types.cpp399
-rw-r--r--libsolidity/ast/Types.h76
-rw-r--r--libsolidity/codegen/Compiler.cpp18
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp29
-rw-r--r--libsolidity/codegen/CompilerUtils.h6
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp45
-rw-r--r--libsolidity/parsing/Parser.cpp20
-rw-r--r--libsolidity/parsing/Parser.h1
18 files changed, 540 insertions, 332 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index d519934d..a7ffcfad 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -39,7 +39,9 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
make_shared<MagicVariableDeclaration>("suicide",
- make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Selfdestruct)),
+ make_shared<MagicVariableDeclaration>("selfdestruct",
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Selfdestruct)),
make_shared<MagicVariableDeclaration>("addmod",
make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::AddMod)),
make_shared<MagicVariableDeclaration>("mulmod",
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 0d9b2477..1547a274 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -160,8 +160,8 @@ private:
void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
- Declaration const* m_currentScope;
- VariableScope* m_currentFunction;
+ Declaration const* m_currentScope = nullptr;
+ VariableScope* m_currentFunction = nullptr;
ErrorList& m_errors;
};
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index e5b1c52b..2fe53e8b 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -31,28 +31,6 @@ using namespace dev;
using namespace dev::solidity;
-bool ReferencesResolver::visit(Return const& _return)
-{
- _return.annotation().functionReturnParameters = m_returnParameters;
- return true;
-}
-
-void ReferencesResolver::endVisit(NewExpression const& _new)
-{
- typeFor(_new.typeName());
-}
-
-bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName)
-{
- Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
- if (!declaration)
- fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
-
- _typeName.annotation().referencedDeclaration = declaration;
-
- return true;
-}
-
bool ReferencesResolver::resolve(ASTNode const& _root)
{
try
@@ -79,6 +57,67 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
return false;
}
+bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
+{
+ _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName());
+ return true;
+}
+
+void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
+{
+ Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
+ if (!declaration)
+ fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
+
+ _typeName.annotation().referencedDeclaration = declaration;
+
+ if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
+ _typeName.annotation().type = make_shared<StructType>(*structDef);
+ else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
+ _typeName.annotation().type = make_shared<EnumType>(*enumDef);
+ else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
+ _typeName.annotation().type = make_shared<ContractType>(*contract);
+ else
+ fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
+}
+
+void ReferencesResolver::endVisit(Mapping const& _typeName)
+{
+ TypePointer keyType = _typeName.keyType().annotation().type;
+ TypePointer valueType = _typeName.valueType().annotation().type;
+ // Convert key type to memory.
+ keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
+ // Convert value type to storage reference.
+ valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
+ _typeName.annotation().type = make_shared<MappingType>(keyType, valueType);
+}
+
+void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
+{
+ TypePointer baseType = _typeName.baseType().annotation().type;
+ if (baseType->storageBytes() == 0)
+ fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
+ if (Expression const* length = _typeName.length())
+ {
+ if (!length->annotation().type)
+ ConstantEvaluator e(*length);
+
+ auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get());
+ if (!lengthType)
+ fatalTypeError(length->location(), "Invalid array length.");
+ else
+ _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
+ }
+ else
+ _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType);
+}
+
+bool ReferencesResolver::visit(Return const& _return)
+{
+ _return.annotation().functionReturnParameters = m_returnParameters;
+ return true;
+}
+
void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
{
if (_variable.annotation().type)
@@ -87,21 +126,23 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
TypePointer type;
if (_variable.typeName())
{
- type = typeFor(*_variable.typeName());
+ type = _variable.typeName()->annotation().type;
using Location = VariableDeclaration::Location;
- Location loc = _variable.referenceLocation();
+ Location varLoc = _variable.referenceLocation();
+ DataLocation typeLoc = DataLocation::Memory;
// References are forced to calldata for external function parameters (not return)
// and memory for parameters (also return) of publicly visible functions.
// They default to memory for function parameters and storage for local variables.
// As an exception, "storage" is allowed for library functions.
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
+ bool isPointer = true;
if (_variable.isExternalCallableParameter())
{
auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
if (contract.isLibrary())
{
- if (loc == Location::Memory)
+ if (varLoc == Location::Memory)
fatalTypeError(_variable.location(),
"Location has to be calldata or storage for external "
"library functions (remove the \"memory\" keyword)."
@@ -110,50 +151,52 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
else
{
// force location of external function parameters (not return) to calldata
- if (loc != Location::Default)
+ if (varLoc != Location::Default)
fatalTypeError(_variable.location(),
"Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)."
);
}
- if (loc == Location::Default)
- type = ref->copyForLocation(DataLocation::CallData, true);
+ if (varLoc == Location::Default)
+ typeLoc = DataLocation::CallData;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
{
auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
// force locations of public or external function (return) parameters to memory
- if (loc == Location::Storage && !contract.isLibrary())
+ if (varLoc == Location::Storage && !contract.isLibrary())
fatalTypeError(_variable.location(),
"Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)."
);
- if (loc == Location::Default || !contract.isLibrary())
- type = ref->copyForLocation(DataLocation::Memory, true);
+ if (varLoc == Location::Default || !contract.isLibrary())
+ typeLoc = DataLocation::Memory;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
else
{
if (_variable.isConstant())
{
- if (loc != Location::Default && loc != Location::Memory)
+ if (varLoc != Location::Default && varLoc != Location::Memory)
fatalTypeError(
_variable.location(),
"Storage location has to be \"memory\" (or unspecified) for constants."
);
- loc = Location::Memory;
+ typeLoc = DataLocation::Memory;
}
- if (loc == Location::Default)
- loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage;
- bool isPointer = !_variable.isStateVariable();
- type = ref->copyForLocation(
- loc == Location::Memory ?
- DataLocation::Memory :
- DataLocation::Storage,
- isPointer
- );
+ else if (varLoc == Location::Default)
+ typeLoc = _variable.isCallableParameter() ? DataLocation::Memory : DataLocation::Storage;
+ else
+ typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
+ isPointer = !_variable.isStateVariable();
}
+
+ type = ref->copyForLocation(typeLoc, isPointer);
}
- else if (loc != Location::Default && !ref)
+ else if (varLoc != Location::Default && !ref)
fatalTypeError(_variable.location(), "Storage location can only be given for array or struct types.");
if (!type)
@@ -167,61 +210,6 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
_variable.annotation().type = type;
}
-TypePointer ReferencesResolver::typeFor(TypeName const& _typeName)
-{
- if (_typeName.annotation().type)
- return _typeName.annotation().type;
-
- TypePointer type;
- if (auto elemTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
- type = Type::fromElementaryTypeName(elemTypeName->typeName());
- else if (auto typeName = dynamic_cast<UserDefinedTypeName const*>(&_typeName))
- {
- Declaration const* declaration = typeName->annotation().referencedDeclaration;
- solAssert(!!declaration, "");
-
- if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
- type = make_shared<StructType>(*structDef);
- else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
- type = make_shared<EnumType>(*enumDef);
- else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
- type = make_shared<ContractType>(*contract);
- else
- fatalTypeError(typeName->location(), "Name has to refer to a struct, enum or contract.");
- }
- else if (auto mapping = dynamic_cast<Mapping const*>(&_typeName))
- {
- TypePointer keyType = typeFor(mapping->keyType());
- TypePointer valueType = typeFor(mapping->valueType());
- // Convert key type to memory.
- keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
- // Convert value type to storage reference.
- valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
- type = make_shared<MappingType>(keyType, valueType);
- }
- else if (auto arrayType = dynamic_cast<ArrayTypeName const*>(&_typeName))
- {
- TypePointer baseType = typeFor(arrayType->baseType());
- if (baseType->storageBytes() == 0)
- fatalTypeError(arrayType->baseType().location(), "Illegal base type of storage size zero for array.");
- if (Expression const* length = arrayType->length())
- {
- if (!length->annotation().type)
- ConstantEvaluator e(*length);
-
- auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get());
- if (!lengthType)
- fatalTypeError(length->location(), "Invalid array length.");
- else
- type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
- }
- else
- type = make_shared<ArrayType>(DataLocation::Storage, baseType);
- }
-
- return _typeName.annotation().type = move(type);
-}
-
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
{
auto err = make_shared<Error>(Error::Type::TypeError);
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
index 6f5ced8c..a2d71dc3 100644
--- a/libsolidity/analysis/ReferencesResolver.h
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -60,13 +60,13 @@ public:
private:
virtual bool visit(Block const&) override { return m_resolveInsideCode; }
virtual bool visit(Identifier const& _identifier) override;
- virtual bool visit(UserDefinedTypeName const& _typeName) override;
+ virtual bool visit(ElementaryTypeName const& _typeName) override;
+ virtual void endVisit(UserDefinedTypeName const& _typeName) override;
+ virtual void endVisit(Mapping const& _typeName) override;
+ virtual void endVisit(ArrayTypeName const& _typeName) override;
virtual bool visit(Return const& _return) override;
- virtual void endVisit(NewExpression const& _new) override;
virtual void endVisit(VariableDeclaration const& _variable) override;
- TypePointer typeFor(TypeName const& _typeName);
-
/// Adds a new error to the list of errors.
void typeError(SourceLocation const& _location, std::string const& _description);
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 1d2d0258..851266bd 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -356,7 +356,16 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
" to " +
parameterTypes[i]->toString() +
" requested."
- );
+ );
+}
+
+void TypeChecker::endVisit(UsingForDirective const& _usingFor)
+{
+ ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
+ _usingFor.libraryName().annotation().referencedDeclaration
+ );
+ if (!library || !library->isLibrary())
+ typeError(_usingFor.libraryName().location(), "Library name expected.");
}
bool TypeChecker::visit(StructDefinition const& _struct)
@@ -1119,7 +1128,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
for (auto it = possibleMembers.begin(); it != possibleMembers.end();)
if (
it->type->category() == Type::Category::Function &&
- !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes)
+ !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes, exprType)
)
it = possibleMembers.erase(it);
else
@@ -1154,6 +1163,15 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
auto& annotation = _memberAccess.annotation();
annotation.referencedDeclaration = possibleMembers.front().declaration;
annotation.type = possibleMembers.front().type;
+
+ if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get()))
+ if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType()))
+ typeError(
+ _memberAccess.location(),
+ "Function \"" + memberName + "\" cannot be called on an object of type " +
+ exprType->toString() + " (expected " + funType->selfType()->toString() + ")"
+ );
+
if (exprType->category() == Type::Category::Struct)
annotation.isLValue = true;
else if (exprType->category() == Type::Category::Array)
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 9563d4a9..7829a23d 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -76,6 +76,7 @@ private:
void checkLibraryRequirements(ContractDefinition const& _contract);
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
+ virtual void endVisit(UsingForDirective const& _usingFor) override;
virtual bool visit(StructDefinition const& _struct) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(VariableDeclaration const& _variable) override;
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index a28d9f4f..1217d945 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -267,6 +267,7 @@ public:
std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; }
+ std::vector<UsingForDirective const*> usingForDirectives() const { return filteredNodes<UsingForDirective>(m_subNodes); }
std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(m_subNodes); }
std::vector<EnumDefinition const*> definedEnums() const { return filteredNodes<EnumDefinition>(m_subNodes); }
std::vector<VariableDeclaration const*> stateVariables() const { return filteredNodes<VariableDeclaration>(m_subNodes); }
@@ -335,6 +336,33 @@ private:
std::vector<ASTPointer<Expression>> m_arguments;
};
+/**
+ * `using LibraryName for uint` will attach all functions from the library LibraryName
+ * to `uint` if the first parameter matches the type. `using LibraryName for *` attaches
+ * the function to any matching type.
+ */
+class UsingForDirective: public ASTNode
+{
+public:
+ UsingForDirective(
+ SourceLocation const& _location,
+ ASTPointer<Identifier> const& _libraryName,
+ ASTPointer<TypeName> const& _typeName
+ ):
+ ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ Identifier const& libraryName() const { return *m_libraryName; }
+ /// @returns the type name the library is attached to, null for `*`.
+ TypeName const* typeName() const { return m_typeName.get(); }
+
+private:
+ ASTPointer<Identifier> m_libraryName;
+ ASTPointer<TypeName> m_typeName;
+};
+
class StructDefinition: public Declaration
{
public:
diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h
index 02dd054a..6aaa77ce 100644
--- a/libsolidity/ast/ASTForward.h
+++ b/libsolidity/ast/ASTForward.h
@@ -39,6 +39,7 @@ class ImportDirective;
class Declaration;
class ContractDefinition;
class InheritanceSpecifier;
+class UsingForDirective;
class StructDefinition;
class EnumDefinition;
class EnumValue;
diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h
index 14c09fb4..f04d9682 100644
--- a/libsolidity/ast/ASTVisitor.h
+++ b/libsolidity/ast/ASTVisitor.h
@@ -47,6 +47,7 @@ public:
virtual bool visit(ImportDirective& _node) { return visitNode(_node); }
virtual bool visit(ContractDefinition& _node) { return visitNode(_node); }
virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); }
+ virtual bool visit(UsingForDirective& _node) { return visitNode(_node); }
virtual bool visit(StructDefinition& _node) { return visitNode(_node); }
virtual bool visit(EnumDefinition& _node) { return visitNode(_node); }
virtual bool visit(EnumValue& _node) { return visitNode(_node); }
@@ -88,6 +89,7 @@ public:
virtual void endVisit(ImportDirective& _node) { endVisitNode(_node); }
virtual void endVisit(ContractDefinition& _node) { endVisitNode(_node); }
virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); }
+ virtual void endVisit(UsingForDirective& _node) { endVisitNode(_node); }
virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); }
virtual void endVisit(EnumDefinition& _node) { endVisitNode(_node); }
virtual void endVisit(EnumValue& _node) { endVisitNode(_node); }
@@ -142,6 +144,7 @@ public:
virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); }
virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); }
virtual bool visit(StructDefinition const& _node) { return visitNode(_node); }
+ virtual bool visit(UsingForDirective const& _node) { return visitNode(_node); }
virtual bool visit(EnumDefinition const& _node) { return visitNode(_node); }
virtual bool visit(EnumValue const& _node) { return visitNode(_node); }
virtual bool visit(ParameterList const& _node) { return visitNode(_node); }
@@ -182,6 +185,7 @@ public:
virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); }
virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); }
+ virtual void endVisit(UsingForDirective const& _node) { endVisitNode(_node); }
virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(EnumDefinition const& _node) { endVisitNode(_node); }
virtual void endVisit(EnumValue const& _node) { endVisitNode(_node); }
diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h
index f65595b8..61370c55 100644
--- a/libsolidity/ast/AST_accept.h
+++ b/libsolidity/ast/AST_accept.h
@@ -123,6 +123,28 @@ void EnumValue::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
+void UsingForDirective::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_libraryName->accept(_visitor);
+ if (m_typeName)
+ m_typeName->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void UsingForDirective::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_libraryName->accept(_visitor);
+ if (m_typeName)
+ m_typeName->accept(_visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
void StructDefinition::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 96f44571..2dc7fb84 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -86,6 +86,11 @@ MemberList& MemberList::operator=(MemberList&& _other)
return *this;
}
+void MemberList::combine(MemberList const & _other)
+{
+ m_memberTypes += _other.m_memberTypes;
+}
+
std::pair<u256, unsigned> const* MemberList::memberStorageOffset(string const& _name) const
{
if (!m_storageOffsets)
@@ -185,7 +190,52 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
return TypePointer();
}
-const MemberList Type::EmptyMemberList;
+MemberList const& Type::members(ContractDefinition const* _currentScope) const
+{
+ if (!m_members[_currentScope])
+ {
+ MemberList::MemberMap members = nativeMembers(_currentScope);
+ if (_currentScope)
+ members += boundFunctions(*this, *_currentScope);
+ m_members[_currentScope] = unique_ptr<MemberList>(new MemberList(move(members)));
+ }
+ return *m_members[_currentScope];
+}
+
+MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope)
+{
+ // Normalise data location of type.
+ TypePointer type = ReferenceType::copyForLocationIfReference(DataLocation::Storage, _type.shared_from_this());
+ set<Declaration const*> seenFunctions;
+ MemberList::MemberMap members;
+ for (ContractDefinition const* contract: _scope.annotation().linearizedBaseContracts)
+ for (UsingForDirective const* ufd: contract->usingForDirectives())
+ {
+ if (ufd->typeName() && *type != *ReferenceType::copyForLocationIfReference(
+ DataLocation::Storage,
+ ufd->typeName()->annotation().type
+ ))
+ continue;
+ auto const& library = dynamic_cast<ContractDefinition const&>(
+ *ufd->libraryName().annotation().referencedDeclaration
+ );
+ for (auto const& it: library.interfaceFunctions())
+ {
+ FunctionType const& funType = *it.second;
+ solAssert(funType.hasDeclaration(), "Tried to bind function without declaration.");
+ if (seenFunctions.count(&funType.declaration()))
+ continue;
+ seenFunctions.insert(&funType.declaration());
+ if (auto fun = funType.asMemberFunction(true, true))
+ members.push_back(MemberList::Member(
+ funType.declaration().name(),
+ fun,
+ &funType.declaration()
+ ));
+ }
+ }
+ return members;
+}
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
@@ -273,12 +323,18 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return commonType;
}
-const MemberList IntegerType::AddressMemberList({
- {"balance", make_shared<IntegerType >(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
- {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
-});
+MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const
+{
+ if (isAddress())
+ return {
+ {"balance", make_shared<IntegerType >(256)},
+ {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)},
+ {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)},
+ {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)}
+ };
+ else
+ return MemberList::MemberMap();
+}
bool IntegerConstantType::isValidLiteral(const Literal& _literal)
{
@@ -858,26 +914,22 @@ string ArrayType::canonicalName(bool _addDataLocation) const
return ret;
}
-MemberList const& ArrayType::members(ContractDefinition const*) const
+MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
{
- if (!m_members)
+ MemberList::MemberMap members;
+ if (!isString())
{
- MemberList::MemberMap members;
- if (!isString())
- {
- members.push_back({"length", make_shared<IntegerType>(256)});
- if (isDynamicallySized() && location() == DataLocation::Storage)
- members.push_back({"push", make_shared<FunctionType>(
- TypePointers{baseType()},
- TypePointers{make_shared<IntegerType>(256)},
- strings{string()},
- strings{string()},
- isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush
- )});
- }
- m_members.reset(new MemberList(members));
+ members.push_back({"length", make_shared<IntegerType>(256)});
+ if (isDynamicallySized() && location() == DataLocation::Storage)
+ members.push_back({"push", make_shared<FunctionType>(
+ TypePointers{baseType()},
+ TypePointers{make_shared<IntegerType>(256)},
+ strings{string()},
+ strings{string()},
+ isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush
+ )});
}
- return *m_members;
+ return members;
}
TypePointer ArrayType::encodingType() const
@@ -956,55 +1008,47 @@ string ContractType::canonicalName(bool) const
return m_contract.annotation().canonicalName;
}
-MemberList const& ContractType::members(ContractDefinition const*) const
+MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) const
{
- // We need to lazy-initialize it because of recursive references.
- if (!m_members)
+ // All address members and all interface functions
+ MemberList::MemberMap members(IntegerType(120, IntegerType::Modifier::Address).nativeMembers(nullptr));
+ if (m_super)
{
- // All address members and all interface functions
- MemberList::MemberMap members(
- IntegerType::AddressMemberList.begin(),
- IntegerType::AddressMemberList.end()
- );
- if (m_super)
- {
- // add the most derived of all functions which are visible in derived contracts
- for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
- for (FunctionDefinition const* function: base->definedFunctions())
+ // add the most derived of all functions which are visible in derived contracts
+ for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
+ for (FunctionDefinition const* function: base->definedFunctions())
+ {
+ if (!function->isVisibleInDerivedContracts())
+ continue;
+ auto functionType = make_shared<FunctionType>(*function, true);
+ bool functionWithEqualArgumentsFound = false;
+ for (auto const& member: members)
{
- if (!function->isVisibleInDerivedContracts())
+ if (member.name != function->name())
+ continue;
+ auto memberType = dynamic_cast<FunctionType const*>(member.type.get());
+ solAssert(!!memberType, "Override changes type.");
+ if (!memberType->hasEqualArgumentTypes(*functionType))
continue;
- auto functionType = make_shared<FunctionType>(*function, true);
- bool functionWithEqualArgumentsFound = false;
- for (auto const& member: members)
- {
- if (member.name != function->name())
- continue;
- auto memberType = dynamic_cast<FunctionType const*>(member.type.get());
- solAssert(!!memberType, "Override changes type.");
- if (!memberType->hasEqualArgumentTypes(*functionType))
- continue;
- functionWithEqualArgumentsFound = true;
- break;
- }
- if (!functionWithEqualArgumentsFound)
- members.push_back(MemberList::Member(
- function->name(),
- functionType,
- function
- ));
+ functionWithEqualArgumentsFound = true;
+ break;
}
- }
- else
- for (auto const& it: m_contract.interfaceFunctions())
- members.push_back(MemberList::Member(
- it.second->declaration().name(),
- it.second->asMemberFunction(m_contract.isLibrary()),
- &it.second->declaration()
- ));
- m_members.reset(new MemberList(members));
+ if (!functionWithEqualArgumentsFound)
+ members.push_back(MemberList::Member(
+ function->name(),
+ functionType,
+ function
+ ));
+ }
}
- return *m_members;
+ else
+ for (auto const& it: m_contract.interfaceFunctions())
+ members.push_back(MemberList::Member(
+ it.second->declaration().name(),
+ it.second->asMemberFunction(m_contract.isLibrary()),
+ &it.second->declaration()
+ ));
+ return members;
}
shared_ptr<FunctionType const> const& ContractType::constructorType() const
@@ -1099,27 +1143,22 @@ string StructType::toString(bool _short) const
return ret;
}
-MemberList const& StructType::members(ContractDefinition const*) const
+MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
{
- // We need to lazy-initialize it because of recursive references.
- if (!m_members)
+ MemberList::MemberMap members;
+ for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
{
- MemberList::MemberMap members;
- for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
- {
- TypePointer type = variable->annotation().type;
- // Skip all mapping members if we are not in storage.
- if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
- continue;
- members.push_back(MemberList::Member(
- variable->name(),
- copyForLocationIfReference(type),
- variable.get())
- );
- }
- m_members.reset(new MemberList(members));
+ TypePointer type = variable->annotation().type;
+ // Skip all mapping members if we are not in storage.
+ if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
+ continue;
+ members.push_back(MemberList::Member(
+ variable->name(),
+ copyForLocationIfReference(type),
+ variable.get())
+ );
}
- return *m_members;
+ return members;
}
TypePointer StructType::interfaceType(bool _inLibrary) const
@@ -1436,6 +1475,20 @@ FunctionType::FunctionType(const EventDefinition& _event):
swap(paramNames, m_parameterNames);
}
+std::vector<string> FunctionType::parameterNames() const
+{
+ if (!bound())
+ return m_parameterNames;
+ return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend());
+}
+
+TypePointers FunctionType::parameterTypes() const
+{
+ if (!bound())
+ return m_parameterTypes;
+ return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
+}
+
bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
@@ -1461,6 +1514,10 @@ bool FunctionType::operator==(Type const& _other) const
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
return false;
+ if (bound() != other.bound())
+ return false;
+ if (bound() && *selfType() != *other.selfType())
+ return false;
return true;
}
@@ -1504,6 +1561,8 @@ unsigned FunctionType::sizeOnStack() const
size++;
if (m_valueSet)
size++;
+ if (bound())
+ size += m_parameterTypes.front()->sizeOnStack();
return size;
}
@@ -1533,7 +1592,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters);
}
-MemberList const& FunctionType::members(ContractDefinition const*) const
+MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const
{
switch (m_location)
{
@@ -1544,52 +1603,53 @@ MemberList const& FunctionType::members(ContractDefinition const*) const
case Location::RIPEMD160:
case Location::Bare:
case Location::BareCallCode:
- if (!m_members)
- {
- MemberList::MemberMap members{
- {
- "value",
+ {
+ MemberList::MemberMap members{
+ {
+ "value",
+ make_shared<FunctionType>(
+ parseElementaryTypeVector({"uint"}),
+ TypePointers{copyAndSetGasOrValue(false, true)},
+ strings(),
+ strings(),
+ Location::SetValue,
+ false,
+ nullptr,
+ m_gasSet,
+ m_valueSet
+ )
+ }
+ };
+ if (m_location != Location::Creation)
+ members.push_back(
+ MemberList::Member(
+ "gas",
make_shared<FunctionType>(
parseElementaryTypeVector({"uint"}),
- TypePointers{copyAndSetGasOrValue(false, true)},
+ TypePointers{copyAndSetGasOrValue(true, false)},
strings(),
strings(),
- Location::SetValue,
+ Location::SetGas,
false,
nullptr,
m_gasSet,
m_valueSet
)
- }
- };
- if (m_location != Location::Creation)
- members.push_back(
- MemberList::Member(
- "gas",
- make_shared<FunctionType>(
- parseElementaryTypeVector({"uint"}),
- TypePointers{copyAndSetGasOrValue(true, false)},
- strings(),
- strings(),
- Location::SetGas,
- false,
- nullptr,
- m_gasSet,
- m_valueSet
- )
- )
- );
- m_members.reset(new MemberList(members));
- }
- return *m_members;
+ )
+ );
+ return members;
+ }
default:
- return EmptyMemberList;
+ return MemberList::MemberMap();
}
}
-bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes) const
+bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
{
- TypePointers const& paramTypes = parameterTypes();
+ solAssert(!bound() || _selfType, "");
+ if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType()))
+ return false;
+ TypePointers paramTypes = parameterTypes();
if (takesArbitraryParameters())
return true;
else if (_argumentTypes.size() != paramTypes.size())
@@ -1678,11 +1738,12 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_arbitraryParameters,
m_declaration,
m_gasSet || _setGas,
- m_valueSet || _setValue
+ m_valueSet || _setValue,
+ m_bound
);
}
-FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
+FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const
{
TypePointers parameterTypes;
for (auto const& t: m_parameterTypes)
@@ -1712,14 +1773,15 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
m_arbitraryParameters,
m_declaration,
m_gasSet,
- m_valueSet
+ m_valueSet,
+ _bound
);
}
vector<string> const FunctionType::parameterTypeNames(bool _addDataLocation) const
{
vector<string> names;
- for (TypePointer const& t: m_parameterTypes)
+ for (TypePointer const& t: parameterTypes())
names.push_back(t->canonicalName(_addDataLocation));
return names;
@@ -1734,6 +1796,12 @@ vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocatio
return names;
}
+TypePointer FunctionType::selfType() const
+{
+ solAssert(bound(), "");
+ return m_parameterTypes.at(0);
+}
+
ASTPointer<ASTString> FunctionType::documentation() const
{
auto function = dynamic_cast<Documented const*>(m_declaration);
@@ -1784,43 +1852,37 @@ unsigned TypeType::sizeOnStack() const
return 0;
}
-MemberList const& TypeType::members(ContractDefinition const* _currentScope) const
+MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const
{
- // We need to lazy-initialize it because of recursive references.
- if (!m_members || m_cachedScope != _currentScope)
+ MemberList::MemberMap members;
+ if (m_actualType->category() == Category::Contract)
{
- MemberList::MemberMap members;
- if (m_actualType->category() == Category::Contract)
- {
- ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
- if (contract.isLibrary())
- for (auto const& it: contract.interfaceFunctions())
- members.push_back(MemberList::Member(
- it.second->declaration().name(),
- it.second->asMemberFunction(true), // use callcode
- &it.second->declaration()
- ));
- else if (_currentScope != nullptr)
- {
- auto const& currentBases = _currentScope->annotation().linearizedBaseContracts;
- if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
- // We are accessing the type of a base contract, so add all public and protected
- // members. Note that this does not add inherited functions on purpose.
- for (Declaration const* decl: contract.inheritableMembers())
- members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
- }
- }
- else if (m_actualType->category() == Category::Enum)
+ ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
+ if (contract.isLibrary())
+ for (auto const& it: contract.interfaceFunctions())
+ members.push_back(MemberList::Member(
+ it.second->declaration().name(),
+ it.second->asMemberFunction(true), // use callcode
+ &it.second->declaration()
+ ));
+ else if (_currentScope != nullptr)
{
- EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
- auto enumType = make_shared<EnumType>(enumDef);
- for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
- members.push_back(MemberList::Member(enumValue->name(), enumType));
+ auto const& currentBases = _currentScope->annotation().linearizedBaseContracts;
+ if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
+ // We are accessing the type of a base contract, so add all public and protected
+ // members. Note that this does not add inherited functions on purpose.
+ for (Declaration const* decl: contract.inheritableMembers())
+ members.push_back(MemberList::Member(decl->name(), decl->type(), decl));
}
- m_members.reset(new MemberList(members));
- m_cachedScope = _currentScope;
}
- return *m_members;
+ else if (m_actualType->category() == Category::Enum)
+ {
+ EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition();
+ auto enumType = make_shared<EnumType>(enumDef);
+ for (ASTPointer<EnumValue> const& enumValue: enumDef.members())
+ members.push_back(MemberList::Member(enumValue->name(), enumType));
+ }
+ return members;
}
ModifierType::ModifierType(const ModifierDefinition& _modifier)
@@ -1866,10 +1928,22 @@ string ModifierType::toString(bool _short) const
MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind)
{
+}
+
+bool MagicType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ MagicType const& other = dynamic_cast<MagicType const&>(_other);
+ return other.m_kind == m_kind;
+}
+
+MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
+{
switch (m_kind)
{
case Kind::Block:
- m_members = MemberList({
+ return MemberList::MemberMap({
{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"timestamp", make_shared<IntegerType>(256)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)},
@@ -1877,35 +1951,24 @@ MagicType::MagicType(MagicType::Kind _kind):
{"number", make_shared<IntegerType>(256)},
{"gaslimit", make_shared<IntegerType>(256)}
});
- break;
case Kind::Message:
- m_members = MemberList({
+ return MemberList::MemberMap({
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(DataLocation::CallData)},
{"sig", make_shared<FixedBytesType>(4)}
});
- break;
case Kind::Transaction:
- m_members = MemberList({
+ return MemberList::MemberMap({
{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
{"gasprice", make_shared<IntegerType>(256)}
});
- break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
}
}
-bool MagicType::operator==(Type const& _other) const
-{
- if (_other.category() != category())
- return false;
- MagicType const& other = dynamic_cast<MagicType const&>(_other);
- return other.m_kind == m_kind;
-}
-
string MagicType::toString(bool) const
{
switch (m_kind)
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index f841a1be..3ebcb2b2 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -90,6 +90,7 @@ public:
MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
MemberList& operator=(MemberList&& _other);
+ void combine(MemberList const& _other);
TypePointer memberType(std::string const& _name) const
{
TypePointer type;
@@ -149,8 +150,6 @@ public:
/// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
- /// Calculates the
-
virtual Category category() const = 0;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
@@ -216,9 +215,9 @@ public:
return _targetType->dataStoredIn(DataLocation::Storage) ? mobileType() : _targetType;
}
- /// Returns the list of all members of this type. Default implementation: no members.
+ /// Returns the list of all members of this type. Default implementation: no members apart from bound.
/// @param _currentScope scope in which the members are accessed.
- virtual MemberList const& members(ContractDefinition const* /*_currentScope*/) const { return EmptyMemberList; }
+ MemberList const& members(ContractDefinition const* _currentScope) const;
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const
{
@@ -251,9 +250,20 @@ public:
/// are returned without modification.
virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); }
+private:
+ /// @returns a member list containing all members added to this type by `using for` directives.
+ static MemberList::MemberMap boundFunctions(Type const& _type, ContractDefinition const& _scope);
+
protected:
- /// Convenience object used when returning an empty member list.
- static const MemberList EmptyMemberList;
+ /// @returns the members native to this type depending on the given context. This function
+ /// is used (in conjunction with boundFunctions to fill m_members below.
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* /*_currentScope*/) const
+ {
+ return MemberList::MemberMap();
+ }
+
+ /// List of member types (parameterised by scape), will be lazy-initialized.
+ mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
};
/**
@@ -281,10 +291,7 @@ public:
virtual unsigned storageBytes() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; }
- virtual MemberList const& members(ContractDefinition const* /*_currentScope*/) const override
- {
- return isAddress() ? AddressMemberList : EmptyMemberList;
- }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
virtual std::string toString(bool _short) const override;
@@ -295,8 +302,6 @@ public:
bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
- static const MemberList AddressMemberList;
-
private:
int m_bits;
Modifier m_modifier;
@@ -517,7 +522,7 @@ public:
virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
- virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override;
virtual TypePointer decodingType() const override;
virtual TypePointer interfaceType(bool _inLibrary) const override;
@@ -541,8 +546,6 @@ private:
TypePointer m_baseType;
bool m_hasDynamicLength = true;
u256 m_length;
- /// List of member types, will be lazy-initialized because of recursive references.
- mutable std::unique_ptr<MemberList> m_members;
};
/**
@@ -570,7 +573,7 @@ public:
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
- virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
@@ -602,8 +605,6 @@ private:
bool m_super = false;
/// Type of the constructor, @see constructorType. Lazily initialized.
mutable FunctionTypePointer m_constructorType;
- /// List of member types, will be lazy-initialized because of recursive references.
- mutable std::unique_ptr<MemberList> m_members;
};
/**
@@ -623,7 +624,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override;
- virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override
{
return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer();
@@ -648,8 +649,6 @@ public:
private:
StructDefinition const& m_struct;
- /// List of member types, will be lazy-initialized because of recursive references.
- mutable std::unique_ptr<MemberList> m_members;
};
/**
@@ -688,8 +687,6 @@ public:
private:
EnumDefinition const& m_enum;
- /// List of member types, will be lazy-initialized because of recursive references.
- mutable std::unique_ptr<MemberList> m_members;
};
/**
@@ -739,7 +736,7 @@ public:
Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas
SHA3, ///< SHA3
- Suicide, ///< SUICIDE
+ Selfdestruct, ///< SELFDESTRUCT
ECRecover, ///< CALL to special contract for ecrecover
SHA256, ///< CALL to special contract for sha256
RIPEMD160, ///< CALL to special contract for ripemd160
@@ -791,7 +788,8 @@ public:
bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr,
bool _gasSet = false,
- bool _valueSet = false
+ bool _valueSet = false,
+ bool _bound = false
):
m_parameterTypes(_parameterTypes),
m_returnParameterTypes(_returnParameterTypes),
@@ -801,15 +799,18 @@ public:
m_arbitraryParameters(_arbitraryParameters),
m_gasSet(_gasSet),
m_valueSet(_valueSet),
+ m_bound(_bound),
m_declaration(_declaration)
{}
- TypePointers const& parameterTypes() const { return m_parameterTypes; }
- std::vector<std::string> const& parameterNames() const { return m_parameterNames; }
+ TypePointers parameterTypes() const;
+ std::vector<std::string> parameterNames() const;
std::vector<std::string> const parameterTypeNames(bool _addDataLocation) const;
TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; }
std::vector<std::string> const returnParameterTypeNames(bool _addDataLocation) const;
+ /// @returns the "self" parameter type for a bound function
+ TypePointer selfType() const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override;
@@ -817,7 +818,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
- virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
/// appropriate external types (i.e. the interfaceType()s) of input/return parameters of
@@ -828,7 +829,9 @@ public:
/// @returns true if this function can take the given argument types (possibly
/// after implicit conversion).
- bool canTakeArguments(TypePointers const& _arguments) const;
+ /// @param _selfType if the function is bound, this has to be supplied and is the type of the
+ /// expression the function is called on.
+ bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
/// @returns true if the types of parameters are equal (does't check return parameter types)
bool hasEqualArgumentTypes(FunctionType const& _other) const;
@@ -855,6 +858,7 @@ public:
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
bool gasSet() const { return m_gasSet; }
bool valueSet() const { return m_valueSet; }
+ bool bound() const { return m_bound; }
/// @returns a copy of this type, where gas or value are set manually. This will never set one
/// of the parameters to fals.
@@ -865,7 +869,8 @@ public:
/// This is needed if external functions are called on other contracts, as they cannot return
/// dynamic values.
/// @param _inLibrary if true, uses CallCode as location.
- FunctionTypePointer asMemberFunction(bool _inLibrary) const;
+ /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`.
+ FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const;
private:
static TypePointers parseElementaryTypeVector(strings const& _types);
@@ -879,8 +884,8 @@ private:
bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
+ bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
bool m_isConstant = false;
- mutable std::unique_ptr<MemberList> m_members;
Declaration const* m_declaration = nullptr;
};
@@ -935,13 +940,10 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
- virtual MemberList const& members(ContractDefinition const* _currentScope) const override;
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
private:
TypePointer m_actualType;
- /// List of member types, will be lazy-initialized because of recursive references.
- mutable std::unique_ptr<MemberList> m_members;
- mutable ContractDefinition const* m_cachedScope = nullptr;
};
@@ -988,14 +990,12 @@ public:
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
virtual unsigned sizeOnStack() const override { return 0; }
- virtual MemberList const& members(ContractDefinition const*) const override { return m_members; }
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
virtual std::string toString(bool _short) const override;
private:
Kind m_kind;
-
- MemberList m_members;
};
}
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index f1d95980..18803b71 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -305,11 +305,19 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
// @todo If base type is an array or struct, it is still calldata-style encoded, so
// we would have to convert it like below.
solAssert(arrayType.location() == DataLocation::Memory, "");
- // compute data pointer
- m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
- m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
- m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
- m_context << u256(0x20) << eth::Instruction::ADD;
+ if (arrayType.isDynamicallySized())
+ {
+ // compute data pointer
+ m_context << eth::Instruction::DUP1 << eth::Instruction::MLOAD;
+ m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
+ m_context << eth::Instruction::SWAP2 << eth::Instruction::SWAP1;
+ m_context << u256(0x20) << eth::Instruction::ADD;
+ }
+ else
+ {
+ m_context << eth::Instruction::DUP1;
+ m_context << u256(arrayType.calldataEncodedSize(true)) << eth::Instruction::ADD;
+ }
}
else
{
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index b57f5b29..bd0857f6 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -695,18 +695,31 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
{
- solAssert(_stackDepth <= 15, "Stack too deep, try removing local variables.");
- for (unsigned j = 0; j < _itemSize; ++j)
- for (unsigned i = 0; i < _stackDepth + _itemSize - 1; ++i)
- m_context << eth::swapInstruction(1 + i);
+ moveIntoStack(_itemSize, _stackDepth);
}
void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
{
- solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
- for (unsigned j = 0; j < _itemSize; ++j)
- for (unsigned i = _stackDepth; i > 0; --i)
- m_context << eth::swapInstruction(i + _itemSize - 1);
+ if (_stackDepth <= _itemSize)
+ for (unsigned i = 0; i < _stackDepth; ++i)
+ rotateStackDown(_stackDepth + _itemSize);
+ else
+ for (unsigned i = 0; i < _itemSize; ++i)
+ rotateStackUp(_stackDepth + _itemSize);
+}
+
+void CompilerUtils::rotateStackUp(unsigned _items)
+{
+ solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
+ for (unsigned i = 1; i < _items; ++i)
+ m_context << eth::swapInstruction(_items - i);
+}
+
+void CompilerUtils::rotateStackDown(unsigned _items)
+{
+ solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
+ for (unsigned i = 1; i < _items; ++i)
+ m_context << eth::swapInstruction(i);
}
void CompilerUtils::popStackElement(Type const& _type)
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 134afd78..55254013 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -134,6 +134,12 @@ public:
void moveToStackTop(unsigned _stackDepth, unsigned _itemSize = 1);
/// Moves @a _itemSize elements past @a _stackDepth other stack elements
void moveIntoStack(unsigned _stackDepth, unsigned _itemSize = 1);
+ /// Rotates the topmost @a _items items on the stack, such that the previously topmost element
+ /// is bottom-most.
+ void rotateStackUp(unsigned _items);
+ /// Rotates the topmost @a _items items on the stack, such that the previously bottom-most element
+ /// is now topmost.
+ void rotateStackDown(unsigned _items);
/// Removes the current value from the top of the stack.
void popStackElement(Type const& _type);
/// Removes element from the top of the stack _amount times.
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 6c288ae7..a090a28c 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -422,6 +422,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
else
{
FunctionType const& function = *functionType;
+ if (function.bound())
+ // Only callcode functions can be bound, this might be lifted later.
+ solAssert(function.location() == Location::CallCode, "");
switch (function.location())
{
case Location::Internal:
@@ -535,7 +538,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{}
);
break;
- case Location::Suicide:
+ case Location::Selfdestruct:
arguments.front()->accept(*this);
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
@@ -672,7 +675,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
_functionCall.expression().accept(*this);
solAssert(function.parameterTypes().size() == 1, "");
solAssert(!!function.parameterTypes()[0], "");
- TypePointer const& paramType = function.parameterTypes()[0];
+ TypePointer paramType = function.parameterTypes()[0];
shared_ptr<ArrayType> arrayType =
function.location() == Location::ArrayPush ?
make_shared<ArrayType>(DataLocation::Storage, paramType) :
@@ -766,7 +769,26 @@ bool ExpressionCompiler::visit(NewExpression const&)
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
+
+ // Check whether the member is a bound function.
ASTString const& member = _memberAccess.memberName();
+ if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
+ if (funType->bound())
+ {
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ *funType->selfType(),
+ true
+ );
+ auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope());
+ solAssert(contract && contract->isLibrary(), "");
+ //@TODO library name might not be unique
+ m_context.appendLibraryAddress(contract->name());
+ m_context << funType->externalIdentifier();
+ utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2);
+ return;
+ }
+
switch (_memberAccess.expression().annotation().type->category())
{
case Type::Category::Contract:
@@ -1239,7 +1261,6 @@ void ExpressionCompiler::appendExternalFunctionCall(
vector<ASTPointer<Expression const>> const& _arguments
)
{
- eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context.
solAssert(
_functionType.takesArbitraryParameters() ||
_arguments.size() == _functionType.parameterTypes().size(), ""
@@ -1249,15 +1270,20 @@ void ExpressionCompiler::appendExternalFunctionCall(
// <stack top>
// value [if _functionType.valueSet()]
// gas [if _functionType.gasSet()]
+ // self object [if bound - moved to top right away]
// function identifier [unless bare]
// contract address
+ unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0;
unsigned gasValueSize = (_functionType.gasSet() ? 1 : 0) + (_functionType.valueSet() ? 1 : 0);
-
- unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + (_functionType.isBareCall() ? 0 : 1));
+ unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1));
unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize);
unsigned valueStackPos = m_context.currentToBaseStackOffset(1);
+ // move self object to top
+ if (_functionType.bound())
+ utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
+
using FunctionKind = FunctionType::Location;
FunctionKind funKind = _functionType.location();
bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode;
@@ -1275,6 +1301,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Evaluate arguments.
TypePointers argumentTypes;
+ TypePointers parameterTypes = _functionType.parameterTypes();
bool manualFunctionId =
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
!_arguments.empty() &&
@@ -1295,6 +1322,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
gasStackPos++;
valueStackPos++;
}
+ if (_functionType.bound())
+ {
+ argumentTypes.push_back(_functionType.selfType());
+ parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
+ }
for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
@@ -1313,7 +1345,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// pointer on the stack).
utils().encodeToMemory(
argumentTypes,
- _functionType.parameterTypes(),
+ parameterTypes,
_functionType.padArguments(),
_functionType.takesArbitraryParameters(),
isCallCode
@@ -1346,6 +1378,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
else
{
+ eth::EVMSchedule schedule;// TODO: Make relevant to current suppose context.
// send all gas except the amount needed to execute "SUB" and "CALL"
// @todo this retains too much gas for now, needs to be fine-tuned.
u256 gasNeededByCaller = schedule.callGas + 10;
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 9272eb4b..2b886121 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -168,6 +168,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
subNodes.push_back(parseModifierDefinition());
else if (currentTokenValue == Token::Event)
subNodes.push_back(parseEventDefinition());
+ else if (currentTokenValue == Token::Using)
+ subNodes.push_back(parseUsingDirective());
else
fatalParserError(std::string("Function, variable, struct or modifier declaration expected."));
}
@@ -475,6 +477,24 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters, anonymous);
}
+ASTPointer<UsingForDirective> Parser::parseUsingDirective()
+{
+ ASTNodeFactory nodeFactory(*this);
+
+ expectToken(Token::Using);
+ //@todo this should actually parse a full path.
+ ASTPointer<Identifier> library(parseIdentifier());
+ ASTPointer<TypeName> typeName;
+ expectToken(Token::For);
+ if (m_scanner->currentToken() == Token::Mul)
+ m_scanner->next();
+ else
+ typeName = parseTypeName(false);
+ nodeFactory.markEndPosition();
+ expectToken(Token::Semicolon);
+ return nodeFactory.createNode<UsingForDirective>(library, typeName);
+}
+
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
{
ASTNodeFactory nodeFactory(*this);
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 663c0f92..5ff46242 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -74,6 +74,7 @@ private:
);
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
+ ASTPointer<UsingForDirective> parseUsingDirective();
ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<Identifier> parseIdentifier();
ASTPointer<TypeName> parseTypeName(bool _allowVar);