Assembly: Added missing source field to legacy assembly json output to complete the source reference

This commit is contained in:
Djordje Mijovic 2020-02-18 10:22:34 +01:00
parent 2d1c4b770f
commit 18dea6b69c
12 changed files with 94 additions and 65 deletions

1
.gitignore vendored
View File

@ -40,6 +40,7 @@ docs/utils/*.pyc
/deps/downloads/
deps/install
deps/cache
cmake-build-debug/
# vim stuff
[._]*.sw[a-p]

View File

@ -15,6 +15,7 @@ Compiler Features:
Bugfixes:
* Parser: Fix an internal error for ``abstract`` without ``contract``.
* Type Checker: Make invalid calls to uncallable types fatal errors.
* Assembly: Added missing `source` field to legacy assembly json output to complete the source reference.

View File

@ -197,10 +197,11 @@ string Assembly::assemblyString(StringMap const& _sourceCodes) const
return tmp.str();
}
Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string _value, string _jumpType)
Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType)
{
Json::Value value;
value["name"] = _name;
value["source"] = _source;
value["begin"] = _begin;
value["end"] = _end;
if (!_value.empty())
@ -217,65 +218,79 @@ string Assembly::toStringInHex(u256 _value)
return hexStr.str();
}
Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices) const
{
Json::Value root;
Json::Value& collection = root[".code"] = Json::arrayValue;
for (AssemblyItem const& i: m_items)
{
unsigned sourceIndex = unsigned(-1);
if (i.location().source)
{
auto iter = _sourceIndices.find(i.location().source->name());
if (iter != _sourceIndices.end())
sourceIndex = iter->second;
}
switch (i.type())
{
case Operation:
collection.append(
createJsonValue(instructionInfo(i.instruction()).name, i.location().start, i.location().end, i.getJumpTypeAsString()));
createJsonValue(
instructionInfo(i.instruction()).name,
sourceIndex,
i.location().start,
i.location().end,
i.getJumpTypeAsString())
);
break;
case Push:
collection.append(
createJsonValue("PUSH", i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString()));
break;
case PushString:
collection.append(
createJsonValue("PUSH tag", i.location().start, i.location().end, m_strings.at((h256)i.data())));
createJsonValue("PUSH tag", sourceIndex, i.location().start, i.location().end, m_strings.at((h256)i.data())));
break;
case PushTag:
if (i.data() == 0)
collection.append(
createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, ""));
createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, ""));
else
collection.append(
createJsonValue("PUSH [tag]", i.location().start, i.location().end, toString(i.data())));
createJsonValue("PUSH [tag]", sourceIndex, i.location().start, i.location().end, toString(i.data())));
break;
case PushSub:
collection.append(
createJsonValue("PUSH [$]", i.location().start, i.location().end, toString(h256(i.data()))));
createJsonValue("PUSH [$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
break;
case PushSubSize:
collection.append(
createJsonValue("PUSH #[$]", i.location().start, i.location().end, toString(h256(i.data()))));
createJsonValue("PUSH #[$]", sourceIndex, i.location().start, i.location().end, toString(h256(i.data()))));
break;
case PushProgramSize:
collection.append(
createJsonValue("PUSHSIZE", i.location().start, i.location().end));
createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end));
break;
case PushLibraryAddress:
collection.append(
createJsonValue("PUSHLIB", i.location().start, i.location().end, m_libraries.at(h256(i.data())))
createJsonValue("PUSHLIB", sourceIndex, i.location().start, i.location().end, m_libraries.at(h256(i.data())))
);
break;
case PushDeployTimeAddress:
collection.append(
createJsonValue("PUSHDEPLOYADDRESS", i.location().start, i.location().end)
createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end)
);
break;
case Tag:
collection.append(
createJsonValue("tag", i.location().start, i.location().end, toString(i.data())));
createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data())));
collection.append(
createJsonValue("JUMPDEST", i.location().start, i.location().end));
createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end));
break;
case PushData:
collection.append(createJsonValue("PUSH data", i.location().start, i.location().end, toStringInHex(i.data())));
collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data())));
break;
default:
assertThrow(false, InvalidOpcode, "");
@ -293,7 +308,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
{
std::stringstream hexStr;
hexStr << hex << i;
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceCodes);
data[hexStr.str()] = m_subs[i]->assemblyJSON(_sourceIndices);
}
}

View File

@ -133,7 +133,7 @@ public:
/// Create a JSON representation of the assembly.
Json::Value assemblyJSON(
StringMap const& _sourceCodes = StringMap()
std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>()
) const;
protected:
@ -145,7 +145,14 @@ protected:
unsigned bytesRequired(unsigned subTagSize) const;
private:
static Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string());
static Json::Value createJsonValue(
std::string _name,
int _source,
int _begin,
int _end,
std::string _value = std::string(),
std::string _jumpType = std::string()
);
static std::string toStringInHex(u256 _value);
protected:

View File

@ -64,9 +64,9 @@ public:
return m_context.assemblyString(_sourceCodes);
}
/// @arg _sourceCodes is the map of input files to source code strings
Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const
Json::Value assemblyJSON(std::map<std::string, unsigned> const& _indices = std::map<std::string, unsigned>()) const
{
return m_context.assemblyJSON(_sourceCodes);
return m_context.assemblyJSON(_indices);
}
/// @returns Assembly items of the normal compiler context
evmasm::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); }

View File

@ -262,9 +262,9 @@ public:
}
/// @arg _sourceCodes is the map of input files to source code strings
Json::Value assemblyJSON(StringMap const& _sourceCodes = StringMap()) const
Json::Value assemblyJSON(std::map<std::string, unsigned> const& _indicies = std::map<std::string, unsigned>()) const
{
return m_asm->assemblyJSON(_sourceCodes);
return m_asm->assemblyJSON(_indicies);
}
evmasm::LinkerObject const& assembledObject() const { return m_asm->assemble(); }

View File

@ -670,14 +670,14 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap _sou
}
/// TODO: cache the JSON
Json::Value CompilerStack::assemblyJSON(string const& _contractName, StringMap const& _sourceCodes) const
Json::Value CompilerStack::assemblyJSON(string const& _contractName) const
{
if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
Contract const& currentContract = contract(_contractName);
if (currentContract.compiler)
return currentContract.compiler->assemblyJSON(_sourceCodes);
return currentContract.compiler->assemblyJSON(sourceIndices());
else
return Json::Value();
}

View File

@ -287,7 +287,7 @@ public:
/// @returns a JSON representation of the assembly.
/// @arg _sourceCodes is the map of input files to source code strings
/// Prerequisite: Successful compilation.
Json::Value assemblyJSON(std::string const& _contractName, StringMap const& _sourceCodes = StringMap()) const;
Json::Value assemblyJSON(std::string const& _contractName) const;
/// @returns a JSON representing the contract ABI.
/// Prerequisite: Successful call to parse or compile.

View File

@ -967,7 +967,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental))
evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental))
evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName, sourceList);
evmData["legacyAssembly"] = compilerStack.assemblyJSON(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental))
evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental))

View File

@ -1258,7 +1258,7 @@ void CommandLineInterface::handleCombinedJSON()
if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful())
contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode);
if (requests.count(g_strAsm) && m_compiler->compilationSuccessful())
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName, m_sourceCodes);
contractData[g_strAsm] = m_compiler->assemblyJSON(contractName);
if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful())
{
auto map = m_compiler->sourceMapping(contractName);
@ -1612,7 +1612,7 @@ void CommandLineInterface::outputCompilationResults()
{
string ret;
if (m_args.count(g_argAsmJson))
ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract, m_sourceCodes));
ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract));
else
ret = m_compiler->assemblyString(contract, m_sourceCodes);

View File

@ -50,6 +50,10 @@ BOOST_AUTO_TEST_SUITE(Assembler)
BOOST_AUTO_TEST_CASE(all_assembly_items)
{
map<string, unsigned> indices = {
{ "root.asm", 0 },
{ "sub.asm", 1 }
};
Assembly _assembly;
auto root_asm = make_shared<CharStream>("lorem ipsum", "root.asm");
_assembly.setSourceLocation({1, 3, root_asm});
@ -119,22 +123,23 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
"auxdata: 0x4266eeaa\n"
);
BOOST_CHECK_EQUAL(
util::jsonCompactPrint(_assembly.assemblyJSON()),
"{\".auxdata\":\"4266eeaa\",\".code\":[{\"begin\":1,\"end\":3,\"name\":\"tag\",\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"2\"},"
"{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"value\":\"someLibrary\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"JUMP\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\"},"
"{\"begin\":1,\"end\":3,\"name\":\"STOP\"}],"
"\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\"}]},"
util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
"{\".auxdata\":\"4266eeaa\",\".code\":["
"{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
"{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"JUMP\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}"
"],\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}]},"
"\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}"
);
}

View File

@ -394,27 +394,27 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["evm"]["legacyAssembly"][".code"].isArray());
BOOST_CHECK_EQUAL(
util::jsonCompactPrint(contract["evm"]["legacyAssembly"][".code"]),
"[{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"80\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"40\"},"
"{\"begin\":0,\"end\":14,\"name\":\"MSTORE\"},"
"{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\"},"
"{\"begin\":5,\"end\":14,\"name\":\"DUP1\"},"
"{\"begin\":2,\"end\":4,\"name\":\"ISZERO\"},"
"{\"begin\":2,\"end\":4,\"name\":\"PUSH [tag]\",\"value\":\"1\"},"
"{\"begin\":2,\"end\":4,\"name\":\"JUMPI\"},"
"{\"begin\":27,\"end\":28,\"name\":\"PUSH\",\"value\":\"0\"},"
"{\"begin\":24,\"end\":25,\"name\":\"DUP1\"},"
"{\"begin\":17,\"end\":29,\"name\":\"REVERT\"},"
"{\"begin\":2,\"end\":4,\"name\":\"tag\",\"value\":\"1\"},"
"{\"begin\":2,\"end\":4,\"name\":\"JUMPDEST\"},"
"{\"begin\":0,\"end\":14,\"name\":\"POP\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":0,\"end\":14,\"name\":\"DUP1\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"0\"},"
"{\"begin\":0,\"end\":14,\"name\":\"CODECOPY\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"0\"},"
"{\"begin\":0,\"end\":14,\"name\":\"RETURN\"}]"
"[{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"80\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"40\"},"
"{\"begin\":0,\"end\":14,\"name\":\"MSTORE\",\"source\":0},"
"{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\",\"source\":0},"
"{\"begin\":5,\"end\":14,\"name\":\"DUP1\",\"source\":-1},"
"{\"begin\":2,\"end\":4,\"name\":\"ISZERO\",\"source\":-1},"
"{\"begin\":2,\"end\":4,\"name\":\"PUSH [tag]\",\"source\":-1,\"value\":\"1\"},"
"{\"begin\":2,\"end\":4,\"name\":\"JUMPI\",\"source\":-1},"
"{\"begin\":27,\"end\":28,\"name\":\"PUSH\",\"source\":-1,\"value\":\"0\"},"
"{\"begin\":24,\"end\":25,\"name\":\"DUP1\",\"source\":-1},"
"{\"begin\":17,\"end\":29,\"name\":\"REVERT\",\"source\":-1},"
"{\"begin\":2,\"end\":4,\"name\":\"tag\",\"source\":-1,\"value\":\"1\"},"
"{\"begin\":2,\"end\":4,\"name\":\"JUMPDEST\",\"source\":-1},"
"{\"begin\":0,\"end\":14,\"name\":\"POP\",\"source\":0},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":0,\"end\":14,\"name\":\"DUP1\",\"source\":0},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
"{\"begin\":0,\"end\":14,\"name\":\"CODECOPY\",\"source\":0},"
"{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"},"
"{\"begin\":0,\"end\":14,\"name\":\"RETURN\",\"source\":0}]"
);
BOOST_CHECK(contract["metadata"].isString());
BOOST_CHECK(solidity::test::isValidMetadata(contract["metadata"].asString()));