aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-06-28 00:29:01 +0800
committerGitHub <noreply@github.com>2018-06-28 00:29:01 +0800
commit4a842ecc823c2d4152cdf2639eb83f2318499f1c (patch)
tree7733ac8095862aa2a9b43b93706ba56df7460a16 /libsolidity
parentce4b233f8f58f04d564aedc3061e7ecb1bf9737a (diff)
parent92cb4acd8a748ef2cf6a00a5a9f41975c23127c2 (diff)
downloaddexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.tar
dexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.tar.gz
dexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.tar.bz2
dexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.tar.lz
dexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.tar.xz
dexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.tar.zst
dexon-solidity-4a842ecc823c2d4152cdf2639eb83f2318499f1c.zip
Merge pull request #4097 from ethereum/noPackedExceptForPacked
[BREAKING] call only takes a single argument and does not pad
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/GlobalContext.cpp8
-rw-r--r--libsolidity/analysis/TypeChecker.cpp73
-rw-r--r--libsolidity/ast/Types.cpp25
-rw-r--r--libsolidity/ast/Types.h9
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp68
5 files changed, 98 insertions, 85 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index 756bb540..7b4bc3aa 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -42,7 +42,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("blockhash", make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)),
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
- make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA3, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
make_shared<MagicVariableDeclaration>("log2", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)),
@@ -55,10 +55,10 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
- make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
- make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)),
- make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA3, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction))
})
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index e833b8fe..77e7cf67 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1745,35 +1745,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
- if (functionType->takesSinglePackedBytesParameter())
- {
- if (
- (arguments.size() > 1) ||
- (arguments.size() == 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
- )
- {
- string msg =
- "This function only accepts a single \"bytes\" argument. Please use "
- "\"abi.encodePacked(...)\" or a similar function to encode the data.";
- if (v050)
- m_errorReporter.typeError(_functionCall.location(), msg);
- else
- m_errorReporter.warning(_functionCall.location(), msg);
- }
-
- if (arguments.size() == 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
- {
- string msg =
- "The provided argument of type " +
- type(*arguments.front())->toString() +
- " is not implicitly convertible to expected type bytes memory.";
- if (v050)
- m_errorReporter.typeError(_functionCall.location(), msg);
- else
- m_errorReporter.warning(_functionCall.location(), msg);
- }
- }
-
if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
@@ -1805,6 +1776,26 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
for (auto const& member: membersRemovedForStructConstructor)
msg += " " + member;
}
+ else if (
+ functionType->kind() == FunctionType::Kind::BareCall ||
+ functionType->kind() == FunctionType::Kind::BareCallCode ||
+ functionType->kind() == FunctionType::Kind::BareDelegateCall
+ )
+ {
+ if (arguments.empty())
+ msg += " This function requires a single bytes argument. Use \"\" as argument to provide empty calldata.";
+ else
+ msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
+ }
+ else if (
+ functionType->kind() == FunctionType::Kind::SHA3 ||
+ functionType->kind() == FunctionType::Kind::SHA256 ||
+ functionType->kind() == FunctionType::Kind::RIPEMD160
+ )
+ msg +=
+ " This function requires a single bytes argument."
+ " Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour"
+ " or abi.encode(...) to use ABI encoding.";
m_errorReporter.typeError(_functionCall.location(), msg);
}
else if (isPositionalCall)
@@ -1841,15 +1832,31 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
- m_errorReporter.typeError(
- arguments[i]->location(),
+ {
+ string msg =
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
type(*arguments[i])->toString() +
" to " +
parameterTypes[i]->toString() +
- " requested."
- );
+ " requested.";
+ if (
+ functionType->kind() == FunctionType::Kind::BareCall ||
+ functionType->kind() == FunctionType::Kind::BareCallCode ||
+ functionType->kind() == FunctionType::Kind::BareDelegateCall
+ )
+ msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
+ else if (
+ functionType->kind() == FunctionType::Kind::SHA3 ||
+ functionType->kind() == FunctionType::Kind::SHA256 ||
+ functionType->kind() == FunctionType::Kind::RIPEMD160
+ )
+ msg +=
+ " This function requires a single bytes argument."
+ " Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour"
+ " or abi.encode(...) to use ABI encoding.";
+ m_errorReporter.typeError(arguments[i]->location(), msg);
+ }
}
}
else
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 69124705..1c4eb76e 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -599,9 +599,9 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress())
return {
{"balance", make_shared<IntegerType>(256)},
- {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)},
- {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
+ {"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
+ {"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
+ {"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
@@ -3005,6 +3005,25 @@ ASTPointer<ASTString> FunctionType::documentation() const
return ASTPointer<ASTString>();
}
+bool FunctionType::padArguments() const
+{
+ // No padding only for hash functions, low-level calls and the packed encoding function.
+ switch (m_kind)
+ {
+ case Kind::BareCall:
+ case Kind::BareCallCode:
+ case Kind::BareDelegateCall:
+ case Kind::SHA256:
+ case Kind::RIPEMD160:
+ case Kind::SHA3:
+ case Kind::ABIEncodePacked:
+ return false;
+ default:
+ return true;
+ }
+ return true;
+}
+
string MappingType::richIdentifier() const
{
return "t_mapping" + identifierList(m_keyType, m_valueType);
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index b2f34dee..4415fb4b 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -1058,18 +1058,21 @@ public:
ASTPointer<ASTString> documentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
- bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); }
+ /// The only functions that do not pad are hash functions, the low-level call functions
+ /// and abi.encodePacked.
+ bool padArguments() const;
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
/// true iff the function takes a single bytes parameter and it is passed on without padding.
- /// @todo until 0.5.0, this is just a "recommendation".
bool takesSinglePackedBytesParameter() const
{
- // @todo add the call kinds here with 0.5.0 and perhaps also log0.
switch (m_kind)
{
case FunctionType::Kind::SHA3:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:
+ case FunctionType::Kind::BareCall:
+ case FunctionType::Kind::BareCallCode:
+ case FunctionType::Kind::BareDelegateCall:
return true;
default:
return false;
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 0470c3ec..ecbd0243 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -699,16 +699,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case FunctionType::Kind::SHA3:
{
- TypePointers argumentTypes;
- for (auto const& arg: arguments)
+ solAssert(arguments.size() == 1, "");
+ solAssert(!function.padArguments(), "");
+ TypePointer const& argType = arguments.front()->annotation().type;
+ solAssert(argType, "");
+ arguments.front()->accept(*this);
+ // Optimization: If type is bytes or string, then do not encode,
+ // but directly compute keccak256 on memory.
+ if (*argType == ArrayType(DataLocation::Memory) || *argType == ArrayType(DataLocation::Memory, true))
{
- arg->accept(*this);
- argumentTypes.push_back(arg->annotation().type);
+ ArrayUtils(m_context).retrieveLength(ArrayType(DataLocation::Memory));
+ m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD;
+ }
+ else
+ {
+ utils().fetchFreeMemoryPointer();
+ utils().packedEncode({argType}, TypePointers());
+ utils().toSizeAfterFreeMemoryPointer();
}
- utils().fetchFreeMemoryPointer();
- solAssert(!function.padArguments(), "");
- utils().packedEncode(argumentTypes, TypePointers());
- utils().toSizeAfterFreeMemoryPointer();
m_context << Instruction::KECCAK256;
break;
}
@@ -1802,13 +1810,14 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (_functionType.bound())
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
+ bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050);
auto funKind = _functionType.kind();
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool useStaticCall =
_functionType.stateMutability() <= StateMutability::View &&
- m_context.experimentalFeatureActive(ExperimentalFeature::V050) &&
+ v050 &&
m_context.evmVersion().hasStaticCall();
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
@@ -1836,38 +1845,12 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Evaluate arguments.
TypePointers argumentTypes;
TypePointers parameterTypes = _functionType.parameterTypes();
- bool manualFunctionId = false;
- if (
- (funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) &&
- !_arguments.empty()
- )
- {
- solAssert(_arguments.front()->annotation().type->mobileType(), "");
- manualFunctionId =
- _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) ==
- CompilerUtils::dataStartOffset;
- }
- if (manualFunctionId)
- {
- // If we have a Bare* and the first type has exactly 4 bytes, use it as
- // function identifier.
- _arguments.front()->accept(*this);
- utils().convertType(
- *_arguments.front()->annotation().type,
- IntegerType(8 * CompilerUtils::dataStartOffset),
- true
- );
- for (unsigned i = 0; i < gasValueSize; ++i)
- m_context << swapInstruction(gasValueSize - i);
- gasStackPos++;
- valueStackPos++;
- }
if (_functionType.bound())
{
argumentTypes.push_back(_functionType.selfType());
parameterTypes.insert(parameterTypes.begin(), _functionType.selfType());
}
- for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
+ for (size_t i = 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
argumentTypes.push_back(_arguments[i]->annotation().type);
@@ -1903,19 +1886,20 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Copy function identifier to memory.
utils().fetchFreeMemoryPointer();
- if (!_functionType.isBareCall() || manualFunctionId)
+ if (!_functionType.isBareCall())
{
m_context << dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes));
utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
}
- // If the function takes arbitrary parameters, copy dynamic length data in place.
+
+ // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
// Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack).
utils().encodeToMemory(
argumentTypes,
parameterTypes,
_functionType.padArguments(),
- _functionType.takesArbitraryParameters(),
+ _functionType.takesArbitraryParameters() || _functionType.isBareCall(),
isCallCode || isDelegateCall
);
@@ -1996,9 +1980,9 @@ void ExpressionCompiler::appendExternalFunctionCall(
unsigned remainsSize =
2 + // contract address, input_memory_end
- _functionType.valueSet() +
- _functionType.gasSet() +
- (!_functionType.isBareCall() || manualFunctionId);
+ (_functionType.valueSet() ? 1 : 0) +
+ (_functionType.gasSet() ? 1 : 0) +
+ (!_functionType.isBareCall() ? 1 : 0);
if (returnSuccessCondition)
m_context << swapInstruction(remainsSize);