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/downloads/
deps/install deps/install
deps/cache deps/cache
cmake-build-debug/
# vim stuff # vim stuff
[._]*.sw[a-p] [._]*.sw[a-p]

View File

@ -15,6 +15,7 @@ Compiler Features:
Bugfixes: Bugfixes:
* Parser: Fix an internal error for ``abstract`` without ``contract``. * Parser: Fix an internal error for ``abstract`` without ``contract``.
* Type Checker: Make invalid calls to uncallable types fatal errors. * 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(); 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; Json::Value value;
value["name"] = _name; value["name"] = _name;
value["source"] = _source;
value["begin"] = _begin; value["begin"] = _begin;
value["end"] = _end; value["end"] = _end;
if (!_value.empty()) if (!_value.empty())
@ -217,65 +218,79 @@ string Assembly::toStringInHex(u256 _value)
return hexStr.str(); 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 root;
Json::Value& collection = root[".code"] = Json::arrayValue; Json::Value& collection = root[".code"] = Json::arrayValue;
for (AssemblyItem const& i: m_items) 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()) switch (i.type())
{ {
case Operation: case Operation:
collection.append( 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; break;
case Push: case Push:
collection.append( 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; break;
case PushString: case PushString:
collection.append( 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; break;
case PushTag: case PushTag:
if (i.data() == 0) if (i.data() == 0)
collection.append( collection.append(
createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, "")); createJsonValue("PUSH [ErrorTag]", sourceIndex, i.location().start, i.location().end, ""));
else else
collection.append( 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; break;
case PushSub: case PushSub:
collection.append( 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; break;
case PushSubSize: case PushSubSize:
collection.append( 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; break;
case PushProgramSize: case PushProgramSize:
collection.append( collection.append(
createJsonValue("PUSHSIZE", i.location().start, i.location().end)); createJsonValue("PUSHSIZE", sourceIndex, i.location().start, i.location().end));
break; break;
case PushLibraryAddress: case PushLibraryAddress:
collection.append( 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; break;
case PushDeployTimeAddress: case PushDeployTimeAddress:
collection.append( collection.append(
createJsonValue("PUSHDEPLOYADDRESS", i.location().start, i.location().end) createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end)
); );
break; break;
case Tag: case Tag:
collection.append( 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( collection.append(
createJsonValue("JUMPDEST", i.location().start, i.location().end)); createJsonValue("JUMPDEST", sourceIndex, i.location().start, i.location().end));
break; break;
case PushData: 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; break;
default: default:
assertThrow(false, InvalidOpcode, ""); assertThrow(false, InvalidOpcode, "");
@ -293,7 +308,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
{ {
std::stringstream hexStr; std::stringstream hexStr;
hexStr << hex << i; 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. /// Create a JSON representation of the assembly.
Json::Value assemblyJSON( Json::Value assemblyJSON(
StringMap const& _sourceCodes = StringMap() std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>()
) const; ) const;
protected: protected:
@ -145,7 +145,14 @@ protected:
unsigned bytesRequired(unsigned subTagSize) const; unsigned bytesRequired(unsigned subTagSize) const;
private: 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); static std::string toStringInHex(u256 _value);
protected: protected:

View File

@ -64,9 +64,9 @@ public:
return m_context.assemblyString(_sourceCodes); return m_context.assemblyString(_sourceCodes);
} }
/// @arg _sourceCodes is the map of input files to source code strings /// @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 /// @returns Assembly items of the normal compiler context
evmasm::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); } 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 /// @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(); } 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 /// 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) if (m_stackState != CompilationSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful."));
Contract const& currentContract = contract(_contractName); Contract const& currentContract = contract(_contractName);
if (currentContract.compiler) if (currentContract.compiler)
return currentContract.compiler->assemblyJSON(_sourceCodes); return currentContract.compiler->assemblyJSON(sourceIndices());
else else
return Json::Value(); return Json::Value();
} }

View File

@ -287,7 +287,7 @@ public:
/// @returns a JSON representation of the assembly. /// @returns a JSON representation of the assembly.
/// @arg _sourceCodes is the map of input files to source code strings /// @arg _sourceCodes is the map of input files to source code strings
/// Prerequisite: Successful compilation. /// 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. /// @returns a JSON representing the contract ABI.
/// Prerequisite: Successful call to parse or compile. /// 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)) if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.assembly", wildcardMatchesExperimental))
evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList); evmData["assembly"] = compilerStack.assemblyString(contractName, sourceList);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.legacyAssembly", wildcardMatchesExperimental)) 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)) if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.methodIdentifiers", wildcardMatchesExperimental))
evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName); evmData["methodIdentifiers"] = compilerStack.methodIdentifiers(contractName);
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "evm.gasEstimates", wildcardMatchesExperimental)) 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()) if (requests.count(g_strOpcodes) && m_compiler->compilationSuccessful())
contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode);
if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) 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()) if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful())
{ {
auto map = m_compiler->sourceMapping(contractName); auto map = m_compiler->sourceMapping(contractName);
@ -1612,7 +1612,7 @@ void CommandLineInterface::outputCompilationResults()
{ {
string ret; string ret;
if (m_args.count(g_argAsmJson)) if (m_args.count(g_argAsmJson))
ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract, m_sourceCodes)); ret = jsonPrettyPrint(m_compiler->assemblyJSON(contract));
else else
ret = m_compiler->assemblyString(contract, m_sourceCodes); 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) BOOST_AUTO_TEST_CASE(all_assembly_items)
{ {
map<string, unsigned> indices = {
{ "root.asm", 0 },
{ "sub.asm", 1 }
};
Assembly _assembly; Assembly _assembly;
auto root_asm = make_shared<CharStream>("lorem ipsum", "root.asm"); auto root_asm = make_shared<CharStream>("lorem ipsum", "root.asm");
_assembly.setSourceLocation({1, 3, root_asm}); _assembly.setSourceLocation({1, 3, root_asm});
@ -119,22 +123,23 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
"auxdata: 0x4266eeaa\n" "auxdata: 0x4266eeaa\n"
); );
BOOST_CHECK_EQUAL( BOOST_CHECK_EQUAL(
util::jsonCompactPrint(_assembly.assemblyJSON()), util::jsonCompactPrint(_assembly.assemblyJSON(indices)),
"{\".auxdata\":\"4266eeaa\",\".code\":[{\"begin\":1,\"end\":3,\"name\":\"tag\",\"value\":\"1\"}," "{\".auxdata\":\"4266eeaa\",\".code\":["
"{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\"}," "{\"begin\":1,\"end\":3,\"name\":\"tag\",\"source\":0,\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"1\"}," "{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"2\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\"}," "{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"value\":\"someLibrary\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"source\":0,\"value\":\"someLibrary\"},"
"{\"begin\":1,\"end\":3,\"name\":\"JUMP\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"source\":0,\"value\":\"1\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"}," "{\"begin\":1,\"end\":3,\"name\":\"JUMP\",\"source\":0},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"source\":0,\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"},"
"{\"begin\":1,\"end\":3,\"name\":\"STOP\"}]," "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0},"
"\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\"}]}," "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}"
"],\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}]},"
"\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" "\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}"
); );
} }

View File

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