mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
prepare_report: Add command-line options for switching between SMT pragma stripping and only disabling the SMT checker
This commit is contained in:
parent
dde6353c5d
commit
b06de9a2d5
@ -4,23 +4,46 @@ const fs = require('fs')
|
|||||||
|
|
||||||
const compiler = require('./solc-js/wrapper.js')(require('./solc-js/soljson.js'))
|
const compiler = require('./solc-js/wrapper.js')(require('./solc-js/soljson.js'))
|
||||||
|
|
||||||
|
|
||||||
|
function loadSource(sourceFileName, stripSMTPragmas)
|
||||||
|
{
|
||||||
|
source = fs.readFileSync(sourceFileName).toString()
|
||||||
|
|
||||||
|
if (stripSMTPragmas)
|
||||||
|
// NOTE: replace() with string parameter replaces only the first occurrence.
|
||||||
|
return source.replace('pragma experimental SMTChecker;', '');
|
||||||
|
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let stripSMTPragmas = false
|
||||||
|
let firstFileArgumentIndex = 2
|
||||||
|
|
||||||
|
if (process.argv.length >= 3 && process.argv[2] === '--strip-smt-pragmas')
|
||||||
|
{
|
||||||
|
stripSMTPragmas = true
|
||||||
|
firstFileArgumentIndex = 3
|
||||||
|
}
|
||||||
|
|
||||||
for (const optimize of [false, true])
|
for (const optimize of [false, true])
|
||||||
{
|
{
|
||||||
for (const filename of process.argv.slice(2))
|
for (const filename of process.argv.slice(firstFileArgumentIndex))
|
||||||
{
|
{
|
||||||
if (filename !== undefined)
|
if (filename !== undefined)
|
||||||
{
|
{
|
||||||
const input = {
|
let input = {
|
||||||
language: 'Solidity',
|
language: 'Solidity',
|
||||||
sources: {
|
sources: {
|
||||||
[filename]: {content: fs.readFileSync(filename).toString()}
|
[filename]: {content: loadSource(filename, stripSMTPragmas)}
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
optimizer: {enabled: optimize},
|
optimizer: {enabled: optimize},
|
||||||
outputSelection: {'*': {'*': ['evm.bytecode.object', 'metadata']}},
|
outputSelection: {'*': {'*': ['evm.bytecode.object', 'metadata']}}
|
||||||
"modelChecker": {"engine": "none"}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!stripSMTPragmas)
|
||||||
|
input['settings']['modelChecker'] = {engine: 'none'}
|
||||||
|
|
||||||
const result = JSON.parse(compiler.compile(JSON.stringify(input)))
|
const result = JSON.parse(compiler.compile(JSON.stringify(input)))
|
||||||
|
|
||||||
|
@ -23,6 +23,12 @@ class CompilerInterface(Enum):
|
|||||||
STANDARD_JSON = 'standard-json'
|
STANDARD_JSON = 'standard-json'
|
||||||
|
|
||||||
|
|
||||||
|
class SMTUse(Enum):
|
||||||
|
PRESERVE = 'preserve'
|
||||||
|
DISABLE = 'disable'
|
||||||
|
STRIP_PRAGMAS = 'strip-pragmas'
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ContractReport:
|
class ContractReport:
|
||||||
contract_name: str
|
contract_name: str
|
||||||
@ -54,12 +60,15 @@ class FileReport:
|
|||||||
return report
|
return report
|
||||||
|
|
||||||
|
|
||||||
def load_source(path: Union[Path, str]) -> str:
|
def load_source(path: Union[Path, str], smt_use: SMTUse) -> str:
|
||||||
# NOTE: newline='' disables newline conversion.
|
# NOTE: newline='' disables newline conversion.
|
||||||
# We want the file exactly as is because changing even a single byte in the source affects metadata.
|
# We want the file exactly as is because changing even a single byte in the source affects metadata.
|
||||||
with open(path, mode='r', encoding='utf8', newline='') as source_file:
|
with open(path, mode='r', encoding='utf8', newline='') as source_file:
|
||||||
file_content = source_file.read()
|
file_content = source_file.read()
|
||||||
|
|
||||||
|
if smt_use == SMTUse.STRIP_PRAGMAS:
|
||||||
|
return file_content.replace('pragma experimental SMTChecker;', '', 1)
|
||||||
|
|
||||||
return file_content
|
return file_content
|
||||||
|
|
||||||
|
|
||||||
@ -126,33 +135,37 @@ def prepare_compiler_input(
|
|||||||
compiler_path: Path,
|
compiler_path: Path,
|
||||||
source_file_name: Path,
|
source_file_name: Path,
|
||||||
optimize: bool,
|
optimize: bool,
|
||||||
interface: CompilerInterface
|
interface: CompilerInterface,
|
||||||
|
smt_use: SMTUse,
|
||||||
) -> Tuple[List[str], str]:
|
) -> Tuple[List[str], str]:
|
||||||
|
|
||||||
if interface == CompilerInterface.STANDARD_JSON:
|
if interface == CompilerInterface.STANDARD_JSON:
|
||||||
json_input: dict = {
|
json_input: dict = {
|
||||||
'language': 'Solidity',
|
'language': 'Solidity',
|
||||||
'sources': {
|
'sources': {
|
||||||
str(source_file_name): {'content': load_source(source_file_name)}
|
str(source_file_name): {'content': load_source(source_file_name, smt_use)}
|
||||||
},
|
},
|
||||||
'settings': {
|
'settings': {
|
||||||
'optimizer': {'enabled': optimize},
|
'optimizer': {'enabled': optimize},
|
||||||
'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}},
|
'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}},
|
||||||
'modelChecker': {'engine': 'none'},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if smt_use == SMTUse.DISABLE:
|
||||||
|
json_input['settings']['modelChecker'] = {'engine': 'none'}
|
||||||
|
|
||||||
command_line = [str(compiler_path), '--standard-json']
|
command_line = [str(compiler_path), '--standard-json']
|
||||||
compiler_input = json.dumps(json_input)
|
compiler_input = json.dumps(json_input)
|
||||||
else:
|
else:
|
||||||
assert interface == CompilerInterface.CLI
|
assert interface == CompilerInterface.CLI
|
||||||
|
|
||||||
compiler_options = [str(source_file_name), '--bin', '--metadata', '--model-checker-engine', 'none']
|
compiler_options = [str(source_file_name), '--bin', '--metadata']
|
||||||
if optimize:
|
if optimize:
|
||||||
compiler_options.append('--optimize')
|
compiler_options.append('--optimize')
|
||||||
|
if smt_use == SMTUse.DISABLE:
|
||||||
|
compiler_options += ['--model-checker-engine', 'none']
|
||||||
|
|
||||||
command_line = [str(compiler_path)] + compiler_options
|
command_line = [str(compiler_path)] + compiler_options
|
||||||
compiler_input = load_source(source_file_name)
|
compiler_input = load_source(source_file_name, smt_use)
|
||||||
|
|
||||||
return (command_line, compiler_input)
|
return (command_line, compiler_input)
|
||||||
|
|
||||||
@ -162,6 +175,7 @@ def run_compiler(
|
|||||||
source_file_name: Path,
|
source_file_name: Path,
|
||||||
optimize: bool,
|
optimize: bool,
|
||||||
interface: CompilerInterface,
|
interface: CompilerInterface,
|
||||||
|
smt_use: SMTUse,
|
||||||
tmp_dir: Path,
|
tmp_dir: Path,
|
||||||
) -> FileReport:
|
) -> FileReport:
|
||||||
|
|
||||||
@ -170,7 +184,8 @@ def run_compiler(
|
|||||||
compiler_path,
|
compiler_path,
|
||||||
Path(source_file_name.name),
|
Path(source_file_name.name),
|
||||||
optimize,
|
optimize,
|
||||||
interface
|
interface,
|
||||||
|
smt_use,
|
||||||
)
|
)
|
||||||
|
|
||||||
process = subprocess.run(
|
process = subprocess.run(
|
||||||
@ -190,7 +205,8 @@ def run_compiler(
|
|||||||
compiler_path.absolute(),
|
compiler_path.absolute(),
|
||||||
Path(source_file_name.name),
|
Path(source_file_name.name),
|
||||||
optimize,
|
optimize,
|
||||||
interface
|
interface,
|
||||||
|
smt_use,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create a copy that we can use directly with the CLI interface
|
# Create a copy that we can use directly with the CLI interface
|
||||||
@ -211,13 +227,20 @@ def run_compiler(
|
|||||||
return parse_cli_output(Path(source_file_name), process.stdout)
|
return parse_cli_output(Path(source_file_name), process.stdout)
|
||||||
|
|
||||||
|
|
||||||
def generate_report(source_file_names: List[str], compiler_path: Path, interface: CompilerInterface):
|
def generate_report(source_file_names: List[str], compiler_path: Path, interface: CompilerInterface, smt_use: SMTUse):
|
||||||
with open('report.txt', mode='w', encoding='utf8', newline='\n') as report_file:
|
with open('report.txt', mode='w', encoding='utf8', newline='\n') as report_file:
|
||||||
for optimize in [False, True]:
|
for optimize in [False, True]:
|
||||||
with TemporaryDirectory(prefix='prepare_report-') as tmp_dir:
|
with TemporaryDirectory(prefix='prepare_report-') as tmp_dir:
|
||||||
for source_file_name in sorted(source_file_names):
|
for source_file_name in sorted(source_file_names):
|
||||||
try:
|
try:
|
||||||
report = run_compiler(compiler_path, Path(source_file_name), optimize, interface, Path(tmp_dir))
|
report = run_compiler(
|
||||||
|
compiler_path,
|
||||||
|
Path(source_file_name),
|
||||||
|
optimize,
|
||||||
|
interface,
|
||||||
|
smt_use,
|
||||||
|
Path(tmp_dir),
|
||||||
|
)
|
||||||
report_file.write(report.format_report())
|
report_file.write(report.format_report())
|
||||||
except subprocess.CalledProcessError as exception:
|
except subprocess.CalledProcessError as exception:
|
||||||
print(
|
print(
|
||||||
@ -250,7 +273,14 @@ def commandline_parser() -> ArgumentParser:
|
|||||||
dest='interface',
|
dest='interface',
|
||||||
default=CompilerInterface.STANDARD_JSON.value,
|
default=CompilerInterface.STANDARD_JSON.value,
|
||||||
choices=[c.value for c in CompilerInterface],
|
choices=[c.value for c in CompilerInterface],
|
||||||
help="Compiler interface to use."
|
help="Compiler interface to use.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--smt-use',
|
||||||
|
dest='smt_use',
|
||||||
|
default=SMTUse.DISABLE.value,
|
||||||
|
choices=[s.value for s in SMTUse],
|
||||||
|
help="What to do about contracts that use the experimental SMT checker."
|
||||||
)
|
)
|
||||||
return parser;
|
return parser;
|
||||||
|
|
||||||
@ -261,4 +291,5 @@ if __name__ == "__main__":
|
|||||||
glob("*.sol"),
|
glob("*.sol"),
|
||||||
Path(options.compiler_path),
|
Path(options.compiler_path),
|
||||||
CompilerInterface(options.interface),
|
CompilerInterface(options.interface),
|
||||||
|
SMTUse(options.smt_use),
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,7 @@ from unittest_helpers import FIXTURE_DIR, LIBSOLIDITY_TEST_DIR, load_fixture, lo
|
|||||||
|
|
||||||
# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports
|
# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports
|
||||||
# pragma pylint: disable=import-error
|
# pragma pylint: disable=import-error
|
||||||
from bytecodecompare.prepare_report import CompilerInterface, FileReport, ContractReport
|
from bytecodecompare.prepare_report import CompilerInterface, FileReport, ContractReport, SMTUse
|
||||||
from bytecodecompare.prepare_report import load_source, parse_cli_output, parse_standard_json_output, prepare_compiler_input
|
from bytecodecompare.prepare_report import load_source, parse_cli_output, parse_standard_json_output, prepare_compiler_input
|
||||||
# pragma pylint: enable=import-error
|
# pragma pylint: enable=import-error
|
||||||
|
|
||||||
@ -98,48 +98,58 @@ class TestFileReport(PrepareReportTestBase):
|
|||||||
|
|
||||||
|
|
||||||
class TestLoadSource(PrepareReportTestBase):
|
class TestLoadSource(PrepareReportTestBase):
|
||||||
def test_load_source(self):
|
def test_load_source_should_strip_smt_pragmas_if_requested(self):
|
||||||
self.assertEqual(load_source(SMT_SMOKE_TEST_SOL_PATH), SMT_SMOKE_TEST_SOL_CODE)
|
expected_file_content = (
|
||||||
|
|
||||||
def test_load_source_preserves_lf_newlines(self):
|
|
||||||
expected_output = (
|
|
||||||
"pragma experimental SMTChecker;\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"contract C {\n"
|
"contract C {\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(load_source(SMT_CONTRACT_WITH_LF_NEWLINES_SOL_PATH), expected_output)
|
self.assertEqual(load_source(SMT_SMOKE_TEST_SOL_PATH, SMTUse.STRIP_PRAGMAS), expected_file_content)
|
||||||
|
|
||||||
|
def test_load_source_should_not_strip_smt_pragmas_if_not_requested(self):
|
||||||
|
self.assertEqual(load_source(SMT_SMOKE_TEST_SOL_PATH, SMTUse.DISABLE), SMT_SMOKE_TEST_SOL_CODE)
|
||||||
|
self.assertEqual(load_source(SMT_SMOKE_TEST_SOL_PATH, SMTUse.PRESERVE), SMT_SMOKE_TEST_SOL_CODE)
|
||||||
|
|
||||||
|
def test_load_source_preserves_lf_newlines(self):
|
||||||
|
expected_output = (
|
||||||
|
"\n"
|
||||||
|
"\n"
|
||||||
|
"contract C {\n"
|
||||||
|
"}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(load_source(SMT_CONTRACT_WITH_LF_NEWLINES_SOL_PATH, SMTUse.STRIP_PRAGMAS), expected_output)
|
||||||
|
|
||||||
def test_load_source_preserves_crlf_newlines(self):
|
def test_load_source_preserves_crlf_newlines(self):
|
||||||
expected_output = (
|
expected_output = (
|
||||||
"pragma experimental SMTChecker;\r\n"
|
"\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"contract C {\r\n"
|
"contract C {\r\n"
|
||||||
"}\r\n"
|
"}\r\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(load_source(SMT_CONTRACT_WITH_CRLF_NEWLINES_SOL_PATH), expected_output)
|
self.assertEqual(load_source(SMT_CONTRACT_WITH_CRLF_NEWLINES_SOL_PATH, SMTUse.STRIP_PRAGMAS), expected_output)
|
||||||
|
|
||||||
def test_load_source_preserves_cr_newlines(self):
|
def test_load_source_preserves_cr_newlines(self):
|
||||||
expected_output = (
|
expected_output = (
|
||||||
"pragma experimental SMTChecker;\r"
|
"\r"
|
||||||
"\r"
|
"\r"
|
||||||
"contract C {\r"
|
"contract C {\r"
|
||||||
"}\r"
|
"}\r"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(load_source(SMT_CONTRACT_WITH_CR_NEWLINES_SOL_PATH), expected_output)
|
self.assertEqual(load_source(SMT_CONTRACT_WITH_CR_NEWLINES_SOL_PATH, SMTUse.STRIP_PRAGMAS), expected_output)
|
||||||
|
|
||||||
def test_load_source_preserves_mixed_newlines(self):
|
def test_load_source_preserves_mixed_newlines(self):
|
||||||
expected_output = (
|
expected_output = (
|
||||||
"pragma experimental SMTChecker;\n"
|
"\n"
|
||||||
"\n"
|
"\n"
|
||||||
"contract C {\r"
|
"contract C {\r"
|
||||||
"}\r\n"
|
"}\r\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(load_source(SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH), expected_output)
|
self.assertEqual(load_source(SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH, SMTUse.STRIP_PRAGMAS), expected_output)
|
||||||
|
|
||||||
|
|
||||||
class TestPrepareCompilerInput(PrepareReportTestBase):
|
class TestPrepareCompilerInput(PrepareReportTestBase):
|
||||||
@ -161,6 +171,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase):
|
|||||||
SMT_SMOKE_TEST_SOL_PATH,
|
SMT_SMOKE_TEST_SOL_PATH,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
interface=CompilerInterface.STANDARD_JSON,
|
interface=CompilerInterface.STANDARD_JSON,
|
||||||
|
smt_use=SMTUse.DISABLE,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(command_line, ['solc', '--standard-json'])
|
self.assertEqual(command_line, ['solc', '--standard-json'])
|
||||||
@ -172,11 +183,12 @@ class TestPrepareCompilerInput(PrepareReportTestBase):
|
|||||||
SMT_SMOKE_TEST_SOL_PATH,
|
SMT_SMOKE_TEST_SOL_PATH,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
interface=CompilerInterface.CLI,
|
interface=CompilerInterface.CLI,
|
||||||
|
smt_use=SMTUse.DISABLE,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
command_line,
|
command_line,
|
||||||
['solc', str(SMT_SMOKE_TEST_SOL_PATH), '--bin', '--metadata', '--model-checker-engine', 'none', '--optimize']
|
['solc', str(SMT_SMOKE_TEST_SOL_PATH), '--bin', '--metadata', '--optimize', '--model-checker-engine', 'none']
|
||||||
)
|
)
|
||||||
self.assertEqual(compiler_input, SMT_SMOKE_TEST_SOL_CODE)
|
self.assertEqual(compiler_input, SMT_SMOKE_TEST_SOL_CODE)
|
||||||
|
|
||||||
@ -204,6 +216,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase):
|
|||||||
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH,
|
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
interface=CompilerInterface.STANDARD_JSON,
|
interface=CompilerInterface.STANDARD_JSON,
|
||||||
|
smt_use=SMTUse.DISABLE,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(command_line, ['solc', '--standard-json'])
|
self.assertEqual(command_line, ['solc', '--standard-json'])
|
||||||
@ -215,6 +228,7 @@ class TestPrepareCompilerInput(PrepareReportTestBase):
|
|||||||
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH,
|
SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH,
|
||||||
optimize=True,
|
optimize=True,
|
||||||
interface=CompilerInterface.CLI,
|
interface=CompilerInterface.CLI,
|
||||||
|
smt_use=SMTUse.DISABLE,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(compiler_input, SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_CODE)
|
self.assertEqual(compiler_input, SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_CODE)
|
||||||
|
Loading…
Reference in New Issue
Block a user