Add abi.decode(bytes data, (...))

This commit is contained in:
chriseth
2018-08-15 10:45:16 +02:00
parent 3c5226cefb
commit 9328ea4c3c
15 changed files with 379 additions and 6 deletions
+75 -4
View File
@@ -525,6 +525,75 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
);
}
TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
{
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
if (arguments.size() != 2)
m_errorReporter.typeError(
_functionCall.location(),
"This function takes two arguments, but " +
toString(arguments.size()) +
" were provided."
);
if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
m_errorReporter.typeError(
arguments.front()->location(),
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
type(*arguments.front())->toString() +
" to bytes memory requested."
);
TypePointer returnType = make_shared<TupleType>();
if (arguments.size() < 2)
return returnType;
// The following is a rather syntactic restriction, but we check it here anyway:
// The second argument has to be a tuple expression containing type names.
TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get());
if (!tupleExpression)
{
m_errorReporter.typeError(
arguments[1]->location(),
"The second argument to \"abi.decode\" has to be a tuple of types."
);
return returnType;
}
vector<TypePointer> components;
for (auto const& typeArgument: tupleExpression->components())
{
solAssert(typeArgument, "");
if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get()))
{
TypePointer actualType = argTypeType->actualType();
solAssert(actualType, "");
// We force memory because the parser currently cannot handle
// data locations. Furthermore, storage can be a little dangerous and
// calldata is not really implemented anyway.
actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType);
solAssert(
!actualType->dataStoredIn(DataLocation::CallData) &&
!actualType->dataStoredIn(DataLocation::Storage),
""
);
if (!actualType->fullEncodingType(false, _abiEncoderV2, false))
m_errorReporter.typeError(
typeArgument->location(),
"Decoding type " + actualType->toString(false) + " not supported."
);
components.push_back(actualType);
}
else
{
m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name.");
components.push_back(make_shared<TupleType>());
}
}
return make_shared<TupleType>(components);
}
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
@@ -1727,7 +1796,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
if (functionType->kind() == FunctionType::Kind::ABIDecode)
_functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
m_errorReporter.typeError(
@@ -1782,8 +1855,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
else if (isPositionalCall)
{
bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
for (size_t i = 0; i < arguments.size(); ++i)
{
auto const& argType = type(*arguments[i]);
@@ -1796,7 +1867,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
errored = true;
}
if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments()))
if (!errored && !argType->fullEncodingType(false, abiEncoderV2, !functionType->padArguments()))
m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+5
View File
@@ -91,6 +91,11 @@ private:
// and reports an error, if not.
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
/// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the
/// return type (which is basically the second argument) if successful. It returns
/// the empty tuple type or error.
TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
virtual bool visit(StructDefinition const& _struct) override;
+1 -1
View File
@@ -295,7 +295,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{
// we can ignore the kind of magic and only look at the name of the member
set<string> static const pureMembers{
"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash"
"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash"
};
if (!pureMembers.count(member))
mutability = StateMutability::View;
+12 -1
View File
@@ -2553,6 +2553,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
default: solAssert(false, "Unknown function location."); break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
@@ -2959,7 +2960,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncode ||
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
m_kind == Kind::ABIEncodeWithSignature;
m_kind == Kind::ABIEncodeWithSignature ||
m_kind == Kind::ABIDecode;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -3315,6 +3317,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
FunctionType::Kind::ABIEncodeWithSignature,
true,
StateMutability::Pure
)},
{"decode", make_shared<FunctionType>(
TypePointers(),
TypePointers(),
strings{},
strings{},
FunctionType::Kind::ABIDecode,
true,
StateMutability::Pure
)}
});
default:
+1
View File
@@ -934,6 +934,7 @@ public:
ABIEncodePacked,
ABIEncodeWithSelector,
ABIEncodeWithSignature,
ABIDecode,
GasLeft ///< gasleft()
};
@@ -1070,6 +1070,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// stack now: <memory pointer>
break;
}
case FunctionType::Kind::ABIDecode:
{
arguments.front()->accept(*this);
TypePointer firstArgType = arguments.front()->annotation().type;
TypePointers const& targetTypes = dynamic_cast<TupleType const&>(*_functionCall.annotation().type).components();
if (
*firstArgType == ArrayType(DataLocation::CallData) ||
*firstArgType == ArrayType(DataLocation::CallData, true)
)
utils().abiDecode(targetTypes, false);
else
{
utils().convertType(*firstArgType, ArrayType(DataLocation::Memory));
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
m_context << Instruction::SWAP1 << Instruction::MLOAD;
// stack now: <mem_pos> <length>
utils().abiDecode(targetTypes, true);
}
break;
}
case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS;
break;