aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/AST.cpp18
-rw-r--r--libsolidity/AST.h12
-rw-r--r--libsolidity/Compiler.cpp16
-rw-r--r--libsolidity/Compiler.h19
-rw-r--r--libsolidity/CompilerContext.cpp2
-rw-r--r--libsolidity/CompilerContext.h12
-rw-r--r--libsolidity/CompilerStack.cpp42
-rw-r--r--libsolidity/CompilerStack.h27
-rw-r--r--libsolidity/ExpressionCompiler.cpp27
-rw-r--r--libsolidity/Parser.cpp12
-rw-r--r--libsolidity/Parser.h8
-rw-r--r--libsolidity/Token.h3
-rw-r--r--libsolidity/Types.cpp38
-rw-r--r--libsolidity/Types.h8
-rw-r--r--libsolidity/grammar.txt2
15 files changed, 159 insertions, 87 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp
index 1ad34cfe..674f33b7 100644
--- a/libsolidity/AST.cpp
+++ b/libsolidity/AST.cpp
@@ -106,6 +106,9 @@ void ContractDefinition::checkTypeRequirements()
));
hashes.insert(hash);
}
+
+ if (isLibrary())
+ checkLibraryRequirements();
}
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
@@ -335,6 +338,17 @@ void ContractDefinition::checkExternalTypeClashes() const
));
}
+void ContractDefinition::checkLibraryRequirements() const
+{
+ solAssert(m_isLibrary, "");
+ if (!m_baseContracts.empty())
+ BOOST_THROW_EXCEPTION(createTypeError("Library is not allowed to inherit."));
+
+ for (auto const& var: m_stateVariables)
+ if (!var->isConstant())
+ BOOST_THROW_EXCEPTION(var->createTypeError("Library cannot have non-constant state variables"));
+}
+
vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const
{
if (!m_interfaceEvents)
@@ -452,6 +466,10 @@ void InheritanceSpecifier::checkTypeRequirements()
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration());
solAssert(base, "Base contract not available.");
+
+ if (base->isLibrary())
+ BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from."));
+
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError(
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index 48790e6a..da6a7d58 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -215,7 +215,7 @@ protected:
/// @}
/**
- * Definition of a contract. This is the only AST nodes where child nodes are not visited in
+ * Definition of a contract or library. This is the only AST nodes where child nodes are not visited in
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
@@ -232,7 +232,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
- std::vector<ASTPointer<EventDefinition>> const& _events
+ std::vector<ASTPointer<EventDefinition>> const& _events,
+ bool _isLibrary
):
Declaration(_location, _name),
Documented(_documentation),
@@ -243,7 +244,8 @@ public:
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions),
m_functionModifiers(_functionModifiers),
- m_events(_events)
+ m_events(_events),
+ m_isLibrary(_isLibrary)
{}
virtual void accept(ASTVisitor& _visitor) override;
@@ -257,6 +259,7 @@ public:
std::vector<ASTPointer<FunctionDefinition>> const& definedFunctions() const { return m_definedFunctions; }
std::vector<ASTPointer<EventDefinition>> const& events() const { return m_events; }
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
+ bool isLibrary() const { return m_isLibrary; }
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
@@ -297,6 +300,8 @@ private:
/// Checks that different functions with external visibility end up having different
/// external argument types (i.e. different signature).
void checkExternalTypeClashes() const;
+ /// Checks that all requirements for a library are fulfilled if this is a library.
+ void checkLibraryRequirements() const;
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
@@ -307,6 +312,7 @@ private:
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions;
std::vector<ASTPointer<ModifierDefinition>> m_functionModifiers;
std::vector<ASTPointer<EventDefinition>> m_events;
+ bool m_isLibrary;
// parsed Natspec documentation of the contract.
std::string m_userDocumentation;
diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp
index 8394efa3..9bdf45f8 100644
--- a/libsolidity/Compiler.cpp
+++ b/libsolidity/Compiler.cpp
@@ -48,8 +48,10 @@ private:
unsigned stackHeight;
};
-void Compiler::compileContract(ContractDefinition const& _contract,
- map<ContractDefinition const*, bytes const*> const& _contracts)
+void Compiler::compileContract(
+ ContractDefinition const& _contract,
+ std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
+)
{
m_context = CompilerContext(); // clear it just in case
{
@@ -70,7 +72,7 @@ void Compiler::compileContract(ContractDefinition const& _contract,
void Compiler::compileClone(
ContractDefinition const& _contract,
- map<ContractDefinition const*, bytes const*> const& _contracts
+ map<ContractDefinition const*, eth::Assembly const*> const& _contracts
)
{
m_context = CompilerContext(); // clear it just in case
@@ -98,11 +100,13 @@ eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _functi
return m_runtimeContext.functionEntryLabelIfExists(_function);
}
-void Compiler::initializeContext(ContractDefinition const& _contract,
- map<ContractDefinition const*, bytes const*> const& _contracts)
+void Compiler::initializeContext(
+ ContractDefinition const& _contract,
+ map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
+)
{
CompilerUtils(m_context).initialiseFreeMemoryPointer();
- m_context.setCompiledContracts(_contracts);
+ m_context.setCompiledContracts(_compiledContracts);
m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts());
registerStateVariables(_contract);
m_context.resetVisitedNodes(&_contract);
diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h
index f283683f..7e1d3222 100644
--- a/libsolidity/Compiler.h
+++ b/libsolidity/Compiler.h
@@ -42,16 +42,19 @@ public:
{
}
- void compileContract(ContractDefinition const& _contract,
- std::map<ContractDefinition const*, bytes const*> const& _contracts);
+ void compileContract(
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
+ );
/// Compiles a contract that uses CALLCODE to call into a pre-deployed version of the given
/// contract at runtime, but contains the full creation-time code.
void compileClone(
ContractDefinition const& _contract,
- std::map<ContractDefinition const*, bytes const*> const& _contracts
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
);
- bytes assembledBytecode() { return m_context.assembledBytecode(); }
- bytes runtimeBytecode() { return m_context.assembledRuntimeBytecode(m_runtimeSub); }
+ eth::Assembly const& assembly() { return m_context.assembly(); }
+ eth::LinkerObject assembledObject() { return m_context.assembledObject(); }
+ eth::LinkerObject runtimeObject() { return m_context.assembledRuntimeObject(m_runtimeSub); }
/// @arg _sourceCodes is the map of input files to source code strings
/// @arg _inJsonFromat shows whether the out should be in Json format
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
@@ -69,8 +72,10 @@ public:
private:
/// Registers the non-function objects inside the contract with the context.
- void initializeContext(ContractDefinition const& _contract,
- std::map<ContractDefinition const*, bytes const*> const& _contracts);
+ void initializeContext(
+ ContractDefinition const& _contract,
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
+ );
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
/// with a new and initialized context. Adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp
index af93578f..717627a5 100644
--- a/libsolidity/CompilerContext.cpp
+++ b/libsolidity/CompilerContext.cpp
@@ -65,7 +65,7 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
m_localVariables.erase(&_declaration);
}
-bytes const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
+eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);
solAssert(ret != m_compiledContracts.end(), "Compiled contract not found.");
diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h
index 590c8797..39f4e75f 100644
--- a/libsolidity/CompilerContext.h
+++ b/libsolidity/CompilerContext.h
@@ -47,8 +47,8 @@ public:
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
void removeVariable(VariableDeclaration const& _declaration);
- void setCompiledContracts(std::map<ContractDefinition const*, bytes const*> const& _contracts) { m_compiledContracts = _contracts; }
- bytes const& compiledContract(ContractDefinition const& _contract) const;
+ void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
+ eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
void setStackOffset(int _offset) { m_asm.setDeposit(_offset); }
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
@@ -112,6 +112,8 @@ public:
void appendProgramSize() { return m_asm.appendProgramSize(); }
/// Adds data to the data section, pushes a reference to the stack
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
+ /// Appends the address (virtual, will be filled in by linker) of a library.
+ void appendLibraryAddress(std::string const& _identifier) { m_asm.appendLibraryAddress(_identifier); }
/// Resets the stack of visited nodes with a new stack having only @c _node
void resetVisitedNodes(ASTNode const* _node);
/// Pops the stack of visited nodes
@@ -135,8 +137,8 @@ public:
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
}
- bytes assembledBytecode() { return m_asm.assemble(); }
- bytes assembledRuntimeBytecode(size_t _subIndex) { m_asm.assemble(); return m_asm.data(u256(_subIndex)); }
+ eth::LinkerObject const& assembledObject() { return m_asm.assemble(); }
+ eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm.sub(_subIndex).assemble(); }
/**
* Helper class to pop the visited nodes stack when a scope closes
@@ -164,7 +166,7 @@ private:
/// Magic global variables like msg, tx or this, distinguished by type.
std::set<Declaration const*> m_magicGlobals;
/// Other already compiled contracts to be used in contract creation calls.
- std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
+ std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts;
/// Storage offsets of state variables
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
/// Offsets of local variables on the stack (relative to stack base).
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index 385533b2..3da982a4 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -153,7 +153,7 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
if (!m_parseSuccessful)
parse();
- map<ContractDefinition const*, bytes const*> contractBytecode;
+ map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
@@ -161,26 +161,24 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
if (!contract->isFullyImplemented())
continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
- compiler->compileContract(*contract, contractBytecode);
+ compiler->compileContract(*contract, compiledContracts);
Contract& compiledContract = m_contracts.at(contract->name());
- compiledContract.bytecode = compiler->assembledBytecode();
- compiledContract.runtimeBytecode = compiler->runtimeBytecode();
- compiledContract.compiler = move(compiler);
- compiler = make_shared<Compiler>(_optimize, _runs);
- compiler->compileContract(*contract, contractBytecode);
- contractBytecode[compiledContract.contract] = &compiledContract.bytecode;
+ compiledContract.compiler = compiler;
+ compiledContract.object = compiler->assembledObject();
+ compiledContract.runtimeObject = compiler->runtimeObject();
+ compiledContracts[compiledContract.contract] = &compiler->assembly();
Compiler cloneCompiler(_optimize, _runs);
- cloneCompiler.compileClone(*contract, contractBytecode);
- compiledContract.cloneBytecode = cloneCompiler.assembledBytecode();
+ cloneCompiler.compileClone(*contract, compiledContracts);
+ compiledContract.cloneObject = cloneCompiler.assembledObject();
}
}
-bytes const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
+eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
{
parse(_sourceCode);
compile(_optimize);
- return bytecode();
+ return object();
}
eth::AssemblyItems const* CompilerStack::assemblyItems(string const& _contractName) const
@@ -195,24 +193,28 @@ eth::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _con
return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr;
}
-bytes const& CompilerStack::bytecode(string const& _contractName) const
+eth::LinkerObject const& CompilerStack::object(string const& _contractName) const
{
- return contract(_contractName).bytecode;
+ return contract(_contractName).object;
}
-bytes const& CompilerStack::runtimeBytecode(string const& _contractName) const
+eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractName) const
{
- return contract(_contractName).runtimeBytecode;
+ return contract(_contractName).runtimeObject;
}
-bytes const& CompilerStack::cloneBytecode(string const& _contractName) const
+eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) const
{
- return contract(_contractName).cloneBytecode;
+ return contract(_contractName).cloneObject;
}
dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
{
- return dev::sha3(runtimeBytecode(_contractName));
+ auto const& obj = runtimeObject(_contractName);
+ if (obj.bytecode.empty() || !obj.linkReferences.empty())
+ return dev::h256();
+ else
+ return dev::sha3(obj.bytecode);
}
Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
@@ -305,7 +307,7 @@ size_t CompilerStack::functionEntryPoint(
return 0;
}
-bytes CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
+eth::LinkerObject CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
{
CompilerStack stack;
return stack.compile(_sourceCode, _optimize);
diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h
index 0d812e02..8db8aff4 100644
--- a/libsolidity/CompilerStack.h
+++ b/libsolidity/CompilerStack.h
@@ -32,6 +32,7 @@
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libevmasm/SourceLocation.h>
+#include <libevmasm/LinkerObject.h>
namespace dev
{
@@ -92,23 +93,25 @@ public:
/// Compiles the source units that were previously added and parsed.
void compile(bool _optimize = false, unsigned _runs = 200);
/// Parses and compiles the given source code.
- /// @returns the compiled bytecode
- bytes const& compile(std::string const& _sourceCode, bool _optimize = false);
+ /// @returns the compiled linker object
+ eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false);
- /// @returns the assembled bytecode for a contract.
- bytes const& bytecode(std::string const& _contractName = "") const;
- /// @returns the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
- bytes const& runtimeBytecode(std::string const& _contractName = "") const;
+ /// @returns the assembled bytecode for a contract (empty if it has to be linked or lacks implementation).
+ eth::LinkerObject const& object(std::string const& _contractName = "") const;
+ /// @returns the runtime bytecode for the contract (empty if it has to be linked or lacks implementation).
+ eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const;
/// @returns the bytecode of a contract that uses an already deployed contract via CALLCODE.
/// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to
/// substituted by the actual address. Note that this sequence starts end ends in three X
/// characters but can contain anything in between.
- bytes const& cloneBytecode(std::string const& _contractName = "") const;
+ eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const;
/// @returns normal contract assembly items
eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const;
/// @returns runtime contract assembly items
eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const;
- /// @returns hash of the runtime bytecode for the contract, i.e. the code that is returned by the constructor.
+ /// @returns hash of the runtime bytecode for the contract, i.e. the code that is
+ /// returned by the constructor or the zero-h256 if the contract still needs to be linked or
+ /// does not have runtime code.
dev::h256 contractCodeHash(std::string const& _contractName = "") const;
/// Streams a verbose version of the assembly to @a _outStream.
@@ -146,7 +149,7 @@ public:
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
/// scanning the source code - this is useful for printing exception information.
- static bytes staticCompile(std::string const& _sourceCode, bool _optimize = false);
+ static eth::LinkerObject staticCompile(std::string const& _sourceCode, bool _optimize = false);
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
/// line and columns are numbered starting from 1 with following order:
@@ -170,9 +173,9 @@ private:
{
ContractDefinition const* contract = nullptr;
std::shared_ptr<Compiler> compiler;
- bytes bytecode;
- bytes runtimeBytecode;
- bytes cloneBytecode;
+ eth::LinkerObject object;
+ eth::LinkerObject runtimeObject;
+ eth::LinkerObject cloneObject;
std::shared_ptr<InterfaceHandler> interfaceHandler;
mutable std::unique_ptr<std::string const> interface;
mutable std::unique_ptr<std::string const> solidityInterface;
diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp
index d5a8362e..b22a78dc 100644
--- a/libsolidity/ExpressionCompiler.cpp
+++ b/libsolidity/ExpressionCompiler.cpp
@@ -454,12 +454,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
ContractDefinition const& contract =
dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
// copy the contract's code into memory
- bytes const& bytecode = m_context.compiledContract(contract);
+ eth::Assembly const& assembly = m_context.compiledContract(contract);
utils().fetchFreeMemoryPointer();
- m_context << u256(bytecode.size()) << eth::Instruction::DUP1;
- //@todo could be done by actually appending the Assembly, but then we probably need to compile
- // multiple times. Will revisit once external fuctions are inlined.
- m_context.appendData(bytecode);
+ // pushes size
+ eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
+ m_context << eth::Instruction::DUP1 << subroutine;
m_context << eth::Instruction::DUP4 << eth::Instruction::CODECOPY;
m_context << eth::Instruction::ADD;
@@ -773,9 +772,15 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
if (dynamic_cast<ContractType const*>(type.actualType().get()))
{
- auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration());
- solAssert(!!function, "Function not found in member access");
- m_context << m_context.functionEntryLabel(*function).pushTag();
+ auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.type());
+ if (funType.location() != FunctionType::Location::Internal)
+ m_context << funType.externalIdentifier();
+ else
+ {
+ auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration());
+ solAssert(!!function, "Function not found in member access");
+ m_context << m_context.functionEntryLabel(*function).pushTag();
+ }
}
else if (auto enumType = dynamic_cast<EnumType const*>(type.actualType().get()))
m_context << enumType->memberValue(_memberAccess.memberName());
@@ -924,9 +929,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
utils().convertType(*variable->value()->type(), *variable->type());
}
}
- else if (dynamic_cast<ContractDefinition const*>(declaration))
+ else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
{
- // no-op
+ if (contract->isLibrary())
+ //@todo name should be unique, change once we have module management
+ m_context.appendLibraryAddress(contract->name());
}
else if (dynamic_cast<EventDefinition const*>(declaration))
{
diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp
index 7f779ed0..24f7734c 100644
--- a/libsolidity/Parser.cpp
+++ b/libsolidity/Parser.cpp
@@ -71,13 +71,14 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
vector<ASTPointer<ASTNode>> nodes;
while (m_scanner->currentToken() != Token::EOS)
{
- switch (m_scanner->currentToken())
+ switch (auto token = m_scanner->currentToken())
{
case Token::Import:
nodes.push_back(parseImportDirective());
break;
case Token::Contract:
- nodes.push_back(parseContractDefinition());
+ case Token::Library:
+ nodes.push_back(parseContractDefinition(token == Token::Library));
break;
default:
BOOST_THROW_EXCEPTION(createParserError(std::string("Expected import directive or contract definition.")));
@@ -113,13 +114,13 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
return nodeFactory.createNode<ImportDirective>(url);
}
-ASTPointer<ContractDefinition> Parser::parseContractDefinition()
+ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
- expectToken(Token::Contract);
+ expectToken(_isLibrary ? Token::Library : Token::Contract);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
vector<ASTPointer<StructDefinition>> structs;
@@ -177,7 +178,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
stateVariables,
functions,
modifiers,
- events
+ events,
+ _isLibrary
);
}
diff --git a/libsolidity/Parser.h b/libsolidity/Parser.h
index 36302f74..79eb73f0 100644
--- a/libsolidity/Parser.h
+++ b/libsolidity/Parser.h
@@ -61,15 +61,17 @@ private:
///@{
///@name Parsing functions for the AST nodes
ASTPointer<ImportDirective> parseImportDirective();
- ASTPointer<ContractDefinition> parseContractDefinition();
+ ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
ASTPointer<EnumValue> parseEnumValue();
- ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(),
- ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
+ ASTPointer<VariableDeclaration> parseVariableDeclaration(
+ VarDeclParserOptions const& _options = VarDeclParserOptions(),
+ ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()
+ );
ASTPointer<ModifierDefinition> parseModifierDefinition();
ASTPointer<EventDefinition> parseEventDefinition();
ASTPointer<ModifierInvocation> parseModifierInvocation();
diff --git a/libsolidity/Token.h b/libsolidity/Token.h
index bc242ecf..1632a693 100644
--- a/libsolidity/Token.h
+++ b/libsolidity/Token.h
@@ -160,6 +160,7 @@ namespace solidity
K(Internal, "internal", 0) \
K(Import, "import", 0) \
K(Is, "is", 0) \
+ K(Library, "library", 0) \
K(Mapping, "mapping", 0) \
K(Memory, "memory", 0) \
K(Modifier, "modifier", 0) \
@@ -305,7 +306,7 @@ namespace solidity
/* Identifiers (not keywords or future reserved words). */ \
T(Identifier, NULL, 0) \
\
- /* Keywords reserved for future. use. */ \
+ /* Keywords reserved for future use. */ \
K(As, "as", 0) \
K(Case, "case", 0) \
K(Catch, "catch", 0) \
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index d1c59ba5..5bc7cd43 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -971,7 +971,7 @@ MemberList const& ContractType::members() const
for (auto const& it: m_contract.interfaceFunctions())
members.push_back(MemberList::Member(
it.second->declaration().name(),
- it.second->asMemberFunction(),
+ it.second->asMemberFunction(false),
&it.second->declaration()
));
m_members.reset(new MemberList(members));
@@ -1538,7 +1538,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
);
}
-FunctionTypePointer FunctionType::asMemberFunction() const
+FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
{
TypePointers parameterTypes;
for (auto const& t: m_parameterTypes)
@@ -1563,7 +1563,7 @@ FunctionTypePointer FunctionType::asMemberFunction() const
returnParameterTypes,
m_parameterNames,
returnParameterNames,
- m_location,
+ _inLibrary ? Location::CallCode : m_location,
m_arbitraryParameters,
m_declaration,
m_gasSet,
@@ -1633,21 +1633,39 @@ u256 TypeType::storageSize() const
<< errinfo_comment("Storage size of non-storable type type requested."));
}
+unsigned TypeType::sizeOnStack() const
+{
+ if (auto contractType = dynamic_cast<ContractType const*>(m_actualType.get()))
+ if (contractType->contractDefinition().isLibrary())
+ return 1;
+ return 0;
+}
+
MemberList const& TypeType::members() const
{
// We need to lazy-initialize it because of recursive references.
if (!m_members)
{
MemberList::MemberMap members;
- if (m_actualType->category() == Category::Contract && m_currentContract != nullptr)
+ if (m_actualType->category() == Category::Contract)
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
- vector<ContractDefinition const*> currentBases = m_currentContract->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));
+ 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 (m_currentContract != nullptr)
+ {
+ vector<ContractDefinition const*> currentBases = m_currentContract->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)
{
diff --git a/libsolidity/Types.h b/libsolidity/Types.h
index 17ae0ce0..11218b7a 100644
--- a/libsolidity/Types.h
+++ b/libsolidity/Types.h
@@ -517,7 +517,7 @@ private:
};
/**
- * The type of a contract instance, there is one distinct type for each contract definition.
+ * The type of a contract instance or library, there is one distinct type for each contract definition.
*/
class ContractType: public Type
{
@@ -788,7 +788,8 @@ public:
/// removed and the location of reference types is changed from CallData to Memory.
/// This is needed if external functions are called on other contracts, as they cannot return
/// dynamic values.
- FunctionTypePointer asMemberFunction() const;
+ /// @param _inLibrary if true, uses CallCode as location.
+ FunctionTypePointer asMemberFunction(bool _inLibrary) const;
private:
static TypePointers parseElementaryTypeVector(strings const& _types);
@@ -851,6 +852,7 @@ public:
/**
* The type of a type reference. The type of "uint32" when used in "a = uint32(2)" is an example
* of a TypeType.
+ * For super contracts or libraries, this has members directly.
*/
class TypeType: public Type
{
@@ -865,7 +867,7 @@ public:
virtual bool canBeStored() const override { return false; }
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
- virtual unsigned sizeOnStack() const override { return 0; }
+ virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
virtual MemberList const& members() const override;
diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt
index 6503516c..467aebee 100644
--- a/libsolidity/grammar.txt
+++ b/libsolidity/grammar.txt
@@ -1,4 +1,4 @@
-ContractDefinition = 'contract' Identifier
+ContractDefinition = ( 'contract' | 'library' ) Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition