Skip to content

Support "standardised" JSON compiler input/output#1639

Merged
axic merged 35 commits intodevelopfrom
json-interface-api
Apr 20, 2017
Merged

Support "standardised" JSON compiler input/output#1639
axic merged 35 commits intodevelopfrom
json-interface-api

Conversation

@axic
Copy link
Contributor

@axic axic commented Feb 2, 2017

Implements #1387. Replaces #712.

Fixes #579, #863, #1645, #1809.

Todo:

  • clean up includes in StandardCompiler.cpp
  • proper error reporting for compiler errors
  • catch exceptions on individual output items (similar to how jsonCompiler works)
  • support AST output
  • support Why3 output
  • support gasEstimates output (depends on Move gasEstimate into CompilerStack #2114)
  • support methodIdentifiers output
  • support linkReferences output
  • control outputs by input selection (likely postponed to a subsequent PR)
  • support URLs in input sources (likely postponed to a subsequent PR)
  • verify input sources against supplied hash (likely postponed to a subsequent PR)
  • support metadata.useLiteralContent
  • add tests

@axic axic changed the title WIP: Support "standardised" JSON compiler input/output [WIP] Support "standardised" JSON compiler input/output Feb 6, 2017
@axic axic force-pushed the json-interface-api branch 3 times, most recently from eda1ab2 to b0ed329 Compare February 14, 2017 17:12
@axic axic force-pushed the json-interface-api branch 4 times, most recently from af166fa to a7502ff Compare March 16, 2017 13:30
@axic
Copy link
Contributor Author

axic commented Mar 16, 2017

Working on this next.

@axic axic self-assigned this Mar 16, 2017
@axic
Copy link
Contributor Author

axic commented Mar 16, 2017

Added (locally) the JS interface: compileStandard(<string>, <readCallback>) -> <string>.

Any better naming?

@axic axic force-pushed the json-interface-api branch 2 times, most recently from a16d202 to 6f07bd0 Compare March 17, 2017 12:30
@axic
Copy link
Contributor Author

axic commented Mar 17, 2017

To keep this PR simple, it will not change the CLI to use StandardCompiler internally. Should be handled in a following PR.

Copy link
Contributor

@chriseth chriseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not yet review all of it.

{

/**
* Easy to use and self-contained Solidity compiler with as few header dependencies as possible.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be updated.

#include <boost/filesystem.hpp>
#include <json/json.h>
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think most of the headers can go, CompilerStack should suffice.

/// File reading callback.
using ReadFileCallback = std::function<ReadFileResult(std::string const&)>;

/// Creates a new compiler stack.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be updated.

@axic
Copy link
Contributor Author

axic commented Mar 29, 2017

Added error reporting of JSON parsing using jsoncpp::getFormattedErrorMessages():

Standard Input JSON: {xxx "language": "Solidity", "sources": { "mortal": { "keccak256": "0x234...", "content": "contract owned { address owner; }x contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }" } }, "settings": { "remappings": [ ":g/dir" ], "optimizer": { "enabled": true, "runs": 500 }, "metadata": { "useLiteralContent": true }, "libraries": { "myFile.sol": { "MyLib": "0x123123" } }, "outputSelection": { "*": { "*": [ "metadata", "evm.bytecode" ], "": [ "ast", "why3" ] } } } }

Standard Output JSON: {"errors":[{"component":"general","message":"* Line 1, Column 2\n  Missing '}' or object member name\n","severity":"error","type":"JSONError"}]}

It looks a bit weird since it uses asterisk for the list, and maybe we could consider using the sourceLocation parameter for location within the JSON if the type is JSONError.

@axic axic force-pushed the json-interface-api branch 6 times, most recently from 38d500c to 9d449df Compare March 30, 2017 01:10
using namespace dev::solidity;

Json::Value formatError(
bool const& _warning,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"small" types are usually passed by value and not by const reference.

class StandardCompiler: boost::noncopyable
{
public:
struct ReadFileResult
Copy link
Contributor

@chriseth chriseth Apr 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the same in CompilerStack.h, right? Can you pull both out into another file? (Likewise with ReadFileCallback)

Copy link
Contributor

@chriseth chriseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not finished reviewing everything.

/// @param _readFile callback to used to read files for import statements. Should return
StandardCompiler(ReadFileCallback const& _readFile = ReadFileCallback())
{
CompilerStack::ReadFileCallback fileReader = [this,_readFile](std::string const& _path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the only thing that this lambda does converting between this ReadFileResult and CompilerStack::ReadFileResult?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Though I wanted to change separate the string into error / result, but that is not so important.

Basically we'd need to pull out struct ReadFileResult {} into its own header which feels rather slim.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in #2111.

return CompilerStack::ReadFileResult{ret.success, ret.contentsOrErrorMessage};
};

m_compilerStack.reset(new CompilerStack(fileReader));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you can get rid of the lambda, you can use

StandardCompiler(ReadFileCallback const& _readFile = ReadFileCallback())
: m_compilerStack(_readFile)
{
}

m_compilerStack.reset(new CompilerStack(fileReader));
}

/// Sets all input parameters according to @a _input which conforms to the standardized input
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And performs compilation and returns the result.

/// Sets all input parameters according to @a _input which conforms to the standardized input
/// format.
Json::Value compile(Json::Value const& _input);
std::string compile(std::string const& _input);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document: Parses input as json, compiles and formats output json as string. Json decoding errors are reported in the same way as regular compiler errors.

using namespace dev;
using namespace dev::solidity;

Json::Value formatError(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For added C++ishness, you can move those functions into an anonymous namespace:

namespace {
... functions... 
}

It makes them translation-unit local (similar to static in C).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will add those.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Json::Value input;
Json::Reader reader;

try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Braces on the next line.


try {
if (!reader.parse(_input, input, false))
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No braces for single statement.

}
catch(...)
{
return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also use a utility function for this? Or do you want to avoid using the json formatter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a hardcoded text in two places to avoid endless exception catching of jsonCompactPrint.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok!

for (auto const& ref: linkReferences)
{
string const& fullname = ref.second;
size_t colon = fullname.find(':');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please assert that colon != string::npos

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

string const& name = fullname.substr(colon + 1);

Json::Value fileObject = ret.get(file, Json::objectValue);
Json::Value libraryArray = fileObject.get(name, Json::arrayValue);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if you use Json::Value& libraryArray here, you do not need the assignment in line 169

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This didn't yet work, would be nice to avoid the current way.

Copy link
Contributor

@chriseth chriseth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm through!

)
(g_argMetadataLiteral.c_str(), "Store referenced sources are literal data in the metadata output.");
(g_argMetadataLiteral.c_str(), "Store referenced sources are literal data in the metadata output.")
(g_argStandardJSON.c_str(), "Process standard JSON input / output.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Process seems a bit too technical for me. Also, this disables some other options, does it? Perhaps we should make it more prominent (i.e. put it in the firstline) and say Switches the compiler into JSON input/output mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the "JSON input/output mode"?

Shouldn't it be irrelevant to the user how it operates internally? Ideally the CLI should go through the JSON IO to reduce the number of APIs we have, but that shouldn't have any effect on the user.

The current --standard-json is only an interface to directly interact with the API.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the idea was to expose the json/io API on all implementations of the compiler, in order to get a unified interface and to avoid the problems we currently have with the filesystem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit confused, but do not really see a point of having two implementations internally in the CLI, one using CompilerStack and the other using StandardCompiler.

I would just change it to use StandardCompiler only.

The above option though is not for switching between these implementation, but to accept a raw JSON input and output a raw JSON output.

if (m_args.count(g_argStandardJSON))
{
string input;
while (!cin.eof())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be something in CommonIO.h to do exactly this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or do you want to convert \r\n to \n? I don't think this is a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's none in CommonIO. If there's a sample code somewhere I'm happy to use that. This came from CommandLineInterface::readInputFilesAndConfigureRemappings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(2) istream& getline (istream& is, string& str);

Extracts characters from is and stores them into str until the delimitation character delim is found (or the newline character, '\n', for (2)).

According to this, it won't cut off \r? And the \n is pushed back:

                string input;
                while (!cin.eof())
                {
                        string tmp;
                        getline(cin, tmp);
                        input.append(tmp + "\n");
                }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, my mistake, there are only functions for files. cin is not opened in binary mode, so I think it will convert \r\n into \n. But actually, this should be irrelevant, since everything is json-encoded and whitespace should be just ignored anyway. So, yes, just leave it like that!

string output = compiler.compile(input);
cout << "Standard Input JSON: " << input << endl << endl;
cout << "Standard Output JSON: " << output << endl;
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLI is split into processInput and actOnInput. I'm not sure if this division makes sense here, but I think we should only run the compile step in the actOnInput function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh and perhaps we should flag an error if --standard-json is combined with anything else.

Copy link
Contributor Author

@axic axic Apr 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out that distinction of processInput vs. actOnInput is already broken by assemble and link in processInput. In that case it makes sense there. I would say at this point of time probably the distinction is not useful, because exception catching is handled mostly in actOnInput anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok!

{
readCallback = [=](string const& _path)
{
char* contents_c = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code should be duplicated above. Can you reuse it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@axic axic merged commit 2ccbc08 into develop Apr 20, 2017
@axic axic removed the in progress label Apr 20, 2017
@axic axic deleted the json-interface-api branch April 20, 2017 19:12
@pipermerriam
Copy link
Contributor

Woooohooo!!!! 🍰 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants