Check availability of data objects already in analysis phase.

This commit is contained in:
chriseth 2019-07-09 14:06:33 +02:00
parent f3bdc79187
commit 2a5280faa0
7 changed files with 72 additions and 27 deletions

View File

@ -18,6 +18,7 @@ Compiler Features:
Bugfixes: Bugfixes:
* View/Pure Checker: Properly detect state variable access through base class. * View/Pure Checker: Properly detect state variable access through base class.
* Yul analyzer: Check availability of data objects already in analysis phase.

View File

@ -383,11 +383,19 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
{ {
if (!expectExpression(arg)) if (!expectExpression(arg))
success = false; success = false;
else if (needsLiteralArguments && arg.type() != typeid(Literal)) else if (needsLiteralArguments)
m_errorReporter.typeError( {
_funCall.functionName.location, if (arg.type() != typeid(Literal))
"Function expects direct literals as arguments." m_errorReporter.typeError(
); _funCall.functionName.location,
"Function expects direct literals as arguments."
);
else if (!m_dataNames.count(boost::get<Literal>(arg).value))
m_errorReporter.typeError(
_funCall.functionName.location,
"Unknown data object \"" + boost::get<Literal>(arg).value.str() + "\"."
);
}
} }
// Use argument size instead of parameter count to avoid misleading errors. // Use argument size instead of parameter count to avoid misleading errors.
m_stackHeight += int(returns) - int(_funCall.arguments.size()); m_stackHeight += int(returns) - int(_funCall.arguments.size());

View File

@ -61,13 +61,15 @@ public:
langutil::ErrorReporter& _errorReporter, langutil::ErrorReporter& _errorReporter,
boost::optional<langutil::Error::Type> _errorTypeForLoose, boost::optional<langutil::Error::Type> _errorTypeForLoose,
Dialect const& _dialect, Dialect const& _dialect,
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(),
std::set<YulString> const& _dataNames = {}
): ):
m_resolver(_resolver), m_resolver(_resolver),
m_info(_analysisInfo), m_info(_analysisInfo),
m_errorReporter(_errorReporter), m_errorReporter(_errorReporter),
m_dialect(_dialect), m_dialect(_dialect),
m_errorTypeForLoose(_errorTypeForLoose) m_errorTypeForLoose(_errorTypeForLoose),
m_dataNames(_dataNames)
{ {
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect)) if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
m_evmVersion = evmDialect->evmVersion(); m_evmVersion = evmDialect->evmVersion();
@ -124,6 +126,8 @@ private:
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
Dialect const& m_dialect; Dialect const& m_dialect;
boost::optional<langutil::Error::Type> m_errorTypeForLoose; boost::optional<langutil::Error::Type> m_errorTypeForLoose;
/// Names of data objects to be referenced by builtin functions with literal arguments.
std::set<YulString> m_dataNames;
ForLoop const* m_currentForLoop = nullptr; ForLoop const* m_currentForLoop = nullptr;
}; };

View File

@ -113,7 +113,20 @@ bool AssemblyStack::analyzeParsed(Object& _object)
{ {
solAssert(_object.code, ""); solAssert(_object.code, "");
_object.analysisInfo = make_shared<AsmAnalysisInfo>(); _object.analysisInfo = make_shared<AsmAnalysisInfo>();
AsmAnalyzer analyzer(*_object.analysisInfo, m_errorReporter, boost::none, languageToDialect(m_language, m_evmVersion));
set<YulString> objectNames;
objectNames.insert(_object.name);
for (auto const& subObject: _object.subIndexByName)
objectNames.insert(subObject.first);
AsmAnalyzer analyzer(
*_object.analysisInfo,
m_errorReporter,
boost::none,
languageToDialect(m_language, m_evmVersion),
{},
objectNames
);
bool success = analyzer.analyze(*_object.code); bool success = analyzer.analyze(*_object.code);
for (auto& subNode: _object.subObjects) for (auto& subNode: _object.subObjects)
if (auto subObject = dynamic_cast<Object*>(subNode.get())) if (auto subObject = dynamic_cast<Object*>(subNode.get()))

View File

@ -278,6 +278,19 @@ BOOST_AUTO_TEST_CASE(args_to_datacopy_are_arbitrary)
BOOST_CHECK(successParse(code)); BOOST_CHECK(successParse(code));
} }
BOOST_AUTO_TEST_CASE(non_existing_objects)
{
BOOST_CHECK(successParse(
"object \"main\" { code { pop(datasize(\"main\")) } }"
));
CHECK_ERROR(
"object \"main\" { code { pop(datasize(\"abc\")) } }",
TypeError,
"Unknown data object"
);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

View File

@ -1,15 +1,18 @@
{ object "main" {
// Arguments to ``datasize`` and ``dataoffset`` need to be code {
// literals. We cannot simplify their arguments, but we can // Arguments to ``datasize`` and ``dataoffset`` need to be
// simplify them as a full expression. // literals. We cannot simplify their arguments, but we can
// ``datacopy`` does not have this restriction. // simplify them as a full expression.
let r := "abc" // ``datacopy`` does not have this restriction.
let a := datasize("abc") let r := "abc"
let x := dataoffset("abc") let a := datasize("abc")
// should be replaced by a let x := dataoffset("abc")
let y := datasize("abc") // should be replaced by a
datacopy("abc", x, y) let y := datasize("abc")
mstore(a, x) datacopy("abc", x, y)
mstore(a, x)
}
data "abc" "Hello, World!"
} }
// ==== // ====
// step: commonSubexpressionEliminator // step: commonSubexpressionEliminator

View File

@ -1,10 +1,13 @@
{ object "main" {
// We should never split arguments to ``dataoffset`` code {
// or ``datasize`` because they need to be literals // We should never split arguments to ``dataoffset``
let x := dataoffset("abc") // or ``datasize`` because they need to be literals
let y := datasize("abc") let x := dataoffset("abc")
// datacopy is fine, though let y := datasize("abc")
datacopy(mload(0), mload(1), mload(2)) // datacopy is fine, though
datacopy(mload(0), mload(1), mload(2))
}
data "abc" "Hello, World!"
} }
// ==== // ====
// step: expressionSplitter // step: expressionSplitter