mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #117 from chriseth/internalTypesForLibrary
Internal types for library
This commit is contained in:
commit
d35a4b849d
@ -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
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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; }
|
||||||
|
@ -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))
|
||||||
|
{
|
||||||
|
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
|
||||||
|
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
|
||||||
|
}
|
||||||
|
else
|
||||||
typesFine = false;
|
typesFine = false;
|
||||||
m_errors += typeChecker.errors();
|
|
||||||
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
|
|
||||||
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
|
|
||||||
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;
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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 += ";";
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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,27 +99,43 @@ 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())
|
||||||
{
|
{
|
||||||
// force location of external function parameters (not return) to calldata
|
auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
|
||||||
if (loc != Location::Default)
|
if (contract.isLibrary())
|
||||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
{
|
||||||
"Location has to be calldata for external functions "
|
if (loc == Location::Memory)
|
||||||
"(remove the \"memory\" or \"storage\" keyword)."
|
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||||
));
|
"Location has to be calldata or storage for external "
|
||||||
type = ref->copyForLocation(DataLocation::CallData, true);
|
"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())
|
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)."
|
||||||
));
|
));
|
||||||
type = ref->copyForLocation(DataLocation::Memory, true);
|
if (loc == Location::Default || !contract.isLibrary())
|
||||||
|
type = ref->copyForLocation(DataLocation::Memory, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -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(
|
||||||
|
@ -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; }
|
||||||
|
@ -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) \
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user