Merge pull request #117 from chriseth/internalTypesForLibrary

Internal types for library
This commit is contained in:
chriseth 2015-10-06 18:29:05 +02:00
commit d35a4b849d
29 changed files with 548 additions and 132 deletions

View File

@ -135,7 +135,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
FunctionType ftype(*v); FunctionType ftype(*v);
solAssert(!!v->annotation().type.get(), ""); solAssert(!!v->annotation().type.get(), "");
functionsSeen.insert(v->name()); 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))); 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)); 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 TypePointer EnumValue::type(ContractDefinition const*) const
{ {
auto parentDef = dynamic_cast<EnumDefinition const*>(scope()); 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)); 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 TypePointer FunctionDefinition::type(ContractDefinition const*) const
{ {
return make_shared<FunctionType>(*this); return make_shared<FunctionType>(*this);
@ -234,7 +248,7 @@ TypePointer FunctionDefinition::type(ContractDefinition const*) const
string FunctionDefinition::externalSignature() const string FunctionDefinition::externalSignature() const
{ {
return FunctionType(*this).externalSignature(name()); return FunctionType(*this).externalSignature();
} }
TypePointer ModifierDefinition::type(ContractDefinition const*) const TypePointer ModifierDefinition::type(ContractDefinition const*) const

View File

@ -352,6 +352,8 @@ public:
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
virtual TypeDeclarationAnnotation& annotation() const override;
private: private:
std::vector<ASTPointer<VariableDeclaration>> m_members; std::vector<ASTPointer<VariableDeclaration>> m_members;
}; };
@ -372,6 +374,8 @@ public:
virtual TypePointer type(ContractDefinition const* m_currentContract) const override; virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
virtual TypeDeclarationAnnotation& annotation() const override;
private: private:
std::vector<ASTPointer<EnumValue>> m_members; std::vector<ASTPointer<EnumValue>> m_members;
}; };
@ -708,17 +712,17 @@ private:
class UserDefinedTypeName: public TypeName class UserDefinedTypeName: public TypeName
{ {
public: public:
UserDefinedTypeName(SourceLocation const& _location, ASTPointer<ASTString> const& _name): UserDefinedTypeName(SourceLocation const& _location, std::vector<ASTString> const& _namePath):
TypeName(_location), m_name(_name) {} TypeName(_location), m_namePath(_namePath) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const 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; virtual UserDefinedTypeNameAnnotation& annotation() const override;
private: private:
ASTPointer<ASTString> m_name; std::vector<ASTString> m_namePath;
}; };
/** /**

View File

@ -40,7 +40,13 @@ struct ASTAnnotation
virtual ~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. /// Whether all functions are implemented.
bool isFullyImplemented = true; bool isFullyImplemented = true;

View File

@ -21,6 +21,7 @@
*/ */
#include <libsolidity/ASTJsonConverter.h> #include <libsolidity/ASTJsonConverter.h>
#include <boost/algorithm/string/join.hpp>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
using namespace std; using namespace std;
@ -144,7 +145,9 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
bool ASTJsonConverter::visit(UserDefinedTypeName 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; return true;
} }

View File

@ -21,6 +21,7 @@
*/ */
#include <libsolidity/ASTPrinter.h> #include <libsolidity/ASTPrinter.h>
#include <boost/algorithm/string/join.hpp>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
using namespace std; using namespace std;
@ -151,7 +152,7 @@ bool ASTPrinter::visit(ElementaryTypeName const& _node)
bool ASTPrinter::visit(UserDefinedTypeName const& _node) bool ASTPrinter::visit(UserDefinedTypeName const& _node)
{ {
writeLine("UserDefinedTypeName \"" + _node.name() + "\""); writeLine("UserDefinedTypeName \"" + boost::algorithm::join(_node.namePath(), ".") + "\"");
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }

View File

@ -68,6 +68,12 @@ void Compiler::compileContract(
packIntoContractCreator(_contract, m_runtimeContext); packIntoContractCreator(_contract, m_runtimeContext);
if (m_optimize) if (m_optimize)
m_context.optimise(m_optimizeRuns); m_context.optimise(m_optimizeRuns);
if (_contract.isLibrary())
{
solAssert(m_runtimeSub != size_t(-1), "");
m_context.injectVersionStampIntoSub(m_runtimeSub);
}
} }
void Compiler::compileClone( void Compiler::compileClone(
@ -252,7 +258,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
eth::AssemblyItem returnTag = m_context.pushNewTag(); eth::AssemblyItem returnTag = m_context.pushNewTag();
fallback->accept(*this); fallback->accept(*this);
m_context << returnTag; m_context << returnTag;
appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes()); appendReturnValuePacker(FunctionType(*fallback).returnParameterTypes(), _contract.isLibrary());
} }
else else
m_context << eth::Instruction::STOP; // function not found m_context << eth::Instruction::STOP; // function not found
@ -268,7 +274,7 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
appendCalldataUnpacker(functionType->parameterTypes()); appendCalldataUnpacker(functionType->parameterTypes());
m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration()));
m_context << returnTag; 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. // Retain the offset pointer as base_offset, the point from which the data offsets are computed.
m_context << eth::Instruction::DUP1; 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 // stack: v1 v2 ... v(k-1) base_offset current_offset
switch (type->category()) TypePointer type = parameterType->decodingType();
{ if (type->category() == Type::Category::Array)
case Type::Category::Array:
{ {
auto const& arrayType = dynamic_cast<ArrayType const&>(*type); auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
solAssert(arrayType.location() != DataLocation::Storage, "");
solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
if (_fromMemory) if (_fromMemory)
{ {
@ -342,9 +346,9 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack()); CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack());
m_context << eth::Instruction::SWAP1; m_context << eth::Instruction::SWAP1;
} }
break;
} }
default: else
{
solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString());
CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true);
CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack()); 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; 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); CompilerUtils utils(m_context);
if (_typeParameters.empty()) if (_typeParameters.empty())
@ -365,7 +369,7 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
utils.fetchFreeMemoryPointer(); utils.fetchFreeMemoryPointer();
//@todo optimization: if we return a single memory array, there should be enough space before //@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. // 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(); utils.toSizeAfterFreeMemoryPointer();
m_context << eth::Instruction::RETURN; m_context << eth::Instruction::RETURN;
} }

View File

@ -87,7 +87,7 @@ private:
/// From memory if @a _fromMemory is true, otherwise from call data. /// From memory if @a _fromMemory is true, otherwise from call data.
/// Expects source offset on the stack, which is removed. /// Expects source offset on the stack, which is removed.
void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); 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 registerStateVariables(ContractDefinition const& _contract);
void initializeStateVariables(ContractDefinition const& _contract); void initializeStateVariables(ContractDefinition const& _contract);

View File

@ -20,10 +20,12 @@
* Utilities for the solidity compiler. * Utilities for the solidity compiler.
*/ */
#include <libsolidity/CompilerContext.h>
#include <utility> #include <utility>
#include <numeric> #include <numeric>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/Compiler.h> #include <libsolidity/Compiler.h>
#include <libsolidity/Version.h>
using namespace std; using namespace std;
@ -177,6 +179,13 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
updateSourceLocation(); 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( eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
FunctionDefinition const& _function, FunctionDefinition const& _function,
vector<ContractDefinition const*>::const_iterator _searchStart vector<ContractDefinition const*>::const_iterator _searchStart

View File

@ -128,6 +128,9 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); 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); } void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
eth::Assembly const& assembly() const { return m_asm; } eth::Assembly const& assembly() const { return m_asm; }

View File

@ -130,12 +130,15 @@ bool CompilerStack::parse()
m_globalContext->setCurrentContract(*contract); m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->currentThis()); resolver.updateDeclaration(*m_globalContext->currentThis());
TypeChecker typeChecker; TypeChecker typeChecker;
if (!typeChecker.checkTypeRequirements(*contract)) if (typeChecker.checkTypeRequirements(*contract))
typesFine = false; {
m_errors += typeChecker.errors();
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract)); contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract)); contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
}
else
typesFine = false;
m_contracts[contract->name()].contract = contract; m_contracts[contract->name()].contract = contract;
m_errors += typeChecker.errors();
} }
m_parseSuccessful = typesFine; m_parseSuccessful = typesFine;
return m_parseSuccessful; return m_parseSuccessful;

View File

@ -153,14 +153,15 @@ void CompilerUtils::encodeToMemory(
TypePointers const& _givenTypes, TypePointers const& _givenTypes,
TypePointers const& _targetTypes, TypePointers const& _targetTypes,
bool _padToWordBoundaries, bool _padToWordBoundaries,
bool _copyDynamicDataInPlace bool _copyDynamicDataInPlace,
bool _encodeAsLibraryTypes
) )
{ {
// stack: <v1> <v2> ... <vn> <mem> // stack: <v1> <v2> ... <vn> <mem>
TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes;
solAssert(targetTypes.size() == _givenTypes.size(), ""); solAssert(targetTypes.size() == _givenTypes.size(), "");
for (TypePointer& t: targetTypes) for (TypePointer& t: targetTypes)
t = t->mobileType()->externalType(); t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
// Stack during operation: // Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem> // <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()); copyToStackTop(argSize - stackPos + dynPointers + 2, _givenTypes[i]->sizeOnStack());
solAssert(!!targetType, "Externalable type expected."); solAssert(!!targetType, "Externalable type expected.");
TypePointer type = targetType; 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::Storage) ||
_givenTypes[i]->dataStoredIn(DataLocation::CallData) || _givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
_givenTypes[i]->category() == Type::Category::StringLiteral _givenTypes[i]->category() == Type::Category::StringLiteral

View File

@ -91,13 +91,16 @@ public:
/// @param _padToWordBoundaries if false, all values are concatenated without padding. /// @param _padToWordBoundaries if false, all values are concatenated without padding.
/// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length) /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length)
/// together with fixed-length data. /// 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 /// @note the locations of target reference types are ignored, because it will always be
/// memory. /// memory.
void encodeToMemory( void encodeToMemory(
TypePointers const& _givenTypes = {}, TypePointers const& _givenTypes = {},
TypePointers const& _targetTypes = {}, TypePointers const& _targetTypes = {},
bool _padToWordBoundaries = true, 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. /// Uses a CALL to the identity contract to perform a memory-to-memory copy.

View File

@ -40,8 +40,10 @@ namespace solidity
class DeclarationContainer class DeclarationContainer
{ {
public: public:
explicit DeclarationContainer(Declaration const* _enclosingDeclaration = nullptr, explicit DeclarationContainer(
DeclarationContainer const* _enclosingContainer = nullptr): Declaration const* _enclosingDeclaration = nullptr,
DeclarationContainer const* _enclosingContainer = nullptr
):
m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {} m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
/// Registers the declaration in the scope unless its name is already declared or the name is empty. /// 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 /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName

View File

@ -585,7 +585,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
} }
if (!event.isAnonymous()) if (!event.isAnonymous())
{ {
m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.name())))); m_context << u256(h256::Arith(dev::sha3(function.externalSignature())));
++numIndexed; ++numIndexed;
} }
solAssert(numIndexed <= 4, "Too many indexed arguments."); solAssert(numIndexed <= 4, "Too many indexed arguments.");
@ -1179,7 +1179,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
argumentTypes, argumentTypes,
_functionType.parameterTypes(), _functionType.parameterTypes(),
_functionType.padArguments(), _functionType.padArguments(),
_functionType.takesArbitraryParameters() _functionType.takesArbitraryParameters(),
isCallCode
); );
// Stack now: // Stack now:

View File

@ -1,5 +1,8 @@
#include <libsolidity/InterfaceHandler.h> #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/AST.h>
#include <libsolidity/CompilerStack.h> #include <libsolidity/CompilerStack.h>
using namespace std; using namespace std;
@ -57,18 +60,18 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
for (auto it: _contractDef.interfaceFunctions()) for (auto it: _contractDef.interfaceFunctions())
{ {
auto externalFunctionType = it.second->externalFunctionType(); auto externalFunctionType = it.second->interfaceFunctionType();
Json::Value method; Json::Value method;
method["type"] = "function"; method["type"] = "function";
method["name"] = it.second->declaration().name(); method["name"] = it.second->declaration().name();
method["constant"] = it.second->isConstant(); method["constant"] = it.second->isConstant();
method["inputs"] = populateParameters( method["inputs"] = populateParameters(
externalFunctionType->parameterNames(), externalFunctionType->parameterNames(),
externalFunctionType->parameterTypeNames() externalFunctionType->parameterTypeNames(_contractDef.isLibrary())
); );
method["outputs"] = populateParameters( method["outputs"] = populateParameters(
externalFunctionType->returnParameterNames(), externalFunctionType->returnParameterNames(),
externalFunctionType->returnParameterTypeNames() externalFunctionType->returnParameterTypeNames(_contractDef.isLibrary())
); );
abi.append(method); abi.append(method);
} }
@ -76,11 +79,11 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
{ {
Json::Value method; Json::Value method;
method["type"] = "constructor"; method["type"] = "constructor";
auto externalFunction = FunctionType(*_contractDef.constructor()).externalFunctionType(); auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
solAssert(!!externalFunction, ""); solAssert(!!externalFunction, "");
method["inputs"] = populateParameters( method["inputs"] = populateParameters(
externalFunction->parameterNames(), externalFunction->parameterNames(),
externalFunction->parameterTypeNames() externalFunction->parameterTypeNames(_contractDef.isLibrary())
); );
abi.append(method); abi.append(method);
} }
@ -96,7 +99,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
{ {
Json::Value input; Json::Value input;
input["name"] = p->name(); input["name"] = p->name();
input["type"] = p->annotation().type->toString(true); input["type"] = p->annotation().type->canonicalName(false);
input["indexed"] = p->isIndexed(); input["indexed"] = p->isIndexed();
params.append(input); params.append(input);
} }
@ -108,33 +111,61 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
string InterfaceHandler::ABISolidityInterface(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) auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes)
{ {
string r = ""; return "(" + join(boost::irange<size_t>(0, _paramNames.size()) | transformed([&](size_t _i) {
solAssert(_paramNames.size() == _paramTypes.size(), "Names and types vector size does not match"); return _paramTypes[_i] + " " + _paramNames[_i];
for (unsigned i = 0; i < _paramNames.size(); ++i) }), ",") + ")";
r += (r.size() ? "," : "(") + _paramTypes[i] + " " + _paramNames[i];
return r.size() ? r + ")" : "()";
}; };
// 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()) if (_contractDef.constructor())
{ {
auto externalFunction = FunctionType(*_contractDef.constructor()).externalFunctionType(); auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
solAssert(!!externalFunction, ""); solAssert(!!externalFunction, "");
ret += ret +=
"function " + "function " +
_contractDef.name() + _contractDef.name() +
populateParameters(externalFunction->parameterNames(), externalFunction->parameterTypeNames()) + populateParameters(
externalFunction->parameterNames(),
externalFunction->parameterTypeNames(_contractDef.isLibrary())
) +
";"; ";";
} }
for (auto const& it: _contractDef.interfaceFunctions()) for (auto const& it: _contractDef.interfaceFunctions())
{ {
ret += "function " + it.second->declaration().name() + ret += "function " + it.second->declaration().name() +
populateParameters(it.second->parameterNames(), it.second->parameterTypeNames()) + populateParameters(
(it.second->isConstant() ? "constant " : ""); it.second->parameterNames(),
it.second->parameterTypeNames(_contractDef.isLibrary())
) + (it.second->isConstant() ? "constant " : "");
if (it.second->returnParameterTypes().size()) 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() == ' ') else if (ret.back() == ' ')
ret.pop_back(); ret.pop_back();
ret += ";"; ret += ";";

View File

@ -125,11 +125,27 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na
return iterator->second.resolveName(_name, false); 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); 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( vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
Identifier const& _identifier, Identifier const& _identifier,
vector<Declaration const*> const& _declarations vector<Declaration const*> const& _declarations
@ -263,6 +279,7 @@ DeclarationRegistrationHelper::DeclarationRegistrationHelper(map<ASTNode const*,
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
{ {
registerDeclaration(_contract, true); registerDeclaration(_contract, true);
_contract.annotation().canonicalName = currentCanonicalName();
return true; return true;
} }
@ -274,6 +291,7 @@ void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct) bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
{ {
registerDeclaration(_struct, true); registerDeclaration(_struct, true);
_struct.annotation().canonicalName = currentCanonicalName();
return true; return true;
} }
@ -285,6 +303,7 @@ void DeclarationRegistrationHelper::endVisit(StructDefinition&)
bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum) bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
{ {
registerDeclaration(_enum, true); registerDeclaration(_enum, true);
_enum.annotation().canonicalName = currentCanonicalName();
return true; return true;
} }
@ -400,5 +419,21 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
enterNewSubScope(_declaration); 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;
}
} }
} }

View File

@ -36,9 +36,8 @@ namespace solidity
{ {
/** /**
* Resolves name references, types and checks types of all expressions. * Resolves name references, typenames and sets the (explicitly given) types for all variable
* Specifically, it checks that all operations are valid for the inferred types. * declarations.
* An exception is throw on the first error.
*/ */
class NameAndTypeResolver: private boost::noncopyable class NameAndTypeResolver: private boost::noncopyable
{ {
@ -59,7 +58,12 @@ public:
/// Resolves a name in the "current" scope. Should only be called during the initial /// Resolves a name in the "current" scope. Should only be called during the initial
/// resolving phase. /// 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 /// returns the vector of declarations without repetitions
static std::vector<Declaration const*> cleanedDeclarations( static std::vector<Declaration const*> cleanedDeclarations(
@ -119,6 +123,9 @@ private:
void closeCurrentScope(); void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope); 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; std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration const* m_currentScope; Declaration const* m_currentScope;
VariableScope* m_currentFunction; VariableScope* m_currentFunction;

View File

@ -522,7 +522,14 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition(); 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 else
BOOST_THROW_EXCEPTION(createParserError("Expected type name")); BOOST_THROW_EXCEPTION(createParserError("Expected type name"));
@ -1036,7 +1043,7 @@ ASTPointer<TypeName> Parser::typeNameIndexAccessStructure(
ASTNodeFactory nodeFactory(*this, _primary); ASTNodeFactory nodeFactory(*this, _primary);
ASTPointer<TypeName> type; ASTPointer<TypeName> type;
if (auto identifier = dynamic_cast<Identifier const*>(_primary.get())) 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())) else if (auto typeName = dynamic_cast<ElementaryTypeNameExpression const*>(_primary.get()))
type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeToken()); type = nodeFactory.createNode<ElementaryTypeName>(typeName->typeToken());
else else

View File

@ -54,20 +54,13 @@ bool ReferencesResolver::visit(Return const& _return)
bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName) bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName)
{ {
auto declarations = m_resolver.nameFromCurrentScope(_typeName.name()); Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
if (declarations.empty()) if (!declaration)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
DeclarationError() << DeclarationError() <<
errinfo_sourceLocation(_typeName.location()) << 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; _typeName.annotation().referencedDeclaration = declaration;
return true; return true;
} }
@ -106,9 +99,21 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
// References are forced to calldata for external function parameters (not return) // References are forced to calldata for external function parameters (not return)
// and memory for parameters (also return) of publicly visible functions. // and memory for parameters (also return) of publicly visible functions.
// They default to memory for function parameters and storage for local variables. // 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 (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{ {
if (_variable.isExternalCallableParameter()) if (_variable.isExternalCallableParameter())
{
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 // force location of external function parameters (not return) to calldata
if (loc != Location::Default) if (loc != Location::Default)
@ -116,16 +121,20 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
"Location has to be calldata for external functions " "Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)." "(remove the \"memory\" or \"storage\" keyword)."
)); ));
}
if (loc == Location::Default)
type = ref->copyForLocation(DataLocation::CallData, true); type = ref->copyForLocation(DataLocation::CallData, true);
} }
else if (_variable.isCallableParameter() && _variable.scope()->isPublic()) 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 // 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( BOOST_THROW_EXCEPTION(_variable.createTypeError(
"Location has to be memory for publicly visible functions " "Location has to be memory for publicly visible functions "
"(remove the \"storage\" keyword)." "(remove the \"storage\" keyword)."
)); ));
if (loc == Location::Default || !contract.isLibrary())
type = ref->copyForLocation(DataLocation::Memory, true); type = ref->copyForLocation(DataLocation::Memory, true);
} }
else else

View File

@ -299,7 +299,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co
if (f->isPartOfExternalInterface()) if (f->isPartOfExternalInterface())
{ {
auto functionType = make_shared<FunctionType>(*f); auto functionType = make_shared<FunctionType>(*f);
externalDeclarations[functionType->externalSignature(f->name())].push_back( externalDeclarations[functionType->externalSignature()].push_back(
make_pair(f.get(), functionType) make_pair(f.get(), functionType)
); );
} }
@ -307,7 +307,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co
if (v->isPartOfExternalInterface()) if (v->isPartOfExternalInterface())
{ {
auto functionType = make_shared<FunctionType>(*v); auto functionType = make_shared<FunctionType>(*v);
externalDeclarations[functionType->externalSignature(v->name())].push_back( externalDeclarations[functionType->externalSignature()].push_back(
make_pair(v.get(), functionType) make_pair(v.get(), functionType)
); );
} }
@ -397,12 +397,13 @@ bool TypeChecker::visit(StructDefinition const& _struct)
bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(FunctionDefinition const& _function)
{ {
bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{ {
if (!type(*var)->canLiveOutsideStorage()) if (!type(*var)->canLiveOutsideStorage())
typeError(*var, "Type is required to live outside storage."); typeError(*var, "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->externalType())) if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
typeError(*var, "Internal type is not allowed for public and external functions."); fatalTypeError(*var, "Internal type is not allowed for public or external functions.");
} }
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers()) for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
visitManually( visitManually(
@ -490,7 +491,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
} }
else if ( else if (
_variable.visibility() >= VariableDeclaration::Visibility::Public && _variable.visibility() >= VariableDeclaration::Visibility::Public &&
!FunctionType(_variable).externalType() !FunctionType(_variable).interfaceFunctionType()
) )
typeError(_variable, "Internal type is not allowed for public state variables."); typeError(_variable, "Internal type is not allowed for public state variables.");
return false; return false;
@ -557,7 +558,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
typeError(_eventDef, "More than 3 indexed arguments for event."); typeError(_eventDef, "More than 3 indexed arguments for event.");
if (!type(*var)->canLiveOutsideStorage()) if (!type(*var)->canLiveOutsideStorage())
typeError(*var, "Type is required to live outside storage."); 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."); typeError(*var, "Internal type is not allowed as event parameter type.");
} }
return false; return false;

View File

@ -839,11 +839,49 @@ string ArrayType::toString(bool _short) const
return ret; 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) if (m_arrayKind != ArrayKind::Ordinary)
return this->copyForLocation(DataLocation::Memory, true); return this->copyForLocation(DataLocation::Memory, true);
TypePointer baseExt = m_baseType->externalType(); TypePointer baseExt = m_baseType->interfaceType(_inLibrary);
if (!baseExt) if (!baseExt)
return TypePointer(); return TypePointer();
if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized()) if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
@ -893,6 +931,11 @@ string ContractType::toString(bool) const
m_contract.name(); m_contract.name();
} }
string ContractType::canonicalName(bool) const
{
return m_contract.annotation().canonicalName;
}
MemberList const& ContractType::members() const MemberList const& ContractType::members() const
{ {
// We need to lazy-initialize it because of recursive references. // We need to lazy-initialize it because of recursive references.
@ -1059,6 +1102,14 @@ MemberList const& StructType::members() const
return *m_members; 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 TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{ {
auto copy = make_shared<StructType>(m_struct, _location); auto copy = make_shared<StructType>(m_struct, _location);
@ -1066,6 +1117,14 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer)
return copy; 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 FunctionTypePointer StructType::constructorType() const
{ {
TypePointers paramTypes; TypePointers paramTypes;
@ -1141,6 +1200,11 @@ string EnumType::toString(bool) const
return string("enum ") + m_enum.name(); return string("enum ") + m_enum.name();
} }
string EnumType::canonicalName(bool) const
{
return m_enum.annotation().canonicalName;
}
bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{ {
return _convertTo.category() == category() || _convertTo.category() == Category::Integer; return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
@ -1330,21 +1394,25 @@ unsigned FunctionType::sizeOnStack() const
return size; 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 paramTypes;
TypePointers retParamTypes; TypePointers retParamTypes;
for (auto type: m_parameterTypes) for (auto type: m_parameterTypes)
{ {
if (auto ext = type->externalType()) if (auto ext = type->interfaceType(isLibraryFunction))
paramTypes.push_back(ext); paramTypes.push_back(ext);
else else
return FunctionTypePointer(); return FunctionTypePointer();
} }
for (auto type: m_returnParameterTypes) for (auto type: m_returnParameterTypes)
{ {
if (auto ext = type->externalType()) if (auto ext = type->interfaceType(isLibraryFunction))
retParamTypes.push_back(ext); retParamTypes.push_back(ext);
else else
return FunctionTypePointer(); 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; solAssert(m_declaration != nullptr, "External signature of function needs declaration");
if (_name == "")
{
solAssert(m_declaration != nullptr, "Function type without name needs a declaration");
funcName = m_declaration->name();
}
string ret = funcName + "(";
FunctionTypePointer external = externalFunctionType(); bool _inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
string ret = m_declaration->name() + "(";
FunctionTypePointer external = interfaceFunctionType();
solAssert(!!external, "External function type requested."); solAssert(!!external, "External function type requested.");
TypePointers externalParameterTypes = external->parameterTypes(); TypePointers externalParameterTypes = external->parameterTypes();
for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it) for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
{ {
solAssert(!!(*it), "Parameter should have external type"); 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 + ")"; 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; vector<string> names;
for (TypePointer const& t: m_parameterTypes) for (TypePointer const& t: m_parameterTypes)
names.push_back(t->toString(true)); names.push_back(t->canonicalName(_addDataLocation));
return names; return names;
} }
vector<string> const FunctionType::returnParameterTypeNames() const vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocation) const
{ {
vector<string> names; vector<string> names;
for (TypePointer const& t: m_returnParameterTypes) for (TypePointer const& t: m_returnParameterTypes)
names.push_back(t->toString(true)); names.push_back(t->canonicalName(_addDataLocation));
return names; return names;
} }
@ -1576,6 +1642,11 @@ string MappingType::toString(bool _short) const
return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")"; 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 u256 VoidType::storageSize() const
{ {
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(

View File

@ -218,6 +218,9 @@ public:
virtual std::string toString(bool _short) const = 0; virtual std::string toString(bool _short) const = 0;
std::string toString() const { return toString(false); } 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 virtual u256 literalValue(Literal const*) const
{ {
BOOST_THROW_EXCEPTION( 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. /// 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: protected:
/// Convenience object used when returning an empty member list. /// Convenience object used when returning an empty member list.
@ -264,7 +276,8 @@ public:
virtual std::string toString(bool _short) const override; 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; } int numBits() const { return m_bits; }
bool isAddress() const { return m_modifier == Modifier::Address; } bool isAddress() const { return m_modifier == Modifier::Address; }
@ -369,7 +382,8 @@ public:
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString(bool) const override { return "bytes" + dev::toString(m_bytes); } 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; } int numBytes() const { return m_bytes; }
@ -395,7 +409,8 @@ public:
virtual std::string toString(bool) const override { return "bool"; } virtual std::string toString(bool) const override { return "bool"; }
virtual u256 literalValue(Literal const* _literal) const override; 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 bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
virtual unsigned sizeOnStack() const override; virtual unsigned sizeOnStack() const override;
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
virtual MemberList const& members() const override virtual MemberList const& members() const override
{ {
return isString() ? EmptyMemberList : s_arrayTypeMemberList; 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 /// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; } bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
@ -534,18 +552,23 @@ public:
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded ) 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 unsigned storageBytes() const override { return 20; }
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
virtual MemberList const& members() 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); 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; } bool isSuper() const { return m_super; }
ContractDefinition const& contractDefinition() const { return m_contract; } ContractDefinition const& contractDefinition() const { return m_contract; }
@ -566,7 +589,7 @@ private:
ContractDefinition const& m_contract; ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited /// If true, it is the "super" type of the current contract, i.e. it contains only inherited
/// members. /// members.
bool m_super; bool m_super = false;
/// Type of the constructor, @see constructorType. Lazily initialized. /// Type of the constructor, @see constructorType. Lazily initialized.
mutable FunctionTypePointer m_constructorType; mutable FunctionTypePointer m_constructorType;
/// List of member types, will be lazy-initialized because of recursive references. /// 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 std::string toString(bool _short) const override;
virtual MemberList const& members() 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; 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 /// @returns a function that peforms the type conversion between a list of struct members
/// and a memory struct of this type. /// and a memory struct of this type.
FunctionTypePointer constructorType() const; FunctionTypePointer constructorType() const;
@ -624,18 +654,23 @@ public:
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override virtual unsigned calldataEncodedSize(bool _padded) const override
{ {
return externalType()->calldataEncodedSize(_padded); return encodingType()->calldataEncodedSize(_padded);
} }
virtual unsigned storageBytes() const override; virtual unsigned storageBytes() const override;
virtual bool canLiveOutsideStorage() const override { return true; } virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override; 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 isValueType() const override { return true; }
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; 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())); 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; } EnumDefinition const& enumDefinition() const { return m_enum; }
/// @returns the value that the string has in the Enum /// @returns the value that the string has in the Enum
@ -684,13 +719,6 @@ public:
virtual Category category() const override { return Category::Function; } 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. /// Creates the type of a function.
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
/// Creates the accessor function type of a state variable. /// Creates the accessor function type of a state variable.
@ -736,10 +764,10 @@ public:
TypePointers const& parameterTypes() const { return m_parameterTypes; } TypePointers const& parameterTypes() const { return m_parameterTypes; }
std::vector<std::string> const& parameterNames() const { return m_parameterNames; } 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; } TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; }
std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; } 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 bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
@ -749,6 +777,13 @@ public:
virtual unsigned sizeOnStack() const override; virtual unsigned sizeOnStack() const override;
virtual MemberList const& members() 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 /// @returns true if this function can take the given argument types (possibly
/// after implicit conversion). /// after implicit conversion).
bool canTakeArguments(TypePointers const& _arguments) const; bool canTakeArguments(TypePointers const& _arguments) const;
@ -759,9 +794,7 @@ public:
bool isBareCall() const; bool isBareCall() const;
Location const& location() const { return m_location; } Location const& location() const { return m_location; }
/// @returns the external signature of this function type given the function name /// @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 std::string externalSignature() const;
/// function type is used
std::string externalSignature(std::string const& _name = "") const;
/// @returns the external identifier of this function (the hash of the signature). /// @returns the external identifier of this function (the hash of the signature).
u256 externalIdentifier() const; u256 externalIdentifier() const;
Declaration const& declaration() const Declaration const& declaration() const
@ -822,7 +855,16 @@ public:
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) 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 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& keyType() const { return m_keyType; }
TypePointer const& valueType() const { return m_valueType; } TypePointer const& valueType() const { return m_valueType; }

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <libdevcore/Assertions.h> #include <libdevcore/Assertions.h>
#include <libsolidity/Exceptions.h>
/// Assertion that throws an InternalCompilerError containing the given description if it is not met. /// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \ #define solAssert(CONDITION, DESCRIPTION) \

View File

@ -22,16 +22,19 @@
#include <libsolidity/Version.h> #include <libsolidity/Version.h>
#include <string> #include <string>
#include <libevmasm/Version.h> #include <libdevcore/CommonData.h>
#include <solidity/BuildInfo.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libevmasm/Version.h>
#include <libsolidity/Utils.h>
#include <solidity/BuildInfo.h>
using namespace dev; using namespace dev;
using namespace dev::solidity; using namespace dev::solidity;
using namespace std; using namespace std;
char const* dev::solidity::VersionNumber = ETH_PROJECT_VERSION; 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::solidity::VersionNumber) +
"-" + "-" +
string(DEV_QUOTED(ETH_COMMIT_HASH)).substr(0, 8) + 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) "/" DEV_QUOTED(ETH_BUILD_TYPE) "-" DEV_QUOTED(ETH_BUILD_PLATFORM)
" linked to libethereum-" + eth::VersionStringLibEvmAsm; " 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;
}

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <libdevcore/Common.h>
namespace dev namespace dev
{ {
@ -32,5 +33,10 @@ namespace solidity
extern char const* VersionNumber; extern char const* VersionNumber;
extern std::string const VersionString; 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();
} }
} }

View File

@ -595,6 +595,36 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
checkInterface(sourceCode, interface); 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() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -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) BOOST_AUTO_TEST_CASE(short_strings)
{ {
// This test verifies that the byte array encoding that combines length and data works // 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_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() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -142,6 +142,22 @@ BOOST_AUTO_TEST_CASE(inheritance)
sourcePart(*contract.definedFunctions().at(1))})); 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() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -933,24 +933,24 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors)
BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr);
FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()");
BOOST_REQUIRE(function && function->hasDeclaration()); BOOST_REQUIRE(function && function->hasDeclaration());
auto returnParams = function->returnParameterTypeNames(); auto returnParams = function->returnParameterTypeNames(false);
BOOST_CHECK_EQUAL(returnParams.at(0), "uint256"); BOOST_CHECK_EQUAL(returnParams.at(0), "uint256");
BOOST_CHECK(function->isConstant()); BOOST_CHECK(function->isConstant());
function = retrieveFunctionBySignature(contract, "map(uint256)"); function = retrieveFunctionBySignature(contract, "map(uint256)");
BOOST_REQUIRE(function && function->hasDeclaration()); BOOST_REQUIRE(function && function->hasDeclaration());
auto params = function->parameterTypeNames(); auto params = function->parameterTypeNames(false);
BOOST_CHECK_EQUAL(params.at(0), "uint256"); BOOST_CHECK_EQUAL(params.at(0), "uint256");
returnParams = function->returnParameterTypeNames(); returnParams = function->returnParameterTypeNames(false);
BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4"); BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4");
BOOST_CHECK(function->isConstant()); BOOST_CHECK(function->isConstant());
function = retrieveFunctionBySignature(contract, "multiple_map(uint256,uint256)"); function = retrieveFunctionBySignature(contract, "multiple_map(uint256,uint256)");
BOOST_REQUIRE(function && function->hasDeclaration()); BOOST_REQUIRE(function && function->hasDeclaration());
params = function->parameterTypeNames(); params = function->parameterTypeNames(false);
BOOST_CHECK_EQUAL(params.at(0), "uint256"); BOOST_CHECK_EQUAL(params.at(0), "uint256");
BOOST_CHECK_EQUAL(params.at(1), "uint256"); BOOST_CHECK_EQUAL(params.at(1), "uint256");
returnParams = function->returnParameterTypeNames(); returnParams = function->returnParameterTypeNames(false);
BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4"); BOOST_CHECK_EQUAL(returnParams.at(0), "bytes4");
BOOST_CHECK(function->isConstant()); BOOST_CHECK(function->isConstant());
} }