diff options
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()); } |