From 47969adf91dc3952592f473e4afbe5e306e477aa Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 18 May 2023 15:22:09 +0200 Subject: [PATCH] Experimental standard library Change import syntax and cover with tests --- CMakeLists.txt | 1 + libsolidity/interface/CompilerStack.cpp | 12 +++++++ libsolidity/parsing/Parser.cpp | 29 ++++++++++++--- libsolidity/parsing/Parser.h | 5 +++ libstdlib/CMakeLists.txt | 18 ++++++++++ libstdlib/src/stub.sol | 9 +++++ libstdlib/stdlib.h.in | 15 ++++++++ libstdlib/stdlib.src.h.in | 13 +++++++ scripts/ASTImportTest.sh | 2 +- scripts/test_antlr_grammar.sh | 2 +- test/libsolidity/SyntaxTest.cpp | 35 ++++++++++--------- .../experimental/parsing_stdlib_import_1.sol | 8 +++++ .../experimental/parsing_stdlib_import_2.sol | 8 +++++ .../experimental/parsing_stdlib_import_3.sol | 8 +++++ .../experimental/parsing_stdlib_import_4.sol | 8 +++++ ...parsing_stdlib_import_without_pragma_1.sol | 3 ++ ...parsing_stdlib_import_without_pragma_2.sol | 3 ++ 17 files changed, 157 insertions(+), 22 deletions(-) create mode 100644 libstdlib/CMakeLists.txt create mode 100644 libstdlib/src/stub.sol create mode 100644 libstdlib/stdlib.h.in create mode 100644 libstdlib/stdlib.src.h.in create mode 100644 test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol diff --git a/CMakeLists.txt b/CMakeLists.txt index c9a7a1876..85d2f64ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,7 @@ add_subdirectory(libevmasm) add_subdirectory(libyul) add_subdirectory(libsolidity) add_subdirectory(libsolc) +add_subdirectory(libstdlib) add_subdirectory(tools) if (NOT EMSCRIPTEN) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f8a5fed6e..aad2bc2ba 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -59,6 +59,8 @@ #include #include +#include + #include #include #include @@ -95,6 +97,7 @@ using namespace std; using namespace solidity; using namespace solidity::langutil; using namespace solidity::frontend; +using namespace solidity::stdlib; using solidity::util::errinfo_comment; @@ -369,6 +372,15 @@ bool CompilerStack::parse() for (auto const& import: ASTNode::filteredNodes(source.ast->nodes())) { solAssert(!import->path().empty(), "Import path cannot be empty."); + // Check whether the import directive is for the standard library, + // and if yes, add specified file to source units to be parsed. + auto it = stdlib::sources.find(import->path()); + if (it != stdlib::sources.end()) + { + auto [name, content] = *it; + m_sources[name].charStream = make_unique(content, name); + sourcesToParse.push_back(name); + } // The current value of `path` is the absolute path as seen from this source file. // We first have to apply remappings before we can store the actual absolute path diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 34fc9644e..efbe85448 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -271,9 +271,9 @@ ASTPointer Parser::parseImportDirective() SourceLocation unitAliasLocation{}; ImportDirective::SymbolAliasList symbolAliases; - if (m_scanner->currentToken() == Token::StringLiteral) + if (isQuotedPath() || isStdlibPath()) { - path = getLiteralAndAdvance(); + path = isQuotedPath() ? getLiteralAndAdvance() : getStdlibImportPathAndAdvance(); if (m_scanner->currentToken() == Token::As) { advance(); @@ -315,9 +315,9 @@ ASTPointer Parser::parseImportDirective() if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from") fatalParserError(8208_error, "Expected \"from\"."); advance(); - if (m_scanner->currentToken() != Token::StringLiteral) + if (!isQuotedPath() && !isStdlibPath()) fatalParserError(6845_error, "Expected import path."); - path = getLiteralAndAdvance(); + path = isQuotedPath() ? getLiteralAndAdvance() : getStdlibImportPathAndAdvance(); } if (path->empty()) fatalParserError(6326_error, "Import path cannot be empty."); @@ -2486,4 +2486,25 @@ ASTPointer Parser::getLiteralAndAdvance() return identifier; } +bool Parser::isQuotedPath() const +{ + return m_scanner->currentToken() == Token::StringLiteral; +} + +bool Parser::isStdlibPath() const +{ + return m_experimentalSolidityEnabledInCurrentSourceUnit + && m_scanner->currentToken() == Token::Identifier + && m_scanner->currentLiteral() == "std"; +} + +ASTPointer Parser::getStdlibImportPathAndAdvance() +{ + ASTPointer std = expectIdentifierToken(); + if (m_scanner->currentToken() == Token::Period) + advance(); + ASTPointer library = expectIdentifierToken(); + return make_shared(*std + "." + *library); +} + } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index c83b307b8..65e8f61f3 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -219,6 +219,11 @@ private: ASTPointer getLiteralAndAdvance(); ///@} + bool isQuotedPath() const; + bool isStdlibPath() const; + + ASTPointer getStdlibImportPathAndAdvance(); + /// Creates an empty ParameterList at the current location (used if parameters can be omitted). ASTPointer createEmptyParameterList(); diff --git a/libstdlib/CMakeLists.txt b/libstdlib/CMakeLists.txt new file mode 100644 index 000000000..a4523b99e --- /dev/null +++ b/libstdlib/CMakeLists.txt @@ -0,0 +1,18 @@ +# This will re-generate the headers if any file within src was modified. +set_directory_properties(PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/stdlib/src/) + +set(STDLIB stub) +set(GENERATED_STDLIB_HEADERS) +foreach(src IN LISTS STDLIB) + set(STDLIB_FILE ${CMAKE_SOURCE_DIR}/libstdlib/src/${src}.sol) + file(READ ${STDLIB_FILE} STDLIB_FILE_CONTENT HEX) + string(REGEX MATCHALL ".." STDLIB_FILE_CONTENT "${STDLIB_FILE_CONTENT}") + list(REMOVE_ITEM STDLIB_FILE_CONTENT "0d") + string(REGEX REPLACE ";" ",\n\t0x" STDLIB_FILE_CONTENT "${STDLIB_FILE_CONTENT}") + set(STDLIB_FILE_CONTENT "0x${STDLIB_FILE_CONTENT}") + set(STDLIB_FILE_NAME ${src}) + configure_file("${CMAKE_SOURCE_DIR}/libstdlib/stdlib.src.h.in" ${CMAKE_BINARY_DIR}/include/libstdlib/${src}.h NEWLINE_STYLE LF @ONLY) + list(APPEND GENERATED_STDLIB_HEADERS ${CMAKE_BINARY_DIR}/include/libstdlib/${src}.h) +endforeach() + +configure_file("${CMAKE_SOURCE_DIR}/libstdlib/stdlib.h.in" ${CMAKE_BINARY_DIR}/include/libstdlib/stdlib.h NEWLINE_STYLE LF @ONLY) diff --git a/libstdlib/src/stub.sol b/libstdlib/src/stub.sol new file mode 100644 index 000000000..46ae21dcf --- /dev/null +++ b/libstdlib/src/stub.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.0; + +pragma experimental solidity; + +function identity(uint256 x) pure returns (uint256) +{ + return x; +} diff --git a/libstdlib/stdlib.h.in b/libstdlib/stdlib.h.in new file mode 100644 index 000000000..3b86029dc --- /dev/null +++ b/libstdlib/stdlib.h.in @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include "libstdlib/stub.h" + +namespace solidity::stdlib +{ + +static std::map sources = { + { "std.stub", stub } +}; + +} // namespace solidity::stdlib diff --git a/libstdlib/stdlib.src.h.in b/libstdlib/stdlib.src.h.in new file mode 100644 index 000000000..0407663d6 --- /dev/null +++ b/libstdlib/stdlib.src.h.in @@ -0,0 +1,13 @@ +// The generation of this file is defined in stdlib/CMakeLists.txt. +// This file was generated by using the content of stdlib/src/@STDLIB_FILE_NAME@.sol. + +#pragma once + +namespace solidity::stdlib +{ + +static char const @STDLIB_FILE_NAME@[] = { + @STDLIB_FILE_CONTENT@, 0 +}; + +} // namespace solidity::stdlib diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index faf98c192..c24526195 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -209,7 +209,7 @@ esac # boost_filesystem_bug specifically tests a local fix for a boost::filesystem # bug. Since the test involves a malformed path, there is no point in running # tests on it. See https://github.com/boostorg/filesystem/issues/176 -IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol") +IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*") NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)" echo "Looking at ${NSOURCES} .sol files..." diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index e84f9b064..4dde11bf7 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -110,7 +110,7 @@ do SOL_FILES+=("$line") done < <( grep --include "*.sol" -riL -E \ - "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (1684|2837|3716|3997|5333|6275|6281|6933|7319|8185|7637)|^==== Source:" \ + "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (1684|2837|3716|3997|5333|6275|6281|6933|7319|8185|7637)|^==== Source:|^pragma experimental solidity;" \ "${ROOT_DIR}/test/libsolidity/syntaxTests" \ "${ROOT_DIR}/test/libsolidity/semanticTests" | # Skipping the unicode tests as I couldn't adapt the lexical grammar to recursively counting RLO/LRO/PDF's. diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ee3c4b4b0..159f9dcb1 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -123,25 +123,28 @@ void SyntaxTest::filterObtainedErrors() string sourceName; if (SourceLocation const* location = currentError->sourceLocation()) { + locationStart = location->start; + locationEnd = location->end; solAssert(location->sourceName, ""); sourceName = *location->sourceName; - solAssert(m_sources.sources.count(sourceName) == 1, ""); - - int preambleSize = - static_cast(compiler().charStream(sourceName).size()) - - static_cast(m_sources.sources[sourceName].size()); - solAssert(preambleSize >= 0, ""); - - // ignore the version & license pragma inserted by the testing tool when calculating locations. - if (location->start != -1) + if(m_sources.sources.count(sourceName) == 1) { - solAssert(location->start >= preambleSize, ""); - locationStart = location->start - preambleSize; - } - if (location->end != -1) - { - solAssert(location->end >= preambleSize, ""); - locationEnd = location->end - preambleSize; + int preambleSize = + static_cast(compiler().charStream(sourceName).size()) - + static_cast(m_sources.sources[sourceName].size()); + solAssert(preambleSize >= 0, ""); + + // ignore the version & license pragma inserted by the testing tool when calculating locations. + if (location->start != -1) + { + solAssert(location->start >= preambleSize, ""); + locationStart = location->start - preambleSize; + } + if (location->end != -1) + { + solAssert(location->end >= preambleSize, ""); + locationEnd = location->end - preambleSize; + } } } m_errorList.emplace_back(SyntaxTestError{ diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol new file mode 100644 index 000000000..beec24c1e --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +import std.stub; +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol new file mode 100644 index 000000000..85d3a4ac7 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +import std.stub as stub; +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol new file mode 100644 index 000000000..7e51a73b1 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +import { identity } from std.stub; +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol new file mode 100644 index 000000000..3c4ff41d6 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +import * as stub from std.stub; +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol new file mode 100644 index 000000000..0106846f9 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol @@ -0,0 +1,3 @@ +import std.stub; +// ---- +// ParserError 9478: (7-10): Expected string literal (path), "*" or alias list. diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol new file mode 100644 index 000000000..3a6c1848b --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol @@ -0,0 +1,3 @@ +import { identity } from std.stub; +// ---- +// ParserError 6845: (25-28): Expected import path.