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,
|
||||
// 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.
|
||||
"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
|
||||
// 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)
|
||||
{
|
||||
static set<string> keys{"enabled", "runs"};
|
||||
static set<string> keys{"details", "enabled", "runs"};
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_compilerStack.reset(false);
|
||||
@ -512,29 +584,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
m_compilerStack.setRemappings(remappings);
|
||||
|
||||
if (settings.isMember("optimizer"))
|
||||
{
|
||||
Json::Value optimizerSettings = settings["optimizer"];
|
||||
|
||||
if (auto result = checkOptimizerKeys(optimizerSettings))
|
||||
if (auto result = parseOptimizerSettings(settings["optimizer"]))
|
||||
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;
|
||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||
if (!jsonLibraries.isObject())
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
@ -53,6 +55,10 @@ public:
|
||||
std::string compile(std::string const& _input) noexcept;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
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)
|
||||
@ -859,6 +859,159 @@ BOOST_AUTO_TEST_CASE(evm_version)
|
||||
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()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user