mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Ability to specify the storage location of a reference type.
This commit is contained in:
parent
4987eec3d1
commit
f4d1acc563
19
AST.cpp
19
AST.cpp
@ -528,6 +528,17 @@ void VariableDeclaration::checkTypeRequirements()
|
|||||||
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
|
BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VariableDeclaration::isFunctionParameter() const
|
||||||
|
{
|
||||||
|
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
|
||||||
|
if (!function)
|
||||||
|
return false;
|
||||||
|
for (auto const& variable: function->getParameters() + function->getReturnParameters())
|
||||||
|
if (variable.get() == this)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool VariableDeclaration::isExternalFunctionParameter() const
|
bool VariableDeclaration::isExternalFunctionParameter() const
|
||||||
{
|
{
|
||||||
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
|
auto const* function = dynamic_cast<FunctionDefinition const*>(getScope());
|
||||||
@ -879,7 +890,7 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
|
|||||||
{
|
{
|
||||||
auto const& arrayType(dynamic_cast<ArrayType const&>(type));
|
auto const& arrayType(dynamic_cast<ArrayType const&>(type));
|
||||||
m_isLValue = (*m_memberName == "length" &&
|
m_isLValue = (*m_memberName == "length" &&
|
||||||
arrayType.getLocation() != ArrayType::Location::CallData && arrayType.isDynamicallySized());
|
arrayType.location() != ReferenceType::Location::CallData && arrayType.isDynamicallySized());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_isLValue = false;
|
m_isLValue = false;
|
||||||
@ -902,7 +913,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
|
|||||||
m_type = make_shared<FixedBytesType>(1);
|
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.location() != ReferenceType::Location::CallData;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::Mapping:
|
case Type::Category::Mapping:
|
||||||
@ -919,7 +930,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
|
|||||||
{
|
{
|
||||||
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
|
TypeType const& type = dynamic_cast<TypeType const&>(*m_base->getType());
|
||||||
if (!m_index)
|
if (!m_index)
|
||||||
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType()));
|
m_type = make_shared<TypeType>(make_shared<ArrayType>(ReferenceType::Location::Memory, type.getActualType()));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_index->checkTypeRequirements(nullptr);
|
m_index->checkTypeRequirements(nullptr);
|
||||||
@ -927,7 +938,7 @@ void IndexAccess::checkTypeRequirements(TypePointers const*)
|
|||||||
if (!length)
|
if (!length)
|
||||||
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
|
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
|
||||||
m_type = make_shared<TypeType>(make_shared<ArrayType>(
|
m_type = make_shared<TypeType>(make_shared<ArrayType>(
|
||||||
ArrayType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
|
ReferenceType::Location::Memory, type.getActualType(), length->literalValue(nullptr)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
17
AST.h
17
AST.h
@ -474,22 +474,26 @@ private:
|
|||||||
class VariableDeclaration: public Declaration
|
class VariableDeclaration: public Declaration
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum Location { Default, Storage, Memory };
|
||||||
|
|
||||||
VariableDeclaration(
|
VariableDeclaration(
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _sourceLocation,
|
||||||
ASTPointer<TypeName> const& _type,
|
ASTPointer<TypeName> const& _type,
|
||||||
ASTPointer<ASTString> const& _name,
|
ASTPointer<ASTString> const& _name,
|
||||||
ASTPointer<Expression> _value,
|
ASTPointer<Expression> _value,
|
||||||
Visibility _visibility,
|
Visibility _visibility,
|
||||||
bool _isStateVar = false,
|
bool _isStateVar = false,
|
||||||
bool _isIndexed = false,
|
bool _isIndexed = false,
|
||||||
bool _isConstant = false
|
bool _isConstant = false,
|
||||||
|
Location _referenceLocation = Location::Default
|
||||||
):
|
):
|
||||||
Declaration(_location, _name, _visibility),
|
Declaration(_sourceLocation, _name, _visibility),
|
||||||
m_typeName(_type),
|
m_typeName(_type),
|
||||||
m_value(_value),
|
m_value(_value),
|
||||||
m_isStateVariable(_isStateVar),
|
m_isStateVariable(_isStateVar),
|
||||||
m_isIndexed(_isIndexed),
|
m_isIndexed(_isIndexed),
|
||||||
m_isConstant(_isConstant){}
|
m_isConstant(_isConstant),
|
||||||
|
m_location(_referenceLocation) {}
|
||||||
|
|
||||||
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;
|
||||||
@ -507,10 +511,14 @@ public:
|
|||||||
|
|
||||||
void checkTypeRequirements();
|
void checkTypeRequirements();
|
||||||
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
|
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
|
||||||
|
/// @returns true if this variable is a parameter or return parameter of a function.
|
||||||
|
bool isFunctionParameter() const;
|
||||||
|
/// @returns true if this variable is a parameter (not return parameter) of an external function.
|
||||||
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; }
|
bool isConstant() const { return m_isConstant; }
|
||||||
|
Location referenceLocation() const { return m_location; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
|
Visibility getDefaultVisibility() const override { return Visibility::Internal; }
|
||||||
@ -521,6 +529,7 @@ private:
|
|||||||
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.
|
bool m_isConstant; ///< Whether the variable is a compile-time constant.
|
||||||
|
Location m_location; ///< Location of the variable if it is of reference type.
|
||||||
|
|
||||||
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
||||||
};
|
};
|
||||||
|
@ -38,10 +38,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// need to leave "target_ref target_byte_off" 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)
|
// stack layout: [source_ref] [source_byte_off] [source length] target_ref target_byte_off (top)
|
||||||
solAssert(_targetType.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_targetType.location() == ReferenceType::Location::Storage, "");
|
||||||
solAssert(
|
solAssert(
|
||||||
_sourceType.getLocation() == ArrayType::Location::CallData ||
|
_sourceType.location() == ReferenceType::Location::CallData ||
|
||||||
_sourceType.getLocation() == ArrayType::Location::Storage,
|
_sourceType.location() == ReferenceType::Location::Storage,
|
||||||
"Given array location not implemented."
|
"Given array location not implemented."
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
|
|
||||||
// TODO unroll loop for small sizes
|
// TODO unroll loop for small sizes
|
||||||
|
|
||||||
bool sourceIsStorage = _sourceType.getLocation() == ArrayType::Location::Storage;
|
bool sourceIsStorage = _sourceType.location() == ReferenceType::Location::Storage;
|
||||||
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
|
bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType;
|
||||||
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
|
bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->getStorageBytes() <= 16;
|
||||||
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
|
bool haveByteOffsetTarget = !directCopy && targetBaseType->getStorageBytes() <= 16;
|
||||||
@ -69,7 +69,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
// stack: target_ref source_ref [source_length]
|
// stack: target_ref source_ref [source_length]
|
||||||
// retrieve source length
|
// retrieve source length
|
||||||
if (_sourceType.getLocation() != ArrayType::Location::CallData || !_sourceType.isDynamicallySized())
|
if (_sourceType.location() != ReferenceType::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
|
||||||
m_context << eth::Instruction::DUP3;
|
m_context << eth::Instruction::DUP3;
|
||||||
@ -82,7 +82,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
if (sourceBaseType->getCategory() == Type::Category::Mapping)
|
if (sourceBaseType->getCategory() == Type::Category::Mapping)
|
||||||
{
|
{
|
||||||
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
|
solAssert(targetBaseType->getCategory() == Type::Category::Mapping, "");
|
||||||
solAssert(_sourceType.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_sourceType.location() == ReferenceType::Location::Storage, "");
|
||||||
// nothing to copy
|
// nothing to copy
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::POP << eth::Instruction::POP
|
<< eth::Instruction::POP << eth::Instruction::POP
|
||||||
@ -106,7 +106,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
|
eth::AssemblyItem copyLoopEndWithoutByteOffset = m_context.newTag();
|
||||||
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
|
m_context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset);
|
||||||
|
|
||||||
if (_sourceType.getLocation() == ArrayType::Location::Storage && _sourceType.isDynamicallySized())
|
if (_sourceType.location() == ReferenceType::Location::Storage && _sourceType.isDynamicallySized())
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
|
// stack: target_ref target_data_end source_length target_data_pos source_data_pos
|
||||||
m_context << eth::Instruction::SWAP2;
|
m_context << eth::Instruction::SWAP2;
|
||||||
@ -155,7 +155,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
// checking is easier.
|
// checking is easier.
|
||||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
// 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);
|
m_context << eth::dupInstruction(3 + byteOffsetSize);
|
||||||
if (_sourceType.getLocation() == ArrayType::Location::Storage)
|
if (_sourceType.location() == ReferenceType::Location::Storage)
|
||||||
{
|
{
|
||||||
if (haveByteOffsetSource)
|
if (haveByteOffsetSource)
|
||||||
m_context << eth::Instruction::DUP2;
|
m_context << eth::Instruction::DUP2;
|
||||||
@ -228,7 +228,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
void ArrayUtils::clearArray(ArrayType const& _type) const
|
void ArrayUtils::clearArray(ArrayType const& _type) const
|
||||||
{
|
{
|
||||||
unsigned stackHeightStart = m_context.getStackHeight();
|
unsigned stackHeightStart = m_context.getStackHeight();
|
||||||
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_type.location() == ReferenceType::Location::Storage, "");
|
||||||
if (_type.getBaseType()->getStorageBytes() < 32)
|
if (_type.getBaseType()->getStorageBytes() < 32)
|
||||||
{
|
{
|
||||||
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
|
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
@ -283,7 +283,7 @@ void ArrayUtils::clearArray(ArrayType const& _type) const
|
|||||||
|
|
||||||
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
||||||
{
|
{
|
||||||
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_type.location() == ReferenceType::Location::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
|
|
||||||
unsigned stackHeightStart = m_context.getStackHeight();
|
unsigned stackHeightStart = m_context.getStackHeight();
|
||||||
@ -311,7 +311,7 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const
|
|||||||
|
|
||||||
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
|
void ArrayUtils::resizeDynamicArray(const ArrayType& _type) const
|
||||||
{
|
{
|
||||||
solAssert(_type.getLocation() == ArrayType::Location::Storage, "");
|
solAssert(_type.location() == ReferenceType::Location::Storage, "");
|
||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
|
if (!_type.isByteArray() && _type.getBaseType()->getStorageBytes() < 32)
|
||||||
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
|
solAssert(_type.getBaseType()->isValueType(), "Invalid storage size for non-value type.");
|
||||||
@ -396,7 +396,7 @@ void ArrayUtils::clearStorageLoop(Type const& _type) const
|
|||||||
|
|
||||||
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
|
void ArrayUtils::convertLengthToSize(ArrayType const& _arrayType, bool _pad) const
|
||||||
{
|
{
|
||||||
if (_arrayType.getLocation() == ArrayType::Location::Storage)
|
if (_arrayType.location() == ReferenceType::Location::Storage)
|
||||||
{
|
{
|
||||||
if (_arrayType.getBaseType()->getStorageSize() <= 1)
|
if (_arrayType.getBaseType()->getStorageSize() <= 1)
|
||||||
{
|
{
|
||||||
@ -432,15 +432,15 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_context << eth::Instruction::DUP1;
|
m_context << eth::Instruction::DUP1;
|
||||||
switch (_arrayType.getLocation())
|
switch (_arrayType.location())
|
||||||
{
|
{
|
||||||
case ArrayType::Location::CallData:
|
case ReferenceType::Location::CallData:
|
||||||
// length is stored on the stack
|
// length is stored on the stack
|
||||||
break;
|
break;
|
||||||
case ArrayType::Location::Memory:
|
case ReferenceType::Location::Memory:
|
||||||
m_context << eth::Instruction::MLOAD;
|
m_context << eth::Instruction::MLOAD;
|
||||||
break;
|
break;
|
||||||
case ArrayType::Location::Storage:
|
case ReferenceType::Location::Storage:
|
||||||
m_context << eth::Instruction::SLOAD;
|
m_context << eth::Instruction::SLOAD;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -449,16 +449,16 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType) const
|
|||||||
|
|
||||||
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
|
void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
|
||||||
{
|
{
|
||||||
ArrayType::Location location = _arrayType.getLocation();
|
ReferenceType::Location location = _arrayType.location();
|
||||||
eth::Instruction load =
|
eth::Instruction load =
|
||||||
location == ArrayType::Location::Storage ? eth::Instruction::SLOAD :
|
location == ReferenceType::Location::Storage ? eth::Instruction::SLOAD :
|
||||||
location == ArrayType::Location::Memory ? eth::Instruction::MLOAD :
|
location == ReferenceType::Location::Memory ? eth::Instruction::MLOAD :
|
||||||
eth::Instruction::CALLDATALOAD;
|
eth::Instruction::CALLDATALOAD;
|
||||||
|
|
||||||
// retrieve length
|
// retrieve length
|
||||||
if (!_arrayType.isDynamicallySized())
|
if (!_arrayType.isDynamicallySized())
|
||||||
m_context << _arrayType.getLength();
|
m_context << _arrayType.getLength();
|
||||||
else if (location == ArrayType::Location::CallData)
|
else if (location == ReferenceType::Location::CallData)
|
||||||
// length is stored on the stack
|
// length is stored on the stack
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
else
|
else
|
||||||
@ -473,15 +473,15 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
|
|||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
if (_arrayType.isDynamicallySized())
|
if (_arrayType.isDynamicallySized())
|
||||||
{
|
{
|
||||||
if (location == ArrayType::Location::Storage)
|
if (location == ReferenceType::Location::Storage)
|
||||||
CompilerUtils(m_context).computeHashStatic();
|
CompilerUtils(m_context).computeHashStatic();
|
||||||
else if (location == ArrayType::Location::Memory)
|
else if (location == ReferenceType::Location::Memory)
|
||||||
m_context << u256(32) << eth::Instruction::ADD;
|
m_context << u256(32) << eth::Instruction::ADD;
|
||||||
}
|
}
|
||||||
// stack: <index> <data_ref>
|
// stack: <index> <data_ref>
|
||||||
switch (location)
|
switch (location)
|
||||||
{
|
{
|
||||||
case ArrayType::Location::CallData:
|
case ReferenceType::Location::CallData:
|
||||||
if (!_arrayType.isByteArray())
|
if (!_arrayType.isByteArray())
|
||||||
m_context
|
m_context
|
||||||
<< eth::Instruction::SWAP1
|
<< eth::Instruction::SWAP1
|
||||||
@ -496,7 +496,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
|
|||||||
false
|
false
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case ArrayType::Location::Storage:
|
case ReferenceType::Location::Storage:
|
||||||
m_context << eth::Instruction::SWAP1;
|
m_context << eth::Instruction::SWAP1;
|
||||||
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
|
if (_arrayType.getBaseType()->getStorageBytes() <= 16)
|
||||||
{
|
{
|
||||||
@ -524,7 +524,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType) const
|
|||||||
m_context << eth::Instruction::ADD << u256(0);
|
m_context << eth::Instruction::ADD << u256(0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ArrayType::Location::Memory:
|
case ReferenceType::Location::Memory:
|
||||||
solAssert(false, "Memory lvalues not yet implemented.");
|
solAssert(false, "Memory lvalues not yet implemented.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
auto const& type = dynamic_cast<ArrayType const&>(_type);
|
auto const& type = dynamic_cast<ArrayType const&>(_type);
|
||||||
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
|
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here.");
|
||||||
|
|
||||||
if (type.getLocation() == ArrayType::Location::CallData)
|
if (type.location() == ReferenceType::Location::CallData)
|
||||||
{
|
{
|
||||||
// stack: target source_offset source_len
|
// stack: target source_offset source_len
|
||||||
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
|
m_context << eth::Instruction::DUP1 << eth::Instruction::DUP3 << eth::Instruction::DUP5
|
||||||
@ -92,7 +92,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(type.getLocation() == ArrayType::Location::Storage, "Memory arrays not yet implemented.");
|
solAssert(type.location() == ReferenceType::Location::Storage, "Memory arrays not yet implemented.");
|
||||||
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
|
m_context << eth::Instruction::POP; // remove offset, arrays always start new slot
|
||||||
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
|
||||||
|
@ -770,12 +770,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
|||||||
m_context << type.getLength();
|
m_context << type.getLength();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
switch (type.getLocation())
|
switch (type.location())
|
||||||
{
|
{
|
||||||
case ArrayType::Location::CallData:
|
case ReferenceType::Location::CallData:
|
||||||
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP;
|
||||||
break;
|
break;
|
||||||
case ArrayType::Location::Storage:
|
case ReferenceType::Location::Storage:
|
||||||
setLValue<StorageArrayLength>(_memberAccess, type);
|
setLValue<StorageArrayLength>(_memberAccess, type);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -816,13 +816,13 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
// remove storage byte offset
|
// remove storage byte offset
|
||||||
if (arrayType.getLocation() == ArrayType::Location::Storage)
|
if (arrayType.location() == ReferenceType::Location::Storage)
|
||||||
m_context << eth::Instruction::POP;
|
m_context << eth::Instruction::POP;
|
||||||
|
|
||||||
_indexAccess.getIndexExpression()->accept(*this);
|
_indexAccess.getIndexExpression()->accept(*this);
|
||||||
// stack layout: <base_ref> [<length>] <index>
|
// stack layout: <base_ref> [<length>] <index>
|
||||||
ArrayUtils(m_context).accessIndex(arrayType);
|
ArrayUtils(m_context).accessIndex(arrayType);
|
||||||
if (arrayType.getLocation() == ArrayType::Location::Storage)
|
if (arrayType.location() == ReferenceType::Location::Storage)
|
||||||
{
|
{
|
||||||
if (arrayType.isByteArray())
|
if (arrayType.isByteArray())
|
||||||
{
|
{
|
||||||
@ -1169,13 +1169,13 @@ void ExpressionCompiler::appendArgumentsCopyToMemory(
|
|||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
|
auto const& arrayType = dynamic_cast<ArrayType const&>(*_arguments[i]->getType());
|
||||||
// move memory reference to top of stack
|
// move memory reference to top of stack
|
||||||
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
|
CompilerUtils(m_context).moveToStackTop(arrayType.getSizeOnStack());
|
||||||
if (arrayType.getLocation() == ArrayType::Location::CallData)
|
if (arrayType.location() == ReferenceType::Location::CallData)
|
||||||
m_context << eth::Instruction::DUP2; // length is on stack
|
m_context << eth::Instruction::DUP2; // length is on stack
|
||||||
else if (arrayType.getLocation() == ArrayType::Location::Storage)
|
else if (arrayType.location() == ReferenceType::Location::Storage)
|
||||||
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
|
m_context << eth::Instruction::DUP3 << eth::Instruction::SLOAD;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(arrayType.getLocation() == ArrayType::Location::Memory, "");
|
solAssert(arrayType.location() == ReferenceType::Location::Memory, "");
|
||||||
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
|
m_context << eth::Instruction::DUP2 << eth::Instruction::MLOAD;
|
||||||
}
|
}
|
||||||
appendTypeMoveToMemory(IntegerType(256), true);
|
appendTypeMoveToMemory(IntegerType(256), true);
|
||||||
|
@ -424,10 +424,49 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
|
|||||||
if (_variable.getTypeName())
|
if (_variable.getTypeName())
|
||||||
{
|
{
|
||||||
TypePointer type = _variable.getTypeName()->toType();
|
TypePointer type = _variable.getTypeName()->toType();
|
||||||
// All array parameter types should point to call data
|
using Location = VariableDeclaration::Location;
|
||||||
if (_variable.isExternalFunctionParameter())
|
Location loc = _variable.referenceLocation();
|
||||||
if (auto const* arrayType = dynamic_cast<ArrayType const*>(type.get()))
|
// References are forced to calldata for external function parameters (not return)
|
||||||
type = arrayType->copyForLocation(ArrayType::Location::CallData);
|
// and memory for parameters (also return) of publicly visible functions.
|
||||||
|
// They default to memory for function parameters and storage for local variables.
|
||||||
|
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
|
||||||
|
{
|
||||||
|
if (_variable.isExternalFunctionParameter())
|
||||||
|
{
|
||||||
|
// force location of external function parameters (not return) to calldata
|
||||||
|
if (loc != Location::Default)
|
||||||
|
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||||
|
"Location has to be calldata for external functions "
|
||||||
|
"(remove the \"memory\" or \"storage\" keyword)."
|
||||||
|
));
|
||||||
|
type = ref->copyForLocation(ReferenceType::Location::CallData);
|
||||||
|
}
|
||||||
|
else if (_variable.isFunctionParameter() && _variable.getScope()->isPublic())
|
||||||
|
{
|
||||||
|
// force locations of public or external function (return) parameters to memory
|
||||||
|
if (loc == VariableDeclaration::Location::Storage)
|
||||||
|
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||||
|
"Location has to be memory for publicly visible functions "
|
||||||
|
"(remove the \"storage\" keyword)."
|
||||||
|
));
|
||||||
|
type = ref->copyForLocation(ReferenceType::Location::Memory);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (loc == Location::Default)
|
||||||
|
loc = _variable.isFunctionParameter() ? Location::Memory : Location::Storage;
|
||||||
|
type = ref->copyForLocation(
|
||||||
|
loc == Location::Memory ?
|
||||||
|
ReferenceType::Location::Memory :
|
||||||
|
ReferenceType::Location::Storage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (loc != Location::Default && !ref)
|
||||||
|
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||||
|
"Storage location can only be given for array or struct types."
|
||||||
|
));
|
||||||
|
|
||||||
_variable.setType(type);
|
_variable.setType(type);
|
||||||
|
|
||||||
if (!_variable.getType())
|
if (!_variable.getType())
|
||||||
|
94
Parser.cpp
94
Parser.cpp
@ -224,7 +224,9 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
|
|||||||
name = make_shared<ASTString>(); // anonymous function
|
name = make_shared<ASTString>(); // anonymous function
|
||||||
else
|
else
|
||||||
name = expectIdentifierToken();
|
name = expectIdentifierToken();
|
||||||
ASTPointer<ParameterList> parameters(parseParameterList());
|
VarDeclParserOptions options;
|
||||||
|
options.allowLocationSpecifier = true;
|
||||||
|
ASTPointer<ParameterList> parameters(parseParameterList(options));
|
||||||
bool isDeclaredConst = false;
|
bool isDeclaredConst = false;
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
||||||
vector<ASTPointer<ModifierInvocation>> modifiers;
|
vector<ASTPointer<ModifierInvocation>> modifiers;
|
||||||
@ -252,7 +254,7 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const*
|
|||||||
{
|
{
|
||||||
bool const permitEmptyParameterList = false;
|
bool const permitEmptyParameterList = false;
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
returnParameters = parseParameterList(permitEmptyParameterList);
|
returnParameters = parseParameterList(options, permitEmptyParameterList);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
returnParameters = createEmptyParameterList();
|
returnParameters = createEmptyParameterList();
|
||||||
@ -319,7 +321,9 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||||
VarDeclParserOptions const& _options, ASTPointer<TypeName> const& _lookAheadArrayType)
|
VarDeclParserOptions const& _options,
|
||||||
|
ASTPointer<TypeName> const& _lookAheadArrayType
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
|
ASTNodeFactory nodeFactory = _lookAheadArrayType ?
|
||||||
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
|
ASTNodeFactory(*this, _lookAheadArrayType) : ASTNodeFactory(*this);
|
||||||
@ -334,20 +338,41 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
}
|
}
|
||||||
bool isIndexed = false;
|
bool isIndexed = false;
|
||||||
bool isDeclaredConst = false;
|
bool isDeclaredConst = false;
|
||||||
ASTPointer<ASTString> identifier;
|
|
||||||
Token::Value token = m_scanner->getCurrentToken();
|
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
||||||
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
|
VariableDeclaration::Location location = VariableDeclaration::Location::Default;
|
||||||
visibility = parseVisibilitySpecifier(token);
|
ASTPointer<ASTString> identifier;
|
||||||
if (_options.allowIndexed && token == Token::Indexed)
|
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
isIndexed = true;
|
Token::Value token = m_scanner->getCurrentToken();
|
||||||
m_scanner->next();
|
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
|
||||||
}
|
{
|
||||||
if (token == Token::Const)
|
if (visibility != Declaration::Visibility::Default)
|
||||||
{
|
BOOST_THROW_EXCEPTION(createParserError("Visibility already specified."));
|
||||||
isDeclaredConst = true;
|
visibility = parseVisibilitySpecifier(token);
|
||||||
m_scanner->next();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_options.allowIndexed && token == Token::Indexed)
|
||||||
|
isIndexed = true;
|
||||||
|
else if (token == Token::Const)
|
||||||
|
isDeclaredConst = true;
|
||||||
|
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
|
||||||
|
{
|
||||||
|
if (location != VariableDeclaration::Location::Default)
|
||||||
|
BOOST_THROW_EXCEPTION(createParserError("Location already specified."));
|
||||||
|
if (!type)
|
||||||
|
BOOST_THROW_EXCEPTION(createParserError("Location specifier needs explicit type name."));
|
||||||
|
location = (
|
||||||
|
token == Token::Memory ?
|
||||||
|
VariableDeclaration::Location::Memory :
|
||||||
|
VariableDeclaration::Location::Storage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
m_scanner->next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
|
|
||||||
@ -371,7 +396,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, isDeclaredConst);
|
isIndexed, isDeclaredConst, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
||||||
@ -388,7 +413,12 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
|||||||
ASTPointer<ASTString> name(expectIdentifierToken());
|
ASTPointer<ASTString> name(expectIdentifierToken());
|
||||||
ASTPointer<ParameterList> parameters;
|
ASTPointer<ParameterList> parameters;
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
if (m_scanner->getCurrentToken() == Token::LParen)
|
||||||
parameters = parseParameterList();
|
{
|
||||||
|
VarDeclParserOptions options;
|
||||||
|
options.allowIndexed = true;
|
||||||
|
options.allowLocationSpecifier = true;
|
||||||
|
parameters = parseParameterList(options);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
parameters = createEmptyParameterList();
|
parameters = createEmptyParameterList();
|
||||||
ASTPointer<Block> block = parseBlock();
|
ASTPointer<Block> block = parseBlock();
|
||||||
@ -407,7 +437,11 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
|
|||||||
ASTPointer<ASTString> name(expectIdentifierToken());
|
ASTPointer<ASTString> name(expectIdentifierToken());
|
||||||
ASTPointer<ParameterList> parameters;
|
ASTPointer<ParameterList> parameters;
|
||||||
if (m_scanner->getCurrentToken() == Token::LParen)
|
if (m_scanner->getCurrentToken() == Token::LParen)
|
||||||
parameters = parseParameterList(true, true);
|
{
|
||||||
|
VarDeclParserOptions options;
|
||||||
|
options.allowIndexed = true;
|
||||||
|
parameters = parseParameterList(options);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
parameters = createEmptyParameterList();
|
parameters = createEmptyParameterList();
|
||||||
bool anonymous = false;
|
bool anonymous = false;
|
||||||
@ -505,12 +539,14 @@ ASTPointer<Mapping> Parser::parseMapping()
|
|||||||
return nodeFactory.createNode<Mapping>(keyType, valueType);
|
return nodeFactory.createNode<Mapping>(keyType, valueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty, bool _allowIndexed)
|
ASTPointer<ParameterList> Parser::parseParameterList(
|
||||||
|
VarDeclParserOptions const& _options,
|
||||||
|
bool _allowEmpty
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
vector<ASTPointer<VariableDeclaration>> parameters;
|
vector<ASTPointer<VariableDeclaration>> parameters;
|
||||||
VarDeclParserOptions options;
|
VarDeclParserOptions options(_options);
|
||||||
options.allowIndexed = _allowIndexed;
|
|
||||||
options.allowEmptyName = true;
|
options.allowEmptyName = true;
|
||||||
expectToken(Token::LParen);
|
expectToken(Token::LParen);
|
||||||
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
|
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RParen)
|
||||||
@ -691,7 +727,7 @@ ASTPointer<Statement> Parser::parseSimpleStatement()
|
|||||||
}
|
}
|
||||||
while (m_scanner->getCurrentToken() == Token::LBrack);
|
while (m_scanner->getCurrentToken() == Token::LBrack);
|
||||||
|
|
||||||
if (m_scanner->getCurrentToken() == Token::Identifier)
|
if (m_scanner->getCurrentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->getCurrentToken()))
|
||||||
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
|
return parseVariableDeclarationStatement(typeNameIndexAccessStructure(primary, indices));
|
||||||
else
|
else
|
||||||
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
|
return parseExpressionStatement(expressionFromIndexAccessStructure(primary, indices));
|
||||||
@ -703,6 +739,7 @@ ASTPointer<VariableDeclarationStatement> Parser::parseVariableDeclarationStateme
|
|||||||
VarDeclParserOptions options;
|
VarDeclParserOptions options;
|
||||||
options.allowVar = true;
|
options.allowVar = true;
|
||||||
options.allowInitialValue = true;
|
options.allowInitialValue = true;
|
||||||
|
options.allowLocationSpecifier = true;
|
||||||
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
|
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(options, _lookAheadArrayType);
|
||||||
ASTNodeFactory nodeFactory(*this, variable);
|
ASTNodeFactory nodeFactory(*this, variable);
|
||||||
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
|
return nodeFactory.createNode<VariableDeclarationStatement>(variable);
|
||||||
@ -944,11 +981,16 @@ Parser::LookAheadInfo Parser::peekStatementType() const
|
|||||||
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))
|
|
||||||
return LookAheadInfo::VariableDeclarationStatement;
|
return LookAheadInfo::VariableDeclarationStatement;
|
||||||
if (mightBeTypeName && m_scanner->peekNextToken() == Token::LBrack)
|
if (mightBeTypeName)
|
||||||
return LookAheadInfo::IndexAccessStructure;
|
{
|
||||||
|
Token::Value next = m_scanner->peekNextToken();
|
||||||
|
if (next == Token::Identifier || Token::isLocationSpecifier(next))
|
||||||
|
return LookAheadInfo::VariableDeclarationStatement;
|
||||||
|
if (m_scanner->peekNextToken() == Token::LBrack)
|
||||||
|
return LookAheadInfo::IndexAccessStructure;
|
||||||
|
}
|
||||||
return LookAheadInfo::ExpressionStatement;
|
return LookAheadInfo::ExpressionStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
Parser.h
9
Parser.h
@ -47,13 +47,15 @@ private:
|
|||||||
/// End position of the current token
|
/// End position of the current token
|
||||||
int getEndPosition() const;
|
int getEndPosition() const;
|
||||||
|
|
||||||
struct VarDeclParserOptions {
|
struct VarDeclParserOptions
|
||||||
|
{
|
||||||
VarDeclParserOptions() {}
|
VarDeclParserOptions() {}
|
||||||
bool allowVar = false;
|
bool allowVar = false;
|
||||||
bool isStateVariable = false;
|
bool isStateVariable = false;
|
||||||
bool allowIndexed = false;
|
bool allowIndexed = false;
|
||||||
bool allowEmptyName = false;
|
bool allowEmptyName = false;
|
||||||
bool allowInitialValue = false;
|
bool allowInitialValue = false;
|
||||||
|
bool allowLocationSpecifier = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
///@{
|
///@{
|
||||||
@ -74,7 +76,10 @@ private:
|
|||||||
ASTPointer<Identifier> parseIdentifier();
|
ASTPointer<Identifier> parseIdentifier();
|
||||||
ASTPointer<TypeName> parseTypeName(bool _allowVar);
|
ASTPointer<TypeName> parseTypeName(bool _allowVar);
|
||||||
ASTPointer<Mapping> parseMapping();
|
ASTPointer<Mapping> parseMapping();
|
||||||
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true, bool _allowIndexed = false);
|
ASTPointer<ParameterList> parseParameterList(
|
||||||
|
VarDeclParserOptions const& _options,
|
||||||
|
bool _allowEmpty = true
|
||||||
|
);
|
||||||
ASTPointer<Block> parseBlock();
|
ASTPointer<Block> parseBlock();
|
||||||
ASTPointer<Statement> parseStatement();
|
ASTPointer<Statement> parseStatement();
|
||||||
ASTPointer<IfStatement> parseIfStatement();
|
ASTPointer<IfStatement> parseIfStatement();
|
||||||
|
3
Token.h
3
Token.h
@ -161,12 +161,14 @@ namespace solidity
|
|||||||
K(Import, "import", 0) \
|
K(Import, "import", 0) \
|
||||||
K(Is, "is", 0) \
|
K(Is, "is", 0) \
|
||||||
K(Mapping, "mapping", 0) \
|
K(Mapping, "mapping", 0) \
|
||||||
|
K(Memory, "memory", 0) \
|
||||||
K(Modifier, "modifier", 0) \
|
K(Modifier, "modifier", 0) \
|
||||||
K(New, "new", 0) \
|
K(New, "new", 0) \
|
||||||
K(Public, "public", 0) \
|
K(Public, "public", 0) \
|
||||||
K(Private, "private", 0) \
|
K(Private, "private", 0) \
|
||||||
K(Return, "return", 0) \
|
K(Return, "return", 0) \
|
||||||
K(Returns, "returns", 0) \
|
K(Returns, "returns", 0) \
|
||||||
|
K(Storage, "storage", 0) \
|
||||||
K(Struct, "struct", 0) \
|
K(Struct, "struct", 0) \
|
||||||
K(Var, "var", 0) \
|
K(Var, "var", 0) \
|
||||||
K(While, "while", 0) \
|
K(While, "while", 0) \
|
||||||
@ -370,6 +372,7 @@ public:
|
|||||||
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
|
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
|
||||||
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
|
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
|
||||||
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
|
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
|
||||||
|
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
|
||||||
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
|
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
|
||||||
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
|
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
|
||||||
|
|
||||||
|
25
Types.cpp
25
Types.cpp
@ -144,9 +144,9 @@ TypePointer Type::fromElementaryTypeName(Token::Value _typeToken)
|
|||||||
else if (_typeToken == Token::Bool)
|
else if (_typeToken == Token::Bool)
|
||||||
return make_shared<BoolType>();
|
return make_shared<BoolType>();
|
||||||
else if (_typeToken == Token::Bytes)
|
else if (_typeToken == Token::Bytes)
|
||||||
return make_shared<ArrayType>(ArrayType::Location::Storage);
|
return make_shared<ArrayType>(ReferenceType::Location::Storage);
|
||||||
else if (_typeToken == Token::String)
|
else if (_typeToken == Token::String)
|
||||||
return make_shared<ArrayType>(ArrayType::Location::Storage, true);
|
return make_shared<ArrayType>(ReferenceType::Location::Storage, true);
|
||||||
else
|
else
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unable to convert elementary typename " +
|
||||||
std::string(Token::toString(_typeToken)) + " to type."));
|
std::string(Token::toString(_typeToken)) + " to type."));
|
||||||
@ -196,10 +196,10 @@ TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length
|
|||||||
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
|
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->getType().get());
|
||||||
if (!length)
|
if (!length)
|
||||||
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
|
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
|
||||||
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType, length->literalValue(nullptr));
|
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType, length->literalValue(nullptr));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return make_shared<ArrayType>(ArrayType::Location::Storage, baseType);
|
return make_shared<ArrayType>(ReferenceType::Location::Storage, baseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
TypePointer Type::forLiteral(Literal const& _literal)
|
TypePointer Type::forLiteral(Literal const& _literal)
|
||||||
@ -674,7 +674,7 @@ bool ArrayType::isImplicitlyConvertibleTo(const Type& _convertTo) const
|
|||||||
return false;
|
return false;
|
||||||
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
|
auto& convertTo = dynamic_cast<ArrayType const&>(_convertTo);
|
||||||
// let us not allow assignment to memory arrays for now
|
// let us not allow assignment to memory arrays for now
|
||||||
if (convertTo.getLocation() != Location::Storage)
|
if (convertTo.location() != Location::Storage)
|
||||||
return false;
|
return false;
|
||||||
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
|
if (convertTo.isByteArray() != isByteArray() || convertTo.isString() != isString())
|
||||||
return false;
|
return false;
|
||||||
@ -778,12 +778,12 @@ TypePointer ArrayType::externalType() const
|
|||||||
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
|
return std::make_shared<ArrayType>(Location::CallData, m_baseType->externalType(), m_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<ArrayType> ArrayType::copyForLocation(ArrayType::Location _location) const
|
TypePointer ArrayType::copyForLocation(ReferenceType::Location _location) const
|
||||||
{
|
{
|
||||||
auto copy = make_shared<ArrayType>(_location);
|
auto copy = make_shared<ArrayType>(_location);
|
||||||
copy->m_arrayKind = m_arrayKind;
|
copy->m_arrayKind = m_arrayKind;
|
||||||
if (m_baseType->getCategory() == Type::Category::Array)
|
if (auto ref = dynamic_cast<ReferenceType const*>(m_baseType.get()))
|
||||||
copy->m_baseType = dynamic_cast<ArrayType const&>(*m_baseType).copyForLocation(_location);
|
copy->m_baseType = ref->copyForLocation(_location);
|
||||||
else
|
else
|
||||||
copy->m_baseType = m_baseType;
|
copy->m_baseType = m_baseType;
|
||||||
copy->m_hasDynamicLength = m_hasDynamicLength;
|
copy->m_hasDynamicLength = m_hasDynamicLength;
|
||||||
@ -934,6 +934,13 @@ MemberList const& StructType::getMembers() const
|
|||||||
return *m_members;
|
return *m_members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer StructType::copyForLocation(ReferenceType::Location _location) const
|
||||||
|
{
|
||||||
|
auto copy = make_shared<StructType>(m_struct);
|
||||||
|
copy->m_location = _location;
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
|
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
|
||||||
{
|
{
|
||||||
auto const* offsets = getMembers().getMemberStorageOffset(_name);
|
auto const* offsets = getMembers().getMemberStorageOffset(_name);
|
||||||
@ -1466,7 +1473,7 @@ MagicType::MagicType(MagicType::Kind _kind):
|
|||||||
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
{"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)},
|
||||||
{"gas", make_shared<IntegerType>(256)},
|
{"gas", make_shared<IntegerType>(256)},
|
||||||
{"value", make_shared<IntegerType>(256)},
|
{"value", make_shared<IntegerType>(256)},
|
||||||
{"data", make_shared<ArrayType>(ArrayType::Location::CallData)},
|
{"data", make_shared<ArrayType>(ReferenceType::Location::CallData)},
|
||||||
{"sig", make_shared<FixedBytesType>(4)}
|
{"sig", make_shared<FixedBytesType>(4)}
|
||||||
}));
|
}));
|
||||||
break;
|
break;
|
||||||
|
41
Types.h
41
Types.h
@ -353,6 +353,24 @@ public:
|
|||||||
virtual TypePointer externalType() const override { return shared_from_this(); }
|
virtual TypePointer externalType() const override { return shared_from_this(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait used by types which are not value types and can be stored either in storage, memory
|
||||||
|
* or calldata. This is currently used by arrays and structs.
|
||||||
|
*/
|
||||||
|
class ReferenceType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Location { Storage, CallData, Memory };
|
||||||
|
explicit ReferenceType(Location _location): m_location(_location) {}
|
||||||
|
Location location() const { return m_location; }
|
||||||
|
|
||||||
|
/// @returns a copy of this type with location (recursively) changed to @a _location.
|
||||||
|
virtual TypePointer copyForLocation(Location _location) const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Location m_location = Location::Storage;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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>[]).
|
||||||
@ -360,27 +378,26 @@ public:
|
|||||||
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
|
* one slot). Dynamically sized arrays (including byte arrays) start with their size as a uint and
|
||||||
* thus start on their own slot.
|
* thus start on their own slot.
|
||||||
*/
|
*/
|
||||||
class ArrayType: public Type
|
class ArrayType: public Type, public ReferenceType
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Location { Storage, CallData, Memory };
|
|
||||||
|
|
||||||
virtual Category getCategory() const override { return Category::Array; }
|
virtual Category getCategory() const override { return Category::Array; }
|
||||||
|
|
||||||
/// Constructor for a byte array ("bytes") and string.
|
/// Constructor for a byte array ("bytes") and string.
|
||||||
explicit ArrayType(Location _location, bool _isString = false):
|
explicit ArrayType(Location _location, bool _isString = false):
|
||||||
m_location(_location),
|
ReferenceType(_location),
|
||||||
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
|
m_arrayKind(_isString ? ArrayKind::String : ArrayKind::Bytes),
|
||||||
m_baseType(std::make_shared<FixedBytesType>(1))
|
m_baseType(std::make_shared<FixedBytesType>(1))
|
||||||
{}
|
{}
|
||||||
/// 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),
|
ReferenceType(_location),
|
||||||
m_baseType(_baseType)
|
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),
|
ReferenceType(_location),
|
||||||
m_baseType(_baseType),
|
m_baseType(_baseType),
|
||||||
m_hasDynamicLength(false),
|
m_hasDynamicLength(false),
|
||||||
m_length(_length)
|
m_length(_length)
|
||||||
@ -400,7 +417,6 @@ public:
|
|||||||
}
|
}
|
||||||
virtual TypePointer externalType() const override;
|
virtual TypePointer externalType() const override;
|
||||||
|
|
||||||
Location getLocation() const { return m_location; }
|
|
||||||
/// @returns true if this is a byte array or a string
|
/// @returns true if this is a byte array or a string
|
||||||
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
|
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
|
||||||
/// @returns true if this is a string
|
/// @returns true if this is a string
|
||||||
@ -408,15 +424,12 @@ public:
|
|||||||
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
|
TypePointer const& getBaseType() const { solAssert(!!m_baseType, ""); return m_baseType;}
|
||||||
u256 const& getLength() const { return m_length; }
|
u256 const& getLength() const { return m_length; }
|
||||||
|
|
||||||
/// @returns a copy of this type with location changed to @a _location
|
TypePointer copyForLocation(Location _location) const override;
|
||||||
/// @todo this might move as far up as Type later
|
|
||||||
std::shared_ptr<ArrayType> copyForLocation(Location _location) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// String is interpreted as a subtype of Bytes.
|
/// String is interpreted as a subtype of Bytes.
|
||||||
enum class ArrayKind { Ordinary, Bytes, String };
|
enum class ArrayKind { Ordinary, Bytes, String };
|
||||||
|
|
||||||
Location m_location;
|
|
||||||
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
|
///< Byte arrays ("bytes") and strings have different semantics from ordinary arrays.
|
||||||
ArrayKind m_arrayKind = ArrayKind::Ordinary;
|
ArrayKind m_arrayKind = ArrayKind::Ordinary;
|
||||||
TypePointer m_baseType;
|
TypePointer m_baseType;
|
||||||
@ -484,11 +497,13 @@ private:
|
|||||||
/**
|
/**
|
||||||
* The type of a struct instance, there is one distinct type per struct definition.
|
* The type of a struct instance, there is one distinct type per struct definition.
|
||||||
*/
|
*/
|
||||||
class StructType: public Type
|
class StructType: public Type, public ReferenceType
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Category getCategory() const override { return Category::Struct; }
|
virtual Category getCategory() const override { return Category::Struct; }
|
||||||
explicit StructType(StructDefinition const& _struct): m_struct(_struct) {}
|
explicit StructType(StructDefinition const& _struct):
|
||||||
|
//@todo only storage until we have non-storage structs
|
||||||
|
ReferenceType(Location::Storage), m_struct(_struct) {}
|
||||||
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 u256 getStorageSize() const override;
|
virtual u256 getStorageSize() const override;
|
||||||
@ -498,6 +513,8 @@ public:
|
|||||||
|
|
||||||
virtual MemberList const& getMembers() const override;
|
virtual MemberList const& getMembers() const override;
|
||||||
|
|
||||||
|
TypePointer copyForLocation(Location _location) const override;
|
||||||
|
|
||||||
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Loading…
Reference in New Issue
Block a user