Skip to content

Commit 9a7c364

Browse files
committed
Process input files in Standard JSON mode just like in other modes
- This makes `-` for stdin work. - `--ignore-missing` now works with `--standard-json`, though it's not very useful because there can be at most one input file. - Separate errors for situations where there are no input files on the command line (this can be detected in the parser) and where they are not present on disk.
1 parent c938f35 commit 9a7c364

16 files changed

+582
-89
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Bugfixes:
1515
* Code Generator: Fix crash when passing an empty string literal to ``bytes.concat()``.
1616
* Code Generator: Fix internal compiler error when calling functions bound to calldata structs and arrays.
1717
* Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals.
18+
* Commandline Interface: Fix crash when a directory path is passed to ``--standard-json``.
19+
* Commandline Interface: Read JSON from standard input when ``--standard-json`` gets ``-`` as a file name.
1820
* Standard JSON: Include source location for errors in files with empty name.
1921
* Type Checker: Fix internal error and prevent static calls to unimplemented modifiers.
2022
* Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation.

solc/CommandLineInterface.cpp

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,18 @@ void CommandLineInterface::handleGasEstimation(string const& _contract)
377377
}
378378
}
379379

380-
bool CommandLineInterface::readInputFilesAndConfigureFileReader()
380+
bool CommandLineInterface::readInputFiles()
381381
{
382+
solAssert(!m_standardJsonInput.has_value(), "");
383+
384+
m_fileReader.setBasePath(m_options.input.basePath);
385+
386+
if (m_fileReader.basePath() != "" && !boost::filesystem::is_directory(m_fileReader.basePath()))
387+
{
388+
serr() << "Base path must be a directory: " << m_fileReader.basePath() << endl;
389+
return false;
390+
}
391+
382392
for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories)
383393
m_fileReader.allowDirectory(allowedDirectory);
384394

@@ -411,16 +421,33 @@ bool CommandLineInterface::readInputFilesAndConfigureFileReader()
411421
}
412422

413423
// NOTE: we ignore the FileNotFound exception as we manually check above
414-
m_fileReader.setSource(infile, readFileAsString(infile.string()));
415-
m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename());
424+
string fileContent = readFileAsString(infile.string());
425+
if (m_options.input.mode == InputMode::StandardJson)
426+
{
427+
solAssert(!m_standardJsonInput.has_value(), "");
428+
m_standardJsonInput = move(fileContent);
429+
}
430+
else
431+
{
432+
m_fileReader.setSource(infile, move(fileContent));
433+
m_fileReader.allowDirectory(boost::filesystem::canonical(infile).remove_filename());
434+
}
416435
}
417436

418437
if (m_options.input.addStdin)
419-
m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin));
438+
{
439+
if (m_options.input.mode == InputMode::StandardJson)
440+
{
441+
solAssert(!m_standardJsonInput.has_value(), "");
442+
m_standardJsonInput = readUntilEnd(m_sin);
443+
}
444+
else
445+
m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin));
446+
}
420447

421-
if (m_fileReader.sourceCodes().size() == 0)
448+
if (m_fileReader.sourceCodes().empty() && !m_standardJsonInput.has_value())
422449
{
423-
serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl;
450+
serr() << "All specified input files either do not exist or are not regular files." << endl;
424451
return false;
425452
}
426453

@@ -502,57 +529,35 @@ bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
502529

503530
bool CommandLineInterface::processInput()
504531
{
505-
m_fileReader.setBasePath(m_options.input.basePath);
506-
507-
if (m_options.input.basePath != "" && !boost::filesystem::is_directory(m_options.input.basePath))
532+
switch (m_options.input.mode)
508533
{
509-
serr() << "Base path must be a directory: \"" << m_options.input.basePath << "\"\n";
510-
return false;
511-
}
512-
513-
if (m_options.input.mode == InputMode::StandardJson)
534+
case InputMode::StandardJson:
514535
{
515-
string input;
516-
if (m_options.input.standardJsonFile.empty())
517-
input = readUntilEnd(m_sin);
518-
else
519-
{
520-
try
521-
{
522-
input = readFileAsString(m_options.input.standardJsonFile);
523-
}
524-
catch (FileNotFound const&)
525-
{
526-
serr() << "File not found: " << m_options.input.standardJsonFile << endl;
527-
return false;
528-
}
529-
catch (NotAFile const&)
530-
{
531-
serr() << "Not a regular file: " << m_options.input.standardJsonFile << endl;
532-
return false;
533-
}
534-
}
536+
solAssert(m_standardJsonInput.has_value(), "");
537+
535538
StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json);
536-
sout() << compiler.compile(std::move(input)) << endl;
539+
sout() << compiler.compile(move(m_standardJsonInput.value())) << endl;
540+
m_standardJsonInput.reset();
537541
return true;
538542
}
539-
540-
if (!readInputFilesAndConfigureFileReader())
541-
return false;
542-
543-
if (m_options.input.mode == InputMode::Assembler)
543+
case InputMode::Assembler:
544+
{
544545
return assemble(
545546
m_options.assembly.inputLanguage,
546547
m_options.assembly.targetMachine,
547548
m_options.optimizer.enabled,
548549
m_options.optimizer.yulSteps
549550
);
550-
551-
if (m_options.input.mode == InputMode::Linker)
551+
}
552+
case InputMode::Linker:
552553
return link();
554+
case InputMode::Compiler:
555+
case InputMode::CompilerWithASTImport:
556+
return compile();
557+
}
553558

554-
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
555-
return compile();
559+
solAssert(false, "");
560+
return false;
556561
}
557562

558563
bool CommandLineInterface::compile()

solc/CommandLineInterface.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class CommandLineInterface
5353

5454
/// Parse command line arguments and return false if we should not continue
5555
bool parseArguments(int _argc, char const* const* _argv);
56+
/// Read the content of all input files and initialize the file reader.
57+
bool readInputFiles();
5658
/// Parse the files and create source code objects
5759
bool processInput();
5860
/// Perform actions on the input depending on provided compiler arguments
@@ -61,6 +63,7 @@ class CommandLineInterface
6163

6264
CommandLineOptions const& options() const { return m_options; }
6365
FileReader const& fileReader() const { return m_fileReader; }
66+
std::optional<std::string> const& standardJsonInput() const { return m_standardJsonInput; }
6467

6568
private:
6669
bool compile();
@@ -95,10 +98,6 @@ class CommandLineInterface
9598
void handleGasEstimation(std::string const& _contract);
9699
void handleStorageLayout(std::string const& _contract);
97100

98-
/// Reads the content of input files specified on the command line and passes them to FileReader.
99-
/// @return false if there are no input files or input files cannot be read.
100-
bool readInputFilesAndConfigureFileReader();
101-
102101
/// Tries to read @ m_sourceCodes as a JSONs holding ASTs
103102
/// such that they can be imported into the compiler (importASTs())
104103
/// (produced by --combined-json ast,compact-format <file.sol>
@@ -129,6 +128,7 @@ class CommandLineInterface
129128
bool m_hasOutput = false;
130129
bool m_error = false; ///< If true, some error occurred.
131130
FileReader m_fileReader;
131+
std::optional<std::string> m_standardJsonInput;
132132
std::unique_ptr<frontend::CompilerStack> m_compiler;
133133
CommandLineOptions m_options;
134134
};

solc/CommandLineParser.cpp

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
265265
{
266266
return
267267
input.paths == _other.input.paths &&
268-
input.standardJsonFile == _other.input.standardJsonFile &&
269268
input.remappings == _other.input.remappings &&
270269
input.addStdin == _other.input.addStdin &&
271270
input.basePath == _other.input.basePath &&
@@ -301,12 +300,20 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
301300
bool CommandLineParser::parseInputPathsAndRemappings()
302301
{
303302
m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0);
303+
304304
if (m_args.count(g_strInputFile))
305305
for (string path: m_args[g_strInputFile].as<vector<string>>())
306306
{
307307
auto eq = find(path.begin(), path.end(), '=');
308308
if (eq != path.end())
309309
{
310+
if (m_options.input.mode == InputMode::StandardJson)
311+
{
312+
serr() << "Import remappings are not accepted on the command line in Standard JSON mode." << endl;
313+
serr() << "Please put them under 'settings.remappings' in the JSON input." << endl;
314+
return false;
315+
}
316+
310317
if (auto r = ImportRemapper::parseRemapping(path))
311318
m_options.input.remappings.emplace_back(std::move(*r));
312319
else
@@ -324,6 +331,25 @@ bool CommandLineParser::parseInputPathsAndRemappings()
324331
m_options.input.paths.insert(path);
325332
}
326333

334+
if (m_options.input.mode == InputMode::StandardJson)
335+
{
336+
if (m_options.input.paths.size() > 1 || (m_options.input.paths.size() == 1 && m_options.input.addStdin))
337+
{
338+
serr() << "Too many input files for --" << g_strStandardJSON << "." << endl;
339+
serr() << "Please either specify a single file name or provide its content on standard input." << endl;
340+
return false;
341+
}
342+
else if (m_options.input.paths.size() == 0)
343+
// Standard JSON mode input used to be handled separately and zero files meant "read from stdin".
344+
// Keep it working that way for backwards-compatibility.
345+
m_options.input.addStdin = true;
346+
}
347+
else if (m_options.input.paths.size() == 0 && !m_options.input.addStdin)
348+
{
349+
serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl;
350+
return false;
351+
}
352+
327353
return true;
328354
}
329355

@@ -882,25 +908,12 @@ General Information)").c_str(),
882908
else
883909
m_options.input.mode = InputMode::Compiler;
884910

885-
if (m_options.input.mode == InputMode::StandardJson)
886-
{
887-
vector<string> inputFiles;
888-
if (m_args.count(g_strInputFile))
889-
inputFiles = m_args[g_strInputFile].as<vector<string>>();
890-
if (inputFiles.size() == 1)
891-
m_options.input.standardJsonFile = inputFiles[0];
892-
else if (inputFiles.size() > 1)
893-
{
894-
serr() << "If --" << g_strStandardJSON << " is used, only zero or one input files are supported." << endl;
895-
return false;
896-
}
897-
898-
return true;
899-
}
900-
901911
if (!parseInputPathsAndRemappings())
902912
return false;
903913

914+
if (m_options.input.mode == InputMode::StandardJson)
915+
return true;
916+
904917
if (m_args.count(g_strLibraries))
905918
for (string const& library: m_args[g_strLibraries].as<vector<string>>())
906919
if (!parseLibraryOption(library))

solc/CommandLineParser.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ struct CommandLineOptions
107107
{
108108
InputMode mode = InputMode::Compiler;
109109
std::set<boost::filesystem::path> paths;
110-
std::string standardJsonFile;
111110
std::vector<ImportRemapper::Remapping> remappings;
112111
bool addStdin = false;
113112
boost::filesystem::path basePath = "";

solc/main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ int main(int argc, char** argv)
5757
solidity::frontend::CommandLineInterface cli(cin, cout, cerr);
5858
if (!cli.parseArguments(argc, argv))
5959
return 1;
60+
if (!cli.readInputFiles())
61+
return 1;
6062
if (!cli.processInput())
6163
return 1;
6264
bool success = false;

test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ set(libyul_sources
152152
detect_stray_source_files("${libyul_sources}" "libyul/")
153153

154154
set(solcli_sources
155+
solc/Common.cpp
156+
solc/Common.h
157+
solc/CommandLineInterface.cpp
155158
solc/CommandLineParser.cpp
156159
)
157160
detect_stray_source_files("${solcli_sources}" "solc/")

test/FilesystemUtils.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ using namespace std;
2424
using namespace solidity;
2525
using namespace solidity::test;
2626

27+
void solidity::test::createFilesWithParentDirs(set<boost::filesystem::path> const& _paths, string const& _content)
28+
{
29+
for (boost::filesystem::path const& path: _paths)
30+
{
31+
if (!path.parent_path().empty())
32+
boost::filesystem::create_directories(path.parent_path());
33+
34+
ofstream newFile(path.string());
35+
newFile << _content;
36+
37+
if (newFile.fail() || !boost::filesystem::exists(path))
38+
BOOST_THROW_EXCEPTION(runtime_error("Failed to create an empty file: \"" + path.string() + "\"."));
39+
}
40+
}
41+
2742
void solidity::test::createFileWithContent(boost::filesystem::path const& _path, string const& content)
2843
{
2944
if (boost::filesystem::is_regular_file(_path))

test/FilesystemUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@
2323

2424
#include <boost/filesystem.hpp>
2525

26+
#include <set>
2627
#include <string>
2728

2829
namespace solidity::test
2930
{
3031

32+
/// Creates all the specified files and fills them with the specifiedcontent. Creates their parent
33+
/// directories if they do not exist. Throws an exception if any part of the operation does not succeed.
34+
void createFilesWithParentDirs(std::set<boost::filesystem::path> const& _paths, std::string const& _content = "");
35+
3136
/// Creates a file with the exact content specified in the second argument.
3237
/// Throws an exception if the file already exists or if the parent directory of the file does not.
3338
void createFileWithContent(boost::filesystem::path const& _path, std::string const& content);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
File not found: input.sol
1+
"input.sol" is not found.

0 commit comments

Comments
 (0)