mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Process and validate standard-json optimizer settings.
This commit is contained in:
parent
cf5c13f9c7
commit
4d10f4b4cf
@ -191,7 +191,30 @@ Input Description
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
// Optimize for how many times you intend to run the code.
|
// Optimize for how many times you intend to run the code.
|
||||||
// Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage.
|
// Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage.
|
||||||
"runs": 200
|
"runs": 200,
|
||||||
|
// Switch optimizer components on or off in detail.
|
||||||
|
// The "enabled" switch above provides two defaults which can be
|
||||||
|
// tweaked here. If "details" is given, "enabled" can be omitted.
|
||||||
|
"details": {
|
||||||
|
// The peephole optimizer is always on if no details are given, use details to switch it off.
|
||||||
|
"peephole": true,
|
||||||
|
// The unused jumpdest remover is always on if no details are given, use details to switch it off.
|
||||||
|
"jumpdestRemover": true,
|
||||||
|
// Sometimes re-orders literals in commutative operations.
|
||||||
|
"orderLiterals": false,
|
||||||
|
// Removes duplicate code blocks
|
||||||
|
"deduplicate": false,
|
||||||
|
// Common subexpression elimination, this is the most complicated step but
|
||||||
|
// can also provide the largest gain.
|
||||||
|
"cse": false,
|
||||||
|
// Optimize representation of literal numbers and strings in code.
|
||||||
|
"constantOptimizer": false,
|
||||||
|
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2.
|
||||||
|
// It can only be activated through the details here.
|
||||||
|
"yul": false,
|
||||||
|
// Future tuning options, currently unused.
|
||||||
|
"yulDetails": {}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
|
"evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
|
||||||
// Metadata settings (optional)
|
// Metadata settings (optional)
|
||||||
|
@ -292,10 +292,27 @@ boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
|
|||||||
|
|
||||||
boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
|
boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
static set<string> keys{"enabled", "runs"};
|
static set<string> keys{"details", "enabled", "runs"};
|
||||||
return checkKeys(_input, keys, "settings.optimizer");
|
return checkKeys(_input, keys, "settings.optimizer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<Json::Value> checkOptimizerDetailsKeys(Json::Value const& _input)
|
||||||
|
{
|
||||||
|
static set<string> keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"};
|
||||||
|
return checkKeys(_input, keys, "settings.optimizer.details");
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting)
|
||||||
|
{
|
||||||
|
if (_details.isMember(_name))
|
||||||
|
{
|
||||||
|
if (!_details[_name].isBool())
|
||||||
|
return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be Boolean");
|
||||||
|
_setting = _details[_name].asBool();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
|
if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
|
||||||
@ -351,6 +368,61 @@ boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSele
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<Json::Value> StandardCompiler::parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||||
|
{
|
||||||
|
if (auto result = checkOptimizerKeys(_jsonInput))
|
||||||
|
return *result;
|
||||||
|
|
||||||
|
OptimiserSettings settings = OptimiserSettings::none();
|
||||||
|
|
||||||
|
if (_jsonInput.isMember("enabled"))
|
||||||
|
{
|
||||||
|
if (!_jsonInput["enabled"].isBool())
|
||||||
|
return formatFatalError("JSONError", "The \"enabled\" setting must be a Boolean.");
|
||||||
|
|
||||||
|
settings = _jsonInput["enabled"].asBool() ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jsonInput.isMember("runs"))
|
||||||
|
{
|
||||||
|
if (!_jsonInput["runs"].isUInt())
|
||||||
|
return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
|
||||||
|
settings.expectedExecutionsPerDeployment = _jsonInput["runs"].asUInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_jsonInput.isMember("details"))
|
||||||
|
{
|
||||||
|
Json::Value const& details = _jsonInput["details"];
|
||||||
|
if (auto result = checkOptimizerDetailsKeys(details))
|
||||||
|
return *result;
|
||||||
|
|
||||||
|
if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole))
|
||||||
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover))
|
||||||
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals))
|
||||||
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "deduplicate", settings.runDeduplicate))
|
||||||
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "cse", settings.runCSE))
|
||||||
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "constantOptimizer", settings.runConstantOptimiser))
|
||||||
|
return *error;
|
||||||
|
if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
|
||||||
|
return *error;
|
||||||
|
if (details.isMember("yulDetails"))
|
||||||
|
{
|
||||||
|
if (!_jsonInput["yulDetails"].isObject())
|
||||||
|
return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting has to be a JSON object.");
|
||||||
|
if (!_jsonInput["yulDetails"].getMemberNames().empty())
|
||||||
|
return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting cannot have any settings yet.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_compilerStack.setOptimiserSettings(std::move(settings));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
m_compilerStack.reset(false);
|
m_compilerStack.reset(false);
|
||||||
@ -512,29 +584,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
|||||||
m_compilerStack.setRemappings(remappings);
|
m_compilerStack.setRemappings(remappings);
|
||||||
|
|
||||||
if (settings.isMember("optimizer"))
|
if (settings.isMember("optimizer"))
|
||||||
{
|
if (auto result = parseOptimizerSettings(settings["optimizer"]))
|
||||||
Json::Value optimizerSettings = settings["optimizer"];
|
|
||||||
|
|
||||||
if (auto result = checkOptimizerKeys(optimizerSettings))
|
|
||||||
return *result;
|
return *result;
|
||||||
|
|
||||||
if (optimizerSettings.isMember("enabled"))
|
|
||||||
{
|
|
||||||
if (!optimizerSettings["enabled"].isBool())
|
|
||||||
return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean.");
|
|
||||||
|
|
||||||
bool const optimize = optimizerSettings["enabled"].asBool();
|
|
||||||
unsigned optimizeRuns = 200;
|
|
||||||
if (optimizerSettings.isMember("runs"))
|
|
||||||
{
|
|
||||||
if (!optimizerSettings["runs"].isUInt())
|
|
||||||
return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
|
|
||||||
optimizeRuns = optimizerSettings["runs"].asUInt();
|
|
||||||
}
|
|
||||||
m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
map<string, h160> libraries;
|
map<string, h160> libraries;
|
||||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||||
if (!jsonLibraries.isObject())
|
if (!jsonLibraries.isObject())
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -53,6 +55,10 @@ public:
|
|||||||
std::string compile(std::string const& _input) noexcept;
|
std::string compile(std::string const& _input) noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Validaes and applies the optimizer settings.
|
||||||
|
/// On error returns the json-formatted error message.
|
||||||
|
boost::optional<Json::Value> parseOptimizerSettings(Json::Value const& _settings);
|
||||||
|
|
||||||
Json::Value compileInternal(Json::Value const& _input);
|
Json::Value compileInternal(Json::Value const& _input);
|
||||||
|
|
||||||
CompilerStack m_compilerStack;
|
CompilerStack m_compilerStack;
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"optimizer": {
|
||||||
|
"details": { "peephole": 7 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer.details.peephole\" must be Boolean","message":"\"settings.optimizer.details.peephole\" must be Boolean","severity":"error","type":"JSONError"}]}
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"optimizer": {
|
||||||
|
"details": { "notThere": true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{"errors":[{"component":"general","formattedMessage":"Unknown key \"notThere\"","message":"Unknown key \"notThere\"","severity":"error","type":"JSONError"}]}
|
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"sources":
|
||||||
|
{
|
||||||
|
"A":
|
||||||
|
{
|
||||||
|
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings":
|
||||||
|
{
|
||||||
|
"optimizer": {
|
||||||
|
"details": { "yulDetails": 7 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{"errors":[{"component":"general","formattedMessage":"The \"yulDetails\" optimizer setting has to be a JSON object.","message":"The \"yulDetails\" optimizer setting has to be a JSON object.","severity":"error","type":"JSONError"}]}
|
@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean)
|
|||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
Json::Value result = compile(input);
|
Json::Value result = compile(input);
|
||||||
BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a boolean."));
|
BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a Boolean."));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number)
|
BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number)
|
||||||
@ -859,6 +859,159 @@ BOOST_AUTO_TEST_CASE(evm_version)
|
|||||||
BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested.");
|
BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(optimizer_settings_default_disabled)
|
||||||
|
{
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"settings": {
|
||||||
|
"outputSelection": {
|
||||||
|
"fileA": { "A": [ "metadata" ] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"fileA": {
|
||||||
|
"content": "contract A { }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
Json::Value result = compile(input);
|
||||||
|
BOOST_CHECK(containsAtMostWarnings(result));
|
||||||
|
Json::Value contract = getContractResult(result, "fileA", "A");
|
||||||
|
BOOST_CHECK(contract.isObject());
|
||||||
|
BOOST_CHECK(contract["metadata"].isString());
|
||||||
|
Json::Value metadata;
|
||||||
|
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
|
||||||
|
|
||||||
|
Json::Value const& optimizer = metadata["settings"]["optimizer"];
|
||||||
|
BOOST_CHECK(optimizer.isMember("enabled"));
|
||||||
|
BOOST_CHECK(optimizer["enabled"].asBool() == false);
|
||||||
|
BOOST_CHECK(!optimizer.isMember("details"));
|
||||||
|
BOOST_CHECK(optimizer["runs"].asUInt() == 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(optimizer_settings_default_enabled)
|
||||||
|
{
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"settings": {
|
||||||
|
"outputSelection": {
|
||||||
|
"fileA": { "A": [ "metadata" ] }
|
||||||
|
},
|
||||||
|
"optimizer": { "enabled": true }
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"fileA": {
|
||||||
|
"content": "contract A { }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
Json::Value result = compile(input);
|
||||||
|
BOOST_CHECK(containsAtMostWarnings(result));
|
||||||
|
Json::Value contract = getContractResult(result, "fileA", "A");
|
||||||
|
BOOST_CHECK(contract.isObject());
|
||||||
|
BOOST_CHECK(contract["metadata"].isString());
|
||||||
|
Json::Value metadata;
|
||||||
|
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
|
||||||
|
|
||||||
|
Json::Value const& optimizer = metadata["settings"]["optimizer"];
|
||||||
|
BOOST_CHECK(optimizer.isMember("enabled"));
|
||||||
|
BOOST_CHECK(optimizer["enabled"].asBool() == true);
|
||||||
|
BOOST_CHECK(!optimizer.isMember("details"));
|
||||||
|
BOOST_CHECK(optimizer["runs"].asUInt() == 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(optimizer_settings_details_exactly_as_default_disabled)
|
||||||
|
{
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"settings": {
|
||||||
|
"outputSelection": {
|
||||||
|
"fileA": { "A": [ "metadata" ] }
|
||||||
|
},
|
||||||
|
"optimizer": { "details": {
|
||||||
|
"constantOptimizer" : false,
|
||||||
|
"cse" : false,
|
||||||
|
"deduplicate" : false,
|
||||||
|
"jumpdestRemover" : true,
|
||||||
|
"orderLiterals" : false,
|
||||||
|
"peephole" : true
|
||||||
|
} }
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"fileA": {
|
||||||
|
"content": "contract A { }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
Json::Value result = compile(input);
|
||||||
|
BOOST_CHECK(containsAtMostWarnings(result));
|
||||||
|
Json::Value contract = getContractResult(result, "fileA", "A");
|
||||||
|
BOOST_CHECK(contract.isObject());
|
||||||
|
BOOST_CHECK(contract["metadata"].isString());
|
||||||
|
Json::Value metadata;
|
||||||
|
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
|
||||||
|
|
||||||
|
Json::Value const& optimizer = metadata["settings"]["optimizer"];
|
||||||
|
BOOST_CHECK(optimizer.isMember("enabled"));
|
||||||
|
// enabled is switched to false instead!
|
||||||
|
BOOST_CHECK(optimizer["enabled"].asBool() == false);
|
||||||
|
BOOST_CHECK(!optimizer.isMember("details"));
|
||||||
|
BOOST_CHECK(optimizer["runs"].asUInt() == 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
|
||||||
|
{
|
||||||
|
char const* input = R"(
|
||||||
|
{
|
||||||
|
"language": "Solidity",
|
||||||
|
"settings": {
|
||||||
|
"outputSelection": {
|
||||||
|
"fileA": { "A": [ "metadata" ] }
|
||||||
|
},
|
||||||
|
"optimizer": { "runs": 600, "details": {
|
||||||
|
"constantOptimizer" : true,
|
||||||
|
"cse" : false,
|
||||||
|
"deduplicate" : true,
|
||||||
|
"jumpdestRemover" : true,
|
||||||
|
"orderLiterals" : false,
|
||||||
|
"peephole" : true,
|
||||||
|
"yul": true
|
||||||
|
} }
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"fileA": {
|
||||||
|
"content": "contract A { }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
Json::Value result = compile(input);
|
||||||
|
BOOST_CHECK(containsAtMostWarnings(result));
|
||||||
|
Json::Value contract = getContractResult(result, "fileA", "A");
|
||||||
|
BOOST_CHECK(contract.isObject());
|
||||||
|
BOOST_CHECK(contract["metadata"].isString());
|
||||||
|
Json::Value metadata;
|
||||||
|
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
|
||||||
|
|
||||||
|
Json::Value const& optimizer = metadata["settings"]["optimizer"];
|
||||||
|
BOOST_CHECK(!optimizer.isMember("enabled"));
|
||||||
|
BOOST_CHECK(optimizer.isMember("details"));
|
||||||
|
BOOST_CHECK(optimizer["details"]["constantOptimizer"].asBool() == true);
|
||||||
|
BOOST_CHECK(optimizer["details"]["cse"].asBool() == false);
|
||||||
|
BOOST_CHECK(optimizer["details"]["deduplicate"].asBool() == true);
|
||||||
|
BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true);
|
||||||
|
BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false);
|
||||||
|
BOOST_CHECK(optimizer["details"]["peephole"].asBool() == true);
|
||||||
|
BOOST_CHECK(optimizer["details"]["yulDetails"].isObject());
|
||||||
|
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8);
|
||||||
|
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user