mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'ethereum/develop' into sol_overloadingFunctions
This commit is contained in:
commit
158795e48f
81
AST.cpp
81
AST.cpp
@ -21,6 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libsolidity/Utils.h>
|
#include <libsolidity/Utils.h>
|
||||||
#include <libsolidity/AST.h>
|
#include <libsolidity/AST.h>
|
||||||
#include <libsolidity/ASTVisitor.h>
|
#include <libsolidity/ASTVisitor.h>
|
||||||
@ -52,6 +53,7 @@ void ContractDefinition::checkTypeRequirements()
|
|||||||
baseSpecifier->checkTypeRequirements();
|
baseSpecifier->checkTypeRequirements();
|
||||||
|
|
||||||
checkIllegalOverrides();
|
checkIllegalOverrides();
|
||||||
|
checkAbstractFunctions();
|
||||||
|
|
||||||
FunctionDefinition const* constructor = getConstructor();
|
FunctionDefinition const* constructor = getConstructor();
|
||||||
if (constructor && !constructor->getReturnParameters().empty())
|
if (constructor && !constructor->getReturnParameters().empty())
|
||||||
@ -60,6 +62,7 @@ void ContractDefinition::checkTypeRequirements()
|
|||||||
|
|
||||||
FunctionDefinition const* fallbackFunction = nullptr;
|
FunctionDefinition const* fallbackFunction = nullptr;
|
||||||
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
|
||||||
|
{
|
||||||
if (function->getName().empty())
|
if (function->getName().empty())
|
||||||
{
|
{
|
||||||
if (fallbackFunction)
|
if (fallbackFunction)
|
||||||
@ -71,6 +74,9 @@ void ContractDefinition::checkTypeRequirements()
|
|||||||
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
|
BOOST_THROW_EXCEPTION(fallbackFunction->getParameterList().createTypeError("Fallback function cannot take parameters."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!function->isFullyImplemented())
|
||||||
|
setFullyImplemented(false);
|
||||||
|
}
|
||||||
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
|
for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
|
||||||
modifier->checkTypeRequirements();
|
modifier->checkTypeRequirements();
|
||||||
|
|
||||||
@ -98,7 +104,7 @@ void ContractDefinition::checkTypeRequirements()
|
|||||||
if (hashes.count(hash))
|
if (hashes.count(hash))
|
||||||
BOOST_THROW_EXCEPTION(createTypeError(
|
BOOST_THROW_EXCEPTION(createTypeError(
|
||||||
std::string("Function signature hash collision for ") +
|
std::string("Function signature hash collision for ") +
|
||||||
it.second->getCanonicalSignature()));
|
it.second->externalSignature()));
|
||||||
hashes.insert(hash);
|
hashes.insert(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,6 +140,28 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContractDefinition::checkAbstractFunctions()
|
||||||
|
{
|
||||||
|
map<string, bool> functions;
|
||||||
|
|
||||||
|
// Search from base to derived
|
||||||
|
for (ContractDefinition const* contract: boost::adaptors::reverse(getLinearizedBaseContracts()))
|
||||||
|
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
|
||||||
|
{
|
||||||
|
string const& name = function->getName();
|
||||||
|
if (!function->isFullyImplemented() && functions.count(name) && functions[name])
|
||||||
|
BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract"));
|
||||||
|
functions[name] = function->isFullyImplemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto const& it: functions)
|
||||||
|
if (!it.second)
|
||||||
|
{
|
||||||
|
setFullyImplemented(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ContractDefinition::checkIllegalOverrides() const
|
void ContractDefinition::checkIllegalOverrides() const
|
||||||
{
|
{
|
||||||
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
||||||
@ -203,8 +231,8 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
|
|||||||
{
|
{
|
||||||
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
|
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions())
|
||||||
{
|
{
|
||||||
string functionSignature = f->getCanonicalSignature();
|
string functionSignature = f->externalSignature();
|
||||||
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && signaturesSeen.count(functionSignature) == 0)
|
if (f->isPartOfExternalInterface() && signaturesSeen.count(functionSignature) == 0)
|
||||||
{
|
{
|
||||||
functionsSeen.insert(f->getName());
|
functionsSeen.insert(f->getName());
|
||||||
signaturesSeen.insert(functionSignature);
|
signaturesSeen.insert(functionSignature);
|
||||||
@ -214,11 +242,12 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
|
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables())
|
||||||
if (v->isPublic() && functionsSeen.count(v->getName()) == 0)
|
if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface())
|
||||||
{
|
{
|
||||||
FunctionType ftype(*v);
|
FunctionType ftype(*v);
|
||||||
|
solAssert(v->getType().get(), "");
|
||||||
functionsSeen.insert(v->getName());
|
functionsSeen.insert(v->getName());
|
||||||
FixedHash<4> hash(dev::sha3(ftype.getCanonicalSignature(v->getName())));
|
FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->getName())));
|
||||||
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
|
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,25 +351,35 @@ TypePointer FunctionDefinition::getType(ContractDefinition const*) const
|
|||||||
void FunctionDefinition::checkTypeRequirements()
|
void FunctionDefinition::checkTypeRequirements()
|
||||||
{
|
{
|
||||||
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
|
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
|
||||||
|
{
|
||||||
if (!var->getType()->canLiveOutsideStorage())
|
if (!var->getType()->canLiveOutsideStorage())
|
||||||
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
|
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
|
||||||
|
if (getVisibility() >= Visibility::Public && !(var->getType()->externalType()))
|
||||||
|
{
|
||||||
|
// todo delete when will be implemented arrays as parameter type in internal functions
|
||||||
|
if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array)
|
||||||
|
BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions."));
|
||||||
|
else
|
||||||
|
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
|
||||||
|
}
|
||||||
|
}
|
||||||
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
|
for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
|
||||||
modifier->checkTypeRequirements(isConstructor() ?
|
modifier->checkTypeRequirements(isConstructor() ?
|
||||||
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
|
dynamic_cast<ContractDefinition const&>(*getScope()).getBaseContracts() :
|
||||||
vector<ASTPointer<InheritanceSpecifier>>());
|
vector<ASTPointer<InheritanceSpecifier>>());
|
||||||
|
if (m_body)
|
||||||
m_body->checkTypeRequirements();
|
m_body->checkTypeRequirements();
|
||||||
}
|
}
|
||||||
|
|
||||||
string FunctionDefinition::getCanonicalSignature() const
|
string FunctionDefinition::externalSignature() const
|
||||||
{
|
{
|
||||||
return FunctionType(*this).getCanonicalSignature(getName());
|
return FunctionType(*this).externalSignature(getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isLValue() const
|
bool VariableDeclaration::isLValue() const
|
||||||
{
|
{
|
||||||
// External function parameters are Read-Only
|
// External function parameters and constant declared variables are Read-Only
|
||||||
return !isExternalFunctionParameter();
|
return !isExternalFunctionParameter() && !m_isConstant;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VariableDeclaration::checkTypeRequirements()
|
void VariableDeclaration::checkTypeRequirements()
|
||||||
@ -349,10 +388,24 @@ void VariableDeclaration::checkTypeRequirements()
|
|||||||
// sets the type.
|
// sets the type.
|
||||||
// Note that assignments before the first declaration are legal because of the special scoping
|
// Note that assignments before the first declaration are legal because of the special scoping
|
||||||
// rules inherited from JavaScript.
|
// rules inherited from JavaScript.
|
||||||
|
if (m_isConstant)
|
||||||
|
{
|
||||||
|
if (!dynamic_cast<ContractDefinition const*>(getScope()))
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Illegal use of \"constant\" specifier."));
|
||||||
|
if ((m_type && !m_type->isValueType()) || !m_value)
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Unitialized \"constant\" variable."));
|
||||||
|
}
|
||||||
if (!m_value)
|
if (!m_value)
|
||||||
return;
|
return;
|
||||||
if (m_type)
|
if (m_type)
|
||||||
|
{
|
||||||
m_value->expectType(*m_type);
|
m_value->expectType(*m_type);
|
||||||
|
if (m_isStateVariable && !m_type->externalType() && getVisibility() >= Visibility::Public)
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for state variables."));
|
||||||
|
|
||||||
|
if (!FunctionType(*this).externalType())
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// no type declared and no previous assignment, infer the type
|
// no type declared and no previous assignment, infer the type
|
||||||
@ -437,6 +490,8 @@ void EventDefinition::checkTypeRequirements()
|
|||||||
numIndexed++;
|
numIndexed++;
|
||||||
if (!var->getType()->canLiveOutsideStorage())
|
if (!var->getType()->canLiveOutsideStorage())
|
||||||
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
|
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
|
||||||
|
if (!var->getType()->externalType())
|
||||||
|
BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed as event parameter type."));
|
||||||
}
|
}
|
||||||
if (numIndexed > 3)
|
if (numIndexed > 3)
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
|
BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
|
||||||
@ -681,6 +736,8 @@ void NewExpression::checkTypeRequirements()
|
|||||||
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
|
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration());
|
||||||
if (!m_contract)
|
if (!m_contract)
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
|
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
|
||||||
|
if (!m_contract->isFullyImplemented())
|
||||||
|
BOOST_THROW_EXCEPTION(m_contract->createTypeError("Trying to create an instance of an abstract contract."));
|
||||||
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
|
shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
|
||||||
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
|
TypePointers const& parameterTypes = contractType->getConstructorType()->getParameterTypes();
|
||||||
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
|
m_type = make_shared<FunctionType>(parameterTypes, TypePointers{contractType},
|
||||||
@ -720,7 +777,7 @@ void IndexAccess::checkTypeRequirements()
|
|||||||
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
|
BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
|
||||||
m_index->expectType(IntegerType(256));
|
m_index->expectType(IntegerType(256));
|
||||||
if (type.isByteArray())
|
if (type.isByteArray())
|
||||||
m_type = make_shared<IntegerType>(8, IntegerType::Modifier::Hash);
|
m_type = make_shared<FixedBytesType>(1);
|
||||||
else
|
else
|
||||||
m_type = type.getBaseType();
|
m_type = type.getBaseType();
|
||||||
m_isLValue = type.getLocation() != ArrayType::Location::CallData;
|
m_isLValue = type.getLocation() != ArrayType::Location::CallData;
|
||||||
|
135
AST.h
135
AST.h
@ -156,6 +156,7 @@ public:
|
|||||||
/// contract types.
|
/// contract types.
|
||||||
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
|
virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
|
||||||
virtual bool isLValue() const { return false; }
|
virtual bool isLValue() const { return false; }
|
||||||
|
virtual bool isPartOfExternalInterface() const { return false; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
|
virtual Visibility getDefaultVisibility() const { return Visibility::Public; }
|
||||||
@ -195,6 +196,22 @@ protected:
|
|||||||
ASTPointer<ASTString> m_documentation;
|
ASTPointer<ASTString> m_documentation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class that is added to AST nodes that can be marked as not being fully implemented
|
||||||
|
*/
|
||||||
|
class ImplementationOptional
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
|
||||||
|
|
||||||
|
/// @return whether this node is fully implemented or not
|
||||||
|
bool isFullyImplemented() const { return m_implemented; }
|
||||||
|
void setFullyImplemented(bool _implemented) { m_implemented = _implemented; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_implemented;
|
||||||
|
};
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,20 +219,24 @@ protected:
|
|||||||
* document order. It first visits all struct declarations, then all variable declarations and
|
* document order. It first visits all struct declarations, then all variable declarations and
|
||||||
* finally all function declarations.
|
* finally all function declarations.
|
||||||
*/
|
*/
|
||||||
class ContractDefinition: public Declaration, public Documented
|
class ContractDefinition: public Declaration, public Documented, public ImplementationOptional
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ContractDefinition(SourceLocation const& _location,
|
ContractDefinition(
|
||||||
ASTPointer<ASTString> const& _name,
|
SourceLocation const& _location,
|
||||||
ASTPointer<ASTString> const& _documentation,
|
ASTPointer<ASTString> const& _name,
|
||||||
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
|
ASTPointer<ASTString> const& _documentation,
|
||||||
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
|
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
|
||||||
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
|
std::vector<ASTPointer<StructDefinition>> const& _definedStructs,
|
||||||
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
|
std::vector<ASTPointer<EnumDefinition>> const& _definedEnums,
|
||||||
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
|
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables,
|
||||||
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
|
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions,
|
||||||
std::vector<ASTPointer<EventDefinition>> const& _events):
|
std::vector<ASTPointer<ModifierDefinition>> const& _functionModifiers,
|
||||||
Declaration(_location, _name), Documented(_documentation),
|
std::vector<ASTPointer<EventDefinition>> const& _events
|
||||||
|
):
|
||||||
|
Declaration(_location, _name),
|
||||||
|
Documented(_documentation),
|
||||||
|
ImplementationOptional(true),
|
||||||
m_baseContracts(_baseContracts),
|
m_baseContracts(_baseContracts),
|
||||||
m_definedStructs(_definedStructs),
|
m_definedStructs(_definedStructs),
|
||||||
m_definedEnums(_definedEnums),
|
m_definedEnums(_definedEnums),
|
||||||
@ -262,6 +283,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void checkIllegalOverrides() const;
|
void checkIllegalOverrides() const;
|
||||||
|
void checkAbstractFunctions();
|
||||||
|
|
||||||
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
|
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& getInterfaceFunctionList() const;
|
||||||
|
|
||||||
@ -377,24 +399,29 @@ private:
|
|||||||
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
|
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FunctionDefinition: public Declaration, public VariableScope, public Documented
|
class FunctionDefinition: public Declaration, public VariableScope, public Documented, public ImplementationOptional
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FunctionDefinition(SourceLocation const& _location, ASTPointer<ASTString> const& _name,
|
FunctionDefinition(
|
||||||
Declaration::Visibility _visibility, bool _isConstructor,
|
SourceLocation const& _location,
|
||||||
ASTPointer<ASTString> const& _documentation,
|
ASTPointer<ASTString> const& _name,
|
||||||
ASTPointer<ParameterList> const& _parameters,
|
Declaration::Visibility _visibility, bool _isConstructor,
|
||||||
bool _isDeclaredConst,
|
ASTPointer<ASTString> const& _documentation,
|
||||||
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
|
ASTPointer<ParameterList> const& _parameters,
|
||||||
ASTPointer<ParameterList> const& _returnParameters,
|
bool _isDeclaredConst,
|
||||||
ASTPointer<Block> const& _body):
|
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
|
||||||
Declaration(_location, _name, _visibility), Documented(_documentation),
|
ASTPointer<ParameterList> const& _returnParameters,
|
||||||
m_isConstructor(_isConstructor),
|
ASTPointer<Block> const& _body
|
||||||
m_parameters(_parameters),
|
):
|
||||||
m_isDeclaredConst(_isDeclaredConst),
|
Declaration(_location, _name, _visibility),
|
||||||
m_functionModifiers(_modifiers),
|
Documented(_documentation),
|
||||||
m_returnParameters(_returnParameters),
|
ImplementationOptional(_body != nullptr),
|
||||||
m_body(_body)
|
m_isConstructor(_isConstructor),
|
||||||
|
m_parameters(_parameters),
|
||||||
|
m_isDeclaredConst(_isDeclaredConst),
|
||||||
|
m_functionModifiers(_modifiers),
|
||||||
|
m_returnParameters(_returnParameters),
|
||||||
|
m_body(_body)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
@ -415,14 +442,15 @@ public:
|
|||||||
getVisibility() >= Visibility::Internal;
|
getVisibility() >= Visibility::Internal;
|
||||||
}
|
}
|
||||||
virtual TypePointer getType(ContractDefinition const*) const override;
|
virtual TypePointer getType(ContractDefinition const*) const override;
|
||||||
|
virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !getName().empty(); }
|
||||||
|
|
||||||
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
|
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
|
||||||
void checkTypeRequirements();
|
void checkTypeRequirements();
|
||||||
|
|
||||||
/// @returns the canonical signature of the function
|
/// @returns the external signature of the function
|
||||||
/// That consists of the name of the function followed by the types of the
|
/// That consists of the name of the function followed by the types of the
|
||||||
/// arguments separated by commas all enclosed in parentheses without any spaces.
|
/// arguments separated by commas all enclosed in parentheses without any spaces.
|
||||||
std::string getCanonicalSignature() const;
|
std::string externalSignature() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_isConstructor;
|
bool m_isConstructor;
|
||||||
@ -440,13 +468,23 @@ private:
|
|||||||
class VariableDeclaration: public Declaration
|
class VariableDeclaration: public Declaration
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VariableDeclaration(SourceLocation const& _location, ASTPointer<TypeName> const& _type,
|
VariableDeclaration(
|
||||||
ASTPointer<ASTString> const& _name, ASTPointer<Expression> _value,
|
SourceLocation const& _location,
|
||||||
Visibility _visibility,
|
ASTPointer<TypeName> const& _type,
|
||||||
bool _isStateVar = false, bool _isIndexed = false):
|
ASTPointer<ASTString> const& _name,
|
||||||
Declaration(_location, _name, _visibility),
|
ASTPointer<Expression> _value,
|
||||||
m_typeName(_type), m_value(_value),
|
Visibility _visibility,
|
||||||
m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {}
|
bool _isStateVar = false,
|
||||||
|
bool _isIndexed = false,
|
||||||
|
bool _isConstant = false
|
||||||
|
):
|
||||||
|
Declaration(_location, _name, _visibility),
|
||||||
|
m_typeName(_type),
|
||||||
|
m_value(_value),
|
||||||
|
m_isStateVariable(_isStateVar),
|
||||||
|
m_isIndexed(_isIndexed),
|
||||||
|
m_isConstant(_isConstant){}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -459,21 +497,24 @@ public:
|
|||||||
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
|
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
|
||||||
|
|
||||||
virtual bool isLValue() const override;
|
virtual bool isLValue() const override;
|
||||||
|
virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstant; }
|
||||||
|
|
||||||
void checkTypeRequirements();
|
void checkTypeRequirements();
|
||||||
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
|
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
|
||||||
bool isExternalFunctionParameter() const;
|
bool isExternalFunctionParameter() const;
|
||||||
bool isStateVariable() const { return m_isStateVariable; }
|
bool isStateVariable() const { return m_isStateVariable; }
|
||||||
bool isIndexed() const { return m_isIndexed; }
|
bool isIndexed() const { return m_isIndexed; }
|
||||||
|
bool isConstant() const { return m_isConstant; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
|
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
|
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
|
||||||
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
|
ASTPointer<Expression> m_value; ///< the assigned value, can be missing
|
||||||
bool m_isStateVariable; ///< Whether or not this is a contract state variable
|
bool m_isStateVariable; ///< Whether or not this is a contract state variable
|
||||||
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
|
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
|
||||||
|
bool m_isConstant; ///< Whether the variable is a compile-time constant.
|
||||||
|
|
||||||
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
||||||
};
|
};
|
||||||
@ -538,17 +579,24 @@ private:
|
|||||||
class EventDefinition: public Declaration, public VariableScope, public Documented
|
class EventDefinition: public Declaration, public VariableScope, public Documented
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EventDefinition(SourceLocation const& _location,
|
EventDefinition(
|
||||||
ASTPointer<ASTString> const& _name,
|
SourceLocation const& _location,
|
||||||
ASTPointer<ASTString> const& _documentation,
|
ASTPointer<ASTString> const& _name,
|
||||||
ASTPointer<ParameterList> const& _parameters):
|
ASTPointer<ASTString> const& _documentation,
|
||||||
Declaration(_location, _name), Documented(_documentation), m_parameters(_parameters) {}
|
ASTPointer<ParameterList> const& _parameters,
|
||||||
|
bool _anonymous = false
|
||||||
|
):
|
||||||
|
Declaration(_location, _name),
|
||||||
|
Documented(_documentation),
|
||||||
|
m_parameters(_parameters),
|
||||||
|
m_anonymous(_anonymous){}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
|
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
|
||||||
ParameterList const& getParameterList() const { return *m_parameters; }
|
ParameterList const& getParameterList() const { return *m_parameters; }
|
||||||
|
bool isAnonymous() const { return m_anonymous; }
|
||||||
|
|
||||||
virtual TypePointer getType(ContractDefinition const* = nullptr) const override
|
virtual TypePointer getType(ContractDefinition const* = nullptr) const override
|
||||||
{
|
{
|
||||||
@ -559,6 +607,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<ParameterList> m_parameters;
|
ASTPointer<ParameterList> m_parameters;
|
||||||
|
bool m_anonymous = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -175,7 +175,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
|
|||||||
if (m_returnParameters)
|
if (m_returnParameters)
|
||||||
m_returnParameters->accept(_visitor);
|
m_returnParameters->accept(_visitor);
|
||||||
listAccept(m_functionModifiers, _visitor);
|
listAccept(m_functionModifiers, _visitor);
|
||||||
m_body->accept(_visitor);
|
if (m_body)
|
||||||
|
m_body->accept(_visitor);
|
||||||
}
|
}
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
@ -188,7 +189,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
|
|||||||
if (m_returnParameters)
|
if (m_returnParameters)
|
||||||
m_returnParameters->accept(_visitor);
|
m_returnParameters->accept(_visitor);
|
||||||
listAccept(m_functionModifiers, _visitor);
|
listAccept(m_functionModifiers, _visitor);
|
||||||
m_body->accept(_visitor);
|
if (m_body)
|
||||||
|
m_body->accept(_visitor);
|
||||||
}
|
}
|
||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
340
ArrayUtils.cpp
340
ArrayUtils.cpp
@ -34,8 +34,10 @@ using namespace solidity;
|
|||||||
|
|
||||||
void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const
|
void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const
|
||||||
{
|
{
|
||||||
// stack layout: [source_ref] target_ref (top)
|
// this copies source to target and also clears target if it was larger
|
||||||
// need to leave target_ref on the stack at the end
|
// need to leave "target_ref target_byte_off" on the stack at the end
|
||||||
|
|
||||||
|
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
|
||||||
solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
|
||||||
solAssert(
|
solAssert(
|
||||||
_sourceType.getLocation() == ArrayType::Location::CallData ||
|
_sourceType.getLocation() == ArrayType::Location::CallData ||
|
||||||
@ -47,14 +49,26 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.getBaseType());
|
Type const* targetBaseType = _targetType.isByteArray() ? &uint256 : &(*_targetType.getBaseType());
|
||||||
Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.getBaseType());
|
Type const* sourceBaseType = _sourceType.isByteArray() ? &uint256 : &(*_sourceType.getBaseType());
|
||||||
|
|
||||||
// this copies source to target and also clears target if it was larger
|
|
||||||
|
|
||||||
// TODO unroll loop for small sizes
|
// TODO unroll loop for small sizes
|
||||||
|
|
||||||
// stack: source_ref [source_length] target_ref
|
bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage;
|
||||||
|
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
|
||||||
|
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
|
||||||
|
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
|
||||||
|
unsigned byteOffsetSize = (haveByteOffsetSource ? 1 : 0) + (haveByteOffsetTarget ? 1 : 0);
|
||||||
|
|
||||||
|
// stack: source_ref [source_byte_off] [source_length] target_ref target_byte_off
|
||||||
// store target_ref
|
// store target_ref
|
||||||
|
// arrays always start at zero byte offset, pop offset
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i)
|
for (unsigned i = _sourceType.getSizeOnStack(); i > 0; --i)
|
||||||
m_context << eth::swapInstruction(i);
|
m_context << eth::swapInstruction(i);
|
||||||
|
// stack: target_ref source_ref [source_byte_off] [source_length]
|
||||||
|
if (sourceIsStorage)
|
||||||
|
// arrays always start at zero byte offset, pop offset
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
// stack: target_ref source_ref [source_length]
|
||||||
|
// retrieve source length
|
||||||
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
|
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
|
||||||
retrieveLength(_sourceType); // otherwise, length is already there
|
retrieveLength(_sourceType); // otherwise, length is already there
|
||||||
// stack: target_ref source_ref source_length
|
// stack: target_ref source_ref source_length
|
||||||
@ -73,6 +87,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP
|
<< eth::Instruction::POP << eth::Instruction::POP
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP;
|
<< eth::Instruction::POP << eth::Instruction::POP;
|
||||||
|
m_context << u256(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// compute hashes (data positions)
|
// compute hashes (data positions)
|
||||||
@ -88,8 +103,8 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
// stack: target_ref target_data_end source_length target_data_pos source_ref
|
||||||
// skip copying if source length is zero
|
// skip copying if source length is zero
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO;
|
m_context << eth::Instruction::DUP3 << eth::Instruction::ISZERO;
|
||||||
eth::AssemblyItem copyLoopEnd = m_context.newTag();
|
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
|
||||||
m_context.appendConditionalJumpTo(copyLoopEnd);
|
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
|
||||||
|
|
||||||
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
|
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
@ -98,88 +113,172 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
convertLengthToSize(_sourceType);
|
convertLengthToSize(_sourceType);
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
m_context << eth::Instruction::DUP3 << eth::Instruction::ADD;
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
|
||||||
|
if (haveByteOffsetTarget)
|
||||||
|
m_context << u256(0);
|
||||||
|
if (haveByteOffsetSource)
|
||||||
|
m_context << u256(0);
|
||||||
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||||
eth::AssemblyItem copyLoopStart = m_context.newTag();
|
eth::AssemblyItem copyLoopStart = m_context.newTag();
|
||||||
m_context << copyLoopStart;
|
m_context << copyLoopStart;
|
||||||
// check for loop condition
|
// check for loop condition
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::DUP2
|
<< eth::dupInstruction(3 + byteOffsetSize) << eth::dupInstruction(2 + byteOffsetSize)
|
||||||
<< eth::Instruction::GT << eth::Instruction::ISZERO;
|
<< eth::Instruction::GT << eth::Instruction::ISZERO;
|
||||||
|
eth::AssemblyItem copyLoopEnd = m_context.newTag();
|
||||||
m_context.appendConditionalJumpTo(copyLoopEnd);
|
m_context.appendConditionalJumpTo(copyLoopEnd);
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||||
// copy
|
// copy
|
||||||
if (sourceBaseType->getCategory() == Type::Category::Array)
|
if (sourceBaseType->getCategory() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::DUP3;
|
solAssert(byteOffsetSize == 0, "Byte offset for array as base type.");
|
||||||
|
m_context << eth::Instruction::DUP3;
|
||||||
|
if (sourceIsStorage)
|
||||||
|
m_context << u256(0);
|
||||||
|
m_context << eth::dupInstruction(sourceIsStorage ? 4 : 3) << u256(0);
|
||||||
copyArrayToStorage(
|
copyArrayToStorage(
|
||||||
dynamic_cast<ArrayType const&>(*targetBaseType),
|
dynamic_cast<ArrayType const&>(*targetBaseType),
|
||||||
dynamic_cast<ArrayType const&>(*sourceBaseType)
|
dynamic_cast<ArrayType const&>(*sourceBaseType)
|
||||||
);
|
);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
||||||
|
}
|
||||||
|
else if (directCopy)
|
||||||
|
{
|
||||||
|
solAssert(byteOffsetSize == 0, "Byte offset for direct copy.");
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::DUP3 << eth::Instruction::SLOAD
|
||||||
|
<< eth::Instruction::DUP3 << eth::Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP3;
|
// Note that we have to copy each element on its own in case conversion is involved.
|
||||||
|
// We might copy too much if there is padding at the last element, but this way end
|
||||||
|
// checking is easier.
|
||||||
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||||
|
m_context << eth::dupInstruction(3 + byteOffsetSize);
|
||||||
if (_sourceType.getLocation() == ArrayType::Location::Storage)
|
if (_sourceType.getLocation() == ArrayType::Location::Storage)
|
||||||
|
{
|
||||||
|
if (haveByteOffsetSource)
|
||||||
|
m_context << eth::Instruction::DUP2;
|
||||||
|
else
|
||||||
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *sourceBaseType).retrieveValue(SourceLocation(), true);
|
||||||
|
}
|
||||||
else if (sourceBaseType->isValueType())
|
else if (sourceBaseType->isValueType())
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false);
|
CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false);
|
||||||
else
|
else
|
||||||
solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString());
|
solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString());
|
||||||
m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack());
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
|
||||||
|
solAssert(2 + byteOffsetSize + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep.");
|
||||||
|
// fetch target storage reference
|
||||||
|
m_context << eth::dupInstruction(2 + byteOffsetSize + sourceBaseType->getSizeOnStack());
|
||||||
|
if (haveByteOffsetTarget)
|
||||||
|
m_context << eth::dupInstruction(1 + byteOffsetSize + sourceBaseType->getSizeOnStack());
|
||||||
|
else
|
||||||
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
|
StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
|
||||||
}
|
}
|
||||||
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||||
// increment source
|
// increment source
|
||||||
m_context
|
if (haveByteOffsetSource)
|
||||||
<< eth::Instruction::SWAP2
|
incrementByteOffset(sourceBaseType->getStorageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
|
||||||
<< (_sourceType.getLocation() == ArrayType::Location::Storage ?
|
else
|
||||||
sourceBaseType->getStorageSize() :
|
m_context
|
||||||
sourceBaseType->getCalldataEncodedSize())
|
<< eth::swapInstruction(2 + byteOffsetSize)
|
||||||
<< eth::Instruction::ADD
|
<< (sourceIsStorage ? sourceBaseType->getStorageSize() : sourceBaseType->getCalldataEncodedSize())
|
||||||
<< eth::Instruction::SWAP2;
|
<< eth::Instruction::ADD
|
||||||
|
<< eth::swapInstruction(2 + byteOffsetSize);
|
||||||
// increment target
|
// increment target
|
||||||
m_context
|
if (haveByteOffsetTarget)
|
||||||
<< eth::Instruction::SWAP1
|
incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
||||||
<< targetBaseType->getStorageSize()
|
else
|
||||||
<< eth::Instruction::ADD
|
m_context
|
||||||
<< eth::Instruction::SWAP1;
|
<< eth::swapInstruction(1 + byteOffsetSize)
|
||||||
|
<< targetBaseType->getStorageSize()
|
||||||
|
<< eth::Instruction::ADD
|
||||||
|
<< eth::swapInstruction(1 + byteOffsetSize);
|
||||||
m_context.appendJumpTo(copyLoopStart);
|
m_context.appendJumpTo(copyLoopStart);
|
||||||
m_context << copyLoopEnd;
|
m_context << copyLoopEnd;
|
||||||
|
if (haveByteOffsetTarget)
|
||||||
|
{
|
||||||
|
// clear elements that might be left over in the current slot in target
|
||||||
|
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
|
||||||
|
m_context << eth::dupInstruction(byteOffsetSize) << eth::Instruction::ISZERO;
|
||||||
|
eth::AssemblyItem copyCleanupLoopEnd = m_context.appendConditionalJump();
|
||||||
|
m_context << eth::dupInstruction(2 + byteOffsetSize) << eth::dupInstruction(1 + byteOffsetSize);
|
||||||
|
StorageItem(m_context, *targetBaseType).setToZero(SourceLocation(), true);
|
||||||
|
incrementByteOffset(targetBaseType->getStorageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
||||||
|
m_context.appendJumpTo(copyLoopEnd);
|
||||||
|
|
||||||
|
m_context << copyCleanupLoopEnd;
|
||||||
|
m_context << eth::Instruction::POP; // might pop the source, but then target is popped next
|
||||||
|
}
|
||||||
|
if (haveByteOffsetSource)
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
m_context << copyLoopEndWithoutByteOffset;
|
||||||
|
|
||||||
// zero-out leftovers in target
|
// zero-out leftovers in target
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end
|
// stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
||||||
// stack: target_ref target_data_end target_data_pos_updated
|
// stack: target_ref target_data_end target_data_pos_updated
|
||||||
clearStorageLoop(*targetBaseType);
|
clearStorageLoop(*targetBaseType);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
|
m_context << u256(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::clearArray(ArrayType const& _type) const
|
void ArrayUtils::clearArray(ArrayType const& _type) const
|
||||||
{
|
{
|
||||||
|
unsigned stackHeightStart = m_context.getStackHeight();
|
||||||
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
||||||
|
if (_type.getBaseType()->getStorageBytes() < 32)
|
||||||
|
{
|
||||||
|
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
solAssert(_type.getBaseType()->getStorageSize() <= 1, "Invalid storage size for type.");
|
||||||
|
}
|
||||||
|
if (_type.getBaseType()->isValueType())
|
||||||
|
solAssert(_type.getBaseType()->getStorageSize() <= 1, "Invalid size for value type.");
|
||||||
|
|
||||||
|
m_context << eth::Instruction::POP; // remove byte offset
|
||||||
if (_type.isDynamicallySized())
|
if (_type.isDynamicallySized())
|
||||||
clearDynamicArray(_type);
|
clearDynamicArray(_type);
|
||||||
else if (_type.getLength() == 0 || _type.getBaseType()->getCategory() == Type::Category::Mapping)
|
else if (_type.getLength() == 0 || _type.getBaseType()->getCategory() == Type::Category::Mapping)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
else if (_type.getLength() < 5) // unroll loop for small arrays @todo choose a good value
|
else if (_type.getBaseType()->isValueType() && _type.getStorageSize() <= 5)
|
||||||
{
|
{
|
||||||
solAssert(!_type.isByteArray(), "");
|
// unroll loop for small arrays @todo choose a good value
|
||||||
|
// Note that we loop over storage slots here, not elements.
|
||||||
|
for (unsigned i = 1; i < _type.getStorageSize(); ++i)
|
||||||
|
m_context
|
||||||
|
<< u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
|
||||||
|
<< u256(1) << eth::Instruction::ADD;
|
||||||
|
m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
|
}
|
||||||
|
else if (!_type.getBaseType()->isValueType() && _type.getLength() <= 4)
|
||||||
|
{
|
||||||
|
// unroll loop for small arrays @todo choose a good value
|
||||||
|
solAssert(_type.getBaseType()->getStorageBytes() >= 32, "Invalid storage size.");
|
||||||
for (unsigned i = 1; i < _type.getLength(); ++i)
|
for (unsigned i = 1; i < _type.getLength(); ++i)
|
||||||
{
|
{
|
||||||
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false);
|
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), false);
|
||||||
m_context << u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD;
|
m_context
|
||||||
|
<< eth::Instruction::POP
|
||||||
|
<< u256(_type.getBaseType()->getStorageSize()) << eth::Instruction::ADD;
|
||||||
}
|
}
|
||||||
|
m_context << u256(0);
|
||||||
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true);
|
StorageItem(m_context, *_type.getBaseType()).setToZero(SourceLocation(), true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(!_type.isByteArray(), "");
|
m_context << eth::Instruction::DUP1 << _type.getLength();
|
||||||
m_context
|
convertLengthToSize(_type);
|
||||||
<< eth::Instruction::DUP1 << u256(_type.getLength())
|
m_context << eth::Instruction::ADD << eth::Instruction::SWAP1;
|
||||||
<< u256(_type.getBaseType()->getStorageSize())
|
if (_type.getBaseType()->getStorageBytes() < 32)
|
||||||
<< eth::Instruction::MUL << eth::Instruction::ADD << eth::Instruction::SWAP1;
|
clearStorageLoop(IntegerType(256));
|
||||||
clearStorageLoop(*_type.getBaseType());
|
else
|
||||||
|
clearStorageLoop(*_type.getBaseType());
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
|
solAssert(m_context.getStackHeight() == stackHeightStart - 2, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
||||||
@ -187,6 +286,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
|
|
||||||
|
unsigned stackHeightStart = m_context.getStackHeight();
|
||||||
// fetch length
|
// fetch length
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
||||||
// set length to zero
|
// set length to zero
|
||||||
@ -196,23 +296,27 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
// compute data positions
|
// compute data positions
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
// stack: len data_pos (len is in slots for byte array and in items for other arrays)
|
// stack: len data_pos
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2 << eth::Instruction::ADD
|
||||||
<< eth::Instruction::SWAP1;
|
<< eth::Instruction::SWAP1;
|
||||||
// stack: data_pos_end data_pos
|
// stack: data_pos_end data_pos
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
clearStorageLoop(IntegerType(256));
|
||||||
else
|
else
|
||||||
clearStorageLoop(*_type.getBaseType());
|
clearStorageLoop(*_type.getBaseType());
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
|
solAssert(m_context.getStackHeight() == stackHeightStart - 1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
|
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
|
||||||
{
|
{
|
||||||
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
|
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
|
||||||
|
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
|
|
||||||
|
unsigned stackHeightStart = m_context.getStackHeight();
|
||||||
eth::AssemblyItem resizeEnd = m_context.newTag();
|
eth::AssemblyItem resizeEnd = m_context.newTag();
|
||||||
|
|
||||||
// stack: ref new_length
|
// stack: ref new_length
|
||||||
@ -240,7 +344,7 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
|
|||||||
// stack: ref new_length data_pos new_size delete_end
|
// stack: ref new_length data_pos new_size delete_end
|
||||||
m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD;
|
m_context << eth::Instruction::SWAP2 << eth::Instruction::ADD;
|
||||||
// stack: ref new_length delete_end delete_start
|
// stack: ref new_length delete_end delete_start
|
||||||
if (_type.isByteArray())
|
if (_type.isByteArray() || _type.getBaseType()->getStorageBytes() < 32)
|
||||||
clearStorageLoop(IntegerType(256));
|
clearStorageLoop(IntegerType(256));
|
||||||
else
|
else
|
||||||
clearStorageLoop(*_type.getBaseType());
|
clearStorageLoop(*_type.getBaseType());
|
||||||
@ -248,10 +352,12 @@ void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
|
|||||||
m_context << resizeEnd;
|
m_context << resizeEnd;
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::POP << eth::Instruction::POP;
|
||||||
|
solAssert(m_context.getStackHeight() == stackHeightStart - 2, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::clearStorageLoop(Type const& _type) const
|
void ArrayUtils::clearStorageLoop(Type const& _type) const
|
||||||
{
|
{
|
||||||
|
unsigned stackHeightStart = m_context.getStackHeight();
|
||||||
if (_type.getCategory() == Type::Category::Mapping)
|
if (_type.getCategory() == Type::Category::Mapping)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
@ -266,13 +372,16 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
|
|||||||
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
|
eth::AssemblyItem zeroLoopEnd = m_context.newTag();
|
||||||
m_context.appendConditionalJumpTo(zeroLoopEnd);
|
m_context.appendConditionalJumpTo(zeroLoopEnd);
|
||||||
// delete
|
// delete
|
||||||
|
m_context << u256(0);
|
||||||
StorageItem(m_context, _type).setToZero(SourceLocation(), false);
|
StorageItem(m_context, _type).setToZero(SourceLocation(), false);
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
// increment
|
// increment
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
m_context << u256(1) << eth::Instruction::ADD;
|
||||||
m_context.appendJumpTo(loopStart);
|
m_context.appendJumpTo(loopStart);
|
||||||
// cleanup
|
// cleanup
|
||||||
m_context << zeroLoopEnd;
|
m_context << zeroLoopEnd;
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
|
solAssert(m_context.getStackHeight() == stackHeightStart - 1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
|
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
|
||||||
@ -282,7 +391,20 @@ void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) con
|
|||||||
if (_arrayType.isByteArray())
|
if (_arrayType.isByteArray())
|
||||||
m_context << u256(31) << eth::Instruction::ADD
|
m_context << u256(31) << eth::Instruction::ADD
|
||||||
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
<< u256(32) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
else if (_arrayType.getBaseType()->getStorageSize() > 1)
|
else if (_arrayType.getBaseType()->getStorageSize() <= 1)
|
||||||
|
{
|
||||||
|
unsigned baseBytes = _arrayType.getBaseType()->getStorageBytes();
|
||||||
|
if (baseBytes == 0)
|
||||||
|
m_context << eth::Instruction::POP << u256(1);
|
||||||
|
else if (baseBytes <= 16)
|
||||||
|
{
|
||||||
|
unsigned itemsPerSlot = 32 / baseBytes;
|
||||||
|
m_context
|
||||||
|
<< u256(itemsPerSlot - 1) << eth::Instruction::ADD
|
||||||
|
<< u256(itemsPerSlot) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
|
m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -318,3 +440,143 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
|
||||||
|
{
|
||||||
|
ArrayType::Location location = _arrayType.getLocation();
|
||||||
|
eth::Instruction load =
|
||||||
|
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
|
||||||
|
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
|
||||||
|
eth::Instruction::CALLDATALOAD;
|
||||||
|
|
||||||
|
// retrieve length
|
||||||
|
if (!_arrayType.isDynamicallySized())
|
||||||
|
m_context << _arrayType.getLength();
|
||||||
|
else if (location == ArrayType::Location::CallData)
|
||||||
|
// length is stored on the stack
|
||||||
|
m_context << eth::Instruction::SWAP1;
|
||||||
|
else
|
||||||
|
m_context << eth::Instruction::DUP2 << load;
|
||||||
|
// stack: <base_ref> <index> <length>
|
||||||
|
// check out-of-bounds access
|
||||||
|
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
|
||||||
|
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
|
||||||
|
// out-of-bounds access throws exception (just STOP for now)
|
||||||
|
m_context << eth::Instruction::STOP;
|
||||||
|
|
||||||
|
m_context << legalAccess;
|
||||||
|
// stack: <base_ref> <index>
|
||||||
|
if (_arrayType.isByteArray())
|
||||||
|
switch (location)
|
||||||
|
{
|
||||||
|
case ArrayType::Location::Storage:
|
||||||
|
// byte array index storage lvalue on stack (goal):
|
||||||
|
// <ref> <byte_number> = <base_ref + index / 32> <index % 32>
|
||||||
|
m_context << u256(32) << eth::Instruction::SWAP2;
|
||||||
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
|
// stack: 32 index data_ref
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
|
||||||
|
<< eth::Instruction::DIV << eth::Instruction::ADD
|
||||||
|
// stack: 32 index (data_ref + index / 32)
|
||||||
|
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
|
||||||
|
<< eth::Instruction::MOD;
|
||||||
|
break;
|
||||||
|
case ArrayType::Location::CallData:
|
||||||
|
// no lvalue, just retrieve the value
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::ADD << eth::Instruction::CALLDATALOAD
|
||||||
|
<< ((u256(0xff) << (256 - 8))) << eth::Instruction::AND;
|
||||||
|
break;
|
||||||
|
case ArrayType::Location::Memory:
|
||||||
|
solAssert(false, "Memory lvalues not yet implemented.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// stack: <base_ref> <index>
|
||||||
|
m_context << eth::Instruction::SWAP1;
|
||||||
|
if (_arrayType.isDynamicallySized())
|
||||||
|
{
|
||||||
|
if (location == ArrayType::Location::Storage)
|
||||||
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
|
else if (location == ArrayType::Location::Memory)
|
||||||
|
m_context << u256(32) << eth::Instruction::ADD;
|
||||||
|
}
|
||||||
|
// stack: <index> <data_ref>
|
||||||
|
switch (location)
|
||||||
|
{
|
||||||
|
case ArrayType::Location::CallData:
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::SWAP1 << _arrayType.getBaseType()->getCalldataEncodedSize()
|
||||||
|
<< eth::Instruction::MUL << eth::Instruction::ADD;
|
||||||
|
if (_arrayType.getBaseType()->isValueType())
|
||||||
|
CompilerUtils(m_context).loadFromMemoryDynamic(*_arrayType.getBaseType(), true, true, false);
|
||||||
|
break;
|
||||||
|
case ArrayType::Location::Storage:
|
||||||
|
m_context << eth::Instruction::SWAP1;
|
||||||
|
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
|
||||||
|
{
|
||||||
|
// stack: <data_ref> <index>
|
||||||
|
// goal:
|
||||||
|
// <ref> <byte_number> = <base_ref + index / itemsPerSlot> <(index % itemsPerSlot) * byteSize>
|
||||||
|
unsigned byteSize = _arrayType.getBaseType()->getStorageBytes();
|
||||||
|
solAssert(byteSize != 0, "");
|
||||||
|
unsigned itemsPerSlot = 32 / byteSize;
|
||||||
|
m_context << u256(itemsPerSlot) << eth::Instruction::SWAP2;
|
||||||
|
// stack: itemsPerSlot index data_ref
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
|
||||||
|
<< eth::Instruction::DIV << eth::Instruction::ADD
|
||||||
|
// stack: itemsPerSlot index (data_ref + index / itemsPerSlot)
|
||||||
|
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1
|
||||||
|
<< eth::Instruction::MOD
|
||||||
|
<< u256(byteSize) << eth::Instruction::MUL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_arrayType.getBaseType()->getStorageSize() != 1)
|
||||||
|
m_context << _arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL;
|
||||||
|
m_context << eth::Instruction::ADD << u256(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ArrayType::Location::Memory:
|
||||||
|
solAssert(false, "Memory lvalues not yet implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const
|
||||||
|
{
|
||||||
|
solAssert(_byteSize < 32, "");
|
||||||
|
solAssert(_byteSize != 0, "");
|
||||||
|
// We do the following, but avoiding jumps:
|
||||||
|
// byteOffset += byteSize
|
||||||
|
// if (byteOffset + byteSize > 32)
|
||||||
|
// {
|
||||||
|
// storageOffset++;
|
||||||
|
// byteOffset = 0;
|
||||||
|
// }
|
||||||
|
if (_byteOffsetPosition > 1)
|
||||||
|
m_context << eth::swapInstruction(_byteOffsetPosition - 1);
|
||||||
|
m_context << u256(_byteSize) << eth::Instruction::ADD;
|
||||||
|
if (_byteOffsetPosition > 1)
|
||||||
|
m_context << eth::swapInstruction(_byteOffsetPosition - 1);
|
||||||
|
// compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
|
||||||
|
m_context
|
||||||
|
<< u256(32) << eth::dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
|
||||||
|
<< eth::Instruction::ADD << eth::Instruction::DIV;
|
||||||
|
// increment storage offset if X == 1 (just add X to it)
|
||||||
|
// stack: X
|
||||||
|
m_context
|
||||||
|
<< eth::swapInstruction(_storageOffsetPosition) << eth::dupInstruction(_storageOffsetPosition + 1)
|
||||||
|
<< eth::Instruction::ADD << eth::swapInstruction(_storageOffsetPosition);
|
||||||
|
// stack: X
|
||||||
|
// set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
|
||||||
|
m_context << u256(1) << eth::Instruction::SUB;
|
||||||
|
// stack: 1 - X
|
||||||
|
if (_byteOffsetPosition == 1)
|
||||||
|
m_context << eth::Instruction::MUL;
|
||||||
|
else
|
||||||
|
m_context
|
||||||
|
<< eth::dupInstruction(_byteOffsetPosition + 1) << eth::Instruction::MUL
|
||||||
|
<< eth::swapInstruction(_byteOffsetPosition) << eth::Instruction::POP;
|
||||||
|
}
|
||||||
|
24
ArrayUtils.h
24
ArrayUtils.h
@ -41,19 +41,19 @@ public:
|
|||||||
|
|
||||||
/// Copies an array to an array in storage. The arrays can be of different types only if
|
/// Copies an array to an array in storage. The arrays can be of different types only if
|
||||||
/// their storage representation is the same.
|
/// their storage representation is the same.
|
||||||
/// Stack pre: [source_reference] target_reference
|
/// Stack pre: source_reference [source_byte_offset/source_length] target_reference target_byte_offset
|
||||||
/// Stack post: target_reference
|
/// Stack post: target_reference target_byte_offset
|
||||||
void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
|
void copyArrayToStorage(ArrayType const& _targetType, ArrayType const& _sourceType) const;
|
||||||
/// Clears the given dynamic or static array.
|
/// Clears the given dynamic or static array.
|
||||||
/// Stack pre: reference
|
/// Stack pre: storage_ref storage_byte_offset
|
||||||
/// Stack post:
|
/// Stack post:
|
||||||
void clearArray(ArrayType const& _type) const;
|
void clearArray(ArrayType const& _type) const;
|
||||||
/// Clears the length and data elements of the array referenced on the stack.
|
/// Clears the length and data elements of the array referenced on the stack.
|
||||||
/// Stack pre: reference
|
/// Stack pre: reference (excludes byte offset)
|
||||||
/// Stack post:
|
/// Stack post:
|
||||||
void clearDynamicArray(ArrayType const& _type) const;
|
void clearDynamicArray(ArrayType const& _type) const;
|
||||||
/// Changes the size of a dynamic array and clears the tail if it is shortened.
|
/// Changes the size of a dynamic array and clears the tail if it is shortened.
|
||||||
/// Stack pre: reference new_length
|
/// Stack pre: reference (excludes byte offset) new_length
|
||||||
/// Stack post:
|
/// Stack post:
|
||||||
void resizeDynamicArray(ArrayType const& _type) const;
|
void resizeDynamicArray(ArrayType const& _type) const;
|
||||||
/// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
|
/// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
|
||||||
@ -67,11 +67,23 @@ public:
|
|||||||
void convertLengthToSize(ArrayType const& _arrayType, bool _pad = false) const;
|
void convertLengthToSize(ArrayType const& _arrayType, bool _pad = false) const;
|
||||||
/// Retrieves the length (number of elements) of the array ref on the stack. This also
|
/// Retrieves the length (number of elements) of the array ref on the stack. This also
|
||||||
/// works for statically-sized arrays.
|
/// works for statically-sized arrays.
|
||||||
/// Stack pre: reference
|
/// Stack pre: reference (excludes byte offset for dynamic storage arrays)
|
||||||
/// Stack post: reference length
|
/// Stack post: reference length
|
||||||
void retrieveLength(ArrayType const& _arrayType) const;
|
void retrieveLength(ArrayType const& _arrayType) const;
|
||||||
|
/// Retrieves the value at a specific index. If the location is storage, only retrieves the
|
||||||
|
/// position.
|
||||||
|
/// Stack pre: reference [length] index
|
||||||
|
/// Stack post for storage: slot byte_offset
|
||||||
|
/// Stack post for calldata: value
|
||||||
|
void accessIndex(ArrayType const& _arrayType) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Adds the given number of bytes to a storage byte offset counter and also increments
|
||||||
|
/// the storage offset if adding this number again would increase the counter over 32.
|
||||||
|
/// @param byteOffsetPosition the stack offset of the storage byte offset
|
||||||
|
/// @param storageOffsetPosition the stack offset of the storage slot offset
|
||||||
|
void incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPosition, unsigned _storageOffsetPosition) const;
|
||||||
|
|
||||||
CompilerContext& m_context;
|
CompilerContext& m_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
46
Compiler.cpp
46
Compiler.cpp
@ -20,12 +20,12 @@
|
|||||||
* Solidity compiler.
|
* Solidity compiler.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/Compiler.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmcore/Instruction.h>
|
||||||
#include <libevmcore/Assembly.h>
|
#include <libevmcore/Assembly.h>
|
||||||
#include <libsolidity/AST.h>
|
#include <libsolidity/AST.h>
|
||||||
#include <libsolidity/Compiler.h>
|
|
||||||
#include <libsolidity/ExpressionCompiler.h>
|
#include <libsolidity/ExpressionCompiler.h>
|
||||||
#include <libsolidity/CompilerUtils.h>
|
#include <libsolidity/CompilerUtils.h>
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ void Compiler::packIntoContractCreator(ContractDefinition const& _contract, Comp
|
|||||||
|
|
||||||
void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_constructor);
|
CompilerContext::LocationSetter locationSetter(m_context, _constructor);
|
||||||
FunctionType constructorType(_constructor);
|
FunctionType constructorType(_constructor);
|
||||||
if (!constructorType.getParameterTypes().empty())
|
if (!constructorType.getParameterTypes().empty())
|
||||||
{
|
{
|
||||||
@ -146,7 +146,7 @@ void Compiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
|||||||
|
|
||||||
void Compiler::appendConstructor(FunctionDefinition const& _constructor)
|
void Compiler::appendConstructor(FunctionDefinition const& _constructor)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_constructor);
|
CompilerContext::LocationSetter locationSetter(m_context, _constructor);
|
||||||
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
||||||
unsigned argumentSize = 0;
|
unsigned argumentSize = 0;
|
||||||
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
for (ASTPointer<VariableDeclaration> const& var: _constructor.getParameters())
|
||||||
@ -192,10 +192,12 @@ void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
|
|||||||
for (auto const& it: interfaceFunctions)
|
for (auto const& it: interfaceFunctions)
|
||||||
{
|
{
|
||||||
FunctionTypePointer const& functionType = it.second;
|
FunctionTypePointer const& functionType = it.second;
|
||||||
|
solAssert(functionType->hasDeclaration(), "");
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, functionType->getDeclaration());
|
||||||
m_context << callDataUnpackerEntryPoints.at(it.first);
|
m_context << callDataUnpackerEntryPoints.at(it.first);
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||||
appendCalldataUnpacker(functionType->getParameterTypes());
|
appendCalldataUnpacker(functionType->getParameterTypes());
|
||||||
m_context.appendJumpTo(m_context.getFunctionEntryLabel(it.second->getDeclaration()));
|
m_context.appendJumpTo(m_context.getFunctionEntryLabel(functionType->getDeclaration()));
|
||||||
m_context << returnTag;
|
m_context << returnTag;
|
||||||
appendReturnValuePacker(functionType->getReturnParameterTypes());
|
appendReturnValuePacker(functionType->getReturnParameterTypes());
|
||||||
}
|
}
|
||||||
@ -226,6 +228,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool
|
|||||||
{
|
{
|
||||||
// Retrieve data start offset by adding length to start offset of previous dynamic type
|
// Retrieve data start offset by adding length to start offset of previous dynamic type
|
||||||
unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument;
|
unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument;
|
||||||
|
solAssert(stackDepth <= 16, "Stack too deep.");
|
||||||
m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth);
|
m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth);
|
||||||
ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true);
|
ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true);
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << eth::Instruction::ADD;
|
||||||
@ -271,22 +274,21 @@ void Compiler::appendReturnValuePacker(TypePointers const& _typeParameters)
|
|||||||
|
|
||||||
void Compiler::registerStateVariables(ContractDefinition const& _contract)
|
void Compiler::registerStateVariables(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts()))
|
for (auto const& var: ContractType(_contract).getStateVariables())
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
|
m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var));
|
||||||
m_context.addStateVariable(*variable);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::initializeStateVariables(ContractDefinition const& _contract)
|
void Compiler::initializeStateVariables(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
|
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
|
||||||
if (variable->getValue())
|
if (variable->getValue() && !variable->isConstant())
|
||||||
ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable);
|
ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
|
bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
|
||||||
{
|
{
|
||||||
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
|
solAssert(_variableDeclaration.isStateVariable(), "Compiler visit to non-state variable declaration.");
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_variableDeclaration);
|
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclaration);
|
||||||
|
|
||||||
m_context.startFunction(_variableDeclaration);
|
m_context.startFunction(_variableDeclaration);
|
||||||
m_breakTags.clear();
|
m_breakTags.clear();
|
||||||
@ -300,7 +302,7 @@ bool Compiler::visit(VariableDeclaration const& _variableDeclaration)
|
|||||||
|
|
||||||
bool Compiler::visit(FunctionDefinition const& _function)
|
bool Compiler::visit(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_function);
|
CompilerContext::LocationSetter locationSetter(m_context, _function);
|
||||||
//@todo to simplify this, the calling convention could by changed such that
|
//@todo to simplify this, the calling convention could by changed such that
|
||||||
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
|
// caller puts: [retarg0] ... [retargm] [return address] [arg0] ... [argn]
|
||||||
// although note that this reduces the size of the visible stack
|
// although note that this reduces the size of the visible stack
|
||||||
@ -357,6 +359,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
|||||||
stackLayout.push_back(i);
|
stackLayout.push_back(i);
|
||||||
stackLayout += vector<int>(c_localVariablesSize, -1);
|
stackLayout += vector<int>(c_localVariablesSize, -1);
|
||||||
|
|
||||||
|
solAssert(stackLayout.size() <= 17, "Stack too deep.");
|
||||||
while (stackLayout.back() != int(stackLayout.size() - 1))
|
while (stackLayout.back() != int(stackLayout.size() - 1))
|
||||||
if (stackLayout.back() < 0)
|
if (stackLayout.back() < 0)
|
||||||
{
|
{
|
||||||
@ -376,15 +379,16 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
|||||||
m_context.removeVariable(*localVariable);
|
m_context.removeVariable(*localVariable);
|
||||||
|
|
||||||
m_context.adjustStackOffset(-(int)c_returnValuesSize);
|
m_context.adjustStackOffset(-(int)c_returnValuesSize);
|
||||||
|
|
||||||
if (!_function.isConstructor())
|
if (!_function.isConstructor())
|
||||||
m_context << eth::Instruction::JUMP;
|
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Compiler::visit(IfStatement const& _ifStatement)
|
bool Compiler::visit(IfStatement const& _ifStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_ifStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _ifStatement);
|
||||||
compileExpression(_ifStatement.getCondition());
|
compileExpression(_ifStatement.getCondition());
|
||||||
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
|
eth::AssemblyItem trueTag = m_context.appendConditionalJump();
|
||||||
if (_ifStatement.getFalseStatement())
|
if (_ifStatement.getFalseStatement())
|
||||||
@ -401,7 +405,7 @@ bool Compiler::visit(IfStatement const& _ifStatement)
|
|||||||
bool Compiler::visit(WhileStatement const& _whileStatement)
|
bool Compiler::visit(WhileStatement const& _whileStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_whileStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _whileStatement);
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
eth::AssemblyItem loopStart = m_context.newTag();
|
||||||
eth::AssemblyItem loopEnd = m_context.newTag();
|
eth::AssemblyItem loopEnd = m_context.newTag();
|
||||||
m_continueTags.push_back(loopStart);
|
m_continueTags.push_back(loopStart);
|
||||||
@ -427,7 +431,7 @@ bool Compiler::visit(WhileStatement const& _whileStatement)
|
|||||||
bool Compiler::visit(ForStatement const& _forStatement)
|
bool Compiler::visit(ForStatement const& _forStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_forStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _forStatement);
|
||||||
eth::AssemblyItem loopStart = m_context.newTag();
|
eth::AssemblyItem loopStart = m_context.newTag();
|
||||||
eth::AssemblyItem loopEnd = m_context.newTag();
|
eth::AssemblyItem loopEnd = m_context.newTag();
|
||||||
m_continueTags.push_back(loopStart);
|
m_continueTags.push_back(loopStart);
|
||||||
@ -464,7 +468,7 @@ bool Compiler::visit(ForStatement const& _forStatement)
|
|||||||
|
|
||||||
bool Compiler::visit(Continue const& _continueStatement)
|
bool Compiler::visit(Continue const& _continueStatement)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_continueStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _continueStatement);
|
||||||
if (!m_continueTags.empty())
|
if (!m_continueTags.empty())
|
||||||
m_context.appendJumpTo(m_continueTags.back());
|
m_context.appendJumpTo(m_continueTags.back());
|
||||||
return false;
|
return false;
|
||||||
@ -472,7 +476,7 @@ bool Compiler::visit(Continue const& _continueStatement)
|
|||||||
|
|
||||||
bool Compiler::visit(Break const& _breakStatement)
|
bool Compiler::visit(Break const& _breakStatement)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_breakStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _breakStatement);
|
||||||
if (!m_breakTags.empty())
|
if (!m_breakTags.empty())
|
||||||
m_context.appendJumpTo(m_breakTags.back());
|
m_context.appendJumpTo(m_breakTags.back());
|
||||||
return false;
|
return false;
|
||||||
@ -480,7 +484,7 @@ bool Compiler::visit(Break const& _breakStatement)
|
|||||||
|
|
||||||
bool Compiler::visit(Return const& _return)
|
bool Compiler::visit(Return const& _return)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_return);
|
CompilerContext::LocationSetter locationSetter(m_context, _return);
|
||||||
//@todo modifications are needed to make this work with functions returning multiple values
|
//@todo modifications are needed to make this work with functions returning multiple values
|
||||||
if (Expression const* expression = _return.getExpression())
|
if (Expression const* expression = _return.getExpression())
|
||||||
{
|
{
|
||||||
@ -499,7 +503,7 @@ bool Compiler::visit(Return const& _return)
|
|||||||
bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_variableDeclarationStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
|
||||||
if (Expression const* expression = _variableDeclarationStatement.getExpression())
|
if (Expression const* expression = _variableDeclarationStatement.getExpression())
|
||||||
{
|
{
|
||||||
compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType());
|
compileExpression(*expression, _variableDeclarationStatement.getDeclaration().getType());
|
||||||
@ -512,7 +516,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
|
|||||||
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_expressionStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement);
|
||||||
Expression const& expression = _expressionStatement.getExpression();
|
Expression const& expression = _expressionStatement.getExpression();
|
||||||
compileExpression(expression);
|
compileExpression(expression);
|
||||||
CompilerUtils(m_context).popStackElement(*expression.getType());
|
CompilerUtils(m_context).popStackElement(*expression.getType());
|
||||||
@ -523,7 +527,7 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
|||||||
bool Compiler::visit(PlaceholderStatement const& _placeholderStatement)
|
bool Compiler::visit(PlaceholderStatement const& _placeholderStatement)
|
||||||
{
|
{
|
||||||
StackHeightChecker checker(m_context);
|
StackHeightChecker checker(m_context);
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_placeholderStatement);
|
CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement);
|
||||||
++m_modifierDepth;
|
++m_modifierDepth;
|
||||||
appendModifierOrFunctionCode();
|
appendModifierOrFunctionCode();
|
||||||
--m_modifierDepth;
|
--m_modifierDepth;
|
||||||
@ -550,7 +554,7 @@ void Compiler::appendModifierOrFunctionCode()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName());
|
ModifierDefinition const& modifier = m_context.getFunctionModifier(modifierInvocation->getName()->getName());
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &modifier);
|
CompilerContext::LocationSetter locationSetter(m_context, modifier);
|
||||||
solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), "");
|
solAssert(modifier.getParameters().size() == modifierInvocation->getArguments().size(), "");
|
||||||
for (unsigned i = 0; i < modifier.getParameters().size(); ++i)
|
for (unsigned i = 0; i < modifier.getParameters().size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -94,8 +94,8 @@ private:
|
|||||||
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
||||||
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
|
eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement
|
||||||
unsigned m_modifierDepth = 0;
|
unsigned m_modifierDepth = 0;
|
||||||
FunctionDefinition const* m_currentFunction;
|
FunctionDefinition const* m_currentFunction = nullptr;
|
||||||
unsigned m_stackCleanupForReturn; ///< this number of stack elements need to be removed before jump to m_returnTag
|
unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
|
||||||
// arguments for base constructors, filled in derived-to-base order
|
// arguments for base constructors, filled in derived-to-base order
|
||||||
std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
|
std::map<FunctionDefinition const*, std::vector<ASTPointer<Expression>> const*> m_baseArguments;
|
||||||
};
|
};
|
||||||
|
@ -37,15 +37,13 @@ void CompilerContext::addMagicGlobal(MagicVariableDeclaration const& _declaratio
|
|||||||
m_magicGlobals.insert(&_declaration);
|
m_magicGlobals.insert(&_declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::addStateVariable(VariableDeclaration const& _declaration)
|
void CompilerContext::addStateVariable(
|
||||||
|
VariableDeclaration const& _declaration,
|
||||||
|
u256 const& _storageOffset,
|
||||||
|
unsigned _byteOffset
|
||||||
|
)
|
||||||
{
|
{
|
||||||
m_stateVariables[&_declaration] = m_stateVariablesSize;
|
m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset);
|
||||||
bigint newSize = bigint(m_stateVariablesSize) + _declaration.getType()->getStorageSize();
|
|
||||||
if (newSize >= bigint(1) << 256)
|
|
||||||
BOOST_THROW_EXCEPTION(TypeError()
|
|
||||||
<< errinfo_comment("State variable does not fit in storage.")
|
|
||||||
<< errinfo_sourceLocation(_declaration.getLocation()));
|
|
||||||
m_stateVariablesSize = u256(newSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::startFunction(Declaration const& _function)
|
void CompilerContext::startFunction(Declaration const& _function)
|
||||||
@ -69,7 +67,7 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
|
|||||||
|
|
||||||
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
|
void CompilerContext::addAndInitializeVariable(VariableDeclaration const& _declaration)
|
||||||
{
|
{
|
||||||
LocationSetter locationSetter(*this, &_declaration);
|
LocationSetter locationSetter(*this, _declaration);
|
||||||
addVariable(_declaration);
|
addVariable(_declaration);
|
||||||
int const size = _declaration.getType()->getSizeOnStack();
|
int const size = _declaration.getType()->getSizeOnStack();
|
||||||
for (int i = 0; i < size; ++i)
|
for (int i = 0; i < size; ++i)
|
||||||
@ -174,46 +172,26 @@ unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
|
|||||||
return m_asm.deposit() - _offset - 1;
|
return m_asm.deposit() - _offset - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
pair<u256, unsigned> CompilerContext::getStorageLocationOfVariable(const Declaration& _declaration) const
|
||||||
{
|
{
|
||||||
auto it = m_stateVariables.find(&_declaration);
|
auto it = m_stateVariables.find(&_declaration);
|
||||||
solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
|
solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompilerContext& CompilerContext::appendJump(eth::AssemblyItem::JumpType _jumpType)
|
||||||
|
{
|
||||||
|
eth::AssemblyItem item(eth::Instruction::JUMP);
|
||||||
|
item.setJumpType(_jumpType);
|
||||||
|
return *this << item;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerContext::resetVisitedNodes(ASTNode const* _node)
|
void CompilerContext::resetVisitedNodes(ASTNode const* _node)
|
||||||
{
|
{
|
||||||
stack<ASTNode const*> newStack;
|
stack<ASTNode const*> newStack;
|
||||||
newStack.push(_node);
|
newStack.push(_node);
|
||||||
std::swap(m_visitedNodes, newStack);
|
std::swap(m_visitedNodes, newStack);
|
||||||
}
|
updateSourceLocation();
|
||||||
|
|
||||||
CompilerContext& CompilerContext::operator<<(eth::AssemblyItem const& _item)
|
|
||||||
{
|
|
||||||
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
|
|
||||||
m_asm.append(_item, m_visitedNodes.top()->getLocation());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerContext& CompilerContext::operator<<(eth::Instruction _instruction)
|
|
||||||
{
|
|
||||||
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
|
|
||||||
m_asm.append(_instruction, m_visitedNodes.top()->getLocation());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerContext& CompilerContext::operator<<(u256 const& _value)
|
|
||||||
{
|
|
||||||
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
|
|
||||||
m_asm.append(_value, m_visitedNodes.top()->getLocation());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompilerContext& CompilerContext::operator<<(bytes const& _data)
|
|
||||||
{
|
|
||||||
solAssert(!m_visitedNodes.empty(), "No node on the visited stack");
|
|
||||||
m_asm.append(_data, m_visitedNodes.top()->getLocation());
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const
|
vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContract(ContractDefinition const& _contract) const
|
||||||
@ -224,5 +202,10 @@ vector<ContractDefinition const*>::const_iterator CompilerContext::getSuperContr
|
|||||||
return ++it;
|
return ++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerContext::updateSourceLocation()
|
||||||
|
{
|
||||||
|
m_asm.setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <utility>
|
||||||
#include <libevmcore/Instruction.h>
|
#include <libevmcore/Instruction.h>
|
||||||
#include <libevmcore/Assembly.h>
|
#include <libevmcore/Assembly.h>
|
||||||
#include <libsolidity/ASTForward.h>
|
#include <libsolidity/ASTForward.h>
|
||||||
@ -42,7 +43,7 @@ class CompilerContext
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
||||||
void addStateVariable(VariableDeclaration const& _declaration);
|
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
||||||
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
||||||
void removeVariable(VariableDeclaration const& _declaration);
|
void removeVariable(VariableDeclaration const& _declaration);
|
||||||
void addAndInitializeVariable(VariableDeclaration const& _declaration);
|
void addAndInitializeVariable(VariableDeclaration const& _declaration);
|
||||||
@ -82,7 +83,7 @@ public:
|
|||||||
/// Converts an offset relative to the current stack height to a value that can be used later
|
/// Converts an offset relative to the current stack height to a value that can be used later
|
||||||
/// with baseToCurrentStackOffset to point to the same stack element.
|
/// with baseToCurrentStackOffset to point to the same stack element.
|
||||||
unsigned currentToBaseStackOffset(unsigned _offset) const;
|
unsigned currentToBaseStackOffset(unsigned _offset) const;
|
||||||
u256 getStorageLocationOfVariable(Declaration const& _declaration) const;
|
std::pair<u256, unsigned> getStorageLocationOfVariable(Declaration const& _declaration) const;
|
||||||
|
|
||||||
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
||||||
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
||||||
@ -91,7 +92,7 @@ public:
|
|||||||
/// Appends a JUMP to a new tag and @returns the tag
|
/// Appends a JUMP to a new tag and @returns the tag
|
||||||
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
|
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
|
||||||
/// Appends a JUMP to a tag already on the stack
|
/// Appends a JUMP to a tag already on the stack
|
||||||
CompilerContext& appendJump() { return *this << eth::Instruction::JUMP; }
|
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
|
||||||
/// Appends a JUMP to a specific tag
|
/// Appends a JUMP to a specific tag
|
||||||
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
|
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
|
||||||
/// Appends pushing of a new tag and @returns the new tag.
|
/// Appends pushing of a new tag and @returns the new tag.
|
||||||
@ -108,19 +109,19 @@ public:
|
|||||||
/// Resets the stack of visited nodes with a new stack having only @c _node
|
/// Resets the stack of visited nodes with a new stack having only @c _node
|
||||||
void resetVisitedNodes(ASTNode const* _node);
|
void resetVisitedNodes(ASTNode const* _node);
|
||||||
/// Pops the stack of visited nodes
|
/// Pops the stack of visited nodes
|
||||||
void popVisitedNodes() { m_visitedNodes.pop(); }
|
void popVisitedNodes() { m_visitedNodes.pop(); updateSourceLocation(); }
|
||||||
/// Pushes an ASTNode to the stack of visited nodes
|
/// Pushes an ASTNode to the stack of visited nodes
|
||||||
void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); }
|
void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
|
||||||
|
|
||||||
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
||||||
CompilerContext& operator<<(eth::AssemblyItem const& _item);
|
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
|
||||||
CompilerContext& operator<<(eth::Instruction _instruction);
|
CompilerContext& operator<<(eth::Instruction _instruction) { m_asm.append(_instruction); return *this; }
|
||||||
CompilerContext& operator<<(u256 const& _value);
|
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
|
||||||
CompilerContext& operator<<(bytes const& _data);
|
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
|
||||||
|
|
||||||
eth::Assembly const& getAssembly() const { return m_asm; }
|
eth::Assembly const& getAssembly() const { return m_asm; }
|
||||||
/// @arg _sourceCodes is the map of input files to source code strings
|
/// @arg _sourceCodes is the map of input files to source code strings
|
||||||
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.streamRLP(_stream, "", _sourceCodes); }
|
void streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap()) const { m_asm.stream(_stream, "", _sourceCodes); }
|
||||||
|
|
||||||
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
|
bytes getAssembledBytecode(bool _optimize = false) { return m_asm.optimise(_optimize).assemble(); }
|
||||||
|
|
||||||
@ -130,22 +131,22 @@ public:
|
|||||||
class LocationSetter: public ScopeGuard
|
class LocationSetter: public ScopeGuard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LocationSetter(CompilerContext& _compilerContext, ASTNode const* _node):
|
LocationSetter(CompilerContext& _compilerContext, ASTNode const& _node):
|
||||||
ScopeGuard(std::bind(&CompilerContext::popVisitedNodes, _compilerContext)) { _compilerContext.pushVisitedNodes(_node); }
|
ScopeGuard([&]{ _compilerContext.popVisitedNodes(); }) { _compilerContext.pushVisitedNodes(&_node); }
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<ContractDefinition const*>::const_iterator getSuperContract(const ContractDefinition &_contract) const;
|
std::vector<ContractDefinition const*>::const_iterator getSuperContract(const ContractDefinition &_contract) const;
|
||||||
|
/// Updates source location set in the assembly.
|
||||||
|
void updateSourceLocation();
|
||||||
|
|
||||||
eth::Assembly m_asm;
|
eth::Assembly m_asm;
|
||||||
/// Magic global variables like msg, tx or this, distinguished by type.
|
/// Magic global variables like msg, tx or this, distinguished by type.
|
||||||
std::set<Declaration const*> m_magicGlobals;
|
std::set<Declaration const*> m_magicGlobals;
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
/// Other already compiled contracts to be used in contract creation calls.
|
||||||
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
|
std::map<ContractDefinition const*, bytes const*> m_compiledContracts;
|
||||||
/// Size of the state variables, offset of next variable to be added.
|
|
||||||
u256 m_stateVariablesSize = 0;
|
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
std::map<Declaration const*, u256> m_stateVariables;
|
std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
/// Offsets of local variables on the stack (relative to stack base).
|
/// Offsets of local variables on the stack (relative to stack base).
|
||||||
std::map<Declaration const*, unsigned> m_localVariables;
|
std::map<Declaration const*, unsigned> m_localVariables;
|
||||||
/// Labels pointing to the entry points of functions.
|
/// Labels pointing to the entry points of functions.
|
||||||
|
@ -41,14 +41,14 @@ namespace solidity
|
|||||||
{
|
{
|
||||||
|
|
||||||
const map<string, string> StandardSources = map<string, string>{
|
const map<string, string> StandardSources = map<string, string>{
|
||||||
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(string3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
|
{"coin", R"(import "CoinReg";import "Config";import "configUser";contract coin is configUser{function coin(bytes3 name, uint denom) {CoinReg(Config(configAddr()).lookup(3)).register(name, denom);}})"},
|
||||||
{"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
|
{"Coin", R"(contract Coin{function isApprovedFor(address _target,address _proxy)constant returns(bool _r){}function isApproved(address _proxy)constant returns(bool _r){}function sendCoinFrom(address _from,uint256 _val,address _to){}function coinBalanceOf(address _a)constant returns(uint256 _r){}function sendCoin(uint256 _val,address _to){}function coinBalance()constant returns(uint256 _r){}function approve(address _a){}})"},
|
||||||
{"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,string3 name,uint256 denom){}function register(string3 name,uint256 denom){}function unregister(){}})"},
|
{"CoinReg", R"(contract CoinReg{function count()constant returns(uint256 r){}function info(uint256 i)constant returns(address addr,bytes3 name,uint256 denom){}function register(bytes3 name,uint256 denom){}function unregister(){}})"},
|
||||||
{"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
|
{"configUser", R"(contract configUser{function configAddr()constant returns(address a){ return 0xc6d9d2cd449a754c494264e1809c50e34d64562b;}})"},
|
||||||
{"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
|
{"Config", R"(contract Config{function lookup(uint256 service)constant returns(address a){}function kill(){}function unregister(uint256 id){}function register(uint256 id,address service){}})"},
|
||||||
{"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
|
{"mortal", R"(import "owned";contract mortal is owned {function kill() { if (msg.sender == owner) suicide(owner); }})"},
|
||||||
{"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(string32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
|
{"named", R"(import "Config";import "NameReg";import "configUser";contract named is configUser {function named(bytes32 name) {NameReg(Config(configAddr()).lookup(1)).register(name);}})"},
|
||||||
{"NameReg", R"(contract NameReg{function register(string32 name){}function addressOf(string32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(string32 name){}})"},
|
{"NameReg", R"(contract NameReg{function register(bytes32 name){}function addressOf(bytes32 name)constant returns(address addr){}function unregister(){}function nameOf(address addr)constant returns(bytes32 name){}})"},
|
||||||
{"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
|
{"owned", R"(contract owned{function owned(){owner = msg.sender;}modifier onlyowner(){if(msg.sender==owner)_}address owner;})"},
|
||||||
{"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
|
{"service", R"(import "Config";import "configUser";contract service is configUser{function service(uint _n){Config(configAddr()).register(_n, this);}})"},
|
||||||
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
|
{"std", R"(import "owned";import "mortal";import "Config";import "configUser";import "NameReg";import "named";)"}
|
||||||
@ -138,6 +138,8 @@ void CompilerStack::compile(bool _optimize)
|
|||||||
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
|
for (ASTPointer<ASTNode> const& node: source->ast->getNodes())
|
||||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||||
{
|
{
|
||||||
|
if (!contract->isFullyImplemented())
|
||||||
|
continue;
|
||||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
|
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize);
|
||||||
compiler->compileContract(*contract, contractBytecode);
|
compiler->compileContract(*contract, contractBytecode);
|
||||||
Contract& compiledContract = m_contracts[contract->getName()];
|
Contract& compiledContract = m_contracts[contract->getName()];
|
||||||
|
@ -93,6 +93,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented.");
|
solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented.");
|
||||||
|
m_context << eth::Instruction::POP; //@todo
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD;
|
||||||
// stack here: memory_offset storage_offset length_bytes
|
// stack here: memory_offset storage_offset length_bytes
|
||||||
// jump to end if length is zero
|
// jump to end if length is zero
|
||||||
@ -138,6 +139,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
{
|
{
|
||||||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable));
|
||||||
unsigned const size = _variable.getType()->getSizeOnStack();
|
unsigned const size = _variable.getType()->getSizeOnStack();
|
||||||
|
solAssert(stackPosition >= size, "Variable size and position mismatch.");
|
||||||
// move variable starting from its top end in the stack
|
// move variable starting from its top end in the stack
|
||||||
if (stackPosition - size + 1 > 16)
|
if (stackPosition - size + 1 > 16)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation())
|
||||||
@ -148,8 +150,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
|
|
||||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||||
{
|
{
|
||||||
if (_stackDepth > 16)
|
solAssert(_stackDepth <= 16, "Stack too deep.");
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep."));
|
|
||||||
for (unsigned i = 0; i < _itemSize; ++i)
|
for (unsigned i = 0; i < _itemSize; ++i)
|
||||||
m_context << eth::dupInstruction(_stackDepth);
|
m_context << eth::dupInstruction(_stackDepth);
|
||||||
}
|
}
|
||||||
@ -178,7 +179,7 @@ void CompilerUtils::computeHashStatic(Type const& _type, bool _padToWordBoundari
|
|||||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
||||||
{
|
{
|
||||||
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
|
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
|
||||||
bool leftAligned = _type.getCategory() == Type::Category::String;
|
bool leftAligned = _type.getCategory() == Type::Category::FixedBytes;
|
||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
m_context << eth::Instruction::POP << u256(0);
|
m_context << eth::Instruction::POP << u256(0);
|
||||||
else
|
else
|
||||||
@ -198,11 +199,10 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
|
|||||||
return numBytes;
|
return numBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
|
||||||
{
|
{
|
||||||
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
|
unsigned numBytes = _type.getCalldataEncodedSize(_padToWordBoundaries);
|
||||||
bool leftAligned = _type.getCategory() == Type::Category::String;
|
bool leftAligned = _type.getCategory() == Type::Category::FixedBytes;
|
||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
else
|
else
|
||||||
|
@ -48,7 +48,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
|
|||||||
if (!_varDecl.getValue())
|
if (!_varDecl.getValue())
|
||||||
return;
|
return;
|
||||||
solAssert(!!_varDecl.getValue()->getType(), "Type information not available.");
|
solAssert(!!_varDecl.getValue()->getType(), "Type information not available.");
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_varDecl);
|
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
|
||||||
_varDecl.getValue()->accept(*this);
|
_varDecl.getValue()->accept(*this);
|
||||||
appendTypeConversion(*_varDecl.getValue()->getType(), *_varDecl.getType(), true);
|
appendTypeConversion(*_varDecl.getValue()->getType(), *_varDecl.getType(), true);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
|
|||||||
|
|
||||||
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
|
void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_varDecl);
|
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
|
||||||
FunctionType accessorType(_varDecl);
|
FunctionType accessorType(_varDecl);
|
||||||
|
|
||||||
unsigned length = 0;
|
unsigned length = 0;
|
||||||
@ -67,9 +67,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
|
length += CompilerUtils(m_context).storeInMemory(length, *paramType, true);
|
||||||
|
|
||||||
// retrieve the position of the variable
|
// retrieve the position of the variable
|
||||||
m_context << m_context.getStorageLocationOfVariable(_varDecl);
|
auto const& location = m_context.getStorageLocationOfVariable(_varDecl);
|
||||||
TypePointer returnType = _varDecl.getType();
|
m_context << location.first;
|
||||||
|
|
||||||
|
TypePointer returnType = _varDecl.getType();
|
||||||
for (TypePointer const& paramType: paramTypes)
|
for (TypePointer const& paramType: paramTypes)
|
||||||
{
|
{
|
||||||
// move offset to memory
|
// move offset to memory
|
||||||
@ -90,9 +91,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
// struct
|
// struct
|
||||||
for (size_t i = 0; i < names.size(); ++i)
|
for (size_t i = 0; i < names.size(); ++i)
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1
|
if (types[i]->getCategory() == Type::Category::Mapping)
|
||||||
<< structType->getStorageOffsetOfMember(names[i])
|
continue;
|
||||||
<< eth::Instruction::ADD;
|
pair<u256, unsigned> const& offsets = structType->getStorageOffsetsOfMember(names[i]);
|
||||||
|
m_context << eth::Instruction::DUP1 << u256(offsets.first) << eth::Instruction::ADD << u256(offsets.second);
|
||||||
StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *types[i]).retrieveValue(SourceLocation(), true);
|
||||||
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
|
solAssert(types[i]->getSizeOnStack() == 1, "Returning struct elements with stack size != 1 not yet implemented.");
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
@ -104,11 +106,13 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
{
|
{
|
||||||
// simple value
|
// simple value
|
||||||
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
|
solAssert(accessorType.getReturnParameterTypes().size() == 1, "");
|
||||||
|
m_context << u256(location.second);
|
||||||
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
|
StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true);
|
||||||
retSizeOnStack = returnType->getSizeOnStack();
|
retSizeOnStack = returnType->getSizeOnStack();
|
||||||
}
|
}
|
||||||
solAssert(retSizeOnStack <= 15, "Stack too deep.");
|
solAssert(retSizeOnStack <= 15, "Stack too deep.");
|
||||||
m_context << eth::dupInstruction(retSizeOnStack + 1) << eth::Instruction::JUMP;
|
m_context << eth::dupInstruction(retSizeOnStack + 1);
|
||||||
|
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
||||||
@ -122,23 +126,25 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
|||||||
Type::Category stackTypeCategory = _typeOnStack.getCategory();
|
Type::Category stackTypeCategory = _typeOnStack.getCategory();
|
||||||
Type::Category targetTypeCategory = _targetType.getCategory();
|
Type::Category targetTypeCategory = _targetType.getCategory();
|
||||||
|
|
||||||
if (stackTypeCategory == Type::Category::String)
|
switch (stackTypeCategory)
|
||||||
{
|
{
|
||||||
StaticStringType const& typeOnStack = dynamic_cast<StaticStringType const&>(_typeOnStack);
|
case Type::Category::FixedBytes:
|
||||||
|
{
|
||||||
|
FixedBytesType const& typeOnStack = dynamic_cast<FixedBytesType const&>(_typeOnStack);
|
||||||
if (targetTypeCategory == Type::Category::Integer)
|
if (targetTypeCategory == Type::Category::Integer)
|
||||||
{
|
{
|
||||||
// conversion from string to hash. no need to clean the high bit
|
// conversion from bytes to integer. no need to clean the high bit
|
||||||
// only to shift right because of opposite alignment
|
// only to shift right because of opposite alignment
|
||||||
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
|
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
|
||||||
solAssert(targetIntegerType.isHash(), "Only conversion between String and Hash is allowed.");
|
|
||||||
solAssert(targetIntegerType.getNumBits() == typeOnStack.getNumBytes() * 8, "The size should be the same.");
|
|
||||||
m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
m_context << (u256(1) << (256 - typeOnStack.getNumBytes() * 8)) << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
|
if (targetIntegerType.getNumBits() < typeOnStack.getNumBytes() * 8)
|
||||||
|
appendTypeConversion(IntegerType(typeOnStack.getNumBytes() * 8), _targetType, _cleanupNeeded);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// clear lower-order bytes for conversion to shorter strings - we always clean
|
// clear lower-order bytes for conversion to shorter bytes - we always clean
|
||||||
solAssert(targetTypeCategory == Type::Category::String, "Invalid type conversion requested.");
|
solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
|
||||||
StaticStringType const& targetType = dynamic_cast<StaticStringType const&>(_targetType);
|
FixedBytesType const& targetType = dynamic_cast<FixedBytesType const&>(_targetType);
|
||||||
if (targetType.getNumBytes() < typeOnStack.getNumBytes())
|
if (targetType.getNumBytes() < typeOnStack.getNumBytes())
|
||||||
{
|
{
|
||||||
if (targetType.getNumBytes() == 0)
|
if (targetType.getNumBytes() == 0)
|
||||||
@ -150,22 +156,24 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (stackTypeCategory == Type::Category::Enum)
|
break;
|
||||||
solAssert(targetTypeCategory == Type::Category::Integer ||
|
case Type::Category::Enum:
|
||||||
targetTypeCategory == Type::Category::Enum, "");
|
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, "");
|
||||||
else if (stackTypeCategory == Type::Category::Integer ||
|
break;
|
||||||
stackTypeCategory == Type::Category::Contract ||
|
case Type::Category::Integer:
|
||||||
stackTypeCategory == Type::Category::IntegerConstant)
|
case Type::Category::Contract:
|
||||||
{
|
case Type::Category::IntegerConstant:
|
||||||
if (targetTypeCategory == Type::Category::String && stackTypeCategory == Type::Category::Integer)
|
if (targetTypeCategory == Type::Category::FixedBytes)
|
||||||
{
|
{
|
||||||
// conversion from hash to string. no need to clean the high bit
|
solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::IntegerConstant,
|
||||||
|
"Invalid conversion to FixedBytesType requested.");
|
||||||
|
// conversion from bytes to string. no need to clean the high bit
|
||||||
// only to shift left because of opposite alignment
|
// only to shift left because of opposite alignment
|
||||||
StaticStringType const& targetStringType = dynamic_cast<StaticStringType const&>(_targetType);
|
FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType);
|
||||||
IntegerType const& typeOnStack = dynamic_cast<IntegerType const&>(_typeOnStack);
|
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
||||||
solAssert(typeOnStack.isHash(), "Only conversion between String and Hash is allowed.");
|
if (targetBytesType.getNumBytes() * 8 > typeOnStack->getNumBits())
|
||||||
solAssert(typeOnStack.getNumBits() == targetStringType.getNumBytes() * 8, "The size should be the same.");
|
appendHighBitsCleanup(*typeOnStack);
|
||||||
m_context << (u256(1) << (256 - typeOnStack.getNumBits())) << eth::Instruction::MUL;
|
m_context << (u256(1) << (256 - targetBytesType.getNumBytes() * 8)) << eth::Instruction::MUL;
|
||||||
}
|
}
|
||||||
else if (targetTypeCategory == Type::Category::Enum)
|
else if (targetTypeCategory == Type::Category::Enum)
|
||||||
// just clean
|
// just clean
|
||||||
@ -175,7 +183,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
|||||||
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
|
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
|
||||||
IntegerType addressType(0, IntegerType::Modifier::Address);
|
IntegerType addressType(0, IntegerType::Modifier::Address);
|
||||||
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
|
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
|
||||||
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
|
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
|
||||||
if (stackTypeCategory == Type::Category::IntegerConstant)
|
if (stackTypeCategory == Type::Category::IntegerConstant)
|
||||||
{
|
{
|
||||||
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
|
IntegerConstantType const& constType = dynamic_cast<IntegerConstantType const&>(_typeOnStack);
|
||||||
@ -187,7 +195,7 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
|
IntegerType const& typeOnStack = stackTypeCategory == Type::Category::Integer
|
||||||
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
|
? dynamic_cast<IntegerType const&>(_typeOnStack) : addressType;
|
||||||
// Widening: clean up according to source type width
|
// Widening: clean up according to source type width
|
||||||
// Non-widening and force: clean up according to target type bits
|
// Non-widening and force: clean up according to target type bits
|
||||||
if (targetType.getNumBits() > typeOnStack.getNumBits())
|
if (targetType.getNumBits() > typeOnStack.getNumBits())
|
||||||
@ -196,15 +204,17 @@ void ExpressionCompiler::appendTypeConversion(Type const& _typeOnStack, Type con
|
|||||||
appendHighBitsCleanup(targetType);
|
appendHighBitsCleanup(targetType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
else if (_typeOnStack != _targetType)
|
default:
|
||||||
// All other types should not be convertible to non-equal types.
|
// All other types should not be convertible to non-equal types.
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid type conversion requested."));
|
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExpressionCompiler::visit(Assignment const& _assignment)
|
bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_assignment);
|
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
|
||||||
_assignment.getRightHandSide().accept(*this);
|
_assignment.getRightHandSide().accept(*this);
|
||||||
if (_assignment.getType()->isValueType())
|
if (_assignment.getType()->isValueType())
|
||||||
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
|
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
|
||||||
@ -226,9 +236,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
|||||||
m_currentLValue->retrieveValue(_assignment.getLocation(), true);
|
m_currentLValue->retrieveValue(_assignment.getLocation(), true);
|
||||||
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
|
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
|
||||||
if (lvalueSize > 0)
|
if (lvalueSize > 0)
|
||||||
|
{
|
||||||
|
solAssert(itemSize + lvalueSize <= 16, "Stack too deep.");
|
||||||
// value [lvalue_ref] updated_value
|
// value [lvalue_ref] updated_value
|
||||||
for (unsigned i = 0; i < itemSize; ++i)
|
for (unsigned i = 0; i < itemSize; ++i)
|
||||||
m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
|
m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
|
m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation());
|
||||||
m_currentLValue.reset();
|
m_currentLValue.reset();
|
||||||
@ -237,7 +250,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_unaryOperation);
|
CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation);
|
||||||
//@todo type checking and creating code for an operator should be in the same place:
|
//@todo type checking and creating code for an operator should be in the same place:
|
||||||
// the operator should know how to convert itself and to which types it applies, so
|
// the operator should know how to convert itself and to which types it applies, so
|
||||||
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
|
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
|
||||||
@ -270,23 +283,24 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
case Token::Dec: // -- (pre- or postfix)
|
case Token::Dec: // -- (pre- or postfix)
|
||||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||||
m_currentLValue->retrieveValue(_unaryOperation.getLocation());
|
m_currentLValue->retrieveValue(_unaryOperation.getLocation());
|
||||||
solAssert(m_currentLValue->sizeOnStack() <= 1, "Not implemented.");
|
|
||||||
if (!_unaryOperation.isPrefixOperation())
|
if (!_unaryOperation.isPrefixOperation())
|
||||||
{
|
{
|
||||||
if (m_currentLValue->sizeOnStack() == 1)
|
// store value for later
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2;
|
solAssert(_unaryOperation.getType()->getSizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||||
else
|
m_context << eth::Instruction::DUP1;
|
||||||
m_context << eth::Instruction::DUP1;
|
if (m_currentLValue->sizeOnStack() > 0)
|
||||||
|
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||||
|
m_context << eth::swapInstruction(i);
|
||||||
}
|
}
|
||||||
m_context << u256(1);
|
m_context << u256(1);
|
||||||
if (_unaryOperation.getOperator() == Token::Inc)
|
if (_unaryOperation.getOperator() == Token::Inc)
|
||||||
m_context << eth::Instruction::ADD;
|
m_context << eth::Instruction::ADD;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB; // @todo avoid the swap
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SUB;
|
||||||
// Stack for prefix: [ref] (*ref)+-1
|
// Stack for prefix: [ref...] (*ref)+-1
|
||||||
// Stack for postfix: *ref [ref] (*ref)+-1
|
// Stack for postfix: *ref [ref...] (*ref)+-1
|
||||||
if (m_currentLValue->sizeOnStack() == 1)
|
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::swapInstruction(i);
|
||||||
m_currentLValue->storeValue(
|
m_currentLValue->storeValue(
|
||||||
*_unaryOperation.getType(), _unaryOperation.getLocation(),
|
*_unaryOperation.getType(), _unaryOperation.getLocation(),
|
||||||
!_unaryOperation.isPrefixOperation());
|
!_unaryOperation.isPrefixOperation());
|
||||||
@ -307,7 +321,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_binaryOperation);
|
CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation);
|
||||||
Expression const& leftExpression = _binaryOperation.getLeftExpression();
|
Expression const& leftExpression = _binaryOperation.getLeftExpression();
|
||||||
Expression const& rightExpression = _binaryOperation.getRightExpression();
|
Expression const& rightExpression = _binaryOperation.getRightExpression();
|
||||||
Type const& commonType = _binaryOperation.getCommonType();
|
Type const& commonType = _binaryOperation.getCommonType();
|
||||||
@ -354,7 +368,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_functionCall);
|
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
|
||||||
using Location = FunctionType::Location;
|
using Location = FunctionType::Location;
|
||||||
if (_functionCall.isTypeConversion())
|
if (_functionCall.isTypeConversion())
|
||||||
{
|
{
|
||||||
@ -405,7 +419,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
_functionCall.getExpression().accept(*this);
|
_functionCall.getExpression().accept(*this);
|
||||||
|
|
||||||
m_context.appendJump();
|
m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
|
||||||
m_context << returnLabel;
|
m_context << returnLabel;
|
||||||
|
|
||||||
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
|
unsigned returnParametersSize = CompilerUtils::getSizeOnStack(function.getReturnParameterTypes());
|
||||||
@ -528,8 +542,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
appendTypeConversion(*arguments[arg - 1]->getType(),
|
appendTypeConversion(*arguments[arg - 1]->getType(),
|
||||||
*function.getParameterTypes()[arg - 1], true);
|
*function.getParameterTypes()[arg - 1], true);
|
||||||
}
|
}
|
||||||
m_context << u256(h256::Arith(dev::sha3(function.getCanonicalSignature(event.getName()))));
|
if (!event.isAnonymous())
|
||||||
++numIndexed;
|
{
|
||||||
|
m_context << u256(h256::Arith(dev::sha3(function.externalSignature(event.getName()))));
|
||||||
|
++numIndexed;
|
||||||
|
}
|
||||||
solAssert(numIndexed <= 4, "Too many indexed arguments.");
|
solAssert(numIndexed <= 4, "Too many indexed arguments.");
|
||||||
// Copy all non-indexed arguments to memory (data)
|
// Copy all non-indexed arguments to memory (data)
|
||||||
m_context << u256(0);
|
m_context << u256(0);
|
||||||
@ -550,10 +567,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
case Location::SHA256:
|
case Location::SHA256:
|
||||||
case Location::RIPEMD160:
|
case Location::RIPEMD160:
|
||||||
{
|
{
|
||||||
|
_functionCall.getExpression().accept(*this);
|
||||||
static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
|
static const map<Location, u256> contractAddresses{{Location::ECRecover, 1},
|
||||||
{Location::SHA256, 2},
|
{Location::SHA256, 2},
|
||||||
{Location::RIPEMD160, 3}};
|
{Location::RIPEMD160, 3}};
|
||||||
m_context << contractAddresses.find(function.getLocation())->second;
|
m_context << contractAddresses.find(function.getLocation())->second;
|
||||||
|
for (unsigned i = function.getSizeOnStack(); i > 0; --i)
|
||||||
|
m_context << eth::swapInstruction(i);
|
||||||
appendExternalFunctionCall(function, arguments, true);
|
appendExternalFunctionCall(function, arguments, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -572,7 +592,7 @@ bool ExpressionCompiler::visit(NewExpression const&)
|
|||||||
|
|
||||||
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_memberAccess);
|
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
|
||||||
ASTString const& member = _memberAccess.getMemberName();
|
ASTString const& member = _memberAccess.getMemberName();
|
||||||
switch (_memberAccess.getExpression().getType()->getCategory())
|
switch (_memberAccess.getExpression().getType()->getCategory())
|
||||||
{
|
{
|
||||||
@ -639,13 +659,18 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
m_context << eth::Instruction::GASPRICE;
|
m_context << eth::Instruction::GASPRICE;
|
||||||
else if (member == "data")
|
else if (member == "data")
|
||||||
m_context << u256(0) << eth::Instruction::CALLDATASIZE;
|
m_context << u256(0) << eth::Instruction::CALLDATASIZE;
|
||||||
|
else if (member == "sig")
|
||||||
|
m_context << u256(0) << eth::Instruction::CALLDATALOAD
|
||||||
|
<< (u256(0xffffffff) << (256 - 32)) << eth::Instruction::AND;
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown magic member."));
|
||||||
break;
|
break;
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
{
|
{
|
||||||
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
|
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
|
||||||
m_context << type.getStorageOffsetOfMember(member) << eth::Instruction::ADD;
|
m_context << eth::Instruction::POP; // structs always align to new slot
|
||||||
|
pair<u256, unsigned> const& offsets = type.getStorageOffsetsOfMember(member);
|
||||||
|
m_context << offsets.first << eth::Instruction::ADD << u256(offsets.second);
|
||||||
setLValueToStorageItem(_memberAccess);
|
setLValueToStorageItem(_memberAccess);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -707,110 +732,43 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
|
|
||||||
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, &_indexAccess);
|
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
|
||||||
_indexAccess.getBaseExpression().accept(*this);
|
_indexAccess.getBaseExpression().accept(*this);
|
||||||
|
|
||||||
Type const& baseType = *_indexAccess.getBaseExpression().getType();
|
Type const& baseType = *_indexAccess.getBaseExpression().getType();
|
||||||
if (baseType.getCategory() == Type::Category::Mapping)
|
if (baseType.getCategory() == Type::Category::Mapping)
|
||||||
{
|
{
|
||||||
|
// storage byte offset is ignored for mappings, it should be zero.
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
|
// stack: storage_base_ref
|
||||||
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
|
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType();
|
||||||
m_context << u256(0);
|
m_context << u256(0); // memory position
|
||||||
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
||||||
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression());
|
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression());
|
||||||
solAssert(baseType.getSizeOnStack() == 1,
|
|
||||||
"Unexpected: Not exactly one stack slot taken by subscriptable expression.");
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
appendTypeMoveToMemory(IntegerType(256));
|
appendTypeMoveToMemory(IntegerType(256));
|
||||||
m_context << u256(0) << eth::Instruction::SHA3;
|
m_context << u256(0) << eth::Instruction::SHA3;
|
||||||
|
m_context << u256(0);
|
||||||
setLValueToStorageItem( _indexAccess);
|
setLValueToStorageItem( _indexAccess);
|
||||||
}
|
}
|
||||||
else if (baseType.getCategory() == Type::Category::Array)
|
else if (baseType.getCategory() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
// stack layout: <base_ref> [<length>] <index>
|
|
||||||
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType);
|
||||||
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
||||||
ArrayType::Location location = arrayType.getLocation();
|
|
||||||
eth::Instruction load =
|
// remove storage byte offset
|
||||||
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
|
if (arrayType.getLocation() == ArrayType::Location::Storage)
|
||||||
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
|
m_context << eth::Instruction::POP;
|
||||||
eth::Instruction::CALLDATALOAD;
|
|
||||||
|
|
||||||
_indexAccess.getIndexExpression()->accept(*this);
|
_indexAccess.getIndexExpression()->accept(*this);
|
||||||
// retrieve length
|
// stack layout: <base_ref> [<length>] <index>
|
||||||
if (!arrayType.isDynamicallySized())
|
ArrayUtils(m_context).accessIndex(arrayType);
|
||||||
m_context << arrayType.getLength();
|
if (arrayType.getLocation() == ArrayType::Location::Storage)
|
||||||
else if (location == ArrayType::Location::CallData)
|
|
||||||
// length is stored on the stack
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
else
|
|
||||||
m_context << eth::Instruction::DUP2 << load;
|
|
||||||
// stack: <base_ref> <index> <length>
|
|
||||||
// check out-of-bounds access
|
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::LT;
|
|
||||||
eth::AssemblyItem legalAccess = m_context.appendConditionalJump();
|
|
||||||
// out-of-bounds access throws exception (just STOP for now)
|
|
||||||
m_context << eth::Instruction::STOP;
|
|
||||||
|
|
||||||
m_context << legalAccess;
|
|
||||||
// stack: <base_ref> <index>
|
|
||||||
if (arrayType.isByteArray())
|
|
||||||
// byte array is packed differently, especially in storage
|
|
||||||
switch (location)
|
|
||||||
{
|
|
||||||
case ArrayType::Location::Storage:
|
|
||||||
// byte array index storage lvalue on stack (goal):
|
|
||||||
// <ref> <byte_number> = <base_ref + index / 32> <index % 32>
|
|
||||||
m_context << u256(32) << eth::Instruction::SWAP2;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
// stack: 32 index data_ref
|
|
||||||
m_context
|
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::DUP3
|
|
||||||
<< eth::Instruction::DIV << eth::Instruction::ADD
|
|
||||||
// stack: 32 index (data_ref + index / 32)
|
|
||||||
<< eth::Instruction::SWAP2 << eth::Instruction::SWAP1 << eth::Instruction::MOD;
|
|
||||||
setLValue<StorageByteArrayElement>(_indexAccess);
|
|
||||||
break;
|
|
||||||
case ArrayType::Location::CallData:
|
|
||||||
// no lvalue, just retrieve the value
|
|
||||||
m_context
|
|
||||||
<< eth::Instruction::ADD << eth::Instruction::CALLDATALOAD
|
|
||||||
<< u256(0) << eth::Instruction::BYTE;
|
|
||||||
break;
|
|
||||||
case ArrayType::Location::Memory:
|
|
||||||
solAssert(false, "Memory lvalues not yet implemented.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
u256 elementSize =
|
if (arrayType.isByteArray())
|
||||||
location == ArrayType::Location::Storage ?
|
setLValue<StorageByteArrayElement>(_indexAccess);
|
||||||
arrayType.getBaseType()->getStorageSize() :
|
else
|
||||||
arrayType.getBaseType()->getCalldataEncodedSize();
|
|
||||||
solAssert(elementSize != 0, "Invalid element size.");
|
|
||||||
if (elementSize > 1)
|
|
||||||
m_context << elementSize << eth::Instruction::MUL;
|
|
||||||
if (arrayType.isDynamicallySized())
|
|
||||||
{
|
|
||||||
if (location == ArrayType::Location::Storage)
|
|
||||||
{
|
|
||||||
m_context << eth::Instruction::SWAP1;
|
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
|
||||||
}
|
|
||||||
else if (location == ArrayType::Location::Memory)
|
|
||||||
m_context << u256(32) << eth::Instruction::ADD;
|
|
||||||
}
|
|
||||||
m_context << eth::Instruction::ADD;
|
|
||||||
switch (location)
|
|
||||||
{
|
|
||||||
case ArrayType::Location::CallData:
|
|
||||||
if (arrayType.getBaseType()->isValueType())
|
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(*arrayType.getBaseType(), true, true, false);
|
|
||||||
break;
|
|
||||||
case ArrayType::Location::Storage:
|
|
||||||
setLValueToStorageItem(_indexAccess);
|
setLValueToStorageItem(_indexAccess);
|
||||||
break;
|
|
||||||
case ArrayType::Location::Memory:
|
|
||||||
solAssert(false, "Memory lvalues not yet implemented.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -821,18 +779,34 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
|
|
||||||
void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, _identifier);
|
||||||
Declaration const* declaration = _identifier.getReferencedDeclaration();
|
Declaration const* declaration = _identifier.getReferencedDeclaration();
|
||||||
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
|
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
|
||||||
{
|
{
|
||||||
if (magicVar->getType()->getCategory() == Type::Category::Contract)
|
switch (magicVar->getType()->getCategory())
|
||||||
|
{
|
||||||
|
case Type::Category::Contract:
|
||||||
// "this" or "super"
|
// "this" or "super"
|
||||||
if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
|
if (!dynamic_cast<ContractType const&>(*magicVar->getType()).isSuper())
|
||||||
m_context << eth::Instruction::ADDRESS;
|
m_context << eth::Instruction::ADDRESS;
|
||||||
|
break;
|
||||||
|
case Type::Category::Integer:
|
||||||
|
// "now"
|
||||||
|
m_context << eth::Instruction::TIMESTAMP;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
|
m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag();
|
||||||
else if (dynamic_cast<VariableDeclaration const*>(declaration))
|
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
setLValueFromDeclaration(*declaration, _identifier);
|
{
|
||||||
|
if (!variable->isConstant())
|
||||||
|
setLValueFromDeclaration(*declaration, _identifier);
|
||||||
|
else
|
||||||
|
variable->getValue()->accept(*this);
|
||||||
|
}
|
||||||
else if (dynamic_cast<ContractDefinition const*>(declaration))
|
else if (dynamic_cast<ContractDefinition const*>(declaration))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
@ -860,11 +834,12 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
|
|
||||||
void ExpressionCompiler::endVisit(Literal const& _literal)
|
void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
|
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
||||||
switch (_literal.getType()->getCategory())
|
switch (_literal.getType()->getCategory())
|
||||||
{
|
{
|
||||||
case Type::Category::IntegerConstant:
|
case Type::Category::IntegerConstant:
|
||||||
case Type::Category::Bool:
|
case Type::Category::Bool:
|
||||||
case Type::Category::String:
|
case Type::Category::FixedBytes:
|
||||||
m_context << _literal.getType()->literalValue(&_literal);
|
m_context << _literal.getType()->literalValue(&_literal);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1115,7 +1090,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio
|
|||||||
if (m_context.isLocalVariable(&_declaration))
|
if (m_context.isLocalVariable(&_declaration))
|
||||||
setLValue<StackVariable>(_expression, _declaration);
|
setLValue<StackVariable>(_expression, _declaration);
|
||||||
else if (m_context.isStateVariable(&_declaration))
|
else if (m_context.isStateVariable(&_declaration))
|
||||||
setLValue<StorageItem>(_expression, _declaration);
|
setLValue<StorageItem>(_expression, _declaration);
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
||||||
<< errinfo_sourceLocation(_expression.getLocation())
|
<< errinfo_sourceLocation(_expression.getLocation())
|
||||||
|
@ -42,7 +42,6 @@ class CompilerContext;
|
|||||||
class Type;
|
class Type;
|
||||||
class IntegerType;
|
class IntegerType;
|
||||||
class ArrayType;
|
class ArrayType;
|
||||||
class StaticStringType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
|
* Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream
|
||||||
@ -140,7 +139,6 @@ void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments con
|
|||||||
m_currentLValue = move(lvalue);
|
m_currentLValue = move(lvalue);
|
||||||
else
|
else
|
||||||
lvalue->retrieveValue(_expression.getLocation(), true);
|
lvalue->retrieveValue(_expression.getLocation(), true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -37,26 +37,27 @@ GlobalContext::GlobalContext():
|
|||||||
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
|
m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
|
||||||
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
|
make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
|
||||||
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
|
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
|
||||||
|
make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
|
||||||
make_shared<MagicVariableDeclaration>("suicide",
|
make_shared<MagicVariableDeclaration>("suicide",
|
||||||
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
|
make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
|
||||||
make_shared<MagicVariableDeclaration>("sha3",
|
make_shared<MagicVariableDeclaration>("sha3",
|
||||||
make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA3, true)),
|
make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
|
||||||
make_shared<MagicVariableDeclaration>("log0",
|
make_shared<MagicVariableDeclaration>("log0",
|
||||||
make_shared<FunctionType>(strings{"hash"},strings{}, FunctionType::Location::Log0)),
|
make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)),
|
||||||
make_shared<MagicVariableDeclaration>("log1",
|
make_shared<MagicVariableDeclaration>("log1",
|
||||||
make_shared<FunctionType>(strings{"hash", "hash"},strings{}, FunctionType::Location::Log1)),
|
make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)),
|
||||||
make_shared<MagicVariableDeclaration>("log2",
|
make_shared<MagicVariableDeclaration>("log2",
|
||||||
make_shared<FunctionType>(strings{"hash", "hash", "hash"},strings{}, FunctionType::Location::Log2)),
|
make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)),
|
||||||
make_shared<MagicVariableDeclaration>("log3",
|
make_shared<MagicVariableDeclaration>("log3",
|
||||||
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log3)),
|
make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)),
|
||||||
make_shared<MagicVariableDeclaration>("log4",
|
make_shared<MagicVariableDeclaration>("log4",
|
||||||
make_shared<FunctionType>(strings{"hash", "hash", "hash", "hash", "hash"},strings{}, FunctionType::Location::Log4)),
|
make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)),
|
||||||
make_shared<MagicVariableDeclaration>("sha256",
|
make_shared<MagicVariableDeclaration>("sha256",
|
||||||
make_shared<FunctionType>(strings(), strings{"hash"}, FunctionType::Location::SHA256, true)),
|
make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)),
|
||||||
make_shared<MagicVariableDeclaration>("ecrecover",
|
make_shared<MagicVariableDeclaration>("ecrecover",
|
||||||
make_shared<FunctionType>(strings{"hash", "hash8", "hash", "hash"}, strings{"address"}, FunctionType::Location::ECRecover)),
|
make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
|
||||||
make_shared<MagicVariableDeclaration>("ripemd160",
|
make_shared<MagicVariableDeclaration>("ripemd160",
|
||||||
make_shared<FunctionType>(strings(), strings{"hash160"}, FunctionType::Location::RIPEMD160, true))})
|
make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))})
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@ std::unique_ptr<std::string> InterfaceHandler::getABIInterface(ContractDefinitio
|
|||||||
Json::Value event;
|
Json::Value event;
|
||||||
event["type"] = "event";
|
event["type"] = "event";
|
||||||
event["name"] = it->getName();
|
event["name"] = it->getName();
|
||||||
|
event["anonymous"] = it->isAnonymous();
|
||||||
Json::Value params(Json::arrayValue);
|
Json::Value params(Json::arrayValue);
|
||||||
for (auto const& p: it->getParameters())
|
for (auto const& p: it->getParameters())
|
||||||
{
|
{
|
||||||
@ -128,7 +129,7 @@ std::unique_ptr<std::string> InterfaceHandler::getUserDocumentation(ContractDefi
|
|||||||
if (!m_notice.empty())
|
if (!m_notice.empty())
|
||||||
{// since @notice is the only user tag if missing function should not appear
|
{// since @notice is the only user tag if missing function should not appear
|
||||||
user["notice"] = Json::Value(m_notice);
|
user["notice"] = Json::Value(m_notice);
|
||||||
methods[it.second->getCanonicalSignature()] = user;
|
methods[it.second->externalSignature()] = user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,8 +175,17 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
|
|||||||
method["author"] = m_author;
|
method["author"] = m_author;
|
||||||
|
|
||||||
Json::Value params(Json::objectValue);
|
Json::Value params(Json::objectValue);
|
||||||
|
std::vector<std::string> paramNames = it.second->getParameterNames();
|
||||||
for (auto const& pair: m_params)
|
for (auto const& pair: m_params)
|
||||||
|
{
|
||||||
|
if (find(paramNames.begin(), paramNames.end(), pair.first) == paramNames.end())
|
||||||
|
// LTODO: mismatching parameter name, throw some form of warning and not just an exception
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
DocstringParsingError() <<
|
||||||
|
errinfo_comment("documented parameter \"" + pair.first + "\" not found found in the function")
|
||||||
|
);
|
||||||
params[pair.first] = pair.second;
|
params[pair.first] = pair.second;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_params.empty())
|
if (!m_params.empty())
|
||||||
method["params"] = params;
|
method["params"] = params;
|
||||||
@ -184,7 +194,7 @@ std::unique_ptr<std::string> InterfaceHandler::getDevDocumentation(ContractDefin
|
|||||||
method["return"] = m_return;
|
method["return"] = m_return;
|
||||||
|
|
||||||
if (!method.empty()) // add the function, only if we have any documentation to add
|
if (!method.empty()) // add the function, only if we have any documentation to add
|
||||||
methods[it.second->getCanonicalSignature()] = method;
|
methods[it.second->externalSignature()] = method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
doc["methods"] = methods;
|
doc["methods"] = methods;
|
||||||
|
186
LValue.cpp
186
LValue.cpp
@ -77,7 +77,8 @@ void StackVariable::setToZero(SourceLocation const& _location, bool) const
|
|||||||
StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration):
|
StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration):
|
||||||
StorageItem(_compilerContext, *_declaration.getType())
|
StorageItem(_compilerContext, *_declaration.getType())
|
||||||
{
|
{
|
||||||
m_context << m_context.getStorageLocationOfVariable(_declaration);
|
auto const& location = m_context.getStorageLocationOfVariable(_declaration);
|
||||||
|
m_context << location.first << u256(location.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
||||||
@ -86,62 +87,78 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
|||||||
if (m_dataType.isValueType())
|
if (m_dataType.isValueType())
|
||||||
{
|
{
|
||||||
solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), "");
|
solAssert(m_dataType.getStorageSize() == m_dataType.getSizeOnStack(), "");
|
||||||
solAssert(m_dataType.getStorageSize() <= numeric_limits<unsigned>::max(),
|
solAssert(m_dataType.getStorageSize() == 1, "Invalid storage size.");
|
||||||
"The storage size of " + m_dataType.toString() + " should fit in an unsigned");
|
|
||||||
m_size = unsigned(m_dataType.getStorageSize());
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
m_size = 0; // unused
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
||||||
{
|
{
|
||||||
|
// stack: storage_key storage_offset
|
||||||
if (!m_dataType.isValueType())
|
if (!m_dataType.isValueType())
|
||||||
return; // no distinction between value and reference for non-value types
|
return; // no distinction between value and reference for non-value types
|
||||||
if (!_remove)
|
if (!_remove)
|
||||||
m_context << eth::Instruction::DUP1;
|
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
||||||
if (m_size == 1)
|
if (m_dataType.getStorageBytes() == 32)
|
||||||
m_context << eth::Instruction::SLOAD;
|
m_context << eth::Instruction::POP << eth::Instruction::SLOAD;
|
||||||
else
|
else
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
{
|
||||||
{
|
m_context
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1;
|
<< eth::Instruction::SWAP1 << eth::Instruction::SLOAD << eth::Instruction::SWAP1
|
||||||
if (i + 1 < m_size)
|
<< u256(0x100) << eth::Instruction::EXP << eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
m_context << u256(1) << eth::Instruction::ADD;
|
if (m_dataType.getCategory() == Type::Category::FixedBytes)
|
||||||
else
|
m_context << (u256(0x1) << (256 - 8 * m_dataType.getStorageBytes())) << eth::Instruction::MUL;
|
||||||
m_context << eth::Instruction::POP;
|
else
|
||||||
}
|
m_context << ((u256(0x1) << (8 * m_dataType.getStorageBytes())) - 1) << eth::Instruction::AND;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
|
void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _location, bool _move) const
|
||||||
{
|
{
|
||||||
// stack layout: value value ... value target_ref
|
// stack: value storage_key storage_offset
|
||||||
if (m_dataType.isValueType())
|
if (m_dataType.isValueType())
|
||||||
{
|
{
|
||||||
if (!_move) // copy values
|
solAssert(m_dataType.getStorageBytes() <= 32, "Invalid storage bytes size.");
|
||||||
|
solAssert(m_dataType.getStorageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
if (m_dataType.getStorageBytes() == 32)
|
||||||
{
|
{
|
||||||
if (m_size + 1 > 16)
|
// offset should be zero
|
||||||
BOOST_THROW_EXCEPTION(CompilerError()
|
m_context << eth::Instruction::POP;
|
||||||
<< errinfo_sourceLocation(_location) << errinfo_comment("Stack too deep."));
|
if (!_move)
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SWAP1;
|
||||||
m_context << eth::dupInstruction(m_size + 1) << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
if (m_size > 1) // store high index value first
|
else
|
||||||
m_context << u256(m_size - 1) << eth::Instruction::ADD;
|
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
|
||||||
{
|
{
|
||||||
if (i + 1 >= m_size)
|
// OR the value into the other values in the storage slot
|
||||||
m_context << eth::Instruction::SSTORE;
|
m_context << u256(0x100) << eth::Instruction::EXP;
|
||||||
else
|
// stack: value storage_ref multiplier
|
||||||
// stack here: value value ... value value (target_ref+offset)
|
// fetch old value
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP2
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
||||||
<< eth::Instruction::SSTORE
|
// stack: value storege_ref multiplier old_full_value
|
||||||
<< u256(1) << eth::Instruction::SWAP1 << eth::Instruction::SUB;
|
// clear bytes in old value
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::DUP2 << ((u256(1) << (8 * m_dataType.getStorageBytes())) - 1)
|
||||||
|
<< eth::Instruction::MUL;
|
||||||
|
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
||||||
|
// stack: value storage_ref multiplier cleared_value
|
||||||
|
m_context
|
||||||
|
<< eth::Instruction::SWAP1 << eth::Instruction::DUP4;
|
||||||
|
// stack: value storage_ref cleared_value multiplier value
|
||||||
|
if (m_dataType.getCategory() == Type::Category::FixedBytes)
|
||||||
|
m_context
|
||||||
|
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(m_dataType).getNumBytes()))
|
||||||
|
<< eth::Instruction::SWAP1 << eth::Instruction::DIV;
|
||||||
|
m_context << eth::Instruction::MUL << eth::Instruction::OR;
|
||||||
|
// stack: value storage_ref updated_value
|
||||||
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
|
if (_move)
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(_sourceType.getCategory() == m_dataType.getCategory(),
|
solAssert(
|
||||||
|
_sourceType.getCategory() == m_dataType.getCategory(),
|
||||||
"Wrong type conversation for assignment.");
|
"Wrong type conversation for assignment.");
|
||||||
if (m_dataType.getCategory() == Type::Category::Array)
|
if (m_dataType.getCategory() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
@ -149,11 +166,12 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
dynamic_cast<ArrayType const&>(m_dataType),
|
dynamic_cast<ArrayType const&>(m_dataType),
|
||||||
dynamic_cast<ArrayType const&>(_sourceType));
|
dynamic_cast<ArrayType const&>(_sourceType));
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::POP;
|
CompilerUtils(m_context).popStackElement(_sourceType);
|
||||||
}
|
}
|
||||||
else if (m_dataType.getCategory() == Type::Category::Struct)
|
else if (m_dataType.getCategory() == Type::Category::Struct)
|
||||||
{
|
{
|
||||||
// stack layout: source_ref target_ref
|
// stack layout: source_ref source_offset target_ref target_offset
|
||||||
|
// note that we have structs, so offsets should be zero and are ignored
|
||||||
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
||||||
solAssert(structType == _sourceType, "Struct assignment with conversion.");
|
solAssert(structType == _sourceType, "Struct assignment with conversion.");
|
||||||
for (auto const& member: structType.getMembers())
|
for (auto const& member: structType.getMembers())
|
||||||
@ -162,26 +180,37 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
TypePointer const& memberType = member.second;
|
TypePointer const& memberType = member.second;
|
||||||
if (memberType->getCategory() == Type::Category::Mapping)
|
if (memberType->getCategory() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
m_context << structType.getStorageOffsetOfMember(member.first)
|
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.first);
|
||||||
<< eth::Instruction::DUP3 << eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context
|
||||||
// stack: source_ref target_ref member_offset source_member_ref
|
<< offsets.first << u256(offsets.second)
|
||||||
|
<< eth::Instruction::DUP6 << eth::Instruction::DUP3
|
||||||
|
<< eth::Instruction::ADD << eth::Instruction::DUP2;
|
||||||
|
// stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_member_ref source_member_off
|
||||||
StorageItem(m_context, *memberType).retrieveValue(_location, true);
|
StorageItem(m_context, *memberType).retrieveValue(_location, true);
|
||||||
// stack: source_ref target_ref member_offset source_value...
|
// stack: source_ref source_off target_ref target_off member_offset source_value...
|
||||||
m_context << eth::dupInstruction(2 + memberType->getSizeOnStack())
|
solAssert(4 + memberType->getSizeOnStack() <= 16, "Stack too deep.");
|
||||||
<< eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD;
|
m_context
|
||||||
// stack: source_ref target_ref member_offset source_value... target_member_ref
|
<< eth::dupInstruction(4 + memberType->getSizeOnStack())
|
||||||
|
<< eth::dupInstruction(3 + memberType->getSizeOnStack()) << eth::Instruction::ADD
|
||||||
|
<< eth::dupInstruction(2 + memberType->getSizeOnStack());
|
||||||
|
// stack: source_ref source_off target_ref target_off member_slot_offset member_byte_offset source_value... target_member_ref target_member_byte_off
|
||||||
StorageItem(m_context, *memberType).storeValue(*memberType, _location, true);
|
StorageItem(m_context, *memberType).storeValue(*memberType, _location, true);
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << eth::Instruction::POP;
|
m_context
|
||||||
|
<< eth::Instruction::POP << eth::Instruction::POP
|
||||||
|
<< eth::Instruction::POP << eth::Instruction::POP;
|
||||||
else
|
else
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context
|
||||||
m_context << eth::Instruction::POP;
|
<< eth::Instruction::SWAP2 << eth::Instruction::POP
|
||||||
|
<< eth::Instruction::SWAP2 << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
BOOST_THROW_EXCEPTION(
|
||||||
<< errinfo_sourceLocation(_location) << errinfo_comment("Invalid non-value type for assignment."));
|
InternalCompilerError()
|
||||||
|
<< errinfo_sourceLocation(_location)
|
||||||
|
<< errinfo_comment("Invalid non-value type for assignment."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +219,13 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
if (m_dataType.getCategory() == Type::Category::Array)
|
if (m_dataType.getCategory() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
if (!_removeReference)
|
if (!_removeReference)
|
||||||
m_context << eth::Instruction::DUP1;
|
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
||||||
ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(m_dataType));
|
ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(m_dataType));
|
||||||
}
|
}
|
||||||
else if (m_dataType.getCategory() == Type::Category::Struct)
|
else if (m_dataType.getCategory() == Type::Category::Struct)
|
||||||
{
|
{
|
||||||
// stack layout: ref
|
// stack layout: storage_key storage_offset
|
||||||
|
// @todo this can be improved for packed types
|
||||||
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
auto const& structType = dynamic_cast<StructType const&>(m_dataType);
|
||||||
for (auto const& member: structType.getMembers())
|
for (auto const& member: structType.getMembers())
|
||||||
{
|
{
|
||||||
@ -203,38 +233,48 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const
|
|||||||
TypePointer const& memberType = member.second;
|
TypePointer const& memberType = member.second;
|
||||||
if (memberType->getCategory() == Type::Category::Mapping)
|
if (memberType->getCategory() == Type::Category::Mapping)
|
||||||
continue;
|
continue;
|
||||||
m_context << structType.getStorageOffsetOfMember(member.first)
|
pair<u256, unsigned> const& offsets = structType.getStorageOffsetsOfMember(member.first);
|
||||||
<< eth::Instruction::DUP2 << eth::Instruction::ADD;
|
m_context
|
||||||
|
<< offsets.first << eth::Instruction::DUP3 << eth::Instruction::ADD
|
||||||
|
<< u256(offsets.second);
|
||||||
StorageItem(m_context, *memberType).setToZero();
|
StorageItem(m_context, *memberType).setToZero();
|
||||||
}
|
}
|
||||||
if (_removeReference)
|
if (_removeReference)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(m_dataType.isValueType(), "Clearing of unsupported type requested: " + m_dataType.toString());
|
solAssert(m_dataType.isValueType(), "Clearing of unsupported type requested: " + m_dataType.toString());
|
||||||
if (m_size == 0 && _removeReference)
|
// @todo actually use offset
|
||||||
m_context << eth::Instruction::POP;
|
if (!_removeReference)
|
||||||
else if (m_size == 1)
|
CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack());
|
||||||
|
if (m_dataType.getStorageBytes() == 32)
|
||||||
|
{
|
||||||
|
// offset should be zero
|
||||||
m_context
|
m_context
|
||||||
<< u256(0) << (_removeReference ? eth::Instruction::SWAP1 : eth::Instruction::DUP2)
|
<< eth::Instruction::POP << u256(0)
|
||||||
<< eth::Instruction::SSTORE;
|
<< eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!_removeReference)
|
m_context << u256(0x100) << eth::Instruction::EXP;
|
||||||
m_context << eth::Instruction::DUP1;
|
// stack: storage_ref multiplier
|
||||||
for (unsigned i = 0; i < m_size; ++i)
|
// fetch old value
|
||||||
if (i + 1 >= m_size)
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD;
|
||||||
m_context << u256(0) << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
// stack: storege_ref multiplier old_full_value
|
||||||
else
|
// clear bytes in old value
|
||||||
m_context << u256(0) << eth::Instruction::DUP2 << eth::Instruction::SSTORE
|
m_context
|
||||||
<< u256(1) << eth::Instruction::ADD;
|
<< eth::Instruction::SWAP1 << ((u256(1) << (8 * m_dataType.getStorageBytes())) - 1)
|
||||||
|
<< eth::Instruction::MUL;
|
||||||
|
m_context << eth::Instruction::NOT << eth::Instruction::AND;
|
||||||
|
// stack: storage_ref cleared_value
|
||||||
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used in StorageByteArrayElement
|
/// Used in StorageByteArrayElement
|
||||||
static IntegerType byteType(8, IntegerType::Modifier::Hash);
|
static FixedBytesType byteType(1);
|
||||||
|
|
||||||
StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext):
|
StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext):
|
||||||
LValue(_compilerContext, byteType)
|
LValue(_compilerContext, byteType)
|
||||||
@ -250,6 +290,7 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove)
|
|||||||
else
|
else
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD
|
||||||
<< eth::Instruction::DUP2 << eth::Instruction::BYTE;
|
<< eth::Instruction::DUP2 << eth::Instruction::BYTE;
|
||||||
|
m_context << (u256(1) << (256 - 8)) << eth::Instruction::MUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const
|
void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const
|
||||||
@ -265,8 +306,9 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo
|
|||||||
m_context << eth::Instruction::DUP2 << u256(0xff) << eth::Instruction::MUL
|
m_context << eth::Instruction::DUP2 << u256(0xff) << eth::Instruction::MUL
|
||||||
<< eth::Instruction::NOT << eth::Instruction::AND;
|
<< eth::Instruction::NOT << eth::Instruction::AND;
|
||||||
// stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte
|
// stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::DUP4 << eth::Instruction::MUL
|
m_context << eth::Instruction::SWAP1;
|
||||||
<< eth::Instruction::OR;
|
m_context << (u256(1) << (256 - 8)) << eth::Instruction::DUP5 << eth::Instruction::DIV
|
||||||
|
<< eth::Instruction::MUL << eth::Instruction::OR;
|
||||||
// stack: value ref new_full_value
|
// stack: value ref new_full_value
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::SSTORE;
|
||||||
if (_move)
|
if (_move)
|
||||||
@ -297,6 +339,8 @@ StorageArrayLength::StorageArrayLength(CompilerContext& _compilerContext, const
|
|||||||
m_arrayType(_arrayType)
|
m_arrayType(_arrayType)
|
||||||
{
|
{
|
||||||
solAssert(m_arrayType.isDynamicallySized(), "");
|
solAssert(m_arrayType.isDynamicallySized(), "");
|
||||||
|
// storage byte offset must be zero
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const
|
void StorageArrayLength::retrieveValue(SourceLocation const&, bool _remove) const
|
||||||
|
10
LValue.h
10
LValue.h
@ -98,7 +98,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to some item in storage. The (starting) position of the item is stored on the stack.
|
* Reference to some item in storage. On the stack this is <storage key> <offset_inside_value>,
|
||||||
|
* where 0 <= offset_inside_value < 32 and an offset of i means that the value is multiplied
|
||||||
|
* by 2**i before storing it.
|
||||||
*/
|
*/
|
||||||
class StorageItem: public LValue
|
class StorageItem: public LValue
|
||||||
{
|
{
|
||||||
@ -107,6 +109,7 @@ public:
|
|||||||
StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration);
|
StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration);
|
||||||
/// Constructs the LValue and assumes that the storage reference is already on the stack.
|
/// Constructs the LValue and assumes that the storage reference is already on the stack.
|
||||||
StorageItem(CompilerContext& _compilerContext, Type const& _type);
|
StorageItem(CompilerContext& _compilerContext, Type const& _type);
|
||||||
|
virtual unsigned sizeOnStack() const { return 2; }
|
||||||
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
|
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
|
||||||
virtual void storeValue(
|
virtual void storeValue(
|
||||||
Type const& _sourceType,
|
Type const& _sourceType,
|
||||||
@ -117,11 +120,6 @@ public:
|
|||||||
SourceLocation const& _location = SourceLocation(),
|
SourceLocation const& _location = SourceLocation(),
|
||||||
bool _removeReference = true
|
bool _removeReference = true
|
||||||
) const override;
|
) const override;
|
||||||
|
|
||||||
private:
|
|
||||||
/// Number of stack elements occupied by the value (not the reference).
|
|
||||||
/// Only used for value types.
|
|
||||||
unsigned m_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
42
Parser.cpp
42
Parser.cpp
@ -164,8 +164,17 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
|||||||
}
|
}
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::RBrace);
|
expectToken(Token::RBrace);
|
||||||
return nodeFactory.createNode<ContractDefinition>(name, docString, baseContracts, structs, enums,
|
return nodeFactory.createNode<ContractDefinition>(
|
||||||
stateVariables, functions, modifiers, events);
|
name,
|
||||||
|
docString,
|
||||||
|
baseContracts,
|
||||||
|
structs,
|
||||||
|
enums,
|
||||||
|
stateVariables,
|
||||||
|
functions,
|
||||||
|
modifiers,
|
||||||
|
events
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
|
ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
|
||||||
@ -247,8 +256,15 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
returnParameters = createEmptyParameterList();
|
returnParameters = createEmptyParameterList();
|
||||||
ASTPointer<Block> block = parseBlock();
|
ASTPointer<Block> block = ASTPointer<Block>();
|
||||||
nodeFactory.setEndPositionFromNode(block);
|
nodeFactory.markEndPosition();
|
||||||
|
if (m_scanner->getCurrentToken() != Token::Semicolon)
|
||||||
|
{
|
||||||
|
block = parseBlock();
|
||||||
|
nodeFactory.setEndPositionFromNode(block);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_scanner->next(); // just consume the ';'
|
||||||
bool const c_isConstructor = (_contractName && *name == *_contractName);
|
bool const c_isConstructor = (_contractName && *name == *_contractName);
|
||||||
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
|
return nodeFactory.createNode<FunctionDefinition>(name, visibility, c_isConstructor, docstring,
|
||||||
parameters, isDeclaredConst, modifiers,
|
parameters, isDeclaredConst, modifiers,
|
||||||
@ -317,6 +333,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
nodeFactory.setEndPositionFromNode(type);
|
nodeFactory.setEndPositionFromNode(type);
|
||||||
}
|
}
|
||||||
bool isIndexed = false;
|
bool isIndexed = false;
|
||||||
|
bool isDeclaredConst = false;
|
||||||
ASTPointer<ASTString> identifier;
|
ASTPointer<ASTString> identifier;
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
Token::Value token = m_scanner->getCurrentToken();
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
||||||
@ -327,7 +344,13 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
isIndexed = true;
|
isIndexed = true;
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
|
if (token == Token::Const)
|
||||||
|
{
|
||||||
|
isDeclaredConst = true;
|
||||||
|
m_scanner->next();
|
||||||
|
}
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
|
|
||||||
if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
|
if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier)
|
||||||
{
|
{
|
||||||
identifier = make_shared<ASTString>("");
|
identifier = make_shared<ASTString>("");
|
||||||
@ -348,7 +371,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
}
|
}
|
||||||
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
|
return nodeFactory.createNode<VariableDeclaration>(type, identifier, value,
|
||||||
visibility, _options.isStateVariable,
|
visibility, _options.isStateVariable,
|
||||||
isIndexed);
|
isIndexed, isDeclaredConst);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
||||||
@ -387,9 +410,15 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
|
|||||||
parameters = parseParameterList(true, true);
|
parameters = parseParameterList(true, true);
|
||||||
else
|
else
|
||||||
parameters = createEmptyParameterList();
|
parameters = createEmptyParameterList();
|
||||||
|
bool anonymous = false;
|
||||||
|
if (m_scanner->getCurrentToken() == Token::Anonymous)
|
||||||
|
{
|
||||||
|
anonymous = true;
|
||||||
|
m_scanner->next();
|
||||||
|
}
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::Semicolon);
|
expectToken(Token::Semicolon);
|
||||||
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters);
|
return nodeFactory.createNode<EventDefinition>(name, docstring, parameters, anonymous);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
|
ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
|
||||||
@ -913,6 +942,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
|
|||||||
// In all other cases, we have an expression statement.
|
// In all other cases, we have an expression statement.
|
||||||
Token::Value token(m_scanner->getCurrentToken());
|
Token::Value token(m_scanner->getCurrentToken());
|
||||||
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
|
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
|
||||||
|
|
||||||
if (token == Token::Mapping || token == Token::Var ||
|
if (token == Token::Mapping || token == Token::Var ||
|
||||||
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
|
(mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier))
|
||||||
return LookAheadInfo::VariableDeclarationStatement;
|
return LookAheadInfo::VariableDeclarationStatement;
|
||||||
|
5
Parser.h
5
Parser.h
@ -34,6 +34,8 @@ class Scanner;
|
|||||||
class Parser
|
class Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Parser() {}
|
||||||
|
|
||||||
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
|
ASTPointer<SourceUnit> parse(std::shared_ptr<Scanner> const& _scanner);
|
||||||
std::shared_ptr<std::string const> const& getSourceName() const;
|
std::shared_ptr<std::string const> const& getSourceName() const;
|
||||||
|
|
||||||
@ -64,8 +66,7 @@ private:
|
|||||||
ASTPointer<StructDefinition> parseStructDefinition();
|
ASTPointer<StructDefinition> parseStructDefinition();
|
||||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
ASTPointer<EnumDefinition> parseEnumDefinition();
|
||||||
ASTPointer<EnumValue> parseEnumValue();
|
ASTPointer<EnumValue> parseEnumValue();
|
||||||
ASTPointer<VariableDeclaration> parseVariableDeclaration(
|
ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(),
|
||||||
VarDeclParserOptions const& _options = VarDeclParserOptions(),
|
|
||||||
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
|
ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>());
|
||||||
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
ASTPointer<ModifierDefinition> parseModifierDefinition();
|
||||||
ASTPointer<EventDefinition> parseEventDefinition();
|
ASTPointer<EventDefinition> parseEventDefinition();
|
||||||
|
117
Token.h
117
Token.h
@ -143,8 +143,8 @@ namespace solidity
|
|||||||
\
|
\
|
||||||
/* Keywords */ \
|
/* Keywords */ \
|
||||||
K(Break, "break", 0) \
|
K(Break, "break", 0) \
|
||||||
K(Case, "case", 0) \
|
|
||||||
K(Const, "constant", 0) \
|
K(Const, "constant", 0) \
|
||||||
|
K(Anonymous, "anonymous", 0) \
|
||||||
K(Continue, "continue", 0) \
|
K(Continue, "continue", 0) \
|
||||||
K(Contract, "contract", 0) \
|
K(Contract, "contract", 0) \
|
||||||
K(Default, "default", 0) \
|
K(Default, "default", 0) \
|
||||||
@ -167,7 +167,6 @@ namespace solidity
|
|||||||
K(Return, "return", 0) \
|
K(Return, "return", 0) \
|
||||||
K(Returns, "returns", 0) \
|
K(Returns, "returns", 0) \
|
||||||
K(Struct, "struct", 0) \
|
K(Struct, "struct", 0) \
|
||||||
K(Switch, "switch", 0) \
|
|
||||||
K(Var, "var", 0) \
|
K(Var, "var", 0) \
|
||||||
K(While, "while", 0) \
|
K(While, "while", 0) \
|
||||||
K(Enum, "enum", 0) \
|
K(Enum, "enum", 0) \
|
||||||
@ -252,77 +251,43 @@ namespace solidity
|
|||||||
K(UInt240, "uint240", 0) \
|
K(UInt240, "uint240", 0) \
|
||||||
K(UInt248, "uint248", 0) \
|
K(UInt248, "uint248", 0) \
|
||||||
K(UInt256, "uint256", 0) \
|
K(UInt256, "uint256", 0) \
|
||||||
K(Hash, "hash", 0) \
|
K(Bytes0, "bytes0", 0) \
|
||||||
K(Hash8, "hash8", 0) \
|
K(Bytes1, "bytes1", 0) \
|
||||||
K(Hash16, "hash16", 0) \
|
K(Bytes2, "bytes2", 0) \
|
||||||
K(Hash24, "hash24", 0) \
|
K(Bytes3, "bytes3", 0) \
|
||||||
K(Hash32, "hash32", 0) \
|
K(Bytes4, "bytes4", 0) \
|
||||||
K(Hash40, "hash40", 0) \
|
K(Bytes5, "bytes5", 0) \
|
||||||
K(Hash48, "hash48", 0) \
|
K(Bytes6, "bytes6", 0) \
|
||||||
K(Hash56, "hash56", 0) \
|
K(Bytes7, "bytes7", 0) \
|
||||||
K(Hash64, "hash64", 0) \
|
K(Bytes8, "bytes8", 0) \
|
||||||
K(Hash72, "hash72", 0) \
|
K(Bytes9, "bytes9", 0) \
|
||||||
K(Hash80, "hash80", 0) \
|
K(Bytes10, "bytes10", 0) \
|
||||||
K(Hash88, "hash88", 0) \
|
K(Bytes11, "bytes11", 0) \
|
||||||
K(Hash96, "hash96", 0) \
|
K(Bytes12, "bytes12", 0) \
|
||||||
K(Hash104, "hash104", 0) \
|
K(Bytes13, "bytes13", 0) \
|
||||||
K(Hash112, "hash112", 0) \
|
K(Bytes14, "bytes14", 0) \
|
||||||
K(Hash120, "hash120", 0) \
|
K(Bytes15, "bytes15", 0) \
|
||||||
K(Hash128, "hash128", 0) \
|
K(Bytes16, "bytes16", 0) \
|
||||||
K(Hash136, "hash136", 0) \
|
K(Bytes17, "bytes17", 0) \
|
||||||
K(Hash144, "hash144", 0) \
|
K(Bytes18, "bytes18", 0) \
|
||||||
K(Hash152, "hash152", 0) \
|
K(Bytes19, "bytes19", 0) \
|
||||||
K(Hash160, "hash160", 0) \
|
K(Bytes20, "bytes20", 0) \
|
||||||
K(Hash168, "hash168", 0) \
|
K(Bytes21, "bytes21", 0) \
|
||||||
K(Hash176, "hash178", 0) \
|
K(Bytes22, "bytes22", 0) \
|
||||||
K(Hash184, "hash184", 0) \
|
K(Bytes23, "bytes23", 0) \
|
||||||
K(Hash192, "hash192", 0) \
|
K(Bytes24, "bytes24", 0) \
|
||||||
K(Hash200, "hash200", 0) \
|
K(Bytes25, "bytes25", 0) \
|
||||||
K(Hash208, "hash208", 0) \
|
K(Bytes26, "bytes26", 0) \
|
||||||
K(Hash216, "hash216", 0) \
|
K(Bytes27, "bytes27", 0) \
|
||||||
K(Hash224, "hash224", 0) \
|
K(Bytes28, "bytes28", 0) \
|
||||||
K(Hash232, "hash232", 0) \
|
K(Bytes29, "bytes29", 0) \
|
||||||
K(Hash240, "hash240", 0) \
|
K(Bytes30, "bytes30", 0) \
|
||||||
K(Hash248, "hash248", 0) \
|
K(Bytes31, "bytes31", 0) \
|
||||||
K(Hash256, "hash256", 0) \
|
K(Bytes32, "bytes32", 0) \
|
||||||
|
K(Bytes, "bytes", 0) \
|
||||||
|
K(Byte, "byte", 0) \
|
||||||
K(Address, "address", 0) \
|
K(Address, "address", 0) \
|
||||||
K(Bool, "bool", 0) \
|
K(Bool, "bool", 0) \
|
||||||
K(Bytes, "bytes", 0) \
|
|
||||||
K(StringType, "string", 0) \
|
|
||||||
K(String0, "string0", 0) \
|
|
||||||
K(String1, "string1", 0) \
|
|
||||||
K(String2, "string2", 0) \
|
|
||||||
K(String3, "string3", 0) \
|
|
||||||
K(String4, "string4", 0) \
|
|
||||||
K(String5, "string5", 0) \
|
|
||||||
K(String6, "string6", 0) \
|
|
||||||
K(String7, "string7", 0) \
|
|
||||||
K(String8, "string8", 0) \
|
|
||||||
K(String9, "string9", 0) \
|
|
||||||
K(String10, "string10", 0) \
|
|
||||||
K(String11, "string11", 0) \
|
|
||||||
K(String12, "string12", 0) \
|
|
||||||
K(String13, "string13", 0) \
|
|
||||||
K(String14, "string14", 0) \
|
|
||||||
K(String15, "string15", 0) \
|
|
||||||
K(String16, "string16", 0) \
|
|
||||||
K(String17, "string17", 0) \
|
|
||||||
K(String18, "string18", 0) \
|
|
||||||
K(String19, "string19", 0) \
|
|
||||||
K(String20, "string20", 0) \
|
|
||||||
K(String21, "string21", 0) \
|
|
||||||
K(String22, "string22", 0) \
|
|
||||||
K(String23, "string23", 0) \
|
|
||||||
K(String24, "string24", 0) \
|
|
||||||
K(String25, "string25", 0) \
|
|
||||||
K(String26, "string26", 0) \
|
|
||||||
K(String27, "string27", 0) \
|
|
||||||
K(String28, "string28", 0) \
|
|
||||||
K(String29, "string29", 0) \
|
|
||||||
K(String30, "string30", 0) \
|
|
||||||
K(String31, "string31", 0) \
|
|
||||||
K(String32, "string32", 0) \
|
|
||||||
K(Text, "text", 0) \
|
|
||||||
K(Real, "real", 0) \
|
K(Real, "real", 0) \
|
||||||
K(UReal, "ureal", 0) \
|
K(UReal, "ureal", 0) \
|
||||||
T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
|
T(TypesEnd, NULL, 0) /* used as type enum end marker */ \
|
||||||
@ -338,6 +303,16 @@ namespace solidity
|
|||||||
/* Identifiers (not keywords or future reserved words). */ \
|
/* Identifiers (not keywords or future reserved words). */ \
|
||||||
T(Identifier, NULL, 0) \
|
T(Identifier, NULL, 0) \
|
||||||
\
|
\
|
||||||
|
/* Keywords reserved for future. use*/ \
|
||||||
|
T(String, "string", 0) \
|
||||||
|
K(Case, "case", 0) \
|
||||||
|
K(Switch, "switch", 0) \
|
||||||
|
K(Throw, "throw", 0) \
|
||||||
|
K(Try, "try", 0) \
|
||||||
|
K(Catch, "catch", 0) \
|
||||||
|
K(Using, "using", 0) \
|
||||||
|
K(Type, "type", 0) \
|
||||||
|
K(TypeOf, "typeof", 0) \
|
||||||
/* Illegal token - not able to scan. */ \
|
/* Illegal token - not able to scan. */ \
|
||||||
T(Illegal, "ILLEGAL", 0) \
|
T(Illegal, "ILLEGAL", 0) \
|
||||||
\
|
\
|
||||||
|
408
Types.cpp
408
Types.cpp
@ -20,14 +20,14 @@
|
|||||||
* Solidity data types
|
* Solidity data types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/Types.h>
|
||||||
|
#include <limits>
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libsolidity/Utils.h>
|
#include <libsolidity/Utils.h>
|
||||||
#include <libsolidity/Types.h>
|
|
||||||
#include <libsolidity/AST.h>
|
#include <libsolidity/AST.h>
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
@ -35,28 +35,113 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
||||||
|
{
|
||||||
|
bigint slotOffset = 0;
|
||||||
|
unsigned byteOffset = 0;
|
||||||
|
map<size_t, pair<u256, unsigned>> offsets;
|
||||||
|
for (size_t i = 0; i < _types.size(); ++i)
|
||||||
|
{
|
||||||
|
TypePointer const& type = _types[i];
|
||||||
|
if (!type->canBeStored())
|
||||||
|
continue;
|
||||||
|
if (byteOffset + type->getStorageBytes() > 32)
|
||||||
|
{
|
||||||
|
// would overflow, go to next slot
|
||||||
|
++slotOffset;
|
||||||
|
byteOffset = 0;
|
||||||
|
}
|
||||||
|
if (slotOffset >= bigint(1) << 256)
|
||||||
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
|
||||||
|
offsets[i] = make_pair(u256(slotOffset), byteOffset);
|
||||||
|
solAssert(type->getStorageSize() >= 1, "Invalid storage size.");
|
||||||
|
if (type->getStorageSize() == 1 && byteOffset + type->getStorageBytes() <= 32)
|
||||||
|
byteOffset += type->getStorageBytes();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slotOffset += type->getStorageSize();
|
||||||
|
byteOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (byteOffset > 0)
|
||||||
|
++slotOffset;
|
||||||
|
if (slotOffset >= bigint(1) << 256)
|
||||||
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Object too large for storage."));
|
||||||
|
m_storageSize = u256(slotOffset);
|
||||||
|
swap(m_offsets, offsets);
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<u256, unsigned> const* StorageOffsets::getOffset(size_t _index) const
|
||||||
|
{
|
||||||
|
if (m_offsets.count(_index))
|
||||||
|
return &m_offsets.at(_index);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberList& MemberList::operator=(MemberList&& _other)
|
||||||
|
{
|
||||||
|
m_memberTypes = std::move(_other.m_memberTypes);
|
||||||
|
m_storageOffsets = std::move(_other.m_storageOffsets);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<u256, unsigned> const* MemberList::getMemberStorageOffset(string const& _name) const
|
||||||
|
{
|
||||||
|
if (!m_storageOffsets)
|
||||||
|
{
|
||||||
|
TypePointers memberTypes;
|
||||||
|
memberTypes.reserve(m_memberTypes.size());
|
||||||
|
for (auto const& nameAndType: m_memberTypes)
|
||||||
|
memberTypes.push_back(nameAndType.second);
|
||||||
|
m_storageOffsets.reset(new StorageOffsets());
|
||||||
|
m_storageOffsets->computeOffsets(memberTypes);
|
||||||
|
}
|
||||||
|
for (size_t index = 0; index < m_memberTypes.size(); ++index)
|
||||||
|
if (m_memberTypes[index].first == _name)
|
||||||
|
return m_storageOffsets->getOffset(index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u256 const& MemberList::getStorageSize() const
|
||||||
|
{
|
||||||
|
// trigger lazy computation
|
||||||
|
getMemberStorageOffset("");
|
||||||
|
return m_storageOffsets->getStorageSize();
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
|
TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
|
||||||
{
|
{
|
||||||
solAssert(Token::isElementaryTypeName(_typeToken), "Elementary type name expected.");
|
char const* tokenCstr = Token::toString(_typeToken);
|
||||||
|
solAssert(Token::isElementaryTypeName(_typeToken),
|
||||||
|
"Expected an elementary type name but got " + ((tokenCstr) ? std::string(Token::toString(_typeToken)) : ""));
|
||||||
|
|
||||||
if (Token::Int <= _typeToken && _typeToken <= Token::Hash256)
|
if (Token::Int <= _typeToken && _typeToken <= Token::Bytes32)
|
||||||
{
|
{
|
||||||
int offset = _typeToken - Token::Int;
|
int offset = _typeToken - Token::Int;
|
||||||
int bytes = offset % 33;
|
int bytes = offset % 33;
|
||||||
if (bytes == 0)
|
if (bytes == 0 && _typeToken != Token::Bytes0)
|
||||||
bytes = 32;
|
bytes = 32;
|
||||||
int modifier = offset / 33;
|
int modifier = offset / 33;
|
||||||
return make_shared<IntegerType>(bytes * 8,
|
switch(modifier)
|
||||||
modifier == 0 ? IntegerType::Modifier::Signed :
|
{
|
||||||
modifier == 1 ? IntegerType::Modifier::Unsigned :
|
case 0:
|
||||||
IntegerType::Modifier::Hash);
|
return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Signed);
|
||||||
|
case 1:
|
||||||
|
return make_shared<IntegerType>(bytes * 8, IntegerType::Modifier::Unsigned);
|
||||||
|
case 2:
|
||||||
|
return make_shared<FixedBytesType>(bytes);
|
||||||
|
default:
|
||||||
|
solAssert(false, "Unexpected modifier value. Should never happen");
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if (_typeToken == Token::Byte)
|
||||||
|
return make_shared<FixedBytesType>(1);
|
||||||
else if (_typeToken == Token::Address)
|
else if (_typeToken == Token::Address)
|
||||||
return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
|
return make_shared<IntegerType>(0, IntegerType::Modifier::Address);
|
||||||
else if (_typeToken == Token::Bool)
|
else if (_typeToken == Token::Bool)
|
||||||
return make_shared<BoolType>();
|
return make_shared<BoolType>();
|
||||||
else if (Token::String0 <= _typeToken && _typeToken <= Token::String32)
|
|
||||||
return make_shared<StaticStringType>(int(_typeToken) - int(Token::String0));
|
|
||||||
else if (_typeToken == Token::Bytes)
|
else if (_typeToken == Token::Bytes)
|
||||||
return make_shared<ArrayType>(ArrayType::Location::Storage);
|
return make_shared<ArrayType>(ArrayType::Location::Storage);
|
||||||
else
|
else
|
||||||
@ -99,6 +184,8 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
|
|||||||
TypePointer baseType = _baseTypeName.toType();
|
TypePointer baseType = _baseTypeName.toType();
|
||||||
if (!baseType)
|
if (!baseType)
|
||||||
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
|
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
|
||||||
|
if (baseType->getStorageBytes() == 0)
|
||||||
|
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array."));
|
||||||
if (_length)
|
if (_length)
|
||||||
{
|
{
|
||||||
if (!_length->getType())
|
if (!_length->getType())
|
||||||
@ -123,7 +210,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
|
|||||||
return make_shared<IntegerConstantType>(_literal);
|
return make_shared<IntegerConstantType>(_literal);
|
||||||
case Token::StringLiteral:
|
case Token::StringLiteral:
|
||||||
//@todo put larger strings into dynamic strings
|
//@todo put larger strings into dynamic strings
|
||||||
return StaticStringType::smallestTypeForLiteral(_literal.getValue());
|
return FixedBytesType::smallestTypeForLiteral(_literal.getValue());
|
||||||
default:
|
default:
|
||||||
return shared_ptr<Type>();
|
return shared_ptr<Type>();
|
||||||
}
|
}
|
||||||
@ -139,7 +226,7 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
|
|||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberList Type::EmptyMemberList = MemberList();
|
const MemberList Type::EmptyMemberList;
|
||||||
|
|
||||||
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
|
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier):
|
||||||
m_bits(_bits), m_modifier(_modifier)
|
m_bits(_bits), m_modifier(_modifier)
|
||||||
@ -159,8 +246,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
if (isAddress())
|
if (isAddress())
|
||||||
return convertTo.isAddress();
|
return convertTo.isAddress();
|
||||||
else if (isHash())
|
|
||||||
return convertTo.isHash();
|
|
||||||
else if (isSigned())
|
else if (isSigned())
|
||||||
return convertTo.isSigned();
|
return convertTo.isSigned();
|
||||||
else
|
else
|
||||||
@ -169,14 +254,10 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
|||||||
|
|
||||||
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.getCategory() == Category::String)
|
|
||||||
{
|
|
||||||
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
|
|
||||||
return isHash() && (m_bits == convertTo.getNumBytes() * 8);
|
|
||||||
}
|
|
||||||
return _convertTo.getCategory() == getCategory() ||
|
return _convertTo.getCategory() == getCategory() ||
|
||||||
_convertTo.getCategory() == Category::Contract ||
|
_convertTo.getCategory() == Category::Contract ||
|
||||||
_convertTo.getCategory() == Category::Enum;
|
_convertTo.getCategory() == Category::Enum ||
|
||||||
|
_convertTo.getCategory() == Category::FixedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
|
TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
|
||||||
@ -187,16 +268,10 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
|
|||||||
// no further unary operators for addresses
|
// no further unary operators for addresses
|
||||||
else if (isAddress())
|
else if (isAddress())
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
// "~" is ok for all other types
|
// for non-address integers, we allow +, -, ++ and --
|
||||||
else if (_operator == Token::BitNot)
|
|
||||||
return shared_from_this();
|
|
||||||
// nothing else for hashes
|
|
||||||
else if (isHash())
|
|
||||||
return TypePointer();
|
|
||||||
// for non-hash integers, we allow +, -, ++ and --
|
|
||||||
else if (_operator == Token::Add || _operator == Token::Sub ||
|
else if (_operator == Token::Add || _operator == Token::Sub ||
|
||||||
_operator == Token::Inc || _operator == Token::Dec ||
|
_operator == Token::Inc || _operator == Token::Dec ||
|
||||||
_operator == Token::After)
|
_operator == Token::After || _operator == Token::BitNot)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
else
|
else
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
@ -214,7 +289,7 @@ string IntegerType::toString() const
|
|||||||
{
|
{
|
||||||
if (isAddress())
|
if (isAddress())
|
||||||
return "address";
|
return "address";
|
||||||
string prefix = isHash() ? "hash" : (isSigned() ? "int" : "uint");
|
string prefix = isSigned() ? "int" : "uint";
|
||||||
return prefix + dev::toString(m_bits);
|
return prefix + dev::toString(m_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,20 +305,18 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
|
|||||||
// All integer types can be compared
|
// All integer types can be compared
|
||||||
if (Token::isCompareOp(_operator))
|
if (Token::isCompareOp(_operator))
|
||||||
return commonType;
|
return commonType;
|
||||||
|
// Nothing else can be done with addresses
|
||||||
// Nothing else can be done with addresses, but hashes can receive bit operators
|
|
||||||
if (commonType->isAddress())
|
if (commonType->isAddress())
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
else if (commonType->isHash() && !Token::isBitOp(_operator))
|
|
||||||
return TypePointer();
|
return commonType;
|
||||||
else
|
|
||||||
return commonType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberList IntegerType::AddressMemberList =
|
const MemberList IntegerType::AddressMemberList({
|
||||||
MemberList({{"balance", make_shared<IntegerType >(256)},
|
{"balance", make_shared<IntegerType >(256)},
|
||||||
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
|
{"call", make_shared<FunctionType>(strings(), strings(), FunctionType::Location::Bare, true)},
|
||||||
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}});
|
{"send", make_shared<FunctionType>(strings{"uint"}, strings{}, FunctionType::Location::Send)}
|
||||||
|
});
|
||||||
|
|
||||||
IntegerConstantType::IntegerConstantType(Literal const& _literal)
|
IntegerConstantType::IntegerConstantType(Literal const& _literal)
|
||||||
{
|
{
|
||||||
@ -284,8 +357,17 @@ IntegerConstantType::IntegerConstantType(Literal const& _literal)
|
|||||||
|
|
||||||
bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
bool IntegerConstantType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
TypePointer integerType = getIntegerType();
|
shared_ptr<IntegerType const> integerType = getIntegerType();
|
||||||
return integerType && integerType->isImplicitlyConvertibleTo(_convertTo);
|
if (!integerType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_convertTo.getCategory() == Category::FixedBytes)
|
||||||
|
{
|
||||||
|
FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo);
|
||||||
|
return convertTo.getNumBytes() * 8 >= integerType->getNumBits();
|
||||||
|
}
|
||||||
|
|
||||||
|
return integerType->isImplicitlyConvertibleTo(_convertTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
bool IntegerConstantType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
@ -433,50 +515,67 @@ shared_ptr<IntegerType const> IntegerConstantType::getIntegerType() const
|
|||||||
: IntegerType::Modifier::Unsigned);
|
: IntegerType::Modifier::Unsigned);
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<StaticStringType> StaticStringType::smallestTypeForLiteral(string const& _literal)
|
shared_ptr<FixedBytesType> FixedBytesType::smallestTypeForLiteral(string const& _literal)
|
||||||
{
|
{
|
||||||
if (_literal.length() <= 32)
|
if (_literal.length() <= 32)
|
||||||
return make_shared<StaticStringType>(_literal.length());
|
return make_shared<FixedBytesType>(_literal.length());
|
||||||
return shared_ptr<StaticStringType>();
|
return shared_ptr<FixedBytesType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
StaticStringType::StaticStringType(int _bytes): m_bytes(_bytes)
|
FixedBytesType::FixedBytesType(int _bytes): m_bytes(_bytes)
|
||||||
{
|
{
|
||||||
solAssert(m_bytes >= 0 && m_bytes <= 32,
|
solAssert(m_bytes >= 0 && m_bytes <= 32,
|
||||||
"Invalid byte number for static string type: " + dev::toString(m_bytes));
|
"Invalid byte number for fixed bytes type: " + dev::toString(m_bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StaticStringType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.getCategory() != getCategory())
|
if (_convertTo.getCategory() != getCategory())
|
||||||
return false;
|
return false;
|
||||||
StaticStringType const& convertTo = dynamic_cast<StaticStringType const&>(_convertTo);
|
FixedBytesType const& convertTo = dynamic_cast<FixedBytesType const&>(_convertTo);
|
||||||
return convertTo.m_bytes >= m_bytes;
|
return convertTo.m_bytes >= m_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StaticStringType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
if (_convertTo.getCategory() == getCategory())
|
return _convertTo.getCategory() == Category::Integer ||
|
||||||
return true;
|
_convertTo.getCategory() == Category::Contract ||
|
||||||
if (_convertTo.getCategory() == Category::Integer)
|
_convertTo.getCategory() == getCategory();
|
||||||
{
|
|
||||||
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
|
|
||||||
if (convertTo.isHash() && (m_bytes * 8 == convertTo.getNumBits()))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StaticStringType::operator==(Type const& _other) const
|
TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
|
||||||
|
{
|
||||||
|
// "delete" and "~" is okay for FixedBytesType
|
||||||
|
if (_operator == Token::Delete)
|
||||||
|
return make_shared<VoidType>();
|
||||||
|
else if (_operator == Token::BitNot)
|
||||||
|
return shared_from_this();
|
||||||
|
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
|
||||||
|
{
|
||||||
|
auto commonType = dynamic_pointer_cast<FixedBytesType const>(Type::commonType(shared_from_this(), _other));
|
||||||
|
if (!commonType)
|
||||||
|
return TypePointer();
|
||||||
|
|
||||||
|
// FixedBytes can be compared and have bitwise operators applied to them
|
||||||
|
if (Token::isCompareOp(_operator) || Token::isBitOp(_operator))
|
||||||
|
return commonType;
|
||||||
|
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixedBytesType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
return false;
|
return false;
|
||||||
StaticStringType const& other = dynamic_cast<StaticStringType const&>(_other);
|
FixedBytesType const& other = dynamic_cast<FixedBytesType const&>(_other);
|
||||||
return other.m_bytes == m_bytes;
|
return other.m_bytes == m_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 StaticStringType::literalValue(const Literal* _literal) const
|
u256 FixedBytesType::literalValue(const Literal* _literal) const
|
||||||
{
|
{
|
||||||
solAssert(_literal, "");
|
solAssert(_literal, "");
|
||||||
u256 value = 0;
|
u256 value = 0;
|
||||||
@ -603,13 +702,21 @@ u256 ArrayType::getStorageSize() const
|
|||||||
{
|
{
|
||||||
if (isDynamicallySized())
|
if (isDynamicallySized())
|
||||||
return 1;
|
return 1;
|
||||||
else
|
|
||||||
|
bigint size;
|
||||||
|
unsigned baseBytes = getBaseType()->getStorageBytes();
|
||||||
|
if (baseBytes == 0)
|
||||||
|
size = 1;
|
||||||
|
else if (baseBytes < 32)
|
||||||
{
|
{
|
||||||
bigint size = bigint(getLength()) * getBaseType()->getStorageSize();
|
unsigned itemsPerSlot = 32 / baseBytes;
|
||||||
if (size >= bigint(1) << 256)
|
size = (bigint(getLength()) + (itemsPerSlot - 1)) / itemsPerSlot;
|
||||||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage."));
|
|
||||||
return max<u256>(1, u256(size));
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
size = bigint(getLength()) * getBaseType()->getStorageSize();
|
||||||
|
if (size >= bigint(1) << 256)
|
||||||
|
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Array too large for storage."));
|
||||||
|
return max<u256>(1, u256(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned ArrayType::getSizeOnStack() const
|
unsigned ArrayType::getSizeOnStack() const
|
||||||
@ -617,6 +724,9 @@ unsigned ArrayType::getSizeOnStack() const
|
|||||||
if (m_location == Location::CallData)
|
if (m_location == Location::CallData)
|
||||||
// offset [length] (stack top)
|
// offset [length] (stack top)
|
||||||
return 1 + (isDynamicallySized() ? 1 : 0);
|
return 1 + (isDynamicallySized() ? 1 : 0);
|
||||||
|
else if (m_location == Location::Storage)
|
||||||
|
// storage_key storage_offset
|
||||||
|
return 2;
|
||||||
else
|
else
|
||||||
// offset
|
// offset
|
||||||
return 1;
|
return 1;
|
||||||
@ -632,6 +742,23 @@ string ArrayType::toString() const
|
|||||||
return ret + "]";
|
return ret + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer ArrayType::externalType() const
|
||||||
|
{
|
||||||
|
if (m_location != Location::CallData)
|
||||||
|
return TypePointer();
|
||||||
|
if (m_isByteArray)
|
||||||
|
return shared_from_this();
|
||||||
|
if (!m_baseType->externalType())
|
||||||
|
return TypePointer();
|
||||||
|
if (m_baseType->getCategory() == Category::Array && m_baseType->isDynamicallySized())
|
||||||
|
return TypePointer();
|
||||||
|
|
||||||
|
if (isDynamicallySized())
|
||||||
|
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType());
|
||||||
|
else
|
||||||
|
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
|
||||||
|
}
|
||||||
|
|
||||||
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
|
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
|
||||||
{
|
{
|
||||||
auto copy = make_shared<ArrayType>(_location);
|
auto copy = make_shared<ArrayType>(_location);
|
||||||
@ -645,7 +772,7 @@ shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location)
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MemberList ArrayType::s_arrayTypeMemberList = MemberList({{"length", make_shared<IntegerType>(256)}});
|
const MemberList ArrayType::s_arrayTypeMemberList({{"length", make_shared<IntegerType>(256)}});
|
||||||
|
|
||||||
bool ContractType::operator==(Type const& _other) const
|
bool ContractType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
@ -706,6 +833,26 @@ u256 ContractType::getFunctionIdentifier(string const& _functionName) const
|
|||||||
return Invalid256;
|
return Invalid256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::getStateVariables() const
|
||||||
|
{
|
||||||
|
vector<VariableDeclaration const*> variables;
|
||||||
|
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.getLinearizedBaseContracts()))
|
||||||
|
for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables())
|
||||||
|
if (!variable->isConstant())
|
||||||
|
variables.push_back(variable.get());
|
||||||
|
TypePointers types;
|
||||||
|
for (auto variable: variables)
|
||||||
|
types.push_back(variable->getType());
|
||||||
|
StorageOffsets offsets;
|
||||||
|
offsets.computeOffsets(types);
|
||||||
|
|
||||||
|
vector<tuple<VariableDeclaration const*, u256, unsigned>> variablesAndOffsets;
|
||||||
|
for (size_t index = 0; index < variables.size(); ++index)
|
||||||
|
if (auto const* offset = offsets.getOffset(index))
|
||||||
|
variablesAndOffsets.push_back(make_tuple(variables[index], offset->first, offset->second));
|
||||||
|
return variablesAndOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
|
TypePointer StructType::unaryOperatorResult(Token::Value _operator) const
|
||||||
{
|
{
|
||||||
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
|
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();
|
||||||
@ -721,12 +868,7 @@ bool StructType::operator==(Type const& _other) const
|
|||||||
|
|
||||||
u256 StructType::getStorageSize() const
|
u256 StructType::getStorageSize() const
|
||||||
{
|
{
|
||||||
bigint size = 0;
|
return max<u256>(1, getMembers().getStorageSize());
|
||||||
for (pair<string, TypePointer> const& member: getMembers())
|
|
||||||
size += member.second->getStorageSize();
|
|
||||||
if (size >= bigint(1) << 256)
|
|
||||||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Struct too large for storage."));
|
|
||||||
return max<u256>(1, u256(size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StructType::canLiveOutsideStorage() const
|
bool StructType::canLiveOutsideStorage() const
|
||||||
@ -755,17 +897,11 @@ MemberList const& StructType::getMembers() const
|
|||||||
return *m_members;
|
return *m_members;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 StructType::getStorageOffsetOfMember(string const& _name) const
|
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
|
||||||
{
|
{
|
||||||
//@todo cache member offset?
|
auto const* offsets = getMembers().getMemberStorageOffset(_name);
|
||||||
u256 offset;
|
solAssert(offsets, "Storage offset of non-existing member requested.");
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
|
return *offsets;
|
||||||
{
|
|
||||||
if (variable->getName() == _name)
|
|
||||||
return offset;
|
|
||||||
offset += variable->getType()->getStorageSize();
|
|
||||||
}
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage offset of non-existing member requested."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
|
||||||
@ -781,6 +917,15 @@ bool EnumType::operator==(Type const& _other) const
|
|||||||
return other.m_enum == m_enum;
|
return other.m_enum == m_enum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned EnumType::getStorageBytes() const
|
||||||
|
{
|
||||||
|
size_t elements = m_enum.getMembers().size();
|
||||||
|
if (elements <= 1)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
return dev::bytesRequired(elements - 1);
|
||||||
|
}
|
||||||
|
|
||||||
string EnumType::toString() const
|
string EnumType::toString() const
|
||||||
{
|
{
|
||||||
return string("enum ") + m_enum.getName();
|
return string("enum ") + m_enum.getName();
|
||||||
@ -925,6 +1070,13 @@ string FunctionType::toString() const
|
|||||||
return name + ")";
|
return name + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u256 FunctionType::getStorageSize() const
|
||||||
|
{
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable function type requested."));
|
||||||
|
}
|
||||||
|
|
||||||
unsigned FunctionType::getSizeOnStack() const
|
unsigned FunctionType::getSizeOnStack() const
|
||||||
{
|
{
|
||||||
Location location = m_location;
|
Location location = m_location;
|
||||||
@ -946,6 +1098,26 @@ unsigned FunctionType::getSizeOnStack() const
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer FunctionType::externalType() const
|
||||||
|
{
|
||||||
|
TypePointers paramTypes;
|
||||||
|
TypePointers retParamTypes;
|
||||||
|
|
||||||
|
for (auto type: m_parameterTypes)
|
||||||
|
{
|
||||||
|
if (!type->externalType())
|
||||||
|
return TypePointer();
|
||||||
|
paramTypes.push_back(type->externalType());
|
||||||
|
}
|
||||||
|
for (auto type: m_returnParameterTypes)
|
||||||
|
{
|
||||||
|
if (!type->externalType())
|
||||||
|
return TypePointer();
|
||||||
|
retParamTypes.push_back(type->externalType());
|
||||||
|
}
|
||||||
|
return make_shared<FunctionType>(paramTypes, retParamTypes, m_location, m_arbitraryParameters);
|
||||||
|
}
|
||||||
|
|
||||||
MemberList const& FunctionType::getMembers() const
|
MemberList const& FunctionType::getMembers() const
|
||||||
{
|
{
|
||||||
switch (m_location)
|
switch (m_location)
|
||||||
@ -975,7 +1147,7 @@ MemberList const& FunctionType::getMembers() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string FunctionType::getCanonicalSignature(std::string const& _name) const
|
string FunctionType::externalSignature(std::string const& _name) const
|
||||||
{
|
{
|
||||||
std::string funcName = _name;
|
std::string funcName = _name;
|
||||||
if (_name == "")
|
if (_name == "")
|
||||||
@ -985,8 +1157,12 @@ string FunctionType::getCanonicalSignature(std::string const& _name) const
|
|||||||
}
|
}
|
||||||
string ret = funcName + "(";
|
string ret = funcName + "(";
|
||||||
|
|
||||||
for (auto it = m_parameterTypes.cbegin(); it != m_parameterTypes.cend(); ++it)
|
TypePointers externalParameterTypes = dynamic_cast<FunctionType const&>(*externalType()).getParameterTypes();
|
||||||
ret += (*it)->toString() + (it + 1 == m_parameterTypes.cend() ? "" : ",");
|
for (auto it = externalParameterTypes.cbegin(); it != externalParameterTypes.cend(); ++it)
|
||||||
|
{
|
||||||
|
solAssert(!!(*it), "Parameter should have external type");
|
||||||
|
ret += (*it)->toString() + (it + 1 == externalParameterTypes.cend() ? "" : ",");
|
||||||
|
}
|
||||||
|
|
||||||
return ret + ")";
|
return ret + ")";
|
||||||
}
|
}
|
||||||
@ -1047,6 +1223,13 @@ string MappingType::toString() const
|
|||||||
return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")";
|
return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u256 VoidType::getStorageSize() const
|
||||||
|
{
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable void type requested."));
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeType::operator==(Type const& _other) const
|
bool TypeType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
@ -1055,6 +1238,13 @@ bool TypeType::operator==(Type const& _other) const
|
|||||||
return *getActualType() == *other.getActualType();
|
return *getActualType() == *other.getActualType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u256 TypeType::getStorageSize() const
|
||||||
|
{
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable type type requested."));
|
||||||
|
}
|
||||||
|
|
||||||
MemberList const& TypeType::getMembers() const
|
MemberList const& TypeType::getMembers() const
|
||||||
{
|
{
|
||||||
// We need to lazy-initialize it because of recursive references.
|
// We need to lazy-initialize it because of recursive references.
|
||||||
@ -1092,6 +1282,13 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier)
|
|||||||
swap(params, m_parameterTypes);
|
swap(params, m_parameterTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u256 ModifierType::getStorageSize() const
|
||||||
|
{
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable type type requested."));
|
||||||
|
}
|
||||||
|
|
||||||
bool ModifierType::operator==(Type const& _other) const
|
bool ModifierType::operator==(Type const& _other) const
|
||||||
{
|
{
|
||||||
if (_other.getCategory() != getCategory())
|
if (_other.getCategory() != getCategory())
|
||||||
@ -1122,22 +1319,29 @@ MagicType::MagicType(MagicType::Kind _kind):
|
|||||||
switch (m_kind)
|
switch (m_kind)
|
||||||
{
|
{
|
||||||
case Kind::Block:
|
case Kind::Block:
|
||||||
m_members = MemberList({{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
m_members = move(MemberList({
|
||||||
{"timestamp", make_shared<IntegerType>(256)},
|
{"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
||||||
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"hash"}, FunctionType::Location::BlockHash)},
|
{"timestamp", make_shared<IntegerType>(256)},
|
||||||
{"difficulty", make_shared<IntegerType>(256)},
|
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)},
|
||||||
{"number", make_shared<IntegerType>(256)},
|
{"difficulty", make_shared<IntegerType>(256)},
|
||||||
{"gaslimit", make_shared<IntegerType>(256)}});
|
{"number", make_shared<IntegerType>(256)},
|
||||||
|
{"gaslimit", make_shared<IntegerType>(256)}
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
case Kind::Message:
|
case Kind::Message:
|
||||||
m_members = MemberList({{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
m_members = move(MemberList({
|
||||||
{"gas", make_shared<IntegerType>(256)},
|
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
||||||
{"value", make_shared<IntegerType>(256)},
|
{"gas", make_shared<IntegerType>(256)},
|
||||||
{"data", make_shared<ArrayType>(ArrayType::Location::CallData)}});
|
{"value", make_shared<IntegerType>(256)},
|
||||||
|
{"data", make_shared<ArrayType>(ArrayType::Location::CallData)},
|
||||||
|
{"sig", make_shared<FixedBytesType>(4)}
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
case Kind::Transaction:
|
case Kind::Transaction:
|
||||||
m_members = MemberList({{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
m_members = move(MemberList({
|
||||||
{"gasprice", make_shared<IntegerType>(256)}});
|
{"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
||||||
|
{"gasprice", make_shared<IntegerType>(256)}
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
|
||||||
|
131
Types.h
131
Types.h
@ -43,6 +43,26 @@ using TypePointer = std::shared_ptr<Type const>;
|
|||||||
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
using FunctionTypePointer = std::shared_ptr<FunctionType const>;
|
||||||
using TypePointers = std::vector<TypePointer>;
|
using TypePointers = std::vector<TypePointer>;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to compute storage offsets of members of structs and contracts.
|
||||||
|
*/
|
||||||
|
class StorageOffsets
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Resets the StorageOffsets objects and determines the position in storage for each
|
||||||
|
/// of the elements of @a _types.
|
||||||
|
void computeOffsets(TypePointers const& _types);
|
||||||
|
/// @returns the offset of the given member, might be null if the member is not part of storage.
|
||||||
|
std::pair<u256, unsigned> const* getOffset(size_t _index) const;
|
||||||
|
/// @returns the total number of slots occupied by all members.
|
||||||
|
u256 const& getStorageSize() const { return m_storageSize; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
u256 m_storageSize;
|
||||||
|
std::map<size_t, std::pair<u256, unsigned>> m_offsets;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of members of a type.
|
* List of members of a type.
|
||||||
*/
|
*/
|
||||||
@ -53,6 +73,7 @@ public:
|
|||||||
|
|
||||||
MemberList() {}
|
MemberList() {}
|
||||||
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
|
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
|
||||||
|
MemberList& operator=(MemberList&& _other);
|
||||||
TypePointer getMemberType(std::string const& _name) const
|
TypePointer getMemberType(std::string const& _name) const
|
||||||
{
|
{
|
||||||
for (auto const& it: m_memberTypes)
|
for (auto const& it: m_memberTypes)
|
||||||
@ -60,12 +81,18 @@ public:
|
|||||||
return it.second;
|
return it.second;
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
|
/// @returns the offset of the given member in storage slots and bytes inside a slot or
|
||||||
|
/// a nullptr if the member is not part of storage.
|
||||||
|
std::pair<u256, unsigned> const* getMemberStorageOffset(std::string const& _name) const;
|
||||||
|
/// @returns the number of storage slots occupied by the members.
|
||||||
|
u256 const& getStorageSize() const;
|
||||||
|
|
||||||
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
|
MemberMap::const_iterator begin() const { return m_memberTypes.begin(); }
|
||||||
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
MemberMap::const_iterator end() const { return m_memberTypes.end(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MemberMap m_memberTypes;
|
MemberMap m_memberTypes;
|
||||||
|
mutable std::unique_ptr<StorageOffsets> m_storageOffsets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,12 +104,12 @@ public:
|
|||||||
enum class Category
|
enum class Category
|
||||||
{
|
{
|
||||||
Integer, IntegerConstant, Bool, Real, Array,
|
Integer, IntegerConstant, Bool, Real, Array,
|
||||||
String, Contract, Struct, Function, OverloadedFunctions, Enum,
|
FixedBytes, Contract, Struct, Function, OverloadedFunctions, Enum,
|
||||||
Mapping, Void, TypeType, Modifier, Magic
|
Mapping, Void, TypeType, Modifier, Magic
|
||||||
};
|
};
|
||||||
|
|
||||||
///@{
|
/// @{
|
||||||
///@name Factory functions
|
/// @name Factory functions
|
||||||
/// Factory functions that convert an AST @ref TypeName to a Type.
|
/// Factory functions that convert an AST @ref TypeName to a Type.
|
||||||
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
|
static TypePointer fromElementaryTypeName(Token::Value _typeToken);
|
||||||
static TypePointer fromElementaryTypeName(std::string const& _name);
|
static TypePointer fromElementaryTypeName(std::string const& _name);
|
||||||
@ -97,6 +124,8 @@ public:
|
|||||||
/// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
|
/// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise
|
||||||
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
|
static TypePointer commonType(TypePointer const& _a, TypePointer const& _b);
|
||||||
|
|
||||||
|
/// Calculates the
|
||||||
|
|
||||||
virtual Category getCategory() const = 0;
|
virtual Category getCategory() const = 0;
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; }
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
@ -126,9 +155,15 @@ public:
|
|||||||
unsigned getCalldataEncodedSize() const { return getCalldataEncodedSize(true); }
|
unsigned getCalldataEncodedSize() const { return getCalldataEncodedSize(true); }
|
||||||
/// @returns true if the type is dynamically encoded in calldata
|
/// @returns true if the type is dynamically encoded in calldata
|
||||||
virtual bool isDynamicallySized() const { return false; }
|
virtual bool isDynamicallySized() const { return false; }
|
||||||
/// @returns number of bytes required to hold this value in storage.
|
/// @returns the number of storage slots required to hold this value in storage.
|
||||||
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
|
/// For dynamically "allocated" types, it returns the size of the statically allocated head,
|
||||||
virtual u256 getStorageSize() const { return 1; }
|
virtual u256 getStorageSize() const { return 1; }
|
||||||
|
/// Multiple small types can be packed into a single storage slot. If such a packing is possible
|
||||||
|
/// this function @returns the size in bytes smaller than 32. Data is moved to the next slot if
|
||||||
|
/// it does not fit.
|
||||||
|
/// In order to avoid computation at runtime of whether such moving is necessary, structs and
|
||||||
|
/// array data (not each element) always start a new slot.
|
||||||
|
virtual unsigned getStorageBytes() const { return 32; }
|
||||||
/// Returns true if the type can be stored in storage.
|
/// Returns true if the type can be stored in storage.
|
||||||
virtual bool canBeStored() const { return true; }
|
virtual bool canBeStored() const { return true; }
|
||||||
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
||||||
@ -152,20 +187,24 @@ public:
|
|||||||
"for type without literals."));
|
"for type without literals."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @returns a type suitable for outside of Solidity, i.e. for contract types it returns address.
|
||||||
|
/// If there is no such type, returns an empty shared pointer.
|
||||||
|
virtual TypePointer externalType() const { return TypePointer(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Convenience object used when returning an empty member list.
|
/// Convenience object used when returning an empty member list.
|
||||||
static const MemberList EmptyMemberList;
|
static const MemberList EmptyMemberList;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any kind of integer type including hash and address.
|
* Any kind of integer type (signed, unsigned, address).
|
||||||
*/
|
*/
|
||||||
class IntegerType: public Type
|
class IntegerType: public Type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Modifier
|
enum class Modifier
|
||||||
{
|
{
|
||||||
Unsigned, Signed, Hash, Address
|
Unsigned, Signed, Address
|
||||||
};
|
};
|
||||||
virtual Category getCategory() const override { return Category::Integer; }
|
virtual Category getCategory() const override { return Category::Integer; }
|
||||||
|
|
||||||
@ -179,14 +218,16 @@ public:
|
|||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; }
|
virtual unsigned getCalldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : m_bits / 8; }
|
||||||
|
virtual unsigned getStorageBytes() const override { return m_bits / 8; }
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const { return isAddress() ? AddressMemberList : EmptyMemberList; }
|
virtual MemberList const& getMembers() const override { return isAddress() ? AddressMemberList : EmptyMemberList; }
|
||||||
|
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
|
|
||||||
|
virtual TypePointer externalType() const override { return shared_from_this(); }
|
||||||
|
|
||||||
int getNumBits() const { return m_bits; }
|
int getNumBits() const { return m_bits; }
|
||||||
bool isHash() const { return m_modifier == Modifier::Hash || m_modifier == Modifier::Address; }
|
|
||||||
bool isAddress() const { return m_modifier == Modifier::Address; }
|
bool isAddress() const { return m_modifier == Modifier::Address; }
|
||||||
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
||||||
|
|
||||||
@ -232,28 +273,32 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* String type with fixed length, up to 32 bytes.
|
* Bytes type with fixed length of up to 32 bytes.
|
||||||
*/
|
*/
|
||||||
class StaticStringType: public Type
|
class FixedBytesType: public Type
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Category getCategory() const override { return Category::String; }
|
virtual Category getCategory() const override { return Category::FixedBytes; }
|
||||||
|
|
||||||
/// @returns the smallest string type for the given literal or an empty pointer
|
/// @returns the smallest bytes type for the given literal or an empty pointer
|
||||||
/// if no type fits.
|
/// if no type fits.
|
||||||
static std::shared_ptr<StaticStringType> smallestTypeForLiteral(std::string const& _literal);
|
static std::shared_ptr<FixedBytesType> smallestTypeForLiteral(std::string const& _literal);
|
||||||
|
|
||||||
explicit StaticStringType(int _bytes);
|
explicit FixedBytesType(int _bytes);
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
|
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
|
virtual unsigned getCalldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
|
||||||
|
virtual unsigned getStorageBytes() const override { return m_bytes; }
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual std::string toString() const override { return "string" + dev::toString(m_bytes); }
|
virtual std::string toString() const override { return "bytes" + dev::toString(m_bytes); }
|
||||||
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(); }
|
||||||
|
|
||||||
int getNumBytes() const { return m_bytes; }
|
int getNumBytes() const { return m_bytes; }
|
||||||
|
|
||||||
@ -273,16 +318,21 @@ public:
|
|||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
|
||||||
|
|
||||||
virtual unsigned getCalldataEncodedSize(bool _padded) const { return _padded ? 32 : 1; }
|
virtual unsigned getCalldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
|
||||||
|
virtual unsigned getStorageBytes() const override { return 1; }
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
|
|
||||||
virtual std::string toString() const override { return "bool"; }
|
virtual std::string toString() 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(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
|
* The type of an array. The flavours are byte array (bytes), statically- (<type>[<length>])
|
||||||
* and dynamically-sized array (<type>[]).
|
* and dynamically-sized array (<type>[]).
|
||||||
|
* In storage, all arrays are packed tightly (as long as more than one elementary type fits in
|
||||||
|
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
|
||||||
|
* thus start on their own slot.
|
||||||
*/
|
*/
|
||||||
class ArrayType: public Type
|
class ArrayType: public Type
|
||||||
{
|
{
|
||||||
@ -293,13 +343,22 @@ public:
|
|||||||
|
|
||||||
/// Constructor for a byte array ("bytes")
|
/// Constructor for a byte array ("bytes")
|
||||||
explicit ArrayType(Location _location):
|
explicit ArrayType(Location _location):
|
||||||
m_location(_location), m_isByteArray(true), m_baseType(std::make_shared<IntegerType>(8)) {}
|
m_location(_location),
|
||||||
|
m_isByteArray(true),
|
||||||
|
m_baseType(std::make_shared<FixedBytesType>(8))
|
||||||
|
{}
|
||||||
/// Constructor for a dynamically sized array type ("type[]")
|
/// Constructor for a dynamically sized array type ("type[]")
|
||||||
ArrayType(Location _location, const TypePointer &_baseType):
|
ArrayType(Location _location, const TypePointer &_baseType):
|
||||||
m_location(_location), m_baseType(_baseType) {}
|
m_location(_location),
|
||||||
|
m_baseType(_baseType)
|
||||||
|
{}
|
||||||
/// Constructor for a fixed-size array type ("type[20]")
|
/// Constructor for a fixed-size array type ("type[20]")
|
||||||
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
|
ArrayType(Location _location, const TypePointer &_baseType, u256 const& _length):
|
||||||
m_location(_location), m_baseType(_baseType), m_hasDynamicLength(false), m_length(_length) {}
|
m_location(_location),
|
||||||
|
m_baseType(_baseType),
|
||||||
|
m_hasDynamicLength(false),
|
||||||
|
m_length(_length)
|
||||||
|
{}
|
||||||
|
|
||||||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
@ -310,6 +369,7 @@ public:
|
|||||||
virtual unsigned getSizeOnStack() const override;
|
virtual unsigned getSizeOnStack() const override;
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; }
|
virtual MemberList const& getMembers() const override { return s_arrayTypeMemberList; }
|
||||||
|
virtual TypePointer externalType() const override;
|
||||||
|
|
||||||
Location getLocation() const { return m_location; }
|
Location getLocation() const { return m_location; }
|
||||||
bool isByteArray() const { return m_isByteArray; }
|
bool isByteArray() const { return m_isByteArray; }
|
||||||
@ -344,10 +404,12 @@ public:
|
|||||||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
|
||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual unsigned getStorageBytes() const override { return 20; }
|
||||||
virtual bool isValueType() const override { return true; }
|
virtual bool isValueType() const override { return true; }
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
virtual MemberList const& getMembers() const override;
|
||||||
|
virtual TypePointer externalType() const override { return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); }
|
||||||
|
|
||||||
bool isSuper() const { return m_super; }
|
bool isSuper() const { return m_super; }
|
||||||
ContractDefinition const& getContractDefinition() const { return m_contract; }
|
ContractDefinition const& getContractDefinition() const { return m_contract; }
|
||||||
@ -360,6 +422,10 @@ public:
|
|||||||
/// not exist.
|
/// not exist.
|
||||||
u256 getFunctionIdentifier(std::string const& _functionName) const;
|
u256 getFunctionIdentifier(std::string const& _functionName) const;
|
||||||
|
|
||||||
|
/// @returns a list of all state variables (including inherited) of the contract and their
|
||||||
|
/// offsets in storage.
|
||||||
|
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> getStateVariables() const;
|
||||||
|
|
||||||
private:
|
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
|
||||||
@ -383,12 +449,12 @@ public:
|
|||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual u256 getStorageSize() const override;
|
virtual u256 getStorageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override;
|
virtual bool canLiveOutsideStorage() const override;
|
||||||
virtual unsigned getSizeOnStack() const override { return 1; /*@todo*/ }
|
virtual unsigned getSizeOnStack() const override { return 2; }
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
virtual MemberList const& getMembers() const override;
|
||||||
|
|
||||||
u256 getStorageOffsetOfMember(std::string const& _name) const;
|
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StructDefinition const& m_struct;
|
StructDefinition const& m_struct;
|
||||||
@ -407,10 +473,12 @@ public:
|
|||||||
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual unsigned getSizeOnStack() const override { return 1; }
|
virtual unsigned getSizeOnStack() const override { return 1; }
|
||||||
|
virtual unsigned getStorageBytes() const override;
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() 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 { return std::make_shared<IntegerType>(8 * int(getStorageBytes())); }
|
||||||
|
|
||||||
EnumDefinition const& getEnumDefinition() const { return m_enum; }
|
EnumDefinition const& getEnumDefinition() const { return m_enum; }
|
||||||
/// @returns the value that the string has in the Enum
|
/// @returns the value that the string has in the Enum
|
||||||
@ -443,6 +511,11 @@ public:
|
|||||||
Bare };
|
Bare };
|
||||||
|
|
||||||
virtual Category getCategory() const override { return Category::Function; }
|
virtual Category getCategory() 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 externaltype.
|
||||||
|
virtual TypePointer externalType() const override;
|
||||||
|
|
||||||
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true);
|
||||||
explicit FunctionType(VariableDeclaration const& _varDecl);
|
explicit FunctionType(VariableDeclaration const& _varDecl);
|
||||||
explicit FunctionType(EventDefinition const& _event);
|
explicit FunctionType(EventDefinition const& _event);
|
||||||
@ -476,16 +549,16 @@ public:
|
|||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
|
virtual u256 getStorageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override;
|
virtual unsigned getSizeOnStack() const override;
|
||||||
virtual MemberList const& getMembers() const override;
|
virtual MemberList const& getMembers() const override;
|
||||||
|
|
||||||
Location const& getLocation() const { return m_location; }
|
Location const& getLocation() const { return m_location; }
|
||||||
/// @returns the canonical 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
|
/// If @a _name is not provided (empty string) then the @c m_declaration member of the
|
||||||
/// function type is used
|
/// function type is used
|
||||||
std::string getCanonicalSignature(std::string const& _name = "") const;
|
std::string externalSignature(std::string const& _name = "") const;
|
||||||
Declaration const& getDeclaration() const
|
Declaration const& getDeclaration() const
|
||||||
{
|
{
|
||||||
solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
|
solAssert(m_declaration, "Requested declaration from a FunctionType that has none");
|
||||||
@ -515,7 +588,7 @@ private:
|
|||||||
std::vector<std::string> m_parameterNames;
|
std::vector<std::string> m_parameterNames;
|
||||||
std::vector<std::string> m_returnParameterNames;
|
std::vector<std::string> m_returnParameterNames;
|
||||||
Location const m_location;
|
Location const m_location;
|
||||||
/// true iff the function takes an arbitrary number of arguments of arbitrary types
|
/// true if the function takes an arbitrary number of arguments of arbitrary types
|
||||||
bool const m_arbitraryParameters = false;
|
bool const m_arbitraryParameters = false;
|
||||||
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
|
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
|
||||||
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
|
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
|
||||||
@ -540,6 +613,7 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of a mapping, there is one distinct type per key/value type pair.
|
* The type of a mapping, there is one distinct type per key/value type pair.
|
||||||
|
* Mappings always occupy their own storage slot, but do not actually use it.
|
||||||
*/
|
*/
|
||||||
class MappingType: public Type
|
class MappingType: public Type
|
||||||
{
|
{
|
||||||
@ -550,6 +624,7 @@ public:
|
|||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual std::string toString() const override;
|
virtual std::string toString() const override;
|
||||||
|
virtual unsigned getSizeOnStack() const override { return 2; }
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
|
|
||||||
TypePointer const& getKeyType() const { return m_keyType; }
|
TypePointer const& getKeyType() const { return m_keyType; }
|
||||||
@ -573,7 +648,7 @@ public:
|
|||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual std::string toString() const override { return "void"; }
|
virtual std::string toString() const override { return "void"; }
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); }
|
virtual u256 getStorageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
virtual unsigned getSizeOnStack() const override { return 0; }
|
||||||
};
|
};
|
||||||
@ -593,7 +668,7 @@ public:
|
|||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
|
virtual u256 getStorageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
virtual unsigned getSizeOnStack() const override { return 0; }
|
||||||
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
|
virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; }
|
||||||
@ -619,7 +694,7 @@ public:
|
|||||||
|
|
||||||
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual bool canBeStored() const override { return false; }
|
||||||
virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
|
virtual u256 getStorageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual bool canLiveOutsideStorage() const override { return false; }
|
||||||
virtual unsigned getSizeOnStack() const override { return 0; }
|
virtual unsigned getSizeOnStack() const override { return 0; }
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
29
Utils.h
29
Utils.h
@ -22,34 +22,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <libdevcore/Assertions.h>
|
||||||
#include <libsolidity/Exceptions.h>
|
|
||||||
|
|
||||||
namespace dev
|
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
/// 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) \
|
||||||
::dev::solidity::solAssertAux(CONDITION, DESCRIPTION, __LINE__, __FILE__, ETH_FUNC)
|
assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
|
||||||
|
|
||||||
inline void solAssertAux(bool _condition, std::string const& _errorDescription, unsigned _line,
|
|
||||||
char const* _file, char const* _function)
|
|
||||||
{
|
|
||||||
if (!_condition)
|
|
||||||
::boost::throw_exception( InternalCompilerError()
|
|
||||||
<< errinfo_comment(_errorDescription)
|
|
||||||
<< ::boost::throw_function(_function)
|
|
||||||
<< ::boost::throw_file(_file)
|
|
||||||
<< ::boost::throw_line(_line));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void solAssertAux(void const* _pointer, std::string const& _errorDescription, unsigned _line,
|
|
||||||
char const* _file, char const* _function)
|
|
||||||
{
|
|
||||||
solAssertAux(_pointer != nullptr, _errorDescription, _line, _file, _function);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user