diff options
Diffstat (limited to 'libsolidity/codegen/ExpressionCompiler.cpp')
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 380 |
1 files changed, 255 insertions, 125 deletions
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 5748d818..82518e8c 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -32,6 +32,7 @@ #include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/LValue.h> #include <libevmasm/GasMeter.h> + using namespace std; namespace dev @@ -87,6 +88,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& FunctionType accessorType(_varDecl); TypePointers paramTypes = accessorType.parameterTypes(); + m_context.adjustStackOffset(1 + CompilerUtils::sizeOnStack(paramTypes)); // retrieve the position of the variable auto const& location = m_context.storageLocationOfVariable(_varDecl); @@ -110,7 +112,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& // move key to memory. utils().copyToStackTop(paramTypes.size() - i, 1); utils().storeInMemory(0); - m_context << u256(64) << u256(0) << Instruction::SHA3; + m_context << u256(64) << u256(0) << Instruction::KECCAK256; // push offset m_context << u256(0); returnType = mappingType->valueType(); @@ -197,21 +199,40 @@ bool ExpressionCompiler::visit(Conditional const& _condition) bool ExpressionCompiler::visit(Assignment const& _assignment) { CompilerContext::LocationSetter locationSetter(m_context, _assignment); + Token::Value op = _assignment.assignmentOperator(); + Token::Value binOp = op == Token::Assign ? op : Token::AssignmentToBinaryOp(op); + Type const& leftType = *_assignment.leftHandSide().annotation().type; + if (leftType.category() == Type::Category::Tuple) + { + solAssert(*_assignment.annotation().type == TupleType(), ""); + solAssert(op == Token::Assign, ""); + } + else + solAssert(*_assignment.annotation().type == leftType, ""); + bool cleanupNeeded = false; + if (op != Token::Assign) + cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp); _assignment.rightHandSide().accept(*this); // Perform some conversion already. This will convert storage types to memory and literals // to their actual type, but will not convert e.g. memory to storage. - TypePointer type = _assignment.rightHandSide().annotation().type->closestTemporaryType( - _assignment.leftHandSide().annotation().type - ); - utils().convertType(*_assignment.rightHandSide().annotation().type, *type); + TypePointer rightIntermediateType; + if (op != Token::Assign && Token::isShiftOp(binOp)) + rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType(); + else + rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType( + _assignment.leftHandSide().annotation().type + ); + solAssert(rightIntermediateType, ""); + utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded); _assignment.leftHandSide().accept(*this); solAssert(!!m_currentLValue, "LValue not retrieved."); - Token::Value op = _assignment.assignmentOperator(); - if (op != Token::Assign) // compound assignment + if (op == Token::Assign) + m_currentLValue->storeValue(*rightIntermediateType, _assignment.location()); + else // compound assignment { - solUnimplementedAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types."); + solAssert(leftType.isValueType(), "Compound operators only available for value types."); unsigned lvalueSize = m_currentLValue->sizeOnStack(); unsigned itemSize = _assignment.annotation().type->sizeOnStack(); if (lvalueSize > 0) @@ -221,16 +242,29 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) // value lvalue_ref value lvalue_ref } m_currentLValue->retrieveValue(_assignment.location(), true); - appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type); + utils().convertType(leftType, leftType, cleanupNeeded); + + if (Token::isShiftOp(binOp)) + appendShiftOperatorCode(binOp, leftType, *rightIntermediateType); + else + { + solAssert(leftType == *rightIntermediateType, ""); + appendOrdinaryBinaryOperatorCode(binOp, leftType); + } if (lvalueSize > 0) { - solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables."); + if (itemSize + lvalueSize > 16) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_sourceLocation(_assignment.location()) << + errinfo_comment("Stack too deep, try removing local variables.") + ); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP; } + m_currentLValue->storeValue(*_assignment.annotation().type, _assignment.location()); } - m_currentLValue->storeValue(*type, _assignment.location()); m_currentLValue.reset(); return false; } @@ -351,20 +385,20 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) Expression const& leftExpression = _binaryOperation.leftExpression(); Expression const& rightExpression = _binaryOperation.rightExpression(); solAssert(!!_binaryOperation.annotation().commonType, ""); - Type const& commonType = *_binaryOperation.annotation().commonType; + TypePointer const& commonType = _binaryOperation.annotation().commonType; Token::Value const c_op = _binaryOperation.getOperator(); if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); - else if (commonType.category() == Type::Category::RationalNumber) - m_context << commonType.literalValue(nullptr); + else if (commonType->category() == Type::Category::RationalNumber) + m_context << commonType->literalValue(nullptr); else { - bool cleanupNeeded = false; - if (Token::isCompareOp(c_op)) - cleanupNeeded = true; - if (commonType.category() == Type::Category::Integer && (c_op == Token::Div || c_op == Token::Mod)) - cleanupNeeded = true; + bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op); + + TypePointer leftTargetType = commonType; + TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; + solAssert(rightTargetType, ""); // for commutative operators, push the literal as late as possible to allow improved optimization auto isLiteral = [](Expression const& _e) @@ -375,21 +409,24 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) if (swap) { leftExpression.accept(*this); - utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded); + utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded); rightExpression.accept(*this); - utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded); + utils().convertType(*rightExpression.annotation().type, *rightTargetType, cleanupNeeded); } else { rightExpression.accept(*this); - utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded); + utils().convertType(*rightExpression.annotation().type, *rightTargetType, cleanupNeeded); leftExpression.accept(*this); - utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded); + utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded); } - if (Token::isCompareOp(c_op)) - appendCompareOperatorCode(c_op, commonType); + if (Token::isShiftOp(c_op)) + // shift only cares about the signedness of both sides + appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType); + else if (Token::isCompareOp(c_op)) + appendCompareOperatorCode(c_op, *commonType); else - appendOrdinaryBinaryOperatorCode(c_op, commonType); + appendOrdinaryBinaryOperatorCode(c_op, *commonType); } // do not visit the child nodes, we already did that explicitly @@ -399,8 +436,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { CompilerContext::LocationSetter locationSetter(m_context, _functionCall); - using Location = FunctionType::Location; - if (_functionCall.annotation().isTypeConversion) + if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); @@ -411,7 +447,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } FunctionTypePointer functionType; - if (_functionCall.annotation().isStructConstructorCall) + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); @@ -442,7 +478,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(found, ""); } - if (_functionCall.annotation().isStructConstructorCall) + if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); @@ -464,10 +500,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) FunctionType const& function = *functionType; if (function.bound()) // Only delegatecall and internal functions can be bound, this might be lifted later. - solAssert(function.location() == Location::DelegateCall || function.location() == Location::Internal, ""); - switch (function.location()) + solAssert(function.kind() == FunctionType::Kind::DelegateCall || function.kind() == FunctionType::Kind::Internal, ""); + switch (function.kind()) { - case Location::Internal: + case FunctionType::Kind::Internal: { // Calling convention: Caller pushes return address and arguments // Callee removes them and pushes return values @@ -490,7 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (m_context.runtimeContext()) // We have a runtime context, so we need the creation part. - m_context << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV; + utils().rightShiftNumberOnStack(32, false); else // Extract the runtime part. m_context << ((u256(1) << 32) - 1) << Instruction::AND; @@ -503,16 +539,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context.adjustStackOffset(returnParametersSize - parameterSize - 1); break; } - case Location::External: - case Location::CallCode: - case Location::DelegateCall: - case Location::Bare: - case Location::BareCallCode: - case Location::BareDelegateCall: + case FunctionType::Kind::External: + case FunctionType::Kind::CallCode: + case FunctionType::Kind::DelegateCall: + case FunctionType::Kind::Bare: + case FunctionType::Kind::BareCallCode: + case FunctionType::Kind::BareDelegateCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; - case Location::Creation: + case FunctionType::Kind::Creation: { _functionCall.expression().accept(*this); solAssert(!function.gasSet(), "Gas limit set for contract creation."); @@ -523,20 +559,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arg->accept(*this); argumentTypes.push_back(arg->annotation().type); } - ContractDefinition const& contract = - dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); - // copy the contract's code into memory - eth::Assembly const& assembly = m_context.compiledContract(contract); - utils().fetchFreeMemoryPointer(); - // TODO we create a copy here, which is actually what we want. - // This should be revisited at the point where we fix - // https://github.com/ethereum/solidity/issues/1092 - // pushes size - auto subroutine = m_context.addSubroutine(make_shared<eth::Assembly>(assembly)); - m_context << Instruction::DUP1 << subroutine; - m_context << Instruction::DUP4 << Instruction::CODECOPY; - - m_context << Instruction::ADD; + ContractDefinition const* contract = + &dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); + m_context.callLowLevelFunction( + "$copyContractCreationCodeToMemory_" + contract->type()->identifier(), + 0, + 1, + [contract](CompilerContext& _context) + { + // copy the contract's code into memory + eth::Assembly const& assembly = _context.compiledContract(*contract); + CompilerUtils(_context).fetchFreeMemoryPointer(); + // pushes size + auto subroutine = _context.addSubroutine(make_shared<eth::Assembly>(assembly)); + _context << Instruction::DUP1 << subroutine; + _context << Instruction::DUP4 << Instruction::CODECOPY; + _context << Instruction::ADD; + } + ); utils().encodeToMemory(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment @@ -548,12 +588,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::CREATE; // Check if zero (out of stack or not enough balance). m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalRevert(); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; } - case Location::SetGas: + case FunctionType::Kind::SetGas: { // stack layout: contract_address function_id [gas] [value] _functionCall.expression().accept(*this); @@ -569,7 +609,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::POP; break; } - case Location::SetValue: + case FunctionType::Kind::SetValue: // stack layout: contract_address function_id [gas] [value] _functionCall.expression().accept(*this); // Note that function is not the original function, but the ".value" function. @@ -578,7 +618,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::POP; arguments.front()->accept(*this); break; - case Location::Send: + case FunctionType::Kind::Send: + case FunctionType::Kind::Transfer: _functionCall.expression().accept(*this); // Provide the gas stipend manually at first because we may send zero ether. // Will be zeroed if we send more than zero ether. @@ -597,7 +638,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) TypePointers{}, strings(), strings(), - Location::Bare, + FunctionType::Kind::Bare, false, nullptr, false, @@ -607,13 +648,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) ), {} ); + if (function.kind() == FunctionType::Kind::Transfer) + { + // Check if zero (out of stack or not enough balance). + m_context << Instruction::ISZERO; + m_context.appendConditionalRevert(); + } break; - case Location::Selfdestruct: + case FunctionType::Kind::Selfdestruct: arguments.front()->accept(*this); utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true); - m_context << Instruction::SUICIDE; + m_context << Instruction::SELFDESTRUCT; + break; + case FunctionType::Kind::Revert: + m_context.appendRevert(); break; - case Location::SHA3: + case FunctionType::Kind::SHA3: { TypePointers argumentTypes; for (auto const& arg: arguments) @@ -624,16 +674,16 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().fetchFreeMemoryPointer(); utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true); utils().toSizeAfterFreeMemoryPointer(); - m_context << Instruction::SHA3; + m_context << Instruction::KECCAK256; break; } - case Location::Log0: - case Location::Log1: - case Location::Log2: - case Location::Log3: - case Location::Log4: + case FunctionType::Kind::Log0: + case FunctionType::Kind::Log1: + case FunctionType::Kind::Log2: + case FunctionType::Kind::Log3: + case FunctionType::Kind::Log4: { - unsigned logNumber = int(function.location()) - int(Location::Log0); + unsigned logNumber = int(function.kind()) - int(FunctionType::Kind::Log0); for (unsigned arg = logNumber; arg > 0; --arg) { arguments[arg]->accept(*this); @@ -650,7 +700,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << logInstruction(logNumber); break; } - case Location::Event: + case FunctionType::Kind::Event: { _functionCall.expression().accept(*this); auto const& event = dynamic_cast<EventDefinition const&>(function.declaration()); @@ -671,7 +721,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) true ); utils().toSizeAfterFreeMemoryPointer(); - m_context << Instruction::SHA3; + m_context << Instruction::KECCAK256; } else utils().convertType( @@ -704,50 +754,50 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << logInstruction(numIndexed); break; } - case Location::BlockHash: + case FunctionType::Kind::BlockHash: { arguments[0]->accept(*this); utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true); m_context << Instruction::BLOCKHASH; break; } - case Location::AddMod: - case Location::MulMod: + case FunctionType::Kind::AddMod: + case FunctionType::Kind::MulMod: { for (unsigned i = 0; i < 3; i ++) { arguments[2 - i]->accept(*this); utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256)); } - if (function.location() == Location::AddMod) + if (function.kind() == FunctionType::Kind::AddMod) m_context << Instruction::ADDMOD; else m_context << Instruction::MULMOD; break; } - case Location::ECRecover: - case Location::SHA256: - case Location::RIPEMD160: + case FunctionType::Kind::ECRecover: + case FunctionType::Kind::SHA256: + case FunctionType::Kind::RIPEMD160: { _functionCall.expression().accept(*this); - static const map<Location, u256> contractAddresses{{Location::ECRecover, 1}, - {Location::SHA256, 2}, - {Location::RIPEMD160, 3}}; - m_context << contractAddresses.find(function.location())->second; + static const map<FunctionType::Kind, u256> contractAddresses{{FunctionType::Kind::ECRecover, 1}, + {FunctionType::Kind::SHA256, 2}, + {FunctionType::Kind::RIPEMD160, 3}}; + m_context << contractAddresses.find(function.kind())->second; for (unsigned i = function.sizeOnStack(); i > 0; --i) m_context << swapInstruction(i); appendExternalFunctionCall(function, arguments); break; } - case Location::ByteArrayPush: - case Location::ArrayPush: + case FunctionType::Kind::ByteArrayPush: + case FunctionType::Kind::ArrayPush: { _functionCall.expression().accept(*this); solAssert(function.parameterTypes().size() == 1, ""); solAssert(!!function.parameterTypes()[0], ""); TypePointer paramType = function.parameterTypes()[0]; shared_ptr<ArrayType> arrayType = - function.location() == Location::ArrayPush ? + function.kind() == FunctionType::Kind::ArrayPush ? make_shared<ArrayType>(DataLocation::Storage, paramType) : make_shared<ArrayType>(DataLocation::Storage); // get the current length @@ -766,17 +816,18 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments[0]->accept(*this); // stack: newLength storageSlot slotOffset argValue TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType()); + solAssert(type, ""); utils().convertType(*arguments[0]->annotation().type, *type); utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack()); // stack: newLength argValue storageSlot slotOffset - if (function.location() == Location::ArrayPush) + if (function.kind() == FunctionType::Kind::ArrayPush) StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true); else StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); break; } - case Location::ObjectCreation: + case FunctionType::Kind::ObjectCreation: { // Will allocate at the end of memory (MSIZE) and not write at all unless the base // type is dynamically sized. @@ -826,6 +877,23 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::POP; break; } + case FunctionType::Kind::Assert: + case FunctionType::Kind::Require: + { + arguments.front()->accept(*this); + utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false); + // jump if condition was met + m_context << Instruction::ISZERO << Instruction::ISZERO; + auto success = m_context.appendConditionalJump(); + if (function.kind() == FunctionType::Kind::Assert) + // condition was not met, flag an error + m_context.appendInvalid(); + else + m_context.appendRevert(); + // the success branch + m_context << success; + break; + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid function type.")); } @@ -853,7 +921,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) *funType->selfType(), true ); - if (funType->location() == FunctionType::Location::Internal) + if (funType->kind() == FunctionType::Kind::Internal) { FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration()); utils().pushCombinedFunctionEntryLabel(funDef); @@ -861,10 +929,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) } else { - solAssert(funType->location() == FunctionType::Location::DelegateCall, ""); + solAssert(funType->kind() == FunctionType::Kind::DelegateCall, ""); auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope()); solAssert(contract && contract->isLibrary(), ""); - m_context.appendLibraryAddress(contract->name()); + m_context.appendLibraryAddress(contract->fullyQualifiedName()); m_context << funType->externalIdentifier(); utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2); } @@ -880,19 +948,44 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) { - if (funType->location() != FunctionType::Location::Internal) - { - _memberAccess.expression().accept(*this); - m_context << funType->externalIdentifier(); - } - else + switch (funType->kind()) { + case FunctionType::Kind::Internal: // We do not visit the expression here on purpose, because in the case of an // internal library function call, this would push the library address forcing // us to link against it although we actually do not need it. - auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration); - solAssert(!!function, "Function not found in member access"); - utils().pushCombinedFunctionEntryLabel(*function); + if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) + utils().pushCombinedFunctionEntryLabel(*function); + else + solAssert(false, "Function not found in member access"); + break; + case FunctionType::Kind::Event: + if (!dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration)) + solAssert(false, "event not found"); + // no-op, because the parent node will do the job + break; + case FunctionType::Kind::External: + case FunctionType::Kind::Creation: + case FunctionType::Kind::DelegateCall: + case FunctionType::Kind::CallCode: + case FunctionType::Kind::Send: + case FunctionType::Kind::Bare: + case FunctionType::Kind::BareCallCode: + case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::Transfer: + _memberAccess.expression().accept(*this); + m_context << funType->externalIdentifier(); + break; + case FunctionType::Kind::Log0: + case FunctionType::Kind::Log1: + case FunctionType::Kind::Log2: + case FunctionType::Kind::Log3: + case FunctionType::Kind::Log4: + case FunctionType::Kind::ECRecover: + case FunctionType::Kind::SHA256: + case FunctionType::Kind::RIPEMD160: + default: + solAssert(false, "unsupported member function"); } } else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get())) @@ -961,7 +1054,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ); m_context << Instruction::BALANCE; } - else if ((set<string>{"send", "call", "callcode", "delegatecall"}).count(member)) + else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), @@ -1121,7 +1214,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) utils().storeInMemoryDynamic(IntegerType(256)); m_context << u256(0); } - m_context << Instruction::SHA3; + m_context << Instruction::KECCAK256; m_context << u256(0); setLValueToStorageItem(_indexAccess); } @@ -1173,10 +1266,10 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) m_context << u256(fixedBytesType.numBytes()); m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); m_context << Instruction::BYTE; - m_context << (u256(1) << (256 - 8)) << Instruction::MUL; + utils().leftShiftNumberOnStack(256 - 8); } else if (baseType.category() == Type::Category::TypeType) { @@ -1218,7 +1311,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) { if (contract->isLibrary()) - m_context.appendLibraryAddress(contract->name()); + m_context.appendLibraryAddress(contract->fullyQualifiedName()); } else if (dynamic_cast<EventDefinition const*>(declaration)) { @@ -1247,6 +1340,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { case Type::Category::RationalNumber: case Type::Category::Bool: + case Type::Category::Integer: m_context << type->literalValue(&_literal); break; case Type::Category::StringLiteral: @@ -1273,11 +1367,12 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type) { + solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types."); if (_operator == Token::Equal || _operator == Token::NotEqual) { if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type)) { - if (funType->location() == FunctionType::Location::Internal) + if (funType->kind() == FunctionType::Kind::Internal) { // We have to remove the upper bits (construction time value) because they might // be "unknown" in one of the operands and not in the other. @@ -1326,8 +1421,6 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator appendArithmeticOperatorCode(_operator, _type); else if (Token::isBitOp(_operator)) appendBitOperatorCode(_operator); - else if (Token::isShiftOp(_operator)) - appendShiftOperatorCode(_operator); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown binary operator.")); } @@ -1356,7 +1449,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty { // Test for division by zero m_context << Instruction::DUP2 << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalInvalid(); if (_operator == Token::Div) m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); @@ -1390,17 +1483,45 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) } } -void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator) +void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType) { - solUnimplemented("Shift operators not yet implemented."); + // stack: shift_amount value_to_shift + + bool c_valueSigned = false; + if (auto valueType = dynamic_cast<IntegerType const*>(&_valueType)) + c_valueSigned = valueType->isSigned(); + else + solAssert(dynamic_cast<FixedBytesType const*>(&_valueType), "Only integer and fixed bytes type supported for shifts."); + + // The amount can be a RationalNumberType too. + bool c_amountSigned = false; + if (auto amountType = dynamic_cast<RationalNumberType const*>(&_shiftAmountType)) + { + // This should be handled by the type checker. + solAssert(amountType->integerType(), ""); + solAssert(!amountType->integerType()->isSigned(), ""); + } + else if (auto amountType = dynamic_cast<IntegerType const*>(&_shiftAmountType)) + c_amountSigned = amountType->isSigned(); + else + solAssert(false, "Invalid shift amount type."); + + // shift by negative amount throws exception + if (c_amountSigned) + { + m_context << u256(0) << Instruction::DUP3 << Instruction::SLT; + m_context.appendConditionalInvalid(); + } + switch (_operator) { case Token::SHL: + m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::MUL; break; case Token::SAR: + m_context << Instruction::SWAP1 << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV); break; case Token::SHR: - break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown shift operator.")); } @@ -1434,11 +1555,10 @@ void ExpressionCompiler::appendExternalFunctionCall( if (_functionType.bound()) utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); - using FunctionKind = FunctionType::Location; - FunctionKind funKind = _functionType.location(); - bool returnSuccessCondition = funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode; - bool isCallCode = funKind == FunctionKind::BareCallCode || funKind == FunctionKind::CallCode; - bool isDelegateCall = funKind == FunctionKind::BareDelegateCall || funKind == FunctionKind::DelegateCall; + auto funKind = _functionType.kind(); + bool returnSuccessCondition = funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode; + bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode; + bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; unsigned retSize = 0; if (returnSuccessCondition) @@ -1455,7 +1575,7 @@ void ExpressionCompiler::appendExternalFunctionCall( TypePointers parameterTypes = _functionType.parameterTypes(); bool manualFunctionId = false; if ( - (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) && + (funKind == FunctionType::Kind::Bare || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) && !_arguments.empty() ) { @@ -1490,7 +1610,7 @@ void ExpressionCompiler::appendExternalFunctionCall( argumentTypes.push_back(_arguments[i]->annotation().type); } - if (funKind == FunctionKind::ECRecover) + if (funKind == FunctionType::Kind::ECRecover) { // Clears 32 bytes of currently free memory and advances free memory pointer. // Output area will be "start of input area" - 32. @@ -1546,7 +1666,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input> m_context << u256(retSize); utils().fetchFreeMemoryPointer(); // This is the start of input - if (funKind == FunctionKind::ECRecover) + if (funKind == FunctionType::Kind::ECRecover) { // In this case, output is 32 bytes before input and has already been cleared. m_context << u256(32) << Instruction::DUP2 << Instruction::SUB << Instruction::SWAP1; @@ -1572,10 +1692,10 @@ void ExpressionCompiler::appendExternalFunctionCall( bool existenceChecked = false; // Check the the target contract exists (has code) for non-low-level calls. - if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall) + if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalRevert(); existenceChecked = true; } @@ -1611,7 +1731,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { //Propagate error condition (if CALL pushes 0 on stack). m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + m_context.appendConditionalRevert(); } utils().popStackSlots(remainsSize); @@ -1620,14 +1740,14 @@ void ExpressionCompiler::appendExternalFunctionCall( { // already there } - else if (funKind == FunctionKind::RIPEMD160) + else if (funKind == FunctionType::Kind::RIPEMD160) { // fix: built-in contract returns right-aligned data utils().fetchFreeMemoryPointer(); utils().loadFromMemoryDynamic(IntegerType(160), false, true, false); utils().convertType(IntegerType(160), FixedBytesType(20)); } - else if (funKind == FunctionKind::ECRecover) + else if (funKind == FunctionType::Kind::ECRecover) { // Output is 32 bytes before input / free mem pointer. // Failing ecrecover cannot be detected, so we clear output before the call. @@ -1688,6 +1808,16 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue<StorageItem>(_expression, *_expression.annotation().type); } +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) +{ + if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) + return true; + else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod)) + return true; + else + return false; +} + CompilerUtils ExpressionCompiler::utils() { return CompilerUtils(m_context); |