|
@ -534,19 +534,25 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) |
|
|
{ |
|
|
{ |
|
|
solAssert(member == "length", "Illegal array member."); |
|
|
solAssert(member == "length", "Illegal array member."); |
|
|
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.getExpression().getType()); |
|
|
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.getExpression().getType()); |
|
|
solAssert(type.isByteArray(), "Non byte arrays not yet implemented here."); |
|
|
if (!type.isDynamicallySized()) |
|
|
switch (type.getLocation()) |
|
|
|
|
|
{ |
|
|
{ |
|
|
case ArrayType::Location::CallData: |
|
|
CompilerUtils(m_context).popStackElement(type); |
|
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; |
|
|
m_context << type.getLength(); |
|
|
break; |
|
|
|
|
|
case ArrayType::Location::Storage: |
|
|
|
|
|
m_context << eth::Instruction::SLOAD; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
solAssert(false, "Unsupported array location."); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
switch (type.getLocation()) |
|
|
|
|
|
{ |
|
|
|
|
|
case ArrayType::Location::CallData: |
|
|
|
|
|
m_context << eth::Instruction::SWAP1 << eth::Instruction::POP; |
|
|
|
|
|
break; |
|
|
|
|
|
case ArrayType::Location::Storage: |
|
|
|
|
|
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _memberAccess.getType()); |
|
|
|
|
|
m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
solAssert(false, "Unsupported array location."); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
default: |
|
|
default: |
|
@ -559,19 +565,55 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) |
|
|
_indexAccess.getBaseExpression().accept(*this); |
|
|
_indexAccess.getBaseExpression().accept(*this); |
|
|
|
|
|
|
|
|
Type const& baseType = *_indexAccess.getBaseExpression().getType(); |
|
|
Type const& baseType = *_indexAccess.getBaseExpression().getType(); |
|
|
solAssert(baseType.getCategory() == Type::Category::Mapping, ""); |
|
|
if (baseType.getCategory() == Type::Category::Mapping) |
|
|
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType(); |
|
|
{ |
|
|
m_context << u256(0); |
|
|
Type const& keyType = *dynamic_cast<MappingType const&>(baseType).getKeyType(); |
|
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); |
|
|
m_context << u256(0); |
|
|
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); |
|
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); |
|
|
solAssert(baseType.getSizeOnStack() == 1, |
|
|
appendExpressionCopyToMemory(keyType, *_indexAccess.getIndexExpression()); |
|
|
"Unexpected: Not exactly one stack slot taken by subscriptable expression."); |
|
|
solAssert(baseType.getSizeOnStack() == 1, |
|
|
m_context << eth::Instruction::SWAP1; |
|
|
"Unexpected: Not exactly one stack slot taken by subscriptable expression."); |
|
|
appendTypeMoveToMemory(IntegerType(256)); |
|
|
m_context << eth::Instruction::SWAP1; |
|
|
m_context << u256(0) << eth::Instruction::SHA3; |
|
|
appendTypeMoveToMemory(IntegerType(256)); |
|
|
|
|
|
m_context << u256(0) << eth::Instruction::SHA3; |
|
|
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); |
|
|
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); |
|
|
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); |
|
|
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); |
|
|
|
|
|
} |
|
|
|
|
|
else if (baseType.getCategory() == Type::Category::Array) |
|
|
|
|
|
{ |
|
|
|
|
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType); |
|
|
|
|
|
solAssert(arrayType.getLocation() == ArrayType::Location::Storage, |
|
|
|
|
|
"TODO: Index acces only implemented for storage arrays."); |
|
|
|
|
|
solAssert(!arrayType.isByteArray(), "TODO: Index acces not implemented for byte arrays."); |
|
|
|
|
|
solAssert(_indexAccess.getIndexExpression(), "Index expression expected."); |
|
|
|
|
|
|
|
|
|
|
|
_indexAccess.getIndexExpression()->accept(*this); |
|
|
|
|
|
// retrieve length
|
|
|
|
|
|
if (arrayType.isDynamicallySized()) |
|
|
|
|
|
m_context << eth::Instruction::DUP2 << eth::Instruction::SLOAD; |
|
|
|
|
|
else |
|
|
|
|
|
m_context << arrayType.getLength(); |
|
|
|
|
|
// 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>
|
|
|
|
|
|
m_context << arrayType.getBaseType()->getStorageSize() << eth::Instruction::MUL; |
|
|
|
|
|
if (arrayType.isDynamicallySized()) |
|
|
|
|
|
{ |
|
|
|
|
|
m_context << eth::Instruction::SWAP1; |
|
|
|
|
|
CompilerUtils(m_context).computeHashStatic(); |
|
|
|
|
|
} |
|
|
|
|
|
m_context << eth::Instruction::ADD; |
|
|
|
|
|
m_currentLValue = LValue(m_context, LValue::LValueType::Storage, _indexAccess.getType()); |
|
|
|
|
|
m_currentLValue.retrieveValueIfLValueNotRequested(_indexAccess); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
solAssert(false, "Index access only allowed for mappings or arrays."); |
|
|
|
|
|
|
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
@ -1049,7 +1091,7 @@ void ExpressionCompiler::LValue::retrieveValue(Location const& _location, bool _ |
|
|
{ |
|
|
{ |
|
|
case LValueType::Stack: |
|
|
case LValueType::Stack: |
|
|
{ |
|
|
{ |
|
|
unsigned stackPos = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); |
|
|
unsigned stackPos = m_context->baseToCurrentStackOffset(m_baseStackOffset); |
|
|
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
|
|
|
if (stackPos >= 15) //@todo correct this by fetching earlier or moving to memory
|
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) |
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) |
|
|
<< errinfo_comment("Stack too deep.")); |
|
|
<< errinfo_comment("Stack too deep.")); |
|
@ -1098,7 +1140,7 @@ void ExpressionCompiler::LValue::storeValue(Type const& _sourceType, Location co |
|
|
{ |
|
|
{ |
|
|
case LValueType::Stack: |
|
|
case LValueType::Stack: |
|
|
{ |
|
|
{ |
|
|
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)) - m_size + 1; |
|
|
unsigned stackDiff = m_context->baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; |
|
|
if (stackDiff > 16) |
|
|
if (stackDiff > 16) |
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) |
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) |
|
|
<< errinfo_comment("Stack too deep.")); |
|
|
<< errinfo_comment("Stack too deep.")); |
|
@ -1201,7 +1243,7 @@ void ExpressionCompiler::LValue::setToZero(Location const& _location) const |
|
|
{ |
|
|
{ |
|
|
case LValueType::Stack: |
|
|
case LValueType::Stack: |
|
|
{ |
|
|
{ |
|
|
unsigned stackDiff = m_context->baseToCurrentStackOffset(unsigned(m_baseStackOffset)); |
|
|
unsigned stackDiff = m_context->baseToCurrentStackOffset(m_baseStackOffset); |
|
|
if (stackDiff > 16) |
|
|
if (stackDiff > 16) |
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) |
|
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_location) |
|
|
<< errinfo_comment("Stack too deep.")); |
|
|
<< errinfo_comment("Stack too deep.")); |
|
|