mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into HEAD
This commit is contained in:
commit
ce50f05fc1
@ -61,7 +61,7 @@ The following elementary types exist:
|
||||
- ``bool``: equivalent to ``uint8`` restricted to the values 0 and 1. For computing the function selector, ``bool`` is used.
|
||||
|
||||
- ``fixed<M>x<N>``: signed fixed-point decimal number of ``M`` bits, ``8 <= M <= 256``,
|
||||
``M % 8 ==0``, and ``0 < N <= 80``, which denotes the value ``v`` as ``v / (10 ** N)``.
|
||||
``M % 8 == 0``, and ``0 < N <= 80``, which denotes the value ``v`` as ``v / (10 ** N)``.
|
||||
|
||||
- ``ufixed<M>x<N>``: unsigned variant of ``fixed<M>x<N>``.
|
||||
|
||||
@ -76,6 +76,9 @@ The following (fixed-size) array type exists:
|
||||
|
||||
- ``<type>[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type.
|
||||
|
||||
.. note::
|
||||
While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler.
|
||||
|
||||
The following non-fixed-size types exist:
|
||||
|
||||
- ``bytes``: dynamic sized byte sequence.
|
||||
@ -108,7 +111,7 @@ them.
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`enum<enums>` |smallest ``uint`` type that is large enough to hold all values |
|
||||
| | |
|
||||
| |For example, an ``enum`` of 255 values or less is mapped to ``uint8`` and |
|
||||
| |For example, an ``enum`` of 256 values or less is mapped to ``uint8`` and |
|
||||
| |an ``enum`` of 256 values is mapped to ``uint16``. |
|
||||
+-------------------------------+-----------------------------------------------------------------------------+
|
||||
|:ref:`struct<structs>` |``tuple`` |
|
||||
|
@ -47,8 +47,6 @@ set(sources
|
||||
ast/ASTAnnotations.h
|
||||
ast/ASTEnums.h
|
||||
ast/ASTForward.h
|
||||
ast/AsmJsonImporter.cpp
|
||||
ast/AsmJsonImporter.h
|
||||
ast/ASTJsonConverter.cpp
|
||||
ast/ASTJsonConverter.h
|
||||
ast/ASTUtils.cpp
|
||||
|
@ -22,18 +22,20 @@
|
||||
*/
|
||||
|
||||
#include <libsolidity/ast/ASTJsonImporter.h>
|
||||
#include <libsolidity/ast/AsmJsonImporter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
|
||||
#include <libyul/AsmJsonImporter.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <liblangutil/Token.h>
|
||||
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <liblangutil/Token.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -582,7 +584,7 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value con
|
||||
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");
|
||||
|
||||
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value());
|
||||
shared_ptr<yul::Block> operations = make_shared<yul::Block>(AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST")));
|
||||
shared_ptr<yul::Block> operations = make_shared<yul::Block>(yul::AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST")));
|
||||
return createASTNode<InlineAssembly>(
|
||||
_node,
|
||||
nullOrASTString(_node, "documentation"),
|
||||
|
@ -22,30 +22,29 @@
|
||||
|
||||
*/
|
||||
|
||||
#include <libsolidity/ast/AsmJsonImporter.h>
|
||||
#include <libsolidity/ast/ASTJsonImporter.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libyul/AsmJsonImporter.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AsmDataForward.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
namespace solidity::frontend
|
||||
namespace solidity::yul
|
||||
{
|
||||
|
||||
using SourceLocation = langutil::SourceLocation;
|
||||
|
||||
SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _node)
|
||||
{
|
||||
astAssert(member(_node, "src").isString(), "'src' must be a string");
|
||||
yulAssert(member(_node, "src").isString(), "'src' must be a string");
|
||||
|
||||
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceName);
|
||||
}
|
||||
@ -55,7 +54,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node)
|
||||
{
|
||||
T r;
|
||||
r.location = createSourceLocation(_node);
|
||||
astAssert(
|
||||
yulAssert(
|
||||
r.location.source && 0 <= r.location.start && r.location.start <= r.location.end,
|
||||
"Invalid source location in Asm AST"
|
||||
);
|
||||
@ -69,21 +68,21 @@ Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _nam
|
||||
return _node[_name];
|
||||
}
|
||||
|
||||
yul::TypedName AsmJsonImporter::createTypedName(Json::Value const& _node)
|
||||
TypedName AsmJsonImporter::createTypedName(Json::Value const& _node)
|
||||
{
|
||||
auto typedName = createAsmNode<yul::TypedName>(_node);
|
||||
auto typedName = createAsmNode<TypedName>(_node);
|
||||
typedName.type = YulString{member(_node, "type").asString()};
|
||||
typedName.name = YulString{member(_node, "name").asString()};
|
||||
return typedName;
|
||||
}
|
||||
|
||||
yul::Statement AsmJsonImporter::createStatement(Json::Value const& _node)
|
||||
Statement AsmJsonImporter::createStatement(Json::Value const& _node)
|
||||
{
|
||||
Json::Value jsonNodeType = member(_node, "nodeType");
|
||||
astAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
|
||||
yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
|
||||
string nodeType = jsonNodeType.asString();
|
||||
|
||||
astAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
|
||||
yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
|
||||
nodeType = nodeType.substr(3);
|
||||
|
||||
if (nodeType == "ExpressionStatement")
|
||||
@ -109,16 +108,16 @@ yul::Statement AsmJsonImporter::createStatement(Json::Value const& _node)
|
||||
else if (nodeType == "Block")
|
||||
return createBlock(_node);
|
||||
else
|
||||
astAssert(false, "Invalid nodeType as statement");
|
||||
yulAssert(false, "Invalid nodeType as statement");
|
||||
}
|
||||
|
||||
yul::Expression AsmJsonImporter::createExpression(Json::Value const& _node)
|
||||
Expression AsmJsonImporter::createExpression(Json::Value const& _node)
|
||||
{
|
||||
Json::Value jsonNodeType = member(_node, "nodeType");
|
||||
astAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
|
||||
yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!");
|
||||
string nodeType = jsonNodeType.asString();
|
||||
|
||||
astAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
|
||||
yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix");
|
||||
nodeType = nodeType.substr(3);
|
||||
|
||||
if (nodeType == "FunctionCall")
|
||||
@ -128,35 +127,35 @@ yul::Expression AsmJsonImporter::createExpression(Json::Value const& _node)
|
||||
else if (nodeType == "Literal")
|
||||
return createLiteral(_node);
|
||||
else
|
||||
astAssert(false, "Invalid nodeType as expression");
|
||||
yulAssert(false, "Invalid nodeType as expression");
|
||||
}
|
||||
|
||||
vector<yul::Expression> AsmJsonImporter::createExpressionVector(Json::Value const& _array)
|
||||
vector<Expression> AsmJsonImporter::createExpressionVector(Json::Value const& _array)
|
||||
{
|
||||
vector<yul::Expression> ret;
|
||||
vector<Expression> ret;
|
||||
for (auto& var: _array)
|
||||
ret.emplace_back(createExpression(var));
|
||||
return ret;
|
||||
}
|
||||
|
||||
vector<yul::Statement> AsmJsonImporter::createStatementVector(Json::Value const& _array)
|
||||
vector<Statement> AsmJsonImporter::createStatementVector(Json::Value const& _array)
|
||||
{
|
||||
vector<yul::Statement> ret;
|
||||
vector<Statement> ret;
|
||||
for (auto& var: _array)
|
||||
ret.emplace_back(createStatement(var));
|
||||
return ret;
|
||||
}
|
||||
|
||||
yul::Block AsmJsonImporter::createBlock(Json::Value const& _node)
|
||||
Block AsmJsonImporter::createBlock(Json::Value const& _node)
|
||||
{
|
||||
auto block = createAsmNode<yul::Block>(_node);
|
||||
auto block = createAsmNode<Block>(_node);
|
||||
block.statements = createStatementVector(_node["statements"]);
|
||||
return block;
|
||||
}
|
||||
|
||||
yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
|
||||
Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
|
||||
{
|
||||
auto lit = createAsmNode<yul::Literal>(_node);
|
||||
auto lit = createAsmNode<Literal>(_node);
|
||||
string kind = member(_node, "kind").asString();
|
||||
|
||||
lit.value = YulString{member(_node, "value").asString()};
|
||||
@ -165,8 +164,8 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
|
||||
if (kind == "number")
|
||||
{
|
||||
langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")};
|
||||
lit.kind = yul::LiteralKind::Number;
|
||||
astAssert(
|
||||
lit.kind = LiteralKind::Number;
|
||||
yulAssert(
|
||||
scanner.currentToken() == Token::Number,
|
||||
"Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + string(" while scanning ") + lit.value.str()
|
||||
);
|
||||
@ -174,8 +173,8 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
|
||||
else if (kind == "bool")
|
||||
{
|
||||
langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")};
|
||||
lit.kind = yul::LiteralKind::Boolean;
|
||||
astAssert(
|
||||
lit.kind = LiteralKind::Boolean;
|
||||
yulAssert(
|
||||
scanner.currentToken() == Token::TrueLiteral ||
|
||||
scanner.currentToken() == Token::FalseLiteral,
|
||||
"Expected true/false literal!"
|
||||
@ -183,45 +182,45 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node)
|
||||
}
|
||||
else if (kind == "string")
|
||||
{
|
||||
lit.kind = yul::LiteralKind::String;
|
||||
astAssert(
|
||||
lit.kind = LiteralKind::String;
|
||||
yulAssert(
|
||||
lit.value.str().size() <= 32,
|
||||
"String literal too long (" + to_string(lit.value.str().size()) + " > 32)"
|
||||
);
|
||||
}
|
||||
else
|
||||
solAssert(false, "unknown type of literal");
|
||||
yulAssert(false, "unknown type of literal");
|
||||
|
||||
return lit;
|
||||
}
|
||||
|
||||
yul::Leave AsmJsonImporter::createLeave(Json::Value const& _node)
|
||||
Leave AsmJsonImporter::createLeave(Json::Value const& _node)
|
||||
{
|
||||
return createAsmNode<yul::Leave>(_node);
|
||||
return createAsmNode<Leave>(_node);
|
||||
}
|
||||
|
||||
yul::Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node)
|
||||
Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node)
|
||||
{
|
||||
auto identifier = createAsmNode<yul::Identifier>(_node);
|
||||
auto identifier = createAsmNode<Identifier>(_node);
|
||||
identifier.name = YulString(member(_node, "name").asString());
|
||||
return identifier;
|
||||
}
|
||||
|
||||
yul::Assignment AsmJsonImporter::createAssignment(Json::Value const& _node)
|
||||
Assignment AsmJsonImporter::createAssignment(Json::Value const& _node)
|
||||
{
|
||||
auto assignment = createAsmNode<yul::Assignment>(_node);
|
||||
auto assignment = createAsmNode<Assignment>(_node);
|
||||
|
||||
if (_node.isMember("variableNames"))
|
||||
for (auto const& var: member(_node, "variableNames"))
|
||||
assignment.variableNames.emplace_back(createIdentifier(var));
|
||||
|
||||
assignment.value = make_unique<yul::Expression>(createExpression(member(_node, "value")));
|
||||
assignment.value = make_unique<Expression>(createExpression(member(_node, "value")));
|
||||
return assignment;
|
||||
}
|
||||
|
||||
yul::FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node)
|
||||
FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node)
|
||||
{
|
||||
auto functionCall = createAsmNode<yul::FunctionCall>(_node);
|
||||
auto functionCall = createAsmNode<FunctionCall>(_node);
|
||||
|
||||
for (auto const& var: member(_node, "arguments"))
|
||||
functionCall.arguments.emplace_back(createExpression(var));
|
||||
@ -231,25 +230,25 @@ yul::FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node)
|
||||
return functionCall;
|
||||
}
|
||||
|
||||
yul::ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node)
|
||||
ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node)
|
||||
{
|
||||
auto statement = createAsmNode<yul::ExpressionStatement>(_node);
|
||||
auto statement = createAsmNode<ExpressionStatement>(_node);
|
||||
statement.expression = createExpression(member(_node, "expression"));
|
||||
return statement;
|
||||
}
|
||||
|
||||
yul::VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node)
|
||||
VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node)
|
||||
{
|
||||
auto varDec = createAsmNode<yul::VariableDeclaration>(_node);
|
||||
auto varDec = createAsmNode<VariableDeclaration>(_node);
|
||||
for (auto const& var: member(_node, "variables"))
|
||||
varDec.variables.emplace_back(createTypedName(var));
|
||||
varDec.value = make_unique<yul::Expression>(createExpression(member(_node, "value")));
|
||||
varDec.value = make_unique<Expression>(createExpression(member(_node, "value")));
|
||||
return varDec;
|
||||
}
|
||||
|
||||
yul::FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node)
|
||||
FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node)
|
||||
{
|
||||
auto funcDef = createAsmNode<yul::FunctionDefinition>(_node);
|
||||
auto funcDef = createAsmNode<FunctionDefinition>(_node);
|
||||
funcDef.name = YulString{member(_node, "name").asString()};
|
||||
|
||||
if (_node.isMember("parameters"))
|
||||
@ -264,53 +263,53 @@ yul::FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value co
|
||||
return funcDef;
|
||||
}
|
||||
|
||||
yul::If AsmJsonImporter::createIf(Json::Value const& _node)
|
||||
If AsmJsonImporter::createIf(Json::Value const& _node)
|
||||
{
|
||||
auto ifStatement = createAsmNode<yul::If>(_node);
|
||||
ifStatement.condition = make_unique<yul::Expression>(createExpression(member(_node, "condition")));
|
||||
auto ifStatement = createAsmNode<If>(_node);
|
||||
ifStatement.condition = make_unique<Expression>(createExpression(member(_node, "condition")));
|
||||
ifStatement.body = createBlock(member(_node, "body"));
|
||||
return ifStatement;
|
||||
}
|
||||
|
||||
yul::Case AsmJsonImporter::createCase(Json::Value const& _node)
|
||||
Case AsmJsonImporter::createCase(Json::Value const& _node)
|
||||
{
|
||||
auto caseStatement = createAsmNode<yul::Case>(_node);
|
||||
auto caseStatement = createAsmNode<Case>(_node);
|
||||
auto const& value = member(_node, "value");
|
||||
if (value.isString())
|
||||
astAssert(value.asString() == "default", "Expected default case");
|
||||
yulAssert(value.asString() == "default", "Expected default case");
|
||||
else
|
||||
caseStatement.value = make_unique<yul::Literal>(createLiteral(value));
|
||||
caseStatement.value = make_unique<Literal>(createLiteral(value));
|
||||
caseStatement.body = createBlock(member(_node, "body"));
|
||||
return caseStatement;
|
||||
}
|
||||
|
||||
yul::Switch AsmJsonImporter::createSwitch(Json::Value const& _node)
|
||||
Switch AsmJsonImporter::createSwitch(Json::Value const& _node)
|
||||
{
|
||||
auto switchStatement = createAsmNode<yul::Switch>(_node);
|
||||
switchStatement.expression = make_unique<yul::Expression>(createExpression(member(_node, "expression")));
|
||||
auto switchStatement = createAsmNode<Switch>(_node);
|
||||
switchStatement.expression = make_unique<Expression>(createExpression(member(_node, "expression")));
|
||||
for (auto const& var: member(_node, "cases"))
|
||||
switchStatement.cases.emplace_back(createCase(var));
|
||||
return switchStatement;
|
||||
}
|
||||
|
||||
yul::ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node)
|
||||
ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node)
|
||||
{
|
||||
auto forLoop = createAsmNode<yul::ForLoop>(_node);
|
||||
auto forLoop = createAsmNode<ForLoop>(_node);
|
||||
forLoop.pre = createBlock(member(_node, "pre"));
|
||||
forLoop.condition = make_unique<yul::Expression>(createExpression(member(_node, "condition")));
|
||||
forLoop.condition = make_unique<Expression>(createExpression(member(_node, "condition")));
|
||||
forLoop.post = createBlock(member(_node, "post"));
|
||||
forLoop.body = createBlock(member(_node, "body"));
|
||||
return forLoop;
|
||||
}
|
||||
|
||||
yul::Break AsmJsonImporter::createBreak(Json::Value const& _node)
|
||||
Break AsmJsonImporter::createBreak(Json::Value const& _node)
|
||||
{
|
||||
return createAsmNode<yul::Break>(_node);
|
||||
return createAsmNode<Break>(_node);
|
||||
}
|
||||
|
||||
yul::Continue AsmJsonImporter::createContinue(Json::Value const& _node)
|
||||
Continue AsmJsonImporter::createContinue(Json::Value const& _node)
|
||||
{
|
||||
return createAsmNode<yul::Continue>(_node);
|
||||
return createAsmNode<Continue>(_node);
|
||||
}
|
||||
|
||||
}
|
@ -29,7 +29,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace solidity::frontend
|
||||
namespace solidity::yul
|
||||
{
|
||||
|
||||
/**
|
@ -6,6 +6,8 @@ add_library(yul
|
||||
AsmDataForward.h
|
||||
AsmJsonConverter.h
|
||||
AsmJsonConverter.cpp
|
||||
AsmJsonImporter.h
|
||||
AsmJsonImporter.cpp
|
||||
AsmParser.cpp
|
||||
AsmParser.h
|
||||
AsmPrinter.cpp
|
||||
|
@ -154,12 +154,12 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
{
|
||||
if (constructed)
|
||||
{
|
||||
soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call.");
|
||||
soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call.");
|
||||
soltestAssert(
|
||||
!test.call().isConstructor,
|
||||
test.call().kind != FunctionCall::Kind::Constructor,
|
||||
"Constructor has to be the first function call expect for library deployments.");
|
||||
}
|
||||
else if (test.call().isLibrary)
|
||||
else if (test.call().kind == FunctionCall::Kind::Library)
|
||||
{
|
||||
soltestAssert(
|
||||
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
|
||||
@ -169,14 +169,23 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
}
|
||||
else
|
||||
{
|
||||
if (test.call().isConstructor)
|
||||
if (test.call().kind == FunctionCall::Kind::Constructor)
|
||||
deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
|
||||
else
|
||||
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
|
||||
constructed = true;
|
||||
}
|
||||
|
||||
if (test.call().isConstructor)
|
||||
if (test.call().kind == FunctionCall::Kind::Storage)
|
||||
{
|
||||
test.setFailure(false);
|
||||
bytes result(1, !storageEmpty(m_contractAddress));
|
||||
test.setRawBytes(result);
|
||||
soltestAssert(test.call().expectations.rawBytes().size() == 1, "");
|
||||
if (test.call().expectations.rawBytes() != result)
|
||||
success = false;
|
||||
}
|
||||
else if (test.call().kind == FunctionCall::Kind::Constructor)
|
||||
{
|
||||
if (m_transactionSuccessful == test.call().expectations.failure)
|
||||
success = false;
|
||||
@ -187,17 +196,20 @@ TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _line
|
||||
else
|
||||
{
|
||||
bytes output;
|
||||
if (test.call().useCallWithoutSignature)
|
||||
if (test.call().kind == FunctionCall::Kind::LowLevel)
|
||||
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
|
||||
else
|
||||
{
|
||||
soltestAssert(
|
||||
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName())
|
||||
.isMember(test.call().signature),
|
||||
"The function " + test.call().signature + " is not known to the compiler");
|
||||
m_allowNonExistingFunctions ||
|
||||
m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
|
||||
"The function " + test.call().signature + " is not known to the compiler"
|
||||
);
|
||||
|
||||
output = callContractFunctionWithValueNoEncoding(
|
||||
test.call().signature, test.call().value.value, test.call().arguments.rawBytes()
|
||||
test.call().signature,
|
||||
test.call().value.value,
|
||||
test.call().arguments.rawBytes()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,10 @@ contract c {
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// storage: empty
|
||||
// test_short() -> 1780731860627700044960722568376587075150542249149356309979516913770823710
|
||||
// storage: nonempty
|
||||
// test_long() -> 67
|
||||
// storage: nonempty
|
||||
// test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979433020
|
||||
// storage: nonempty
|
||||
|
@ -0,0 +1,29 @@
|
||||
contract Test {
|
||||
bytes x;
|
||||
function set(bytes memory _a) public { x = _a; }
|
||||
}
|
||||
// ----
|
||||
// set(bytes): 0x20, 3, "abc"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 0
|
||||
// storage: empty
|
||||
// set(bytes): 0x20, 31, "1234567890123456789012345678901"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 3, "abc"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 0
|
||||
// storage: empty
|
||||
// set(bytes): 0x20, 3, "abc"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 0
|
||||
// storage: empty
|
||||
// set(bytes): 0x20, 66, "12345678901234567890123456789012", "12345678901234567890123456789012", "12"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 3, "abc"
|
||||
// storage: nonempty
|
||||
// set(bytes): 0x20, 0
|
||||
// storage: empty
|
@ -58,6 +58,7 @@ namespace solidity::frontend::test
|
||||
K(Library, "library", 0) \
|
||||
K(Right, "right", 0) \
|
||||
K(Failure, "FAILURE", 0) \
|
||||
K(Storage, "storage", 0) \
|
||||
|
||||
namespace soltest
|
||||
{
|
||||
@ -275,16 +276,23 @@ struct FunctionCall
|
||||
MultiLine
|
||||
};
|
||||
DisplayMode displayMode = DisplayMode::SingleLine;
|
||||
/// Marks this function call as the constructor.
|
||||
bool isConstructor = false;
|
||||
/// If this function call's signature has no name and no arguments,
|
||||
/// a low-level call with unstructured calldata will be issued.
|
||||
bool useCallWithoutSignature = false;
|
||||
|
||||
enum class Kind {
|
||||
Regular,
|
||||
/// Marks this function call as the constructor.
|
||||
Constructor,
|
||||
/// If this function call's signature has no name and no arguments,
|
||||
/// a low-level call with unstructured calldata will be issued.
|
||||
LowLevel,
|
||||
/// Marks a library deployment call.
|
||||
Library,
|
||||
/// Check that the storage of the current contract is empty or non-empty.
|
||||
Storage
|
||||
};
|
||||
Kind kind = Kind::Regular;
|
||||
/// Marks this function call as "short-handed", meaning
|
||||
/// no `->` declared.
|
||||
bool omitsArrow = true;
|
||||
/// Marks a library deployment call.
|
||||
bool isLibrary = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -82,12 +82,31 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
|
||||
expect(Token::Colon);
|
||||
call.signature = m_scanner.currentLiteral();
|
||||
expect(Token::Identifier);
|
||||
call.isLibrary = true;
|
||||
call.kind = FunctionCall::Kind::Library;
|
||||
call.expectations.failure = false;
|
||||
}
|
||||
else if (accept(Token::Storage, true))
|
||||
{
|
||||
expect(Token::Colon);
|
||||
call.expectations.failure = false;
|
||||
call.expectations.result.push_back(Parameter());
|
||||
// empty / non-empty is encoded as false / true
|
||||
if (m_scanner.currentLiteral() == "empty")
|
||||
call.expectations.result.back().rawBytes = bytes(1, uint8_t(false));
|
||||
else if (m_scanner.currentLiteral() == "nonempty")
|
||||
call.expectations.result.back().rawBytes = bytes(1, uint8_t(true));
|
||||
else
|
||||
throw TestParserError("Expected \"empty\" or \"nonempty\".");
|
||||
call.kind = FunctionCall::Kind::Storage;
|
||||
m_scanner.scanNextToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature();
|
||||
bool lowLevelCall = false;
|
||||
tie(call.signature, lowLevelCall) = parseFunctionSignature();
|
||||
if (lowLevelCall)
|
||||
call.kind = FunctionCall::Kind::LowLevel;
|
||||
|
||||
if (accept(Token::Comma, true))
|
||||
call.value = parseFunctionCallValue();
|
||||
|
||||
@ -124,8 +143,7 @@ vector<solidity::frontend::test::FunctionCall> TestFileParser::parseFunctionCall
|
||||
call.expectations.comment = parseComment();
|
||||
|
||||
if (call.signature == "constructor()")
|
||||
call.isConstructor = true;
|
||||
|
||||
call.kind = FunctionCall::Kind::Constructor;
|
||||
}
|
||||
|
||||
calls.emplace_back(std::move(call));
|
||||
@ -481,6 +499,7 @@ void TestFileParser::Scanner::scanNextToken()
|
||||
if (_literal == "right") return TokenDesc{Token::Right, _literal};
|
||||
if (_literal == "hex") return TokenDesc{Token::Hex, _literal};
|
||||
if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal};
|
||||
if (_literal == "storage") return TokenDesc{Token::Storage, _literal};
|
||||
return TokenDesc{Token::Identifier, _literal};
|
||||
};
|
||||
|
||||
|
@ -44,7 +44,8 @@ namespace solidity::frontend::test
|
||||
* // h(uint256), 1 ether: 42
|
||||
* // -> FAILURE # If REVERT or other EVM failure was detected #
|
||||
* // () # Call fallback function #
|
||||
* // (), 1 ether # Call ether function #
|
||||
* // (), 1 ether # Call receive ether function #
|
||||
* // EMPTY_STORAGE # Check that storage is empty
|
||||
* ...
|
||||
*/
|
||||
class TestFileParser
|
||||
|
@ -82,8 +82,8 @@ void testFunctionCall(
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor);
|
||||
BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary);
|
||||
BOOST_REQUIRE_EQUAL(_call.kind == FunctionCall::Kind::Constructor, _isConstructor);
|
||||
BOOST_REQUIRE_EQUAL(_call.kind == FunctionCall::Kind::Library, _isLibrary);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(TestFileParserTest)
|
||||
@ -953,6 +953,28 @@ BOOST_AUTO_TEST_CASE(library)
|
||||
);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(empty_storage)
|
||||
{
|
||||
char const* source = R"(
|
||||
// storage: empty
|
||||
)";
|
||||
auto const calls = parse(source);
|
||||
BOOST_REQUIRE_EQUAL(calls.size(), 1);
|
||||
BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage);
|
||||
BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 0));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(nonempty_storage)
|
||||
{
|
||||
char const* source = R"(
|
||||
// storage: nonempty
|
||||
)";
|
||||
auto const calls = parse(source);
|
||||
BOOST_REQUIRE_EQUAL(calls.size(), 1);
|
||||
BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage);
|
||||
BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
@ -56,11 +56,25 @@ string TestFunctionCall::format(
|
||||
string newline = formatToken(Token::Newline);
|
||||
string failure = formatToken(Token::Failure);
|
||||
|
||||
if (m_call.isLibrary)
|
||||
if (m_call.kind == FunctionCall::Kind::Library)
|
||||
{
|
||||
stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature;
|
||||
return;
|
||||
}
|
||||
else if (m_call.kind == FunctionCall::Kind::Storage)
|
||||
{
|
||||
stream << _linePrefix << newline << ws << "storage" << colon << ws;
|
||||
soltestAssert(m_rawBytes.size() == 1, "");
|
||||
soltestAssert(m_call.expectations.rawBytes().size() == 1, "");
|
||||
bool isEmpty = _renderResult ? m_rawBytes.front() == 0 : m_call.expectations.rawBytes().front() == 0;
|
||||
string output = isEmpty ? "empty" : "nonempty";
|
||||
if (_renderResult && !matchesExpectation())
|
||||
AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << output;
|
||||
else
|
||||
stream << output;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// Formats the function signature. This is the same independent from the display-mode.
|
||||
stream << _linePrefix << newline << ws << m_call.signature;
|
||||
|
@ -115,8 +115,6 @@ uint64_t popcnt(uint64_t _v)
|
||||
|
||||
}
|
||||
|
||||
using u512 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<512, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
|
||||
|
||||
u256 EwasmBuiltinInterpreter::evalBuiltin(
|
||||
YulString _functionName,
|
||||
vector<Expression> const& _arguments,
|
||||
@ -144,14 +142,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(
|
||||
else if (fun == "datacopy")
|
||||
{
|
||||
// This is identical to codecopy.
|
||||
if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)))
|
||||
copyZeroExtended(
|
||||
m_state.memory,
|
||||
m_state.code,
|
||||
static_cast<size_t>(_evaluatedArguments.at(0)),
|
||||
static_cast<size_t>(_evaluatedArguments.at(1) & numeric_limits<size_t>::max()),
|
||||
static_cast<size_t>(_evaluatedArguments.at(2))
|
||||
);
|
||||
accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2));
|
||||
copyZeroExtended(
|
||||
m_state.memory,
|
||||
m_state.code,
|
||||
static_cast<size_t>(_evaluatedArguments.at(0)),
|
||||
static_cast<size_t>(_evaluatedArguments.at(1) & numeric_limits<size_t>::max()),
|
||||
static_cast<size_t>(_evaluatedArguments.at(2))
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
else if (fun == "i32.drop" || fun == "i64.drop" || fun == "nop")
|
||||
@ -324,11 +322,11 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
|
||||
{
|
||||
if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.calldata.size())
|
||||
throw ExplicitlyTerminated();
|
||||
if (accessMemory(arg[0], arg[2]))
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.calldata,
|
||||
size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
|
||||
);
|
||||
accessMemory(arg[0], arg[2]);
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.calldata,
|
||||
size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
|
||||
);
|
||||
return {};
|
||||
}
|
||||
else if (_fun == "getCallDataSize")
|
||||
@ -377,11 +375,11 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
|
||||
}
|
||||
else if (_fun == "codeCopy")
|
||||
{
|
||||
if (accessMemory(arg[0], arg[2]))
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.code,
|
||||
size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
|
||||
);
|
||||
accessMemory(arg[0], arg[2]);
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.code,
|
||||
size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
else if (_fun == "getCodeSize")
|
||||
@ -407,12 +405,12 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
|
||||
else if (_fun == "externalCodeCopy")
|
||||
{
|
||||
readAddress(arg[0]);
|
||||
if (accessMemory(arg[1], arg[3]))
|
||||
// TODO this way extcodecopy and codecopy do the same thing.
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.code,
|
||||
size_t(arg[1]), size_t(arg[2]), size_t(arg[3])
|
||||
);
|
||||
accessMemory(arg[1], arg[3]);
|
||||
// TODO this way extcodecopy and codecopy do the same thing.
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.code,
|
||||
size_t(arg[1]), size_t(arg[2]), size_t(arg[3])
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
else if (_fun == "getExternalCodeSize")
|
||||
@ -454,16 +452,16 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
|
||||
else if (_fun == "finish")
|
||||
{
|
||||
bytes data;
|
||||
if (accessMemory(arg[0], arg[1]))
|
||||
data = readMemory(arg[0], arg[1]);
|
||||
accessMemory(arg[0], arg[1]);
|
||||
data = readMemory(arg[0], arg[1]);
|
||||
logTrace(evmasm::Instruction::RETURN, {}, data);
|
||||
throw ExplicitlyTerminated();
|
||||
}
|
||||
else if (_fun == "revert")
|
||||
{
|
||||
bytes data;
|
||||
if (accessMemory(arg[0], arg[1]))
|
||||
data = readMemory(arg[0], arg[1]);
|
||||
accessMemory(arg[0], arg[1]);
|
||||
data = readMemory(arg[0], arg[1]);
|
||||
logTrace(evmasm::Instruction::REVERT, {}, data);
|
||||
throw ExplicitlyTerminated();
|
||||
}
|
||||
@ -473,11 +471,11 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
|
||||
{
|
||||
if (arg[1] + arg[2] < arg[1] || arg[1] + arg[2] > m_state.returndata.size())
|
||||
throw ExplicitlyTerminated();
|
||||
if (accessMemory(arg[0], arg[2]))
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.calldata,
|
||||
size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
|
||||
);
|
||||
accessMemory(arg[0], arg[2]);
|
||||
copyZeroExtended(
|
||||
m_state.memory, m_state.calldata,
|
||||
size_t(arg[0]), size_t(arg[1]), size_t(arg[2])
|
||||
);
|
||||
return {};
|
||||
}
|
||||
else if (_fun == "selfDestruct")
|
||||
@ -494,18 +492,15 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector<uint64_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool EwasmBuiltinInterpreter::accessMemory(u256 const& _offset, u256 const& _size)
|
||||
void EwasmBuiltinInterpreter::accessMemory(u256 const& _offset, u256 const& _size)
|
||||
{
|
||||
if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size)))
|
||||
{
|
||||
u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f);
|
||||
m_state.msize = max(m_state.msize, newSize);
|
||||
return _size <= 0xffff;
|
||||
}
|
||||
else
|
||||
m_state.msize = u256(-1);
|
||||
// Single WebAssembly page.
|
||||
// TODO: Support expansion in this interpreter.
|
||||
m_state.msize = 65536;
|
||||
|
||||
return false;
|
||||
if (((_offset + _size) < _offset) || ((_offset + _size) > m_state.msize))
|
||||
// Ewasm throws out of bounds exception as opposed to the EVM.
|
||||
throw ExplicitlyTerminated();
|
||||
}
|
||||
|
||||
bytes EwasmBuiltinInterpreter::readMemory(uint64_t _offset, uint64_t _size)
|
||||
|
@ -91,8 +91,7 @@ private:
|
||||
|
||||
/// Checks if the memory access is not too large for the interpreter and adjusts
|
||||
/// msize accordingly.
|
||||
/// @returns false if the amount of bytes read is lager than 0xffff
|
||||
bool accessMemory(u256 const& _offset, u256 const& _size = 32);
|
||||
void accessMemory(u256 const& _offset, u256 const& _size = 32);
|
||||
/// @returns the memory contents at the provided address.
|
||||
/// Does not adjust msize, use @a accessMemory for that
|
||||
bytes readMemory(uint64_t _offset, uint64_t _size = 32);
|
||||
|
Loading…
Reference in New Issue
Block a user