aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libsolidity/AST.cpp18
-rw-r--r--libsolidity/AST.h12
-rw-r--r--libsolidity/ASTAnnotations.h8
-rw-r--r--libsolidity/ASTJsonConverter.cpp5
-rw-r--r--libsolidity/ASTPrinter.cpp3
-rw-r--r--libsolidity/Compiler.cpp26
-rw-r--r--libsolidity/Compiler.h2
-rw-r--r--libsolidity/CompilerContext.cpp9
-rw-r--r--libsolidity/CompilerContext.h3
-rw-r--r--libsolidity/CompilerStack.cpp11
-rw-r--r--libsolidity/CompilerUtils.cpp14
-rw-r--r--libsolidity/CompilerUtils.h5
-rw-r--r--libsolidity/DeclarationContainer.h6
-rw-r--r--libsolidity/ExpressionCompiler.cpp5
-rw-r--r--libsolidity/InterfaceHandler.cpp65
-rw-r--r--libsolidity/NameAndTypeResolver.cpp37
-rw-r--r--libsolidity/NameAndTypeResolver.h15
-rw-r--r--libsolidity/Parser.cpp11
-rw-r--r--libsolidity/ReferencesResolver.cpp47
-rw-r--r--libsolidity/TypeChecker.cpp13
-rw-r--r--libsolidity/Types.cpp109
-rw-r--r--libsolidity/Types.h88
-rw-r--r--libsolidity/Utils.h1
-rw-r--r--libsolidity/Version.cpp36
-rw-r--r--libsolidity/Version.h6
-rw-r--r--test/libsolidity/SolidityABIJSON.cpp30
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp67
-rw-r--r--test/libsolidity/SolidityInterface.cpp16
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp10
29 files changed, 547 insertions, 131 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp
index 00b51c42..d55bc13c 100644
--- a/libsolidity/AST.cpp
+++ b/libsolidity/AST.cpp
@@ -135,7 +135,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
FunctionType ftype(*v);
solAssert(!!v->annotation().type.get(), "");
functionsSeen.insert(v->name());
- FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->name())));
+ FixedHash<4> hash(dev::sha3(ftype.externalSignature()));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
}
}
@@ -215,6 +215,13 @@ TypePointer StructDefinition::type(ContractDefinition const*) const
return make_shared<TypeType>(make_shared<StructType>(*this));
}
+TypeDeclarationAnnotation& StructDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new TypeDeclarationAnnotation();
+ return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
+}
+
TypePointer EnumValue::type(ContractDefinition const*) const
{
auto parentDef = dynamic_cast<EnumDefinition const*>(scope());
@@ -227,6 +234,13 @@ TypePointer EnumDefinition::type(ContractDefinition const*) const
return make_shared<TypeType>(make_shared<EnumType>(*this));
}
+TypeDeclarationAnnotation& EnumDefinition::annotation() const
+{
+ if (!m_annotation)
+ m_annotation = new TypeDeclarationAnnotation();
+ return static_cast<TypeDeclarationAnnotation&>(*m_annotation);
+}
+
TypePointer FunctionDefinition::type(ContractDefinition const*) const
{
return make_shared<FunctionType>(*this);
@@ -234,7 +248,7 @@ TypePointer FunctionDefinition::type(ContractDefinition const*) const
string FunctionDefinition::externalSignature() const
{
- return FunctionType(*this).externalSignature(name());
+ return FunctionType(*this).externalSignature();
}
TypePointer ModifierDefinition::type(ContractDefinition const*) const
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index c7eaa41d..075c1ff5 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -352,6 +352,8 @@ public:
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
+ virtual TypeDeclarationAnnotation& annotation() const override;
+
private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
@@ -372,6 +374,8 @@ public:
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
+ virtual TypeDeclarationAnnotation& annotation() const override;
+
private:
std::vector<ASTPointer<EnumValue>> m_members;
};
@@ -708,17 +712,17 @@ private:
class UserDefinedTypeName: public TypeName
{
public:
- UserDefinedTypeName(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
- TypeName(_location), m_name(_name) {}
+ UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath):
+ TypeName(_location), m_namePath(_namePath) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- ASTString const& name() const { return *m_name; }
+ std::vector<ASTString> const& namePath() const { return m_namePath; }
virtual UserDefinedTypeNameAnnotation& annotation() const override;
private:
- ASTPointer<ASTString> m_name;
+ std::vector<ASTString> m_namePath;
};
/**
diff --git a/libsolidity/ASTAnnotations.h b/libsolidity/ASTAnnotations.h
index 195f11c8..dad7b205 100644
--- a/libsolidity/ASTAnnotations.h
+++ b/libsolidity/ASTAnnotations.h
@@ -40,7 +40,13 @@ struct ASTAnnotation
virtual ~ASTAnnotation() {}
};
-struct ContractDefinitionAnnotation: ASTAnnotation
+struct TypeDeclarationAnnotation: ASTAnnotation
+{
+ /// The name of this type, prefixed by proper namespaces if globally accessible.
+ std::string canonicalName;
+};
+
+struct ContractDefinitionAnnotation: TypeDeclarationAnnotation
{
/// Whether all functions are implemented.
bool isFullyImplemented = true;
diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp
index d0f76fb8..4c14f2b2 100644
--- a/libsolidity/ASTJsonConverter.cpp
+++ b/libsolidity/ASTJsonConverter.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/ASTJsonConverter.h>
+#include <boost/algorithm/string/join.hpp>
#include <libsolidity/AST.h>
using namespace std;
@@ -144,7 +145,9 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
{
- addJsonNode("UserDefinedTypeName", { make_pair("name", _node.name()) });
+ addJsonNode("UserDefinedTypeName", {
+ make_pair("name", boost::algorithm::join(_node.namePath(), "."))
+ });
return true;
}
diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp
index cebf6b8b..534f7c78 100644
--- a/libsolidity/ASTPrinter.cpp
+++ b/libsolidity/ASTPrinter.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/ASTPrinter.h>
+#include <boost/algorithm/string/join.hpp>
#include <libsolidity/AST.h>
using namespace std;
@@ -151,7 +152,7 @@ bool ASTPrinter::visit(ElementaryTypeName const& _node)
bool ASTPrinter::visit(UserDefinedTypeName const& _node)
{
- writeLine("UserDefinedTypeName \"" + _node.name() + "\"");
+ writeLine("UserDefinedTypeName \"" + boost::algorithm::join(_node.namePath(), ".") + "\"");
printSourcePart(_node);
return goDeeper();
}
diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp
index 969c8f74..6fb09def 100644
--- a/libsolidity/Compiler.cpp
+++ b/libsolidity/Compiler.cpp
@@ -68,6 +68,12 @@ void Compiler::compileContract(
packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize)
m_context.optimise(m_optimizeRuns);
+
+ if (_contract.isLibrary())
+ {
+ solAssert(m_runtimeSub != size_t(-1), "");
+ m_context.injectVersionStampIntoSub(m_runtimeSub);
+ }
}
void Compiler::compileClone(
@@ -252,7 +258,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this);
m_context << returnTag;
- appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes());
+ appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary());
}
else
m_context << eth::Instruction::STOP; // function not found
@@ -268,7 +274,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
appendCalldataUnpacker(functionType->parameterTypes());
m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
m_context << returnTag;
- appendReturnValuePacker(functionType->returnParameterTypes());
+ appendReturnValuePacker(functionType->returnParameterTypes(), _contract.isLibrary());
}
}
@@ -280,15 +286,13 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
// Retain the offset pointer as base_offset, the point from which the data offsets are computed.
m_context << eth::Instruction::DUP1;
- for (TypePointer const& type: _typeParameters)
+ for (TypePointer const& parameterType: _typeParameters)
{
// stack: v1 v2 ... v(k-1) base_offset current_offset
- switch (type->category())
- {
- case Type::Category::Array:
+ TypePointer type = parameterType->decodingType();
+ if (type->category() == Type::Category::Array)
{
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
- solAssert(arrayType.location() != DataLocation::Storage, "");
solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
if (_fromMemory)
{
@@ -342,9 +346,9 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
m_context << eth::Instruction::SWAP1;
}
- break;
}
- default:
+ else
+ {
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack());
@@ -355,7 +359,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
m_context << eth::Instruction::POP << eth::Instruction::POP;
}
-void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
+void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary)
{
CompilerUtils utils(m_context);
if (_typeParameters.empty())
@@ -365,7 +369,7 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
utils.fetchFreeMemoryPointer();
//@todo optimization: if we return a single memory array, there should be enough space before
// its data to add the needed parts and we avoid a memory copy.
- utils.encodeToMemory(_typeParameters, _typeParameters);
+ utils.encodeToMemory(_typeParameters, _typeParameters, true, false, _isLibrary);
utils.toSizeAfterFreeMemoryPointer();
m_context << eth::Instruction::RETURN;
}
diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h
index c3bb838a..3cf1004a 100644
--- a/libsolidity/Compiler.h
+++ b/libsolidity/Compiler.h
@@ -87,7 +87,7 @@ private:
/// From memory if @a _fromMemory is true, otherwise from call data.
/// Expects source offset on the stack, which is removed.
void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false);
- void appendReturnValuePacker(TypePointers const& _typeParameters);
+ void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary);
void registerStateVariables(ContractDefinition const& _contract);
void initializeStateVariables(ContractDefinition const& _contract);
diff --git a/libsolidity/CompilerContext.cpp b/libsolidity/CompilerContext.cpp
index 717627a5..fa7f9c77 100644
--- a/libsolidity/CompilerContext.cpp
+++ b/libsolidity/CompilerContext.cpp
@@ -20,10 +20,12 @@
* Utilities for the solidity compiler.
*/
+#include <libsolidity/CompilerContext.h>
#include <utility>
#include <numeric>
#include <libsolidity/AST.h>
#include <libsolidity/Compiler.h>
+#include <libsolidity/Version.h>
using namespace std;
@@ -177,6 +179,13 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
updateSourceLocation();
}
+void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
+{
+ eth::Assembly& sub = m_asm.sub(_subIndex);
+ sub.injectStart(eth::Instruction::POP);
+ sub.injectStart(fromBigEndian<u256>(binaryVersion()));
+}
+
eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
FunctionDefinition const& _function,
vector<ContractDefinition const*>::const_iterator _searchStart
diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h
index 46ebfcf8..18865091 100644
--- a/libsolidity/CompilerContext.h
+++ b/libsolidity/CompilerContext.h
@@ -128,6 +128,9 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
+ /// Prepends "PUSH <compiler version number> POP"
+ void injectVersionStampIntoSub(size_t _subIndex);
+
void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
eth::Assembly const& assembly() const { return m_asm; }
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index 6ee19d58..7d9ca32c 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -130,12 +130,15 @@ bool CompilerStack::parse()
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->currentThis());
TypeChecker typeChecker;
- if (!typeChecker.checkTypeRequirements(*contract))
+ if (typeChecker.checkTypeRequirements(*contract))
+ {
+ contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
+ contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
+ }
+ else
typesFine = false;
- m_errors += typeChecker.errors();
- contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
- contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
m_contracts[contract->name()].contract = contract;
+ m_errors += typeChecker.errors();
}
m_parseSuccessful = typesFine;
return m_parseSuccessful;
diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp
index d6624ca4..e1152202 100644
--- a/libsolidity/CompilerUtils.cpp
+++ b/libsolidity/CompilerUtils.cpp
@@ -153,14 +153,15 @@ void CompilerUtils::encodeToMemory(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _padToWordBoundaries,
- bool _copyDynamicDataInPlace
+ bool _copyDynamicDataInPlace,
+ bool _encodeAsLibraryTypes
)
{
// stack: <v1> <v2> ... <vn> <mem>
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes)
- t = t->mobileType()->externalType();
+ t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
@@ -188,7 +189,14 @@ void CompilerUtils::encodeToMemory(
copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->sizeOnStack());
solAssert(!!targetType, "Externalable type expected.");
TypePointer type = targetType;
- if (
+ if (_givenTypes[i]->dataStoredIn(DataLocation::Storage) && targetType->isValueType())
+ {
+ // special case: convert storage reference type to value type - this is only
+ // possible for library calls where we just forward the storage reference
+ solAssert(_encodeAsLibraryTypes, "");
+ solAssert(_givenTypes[i]->sizeOnStack() == 1, "");
+ }
+ else if (
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
_givenTypes[i]->category() == Type::Category::StringLiteral
diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h
index 568a6307..f335eed5 100644
--- a/libsolidity/CompilerUtils.h
+++ b/libsolidity/CompilerUtils.h
@@ -91,13 +91,16 @@ public:
/// @param _padToWordBoundaries if false, all values are concatenated without padding.
/// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length)
/// together with fixed-length data.
+ /// @param _encodeAsLibraryTypes if true, encodes for a library function, e.g. does not
+ /// convert storage pointer types to memory types.
/// @note the locations of target reference types are ignored, because it will always be
/// memory.
void encodeToMemory(
TypePointers const& _givenTypes = {},
TypePointers const& _targetTypes = {},
bool _padToWordBoundaries = true,
- bool _copyDynamicDataInPlace = false
+ bool _copyDynamicDataInPlace = false,
+ bool _encodeAsLibraryTypes = false
);
/// Uses a CALL to the identity contract to perform a memory-to-memory copy.
diff --git a/libsolidity/DeclarationContainer.h b/libsolidity/DeclarationContainer.h
index ffbd1fcb..3d6ed2cc 100644
--- a/libsolidity/DeclarationContainer.h
+++ b/libsolidity/DeclarationContainer.h
@@ -40,8 +40,10 @@ namespace solidity
class DeclarationContainer
{
public:
- explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr,
- DeclarationContainer const* _enclosingContainer = nullptr):
+ explicit DeclarationContainer(
+ Declaration const* _enclosingDeclaration = nullptr,
+ DeclarationContainer const* _enclosingContainer = nullptr
+ ):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared or the name is empty.
/// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp
index 49bf2559..c11ef29e 100644
--- a/libsolidity/ExpressionCompiler.cpp
+++ b/libsolidity/ExpressionCompiler.cpp
@@ -585,7 +585,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
if (!event.isAnonymous())
{
- m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.name()))));
+ m_context << u256(h256::Arith(dev::sha3(function.externalSignature())));
++numIndexed;
}
solAssert(numIndexed <= 4, "Too many indexed arguments.");
@@ -1179,7 +1179,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
argumentTypes,
_functionType.parameterTypes(),
_functionType.padArguments(),
- _functionType.takesArbitraryParameters()
+ _functionType.takesArbitraryParameters(),
+ isCallCode
);
// Stack now:
diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp
index 50006caf..d23d8264 100644
--- a/libsolidity/InterfaceHandler.cpp
+++ b/libsolidity/InterfaceHandler.cpp
@@ -1,5 +1,8 @@
#include <libsolidity/InterfaceHandler.h>
+#include <boost/range/adaptor/transformed.hpp>
+#include <boost/range/irange.hpp>
+#include <boost/algorithm/string/join.hpp>
#include <libsolidity/AST.h>
#include <libsolidity/CompilerStack.h>
using namespace std;
@@ -57,18 +60,18 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
for (auto it: _contractDef.interfaceFunctions())
{
- auto externalFunctionType = it.second->externalFunctionType();
+ auto externalFunctionType = it.second->interfaceFunctionType();
Json::Value method;
method["type"] = "function";
method["name"] = it.second->declaration().name();
method["constant"] = it.second->isConstant();
method["inputs"] = populateParameters(
externalFunctionType->parameterNames(),
- externalFunctionType->parameterTypeNames()
+ externalFunctionType->parameterTypeNames(_contractDef.isLibrary())
);
method["outputs"] = populateParameters(
externalFunctionType->returnParameterNames(),
- externalFunctionType->returnParameterTypeNames()
+ externalFunctionType->returnParameterTypeNames(_contractDef.isLibrary())
);
abi.append(method);
}
@@ -76,11 +79,11 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
{
Json::Value method;
method["type"] = "constructor";
- auto externalFunction = FunctionType(*_contractDef.constructor()).externalFunctionType();
+ auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
solAssert(!!externalFunction, "");
method["inputs"] = populateParameters(
externalFunction->parameterNames(),
- externalFunction->parameterTypeNames()
+ externalFunction->parameterTypeNames(_contractDef.isLibrary())
);
abi.append(method);
}
@@ -96,7 +99,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
{
Json::Value input;
input["name"] = p->name();
- input["type"] = p->annotation().type->toString(true);
+ input["type"] = p->annotation().type->canonicalName(false);
input["indexed"] = p->isIndexed();
params.append(input);
}
@@ -108,33 +111,61 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
string InterfaceHandler::ABISolidityInterface(ContractDefinition const& _contractDef)
{
- string ret = "contract " + _contractDef.name() + "{";
+ using namespace boost::adaptors;
+ using namespace boost::algorithm;
+ string ret = (_contractDef.isLibrary() ? "library " : "contract ") + _contractDef.name() + "{";
auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
{
- string r = "";
- solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match");
- for (unsigned i = 0; i < _paramNames.size(); ++i)
- r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
- return r.size() ? r + ")" : "()";
+ return "(" + join(boost::irange<size_t>(0, _paramNames.size()) | transformed([&](size_t _i) {
+ return _paramTypes[_i] + " " + _paramNames[_i];
+ }), ",") + ")";
};
+ // If this is a library, include all its enum and struct types. Should be more intelligent
+ // in the future and check what is actually used (it might even use types from other libraries
+ // or contracts or in the global scope).
+ if (_contractDef.isLibrary())
+ {
+ for (auto const& stru: _contractDef.definedStructs())
+ {
+ ret += "struct " + stru->name() + "{";
+ for (ASTPointer<VariableDeclaration> const& _member: stru->members())
+ ret += _member->type(nullptr)->canonicalName(false) + " " + _member->name() + ";";
+ ret += "}";
+ }
+ for (auto const& enu: _contractDef.definedEnums())
+ {
+ ret += "enum " + enu->name() + "{" +
+ join(enu->members() | transformed([](ASTPointer<EnumValue> const& _value) {
+ return _value->name();
+ }), ",") + "}";
+ }
+ }
if (_contractDef.constructor())
{
- auto externalFunction = FunctionType(*_contractDef.constructor()).externalFunctionType();
+ auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
solAssert(!!externalFunction, "");
ret +=
"function " +
_contractDef.name() +
- populateParameters(externalFunction->parameterNames(), externalFunction->parameterTypeNames()) +
+ populateParameters(
+ externalFunction->parameterNames(),
+ externalFunction->parameterTypeNames(_contractDef.isLibrary())
+ ) +
";";
}
for (auto const& it: _contractDef.interfaceFunctions())
{
ret += "function " + it.second->declaration().name() +
- populateParameters(it.second->parameterNames(), it.second->parameterTypeNames()) +
- (it.second->isConstant() ? "constant " : "");
+ populateParameters(
+ it.second->parameterNames(),
+ it.second->parameterTypeNames(_contractDef.isLibrary())
+ ) + (it.second->isConstant() ? "constant " : "");
if (it.second->returnParameterTypes().size())
- ret += "returns" + populateParameters(it.second->returnParameterNames(), it.second->returnParameterTypeNames());
+ ret += "returns" + populateParameters(
+ it.second->returnParameterNames(),
+ it.second->returnParameterTypeNames(_contractDef.isLibrary())
+ );
else if (ret.back() == ' ')
ret.pop_back();
ret += ";";
diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp
index c3e31728..4747542d 100644
--- a/libsolidity/NameAndTypeResolver.cpp
+++ b/libsolidity/NameAndTypeResolver.cpp
@@ -125,11 +125,27 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na
return iterator->second.resolveName(_name, false);
}
-vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive)
+vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
{
return m_currentScope->resolveName(_name, _recursive);
}
+Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const
+{
+ solAssert(!_path.empty(), "");
+ vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive);
+ for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
+ {
+ if (!m_scopes.count(candidates.front()))
+ return nullptr;
+ candidates = m_scopes.at(candidates.front()).resolveName(_path[i], false);
+ }
+ if (candidates.size() == 1)
+ return candidates.front();
+ else
+ return nullptr;
+}
+
vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
Identifier const& _identifier,
vector<Declaration const*> const& _declarations
@@ -263,6 +279,7 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*,
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
{
registerDeclaration(_contract, true);
+ _contract.annotation().canonicalName = currentCanonicalName();
return true;
}
@@ -274,6 +291,7 @@ void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
{
registerDeclaration(_struct, true);
+ _struct.annotation().canonicalName = currentCanonicalName();
return true;
}
@@ -285,6 +303,7 @@ void DeclarationRegistrationHelper::endVisit(StructDefinition&)
bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
{
registerDeclaration(_enum, true);
+ _enum.annotation().canonicalName = currentCanonicalName();
return true;
}
@@ -400,5 +419,21 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
enterNewSubScope(_declaration);
}
+string DeclarationRegistrationHelper::currentCanonicalName() const
+{
+ string ret;
+ for (
+ Declaration const* scope = m_currentScope;
+ scope != nullptr;
+ scope = m_scopes[scope].enclosingDeclaration()
+ )
+ {
+ if (!ret.empty())
+ ret = "." + ret;
+ ret = scope->name() + ret;
+ }
+ return ret;
+}
+
}
}
diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h
index d28671ed..f5f4c6ce 100644
--- a/libsolidity/NameAndTypeResolver.h
+++ b/libsolidity/NameAndTypeResolver.h
@@ -36,9 +36,8 @@ namespace solidity
{
/**
- * Resolves name references, types and checks types of all expressions.
- * Specifically, it checks that all operations are valid for the inferred types.
- * An exception is throw on the first error.
+ * Resolves name references, typenames and sets the (explicitly given) types for all variable
+ * declarations.
*/
class NameAndTypeResolver: private boost::noncopyable
{
@@ -59,7 +58,12 @@ public:
/// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase.
- std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true);
+ std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const;
+
+ /// Resolves a path starting from the "current" scope. Should only be called during the initial
+ /// resolving phase.
+ /// @note Returns a null pointer if any component in the path was not unique or not found.
+ Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
/// returns the vector of declarations without repetitions
static std::vector<Declaration const*> cleanedDeclarations(
@@ -119,6 +123,9 @@ private:
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
+ /// @returns the canonical name of the current scope.
+ std::string currentCanonicalName() const;
+
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration const* m_currentScope;
VariableScope* m_currentFunction;
diff --git a/libsolidity/Parser.cpp b/libsolidity/Parser.cpp
index f3b654ea..94e9c0ea 100644
--- a/libsolidity/Parser.cpp
+++ b/libsolidity/Parser.cpp
@@ -522,7 +522,14 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{
ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition();
- type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken());
+ vector<ASTString> identifierPath{*expectIdentifierToken()};
+ while (m_scanner->currentToken() == Token::Period)
+ {
+ m_scanner->next();
+ nodeFactory.markEndPosition();
+ identifierPath.push_back(*expectIdentifierToken());
+ }
+ type = nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
}
else
BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
@@ -1036,7 +1043,7 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<TypeName> type;
if (auto identifier = dynamic_cast<Identifier const*>(_primary.get()))
- type = nodeFactory.createNode<UserDefinedTypeName>(make_shared<ASTString>(identifier->name()));
+ type = nodeFactory.createNode<UserDefinedTypeName>(vector<ASTString>{identifier->name()});
else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeToken());
else
diff --git a/libsolidity/ReferencesResolver.cpp b/libsolidity/ReferencesResolver.cpp
index 623ac8f7..32c1728f 100644
--- a/libsolidity/ReferencesResolver.cpp
+++ b/libsolidity/ReferencesResolver.cpp
@@ -54,20 +54,13 @@ bool ReferencesResolver::visit(Return const& _return)
bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName)
{
- auto declarations = m_resolver.nameFromCurrentScope(_typeName.name());
- if (declarations.empty())
+ Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
+ if (!declaration)
BOOST_THROW_EXCEPTION(
DeclarationError() <<
errinfo_sourceLocation(_typeName.location()) <<
- errinfo_comment("Undeclared identifier.")
+ errinfo_comment("Identifier not found or not unique.")
);
- else if (declarations.size() > 1)
- BOOST_THROW_EXCEPTION(
- DeclarationError() <<
- errinfo_sourceLocation(_typeName.location()) <<
- errinfo_comment("Duplicate identifier.")
- );
- Declaration const* declaration = *declarations.begin();
_typeName.annotation().referencedDeclaration = declaration;
return true;
}
@@ -106,27 +99,43 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
// 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()))
{
if (_variable.isExternalCallableParameter())
{
- // force location of external function parameters (not return) to calldata
- if (loc != Location::Default)
- BOOST_THROW_EXCEPTION(_variable.createTypeError(
- "Location has to be calldata for external functions "
- "(remove the \"memory\" or \"storage\" keyword)."
- ));
- type = ref->copyForLocation(DataLocation::CallData, true);
+ auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
+ if (contract.isLibrary())
+ {
+ if (loc == Location::Memory)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be calldata or storage for external "
+ "library functions (remove the \"memory\" keyword)."
+ ));
+ }
+ else
+ {
+ // force location of external function parameters (not return) to calldata
+ if (loc != Location::Default)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be calldata for external functions "
+ "(remove the \"memory\" or \"storage\" keyword)."
+ ));
+ }
+ if (loc == Location::Default)
+ type = ref->copyForLocation(DataLocation::CallData, true);
}
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 == VariableDeclaration::Location::Storage)
+ if (loc == Location::Storage && !contract.isLibrary())
BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)."
));
- type = ref->copyForLocation(DataLocation::Memory, true);
+ if (loc == Location::Default || !contract.isLibrary())
+ type = ref->copyForLocation(DataLocation::Memory, true);
}
else
{
diff --git a/libsolidity/TypeChecker.cpp b/libsolidity/TypeChecker.cpp
index 48a8a536..fe6fb970 100644
--- a/libsolidity/TypeChecker.cpp
+++ b/libsolidity/TypeChecker.cpp
@@ -299,7 +299,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co
if (f->isPartOfExternalInterface())
{
auto functionType = make_shared<FunctionType>(*f);
- externalDeclarations[functionType->externalSignature(f->name())].push_back(
+ externalDeclarations[functionType->externalSignature()].push_back(
make_pair(f.get(), functionType)
);
}
@@ -307,7 +307,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co
if (v->isPartOfExternalInterface())
{
auto functionType = make_shared<FunctionType>(*v);
- externalDeclarations[functionType->externalSignature(v->name())].push_back(
+ externalDeclarations[functionType->externalSignature()].push_back(
make_pair(v.get(), functionType)
);
}
@@ -397,12 +397,13 @@ bool TypeChecker::visit(StructDefinition const& _struct)
bool TypeChecker::visit(FunctionDefinition const& _function)
{
+ bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (!type(*var)->canLiveOutsideStorage())
typeError(*var, "Type is required to live outside storage.");
- if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->externalType()))
- typeError(*var, "Internal type is not allowed for public and external functions.");
+ if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
+ fatalTypeError(*var, "Internal type is not allowed for public or external functions.");
}
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
visitManually(
@@ -490,7 +491,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
}
else if (
_variable.visibility() >= VariableDeclaration::Visibility::Public &&
- !FunctionType(_variable).externalType()
+ !FunctionType(_variable).interfaceFunctionType()
)
typeError(_variable, "Internal type is not allowed for public state variables.");
return false;
@@ -557,7 +558,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
typeError(_eventDef, "More than 3 indexed arguments for event.");
if (!type(*var)->canLiveOutsideStorage())
typeError(*var, "Type is required to live outside storage.");
- if (!type(*var)->externalType())
+ if (!type(*var)->interfaceType(false))
typeError(*var, "Internal type is not allowed as event parameter type.");
}
return false;
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index 435385e6..f0c67bba 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -839,11 +839,49 @@ string ArrayType::toString(bool _short) const
return ret;
}
-TypePointer ArrayType::externalType() const
+string ArrayType::canonicalName(bool _addDataLocation) const
{
+ string ret;
+ if (isString())
+ ret = "string";
+ else if (isByteArray())
+ ret = "bytes";
+ else
+ {
+ ret = baseType()->canonicalName(false) + "[";
+ if (!isDynamicallySized())
+ ret += length().str();
+ ret += "]";
+ }
+ if (_addDataLocation && location() == DataLocation::Storage)
+ ret += " storage";
+ return ret;
+}
+
+TypePointer ArrayType::encodingType() const
+{
+ if (location() == DataLocation::Storage)
+ return make_shared<IntegerType>(256);
+ else
+ return this->copyForLocation(DataLocation::Memory, true);
+}
+
+TypePointer ArrayType::decodingType() const
+{
+ if (location() == DataLocation::Storage)
+ return make_shared<IntegerType>(256);
+ else
+ return shared_from_this();
+}
+
+TypePointer ArrayType::interfaceType(bool _inLibrary) const
+{
+ if (_inLibrary && location() == DataLocation::Storage)
+ return shared_from_this();
+
if (m_arrayKind != ArrayKind::Ordinary)
return this->copyForLocation(DataLocation::Memory, true);
- TypePointer baseExt = m_baseType->externalType();
+ TypePointer baseExt = m_baseType->interfaceType(_inLibrary);
if (!baseExt)
return TypePointer();
if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
@@ -893,6 +931,11 @@ string ContractType::toString(bool) const
m_contract.name();
}
+string ContractType::canonicalName(bool) const
+{
+ return m_contract.annotation().canonicalName;
+}
+
MemberList const& ContractType::members() const
{
// We need to lazy-initialize it because of recursive references.
@@ -1059,6 +1102,14 @@ MemberList const& StructType::members() const
return *m_members;
}
+TypePointer StructType::interfaceType(bool _inLibrary) const
+{
+ if (_inLibrary && location() == DataLocation::Storage)
+ return shared_from_this();
+ else
+ return TypePointer();
+}
+
TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{
auto copy = make_shared<StructType>(m_struct, _location);
@@ -1066,6 +1117,14 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer)
return copy;
}
+string StructType::canonicalName(bool _addDataLocation) const
+{
+ string ret = m_struct.annotation().canonicalName;
+ if (_addDataLocation && location() == DataLocation::Storage)
+ ret += " storage";
+ return ret;
+}
+
FunctionTypePointer StructType::constructorType() const
{
TypePointers paramTypes;
@@ -1141,6 +1200,11 @@ string EnumType::toString(bool) const
return string("enum ") + m_enum.name();
}
+string EnumType::canonicalName(bool) const
+{
+ return m_enum.annotation().canonicalName;
+}
+
bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
@@ -1330,21 +1394,25 @@ unsigned FunctionType::sizeOnStack() const
return size;
}
-FunctionTypePointer FunctionType::externalFunctionType() const
+FunctionTypePointer FunctionType::interfaceFunctionType() const
{
+ // Note that m_declaration might also be a state variable!
+ solAssert(m_declaration, "Declaration needed to determine interface function type.");
+ bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
+
TypePointers paramTypes;
TypePointers retParamTypes;
for (auto type: m_parameterTypes)
{
- if (auto ext = type->externalType())
+ if (auto ext = type->interfaceType(isLibraryFunction))
paramTypes.push_back(ext);
else
return FunctionTypePointer();
}
for (auto type: m_returnParameterTypes)
{
- if (auto ext = type->externalType())
+ if (auto ext = type->interfaceType(isLibraryFunction))
retParamTypes.push_back(ext);
else
return FunctionTypePointer();
@@ -1452,23 +1520,21 @@ bool FunctionType::isBareCall() const
}
}
-string FunctionType::externalSignature(std::string const& _name) const
+string FunctionType::externalSignature() const
{
- std::string funcName = _name;
- if (_name == "")
- {
- solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
- funcName = m_declaration->name();
- }
- string ret = funcName + "(";
+ solAssert(m_declaration != nullptr, "External signature of function needs declaration");
+
+ bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
+
+ string ret = m_declaration->name() + "(";
- FunctionTypePointer external = externalFunctionType();
+ FunctionTypePointer external = interfaceFunctionType();
solAssert(!!external, "External function type requested.");
TypePointers externalParameterTypes = external->parameterTypes();
for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
{
solAssert(!!(*it), "Parameter should have external type");
- ret += (*it)->toString(true) + (it + 1 == externalParameterTypes.cend() ? "" : ",");
+ ret += (*it)->canonicalName(_inLibrary) + (it + 1 == externalParameterTypes.cend() ? "" : ",");
}
return ret + ")";
@@ -1536,20 +1602,20 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const
);
}
-vector<string> const FunctionType::parameterTypeNames() const
+vector<string> const FunctionType::parameterTypeNames(bool _addDataLocation) const
{
vector<string> names;
for (TypePointer const& t: m_parameterTypes)
- names.push_back(t->toString(true));
+ names.push_back(t->canonicalName(_addDataLocation));
return names;
}
-vector<string> const FunctionType::returnParameterTypeNames() const
+vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocation) const
{
vector<string> names;
for (TypePointer const& t: m_returnParameterTypes)
- names.push_back(t->toString(true));
+ names.push_back(t->canonicalName(_addDataLocation));
return names;
}
@@ -1576,6 +1642,11 @@ string MappingType::toString(bool _short) const
return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")";
}
+string MappingType::canonicalName(bool) const
+{
+ return "mapping(" + keyType()->canonicalName(false) + " => " + valueType()->canonicalName(false) + ")";
+}
+
u256 VoidType::storageSize() const
{
BOOST_THROW_EXCEPTION(
diff --git a/libsolidity/Types.h b/libsolidity/Types.h
index 73111e48..7a65ca92 100644
--- a/libsolidity/Types.h
+++ b/libsolidity/Types.h
@@ -218,6 +218,9 @@ public:
virtual std::string toString(bool _short) const = 0;
std::string toString() const { return toString(false); }
+ /// @returns the canonical name of this type for use in function signatures.
+ /// @param _addDataLocation if true, includes data location for reference types if it is "storage".
+ virtual std::string canonicalName(bool /*_addDataLocation*/) const { return toString(true); }
virtual u256 literalValue(Literal const*) const
{
BOOST_THROW_EXCEPTION(
@@ -226,9 +229,18 @@ public:
);
}
- /// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address.
+ /// @returns a (simpler) type that is encoded in the same way for external function calls.
+ /// This for example returns address for contract types.
/// If there is no such type, returns an empty shared pointer.
- virtual TypePointer externalType() const { return TypePointer(); }
+ virtual TypePointer encodingType() const { return TypePointer(); }
+ /// @returns a (simpler) type that is used when decoding this type in calldata.
+ virtual TypePointer decodingType() const { return encodingType(); }
+ /// @returns a type that will be used outside of Solidity for e.g. function signatures.
+ /// This for example returns address for contract types.
+ /// If there is no such type, returns an empty shared pointer.
+ /// @param _inLibrary if set, returns types as used in a library, e.g. struct and contract types
+ /// are returned without modification.
+ virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); }
protected:
/// Convenience object used when returning an empty member list.
@@ -264,7 +276,8 @@ public:
virtual std::string toString(bool _short) const override;
- virtual TypePointer externalType() const override { return shared_from_this(); }
+ virtual TypePointer encodingType() const override { return shared_from_this(); }
+ virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
int numBits() const { return m_bits; }
bool isAddress() const { return m_modifier == Modifier::Address; }
@@ -369,7 +382,8 @@ public:
virtual bool isValueType() const override { return true; }
virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); }
- virtual TypePointer externalType() const override { return shared_from_this(); }
+ virtual TypePointer encodingType() const override { return shared_from_this(); }
+ virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
int numBytes() const { return m_bytes; }
@@ -395,7 +409,8 @@ public:
virtual std::string toString(bool) const override { return "bool"; }
virtual u256 literalValue(Literal const* _literal) const override;
- virtual TypePointer externalType() const override { return shared_from_this(); }
+ virtual TypePointer encodingType() const override { return shared_from_this(); }
+ virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
};
/**
@@ -489,11 +504,14 @@ public:
virtual bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
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() const override
{
return isString() ? EmptyMemberList : s_arrayTypeMemberList;
}
- virtual TypePointer externalType() const override;
+ virtual TypePointer encodingType() const override;
+ virtual TypePointer decodingType() const override;
+ virtual TypePointer interfaceType(bool _inLibrary) const override;
/// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
@@ -534,18 +552,23 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded ) const override
{
- return externalType()->calldataEncodedSize(_padded);
+ return encodingType()->calldataEncodedSize(_padded);
}
virtual unsigned storageBytes() const override { return 20; }
virtual bool canLiveOutsideStorage() const override { return true; }
virtual bool isValueType() const override { return true; }
virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
virtual MemberList const& members() const override;
- virtual TypePointer externalType() const override
+ virtual TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
}
+ virtual TypePointer interfaceType(bool _inLibrary) const override
+ {
+ return _inLibrary ? shared_from_this() : encodingType();
+ }
bool isSuper() const { return m_super; }
ContractDefinition const& contractDefinition() const { return m_contract; }
@@ -566,7 +589,7 @@ private:
ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited
/// members.
- bool m_super;
+ 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.
@@ -591,9 +614,16 @@ public:
virtual std::string toString(bool _short) const override;
virtual MemberList const& members() const override;
+ virtual TypePointer encodingType() const override
+ {
+ return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer();
+ }
+ virtual TypePointer interfaceType(bool _inLibrary) const override;
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
+
/// @returns a function that peforms the type conversion between a list of struct members
/// and a memory struct of this type.
FunctionTypePointer constructorType() const;
@@ -624,18 +654,23 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override
{
- return externalType()->calldataEncodedSize(_padded);
+ return encodingType()->calldataEncodedSize(_padded);
}
virtual unsigned storageBytes() const override;
virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
virtual bool isValueType() const override { return true; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer externalType() const override
+ virtual TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(8 * int(storageBytes()));
}
+ virtual TypePointer interfaceType(bool _inLibrary) const override
+ {
+ return _inLibrary ? shared_from_this() : encodingType();
+ }
EnumDefinition const& enumDefinition() const { return m_enum; }
/// @returns the value that the string has in the Enum
@@ -684,13 +719,6 @@ public:
virtual Category category() const override { return Category::Function; }
- /// @returns TypePointer of a new FunctionType object. All input/return parameters are an
- /// appropriate external types of input/return parameters of current function.
- /// Returns an empty shared pointer if one of the input/return parameters does not have an
- /// external type.
- FunctionTypePointer externalFunctionType() const;
- virtual TypePointer externalType() const override { return externalFunctionType(); }
-
/// Creates the type of a function.
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
/// Creates the accessor function type of a state variable.
@@ -736,10 +764,10 @@ public:
TypePointers const& parameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& parameterNames() const { return m_parameterNames; }
- std::vector<std::string> const parameterTypeNames() 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() const;
+ std::vector<std::string> const returnParameterTypeNames(bool _addDataLocation) const;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override;
@@ -749,6 +777,13 @@ public:
virtual unsigned sizeOnStack() const override;
virtual MemberList const& members() 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
+ /// current function.
+ /// Returns an empty shared pointer if one of the input/return parameters does not have an
+ /// external type.
+ FunctionTypePointer interfaceFunctionType() const;
+
/// @returns true if this function can take the given argument types (possibly
/// after implicit conversion).
bool canTakeArguments(TypePointers const& _arguments) const;
@@ -759,9 +794,7 @@ public:
bool isBareCall() const;
Location const& location() const { return m_location; }
/// @returns the external signature of this function type given the function name
- /// If @a _name is not provided (empty string) then the @c m_declaration member of the
- /// function type is used
- std::string externalSignature(std::string const& _name = "") const;
+ std::string externalSignature() const;
/// @returns the external identifier of this function (the hash of the signature).
u256 externalIdentifier() const;
Declaration const& declaration() const
@@ -822,7 +855,16 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName(bool _addDataLocation) const override;
virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual TypePointer encodingType() const override
+ {
+ return std::make_shared<IntegerType>(256);
+ }
+ virtual TypePointer interfaceType(bool _inLibrary) const override
+ {
+ return _inLibrary ? shared_from_this() : TypePointer();
+ }
TypePointer const& keyType() const { return m_keyType; }
TypePointer const& valueType() const { return m_valueType; }
diff --git a/libsolidity/Utils.h b/libsolidity/Utils.h
index 05c5fa6f..6c8e3b33 100644
--- a/libsolidity/Utils.h
+++ b/libsolidity/Utils.h
@@ -23,6 +23,7 @@
#pragma once
#include <libdevcore/Assertions.h>
+#include <libsolidity/Exceptions.h>
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \
diff --git a/libsolidity/Version.cpp b/libsolidity/Version.cpp
index c6b5c509..09a6d84b 100644
--- a/libsolidity/Version.cpp
+++ b/libsolidity/Version.cpp
@@ -22,16 +22,19 @@
#include <libsolidity/Version.h>
#include <string>
+#include <libdevcore/CommonData.h>
+#include <libdevcore/Common.h>
#include <libevmasm/Version.h>
+#include <libsolidity/Utils.h>
#include <solidity/BuildInfo.h>
-#include <libdevcore/Common.h>
using namespace dev;
using namespace dev::solidity;
using namespace std;
char const* dev::solidity::VersionNumber = ETH_PROJECT_VERSION;
-extern string const dev::solidity::VersionString =
+
+string const dev::solidity::VersionString =
string(dev::solidity::VersionNumber) +
"-" +
string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) +
@@ -39,3 +42,32 @@ extern string const dev::solidity::VersionString =
"/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM)
" linked to libethereum-" + eth::VersionStringLibEvmAsm;
+
+bytes dev::solidity::binaryVersion()
+{
+ bytes ret{0};
+ size_t i = 0;
+ auto parseDecimal = [&]()
+ {
+ size_t ret = 0;
+ solAssert('0' <= VersionString[i] && VersionString[i] <= '9', "");
+ for (; i < VersionString.size() && '0' <= VersionString[i] && VersionString[i] <= '9'; ++i)
+ ret = ret * 10 + (VersionString[i] - '0');
+ return ret;
+ };
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '.', "");
+ ++i;
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '.', "");
+ ++i;
+ ret.push_back(byte(parseDecimal()));
+ solAssert(i < VersionString.size() && VersionString[i] == '-', "");
+ ++i;
+ solAssert(i + 7 < VersionString.size(), "");
+ ret += fromHex(VersionString.substr(i, 8));
+ solAssert(ret.size() == 1 + 3 + 4, "");
+
+ return ret;
+}
+
diff --git a/libsolidity/Version.h b/libsolidity/Version.h
index 6e00f07b..fea73997 100644
--- a/libsolidity/Version.h
+++ b/libsolidity/Version.h
@@ -23,6 +23,7 @@
#pragma once
#include <string>
+#include <libdevcore/Common.h>
namespace dev
{
@@ -32,5 +33,10 @@ namespace solidity
extern char const* VersionNumber;
extern std::string const VersionString;
+/// @returns a binary form of the version string, where A.B.C-HASH is encoded such that
+/// the first byte is zero, the following three bytes encode A B and C (interpreted as decimals)
+/// and HASH is interpreted as 8 hex digits and encoded into the last four bytes.
+bytes binaryVersion();
+
}
}
diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp
index 69504e3d..f3004b5f 100644
--- a/test/libsolidity/SolidityABIJSON.cpp
+++ b/test/libsolidity/SolidityABIJSON.cpp
@@ -595,6 +595,36 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
checkInterface(sourceCode, interface);
}
+BOOST_AUTO_TEST_CASE(library_function)
+{
+ char const* sourceCode = R"(
+ library test {
+ struct StructType { uint a; }
+ function f(StructType storage b, uint[] storage c, test d) returns (uint[] e, StructType storage f){}
+ }
+ )";
+
+ char const* interface = R"(
+ [
+ {
+ "constant" : false,
+ "name": "f",
+ "inputs": [
+ { "name": "b", "type": "test.StructType storage" },
+ { "name": "c", "type": "uint256[] storage" },
+ { "name": "d", "type": "test" }
+ ],
+ "outputs": [
+ { "name": "e", "type": "uint256[]" },
+ { "name": "f", "type": "test.StructType storage" }
+ ],
+ "type" : "function"
+ }
+ ]
+ )";
+ checkInterface(sourceCode, interface);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index c24701d5..a17a3be5 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -5382,6 +5382,62 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type)
);
}
+BOOST_AUTO_TEST_CASE(internal_types_in_library)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ function find(uint16[] storage _haystack, uint16 _needle) constant returns (uint)
+ {
+ for (uint i = 0; i < _haystack.length; ++i)
+ if (_haystack[i] == _needle)
+ return i;
+ return uint(-1);
+ }
+ }
+ contract Test {
+ mapping(string => uint16[]) data;
+ function f() returns (uint a, uint b)
+ {
+ data["abc"].length = 20;
+ data["abc"][4] = 9;
+ data["abc"][17] = 3;
+ a = Lib.find(data["abc"], 9);
+ b = Lib.find(data["abc"], 3);
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(4), u256(17)));
+}
+
+BOOST_AUTO_TEST_CASE(using_library_structs)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ struct Data { uint a; uint[] b; }
+ function set(Data storage _s)
+ {
+ _s.a = 7;
+ _s.b.length = 20;
+ _s.b[19] = 8;
+ }
+ }
+ contract Test {
+ mapping(string => Lib.Data) data;
+ function f() returns (uint a, uint b)
+ {
+ Lib.set(data["abc"]);
+ a = data["abc"].a;
+ b = data["abc"].b[19];
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "Lib");
+ compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}});
+ BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7), u256(8)));
+}
+
BOOST_AUTO_TEST_CASE(short_strings)
{
// This test verifies that the byte array encoding that combines length and data works
@@ -5511,6 +5567,17 @@ BOOST_AUTO_TEST_CASE(calldata_offset)
BOOST_CHECK(callContractFunction("last()", encodeArgs()) == encodeDyn(string("nd")));
}
+BOOST_AUTO_TEST_CASE(version_stamp_for_libraries)
+{
+ char const* sourceCode = "library lib {}";
+ m_optimize = true;
+ bytes runtimeCode = compileAndRun(sourceCode, 0, "lib");
+ BOOST_CHECK(runtimeCode.size() >= 8);
+ BOOST_CHECK_EQUAL(runtimeCode[0], int(eth::Instruction::PUSH6)); // might change once we switch to 1.x.x
+ BOOST_CHECK_EQUAL(runtimeCode[1], 1); // might change once we switch away from x.1.x
+ BOOST_CHECK_EQUAL(runtimeCode[7], int(eth::Instruction::POP));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp
index d77bccbd..f0d2be20 100644
--- a/test/libsolidity/SolidityInterface.cpp
+++ b/test/libsolidity/SolidityInterface.cpp
@@ -142,6 +142,22 @@ BOOST_AUTO_TEST_CASE(inheritance)
sourcePart(*contract.definedFunctions().at(1))}));
}
+BOOST_AUTO_TEST_CASE(libraries)
+{
+ char const* sourceCode = R"(
+ library Lib {
+ struct Str { uint a; }
+ enum E { E1, E2 }
+ function f(uint[] x,Str storage y,E z) external;
+ }
+ )";
+ ContractDefinition const& contract = checkInterface(sourceCode);
+ BOOST_CHECK(contract.isLibrary());
+ set<string> expectedFunctions({"function f(uint256[] x,Lib.Str storage y,Lib.E z);"});
+ BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size());
+ BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0))}));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index c386e2b4..b4f16913 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -933,24 +933,24 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr);
FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()");
BOOST_REQUIRE(function && function->hasDeclaration());
- auto returnParams = function->returnParameterTypeNames();
+ auto returnParams = function->returnParameterTypeNames(false);
BOOST_CHECK_EQUAL(returnParams.at(0), "uint256");
BOOST_CHECK(function->isConstant());
function = retrieveFunctionBySignature(contract, "map(uint256)");
BOOST_REQUIRE(function && function->hasDeclaration());
- auto params = function->parameterTypeNames();
+ auto params = function->parameterTypeNames(false);
BOOST_CHECK_EQUAL(params.at(0), "uint256");
- returnParams = function->returnParameterTypeNames();
+ returnParams = function->returnParameterTypeNames(false);
BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4");
BOOST_CHECK(function->isConstant());
function = retrieveFunctionBySignature(contract, "multiple_map(uint256,uint256)");
BOOST_REQUIRE(function && function->hasDeclaration());
- params = function->parameterTypeNames();
+ params = function->parameterTypeNames(false);
BOOST_CHECK_EQUAL(params.at(0), "uint256");
BOOST_CHECK_EQUAL(params.at(1), "uint256");
- returnParams = function->returnParameterTypeNames();
+ returnParams = function->returnParameterTypeNames(false);
BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4");
BOOST_CHECK(function->isConstant());
}