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);
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

View File

@ -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;
};
/**

View File

@ -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;

View File

@ -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;
}

View File

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

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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; }

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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:

View File

@ -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 += ";";

View File

@ -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;
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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
{

View File

@ -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;

View File

@ -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");
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.");
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(

View File

@ -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; }

View File

@ -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) \

View File

@ -22,16 +22,19 @@
#include <libsolidity/Version.h>
#include <string>
#include <libevmasm/Version.h>
#include <solidity/BuildInfo.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Common.h>
#include <libevmasm/Version.h>
#include <libsolidity/Utils.h>
#include <solidity/BuildInfo.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;
}

View File

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

View File

@ -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()
}

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)
{
// 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()
}

View File

@ -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()
}

View File

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