Skip to content

Commit 91c7b32

Browse files
aarltcameelr0qs
committed
CLI test for mass EVM assembly import/export
Co-authored-by: Kamil Śliwak <[email protected]> Co-authored-by: r0qs <[email protected]>
1 parent 67e11fc commit 91c7b32

File tree

7 files changed

+654
-10
lines changed

7 files changed

+654
-10
lines changed

Diff for: .circleci/config.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ jobs:
11231123

11241124
t_osx_cli:
11251125
<<: *base_osx
1126-
parallelism: 7 # Should match number of tests in .circleci/cli.sh
1126+
parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py
11271127
steps:
11281128
- checkout
11291129
- install_dependencies_osx
@@ -1218,7 +1218,7 @@ jobs:
12181218

12191219
t_ubu_cli: &t_ubu_cli
12201220
<<: *base_ubuntu2204_small
1221-
parallelism: 7 # Should match number of tests in .circleci/cli.sh
1221+
parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py
12221222
steps:
12231223
- cmdline_tests
12241224

@@ -1237,7 +1237,7 @@ jobs:
12371237
t_ubu_asan_cli:
12381238
# Runs slightly faster on medium but we only run it nightly so efficiency matters more.
12391239
<<: *base_ubuntu2204
1240-
parallelism: 7 # Should match number of tests in .circleci/cli.sh
1240+
parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py
12411241
environment:
12421242
<<: *base_ubuntu2204_env
12431243
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
@@ -1286,7 +1286,7 @@ jobs:
12861286

12871287
t_ubu_ubsan_clang_cli:
12881288
<<: *base_ubuntu2204_clang
1289-
parallelism: 7 # Should match number of tests in .circleci/cli.sh
1289+
parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py
12901290
steps:
12911291
- cmdline_tests
12921292

Diff for: .circleci/parallel_cli_tests.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# TODO: We should switch to time-based splitting but that requires JUnit XML report support in cmdlineTests.sh.
99
tests_to_run_in_parallel = [
1010
'~ast_import_export', # ~7 min
11+
'~evmasm_import_export', # ~5 min
1112
'~ast_export_with_stop_after_parsing', # ~4 min
1213
'~soljson_via_fuzzer', # ~3 min
1314
'~via_ir_equivalence', # ~1 min

Diff for: scripts/ASTImportTest.sh

+123-4
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,48 @@
2323
# ast import/export tests:
2424
# - first exporting a .sol file to JSON, then loading it into the compiler
2525
# and exporting it again. The second JSON should be identical to the first.
26+
#
27+
# evm-assembly import/export tests:
28+
# - first a .sol file will be compiled and the EVM Assembly will be exported
29+
# to JSON format using --asm-json command-line option.
30+
# The EVM Assembly JSON output is then imported with --import-asm-json
31+
# and compiled again. The binary generated initially and after the import
32+
# should be identical.
2633

2734
set -euo pipefail
2835

2936
READLINK=readlink
3037
if [[ "$OSTYPE" == "darwin"* ]]; then
3138
READLINK=greadlink
3239
fi
40+
EXPR="expr"
41+
if [[ "$OSTYPE" == "darwin"* ]]; then
42+
EXPR="gexpr"
43+
fi
44+
3345
REPO_ROOT=$(${READLINK} -f "$(dirname "$0")"/..)
3446
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build}
3547
SOLC="${SOLIDITY_BUILD_DIR}/solc/solc"
3648
SPLITSOURCES="${REPO_ROOT}/scripts/splitSources.py"
3749

3850
# shellcheck source=scripts/common.sh
3951
source "${REPO_ROOT}/scripts/common.sh"
52+
# shellcheck source=scripts/common_cmdline.sh
53+
source "${REPO_ROOT}/scripts/common_cmdline.sh"
4054

4155
function print_usage
4256
{
43-
echo "Usage: ${0} ast [--exit-on-error|--help]."
57+
echo "Usage: ${0} ast|evm-assembly [--exit-on-error|--help]."
4458
}
4559

4660
function print_used_commands
4761
{
4862
local test_directory="$1"
4963
local export_command="$2"
5064
local import_command="$3"
51-
printError "You can find the files used for this test here: ${test_directory}"
52-
printError "Used commands for test:"
65+
echo
66+
printError "You can find the files used for this test in ${test_directory}"
67+
printError "Commands used in the test:"
5368
printError "# export"
5469
echo "$ ${export_command}" >&2
5570
printError "# import"
@@ -81,6 +96,7 @@ for PARAM in "$@"
8196
do
8297
case "$PARAM" in
8398
ast) check_import_test_type_unset ; IMPORT_TEST_TYPE="ast" ;;
99+
evm-assembly) check_import_test_type_unset ; IMPORT_TEST_TYPE="evm-assembly" ;;
84100
--help) print_usage ; exit 0 ;;
85101
--exit-on-error) EXIT_ON_ERROR=1 ;;
86102
*) fail "Unknown option '$PARAM'. Aborting. $(print_usage)" ;;
@@ -89,6 +105,7 @@ done
89105

90106
SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests"
91107
ASTJSONTESTS_DIR="${REPO_ROOT}/test/libsolidity/ASTJSON"
108+
SEMANTICTESTS_DIR="${REPO_ROOT}/test/libsolidity/semanticTests"
92109

93110
FAILED=0
94111
UNCOMPILABLE=0
@@ -153,6 +170,92 @@ function test_ast_import_export_equivalence
153170
TESTED=$((TESTED + 1))
154171
}
155172

173+
function run_solc
174+
{
175+
local parameters=( "${@}" )
176+
177+
if ! "${SOLC}" "${parameters[@]}" > /dev/null 2> solc_stderr
178+
then
179+
printError "ERROR: ${parameters[*]}"
180+
printError "${PWD}"
181+
# FIXME: EXIT_ON_ERROR seems to be ignored here and in some other places.
182+
# We just exit unconditionally instead.
183+
fail "$(cat solc_stderr)"
184+
fi
185+
rm solc_stderr
186+
}
187+
188+
function run_solc_store_stdout
189+
{
190+
local output_file=$1
191+
local parameters=( "${@:2}" )
192+
193+
if ! "${SOLC}" "${parameters[@]}" > "${output_file}" 2> "${output_file}.error"
194+
then
195+
printError "ERROR: ${parameters[*]}"
196+
printError "${PWD}"
197+
fail "$(cat "${output_file}.error")"
198+
fi
199+
rm "${output_file}.error"
200+
}
201+
202+
function test_evmjson_import_export_equivalence
203+
{
204+
local sol_file="$1"
205+
local input_files=( "${@:2}" )
206+
207+
# Generate bytecode and EVM assembly JSON through normal complication
208+
mkdir -p export/
209+
local export_options=(--bin --asm-json "${input_files[@]}" --output-dir export/)
210+
run_solc "${export_options[@]}"
211+
212+
# NOTE: If there is no bytecode, the compiler produces a JSON file that contains just 'null'.
213+
# This is not accepted by assembly import though so we must skip such contracts.
214+
echo -n null > null
215+
find export/ -name '*.json' -exec cmp --quiet --bytes=4 {} null \; -delete
216+
217+
find export/ -name '*.bin' -size 0 -delete
218+
219+
for asm_json_file in export/*.json
220+
do
221+
mv "${asm_json_file}" "${asm_json_file/_evm/}"
222+
done
223+
224+
# Import EVM assembly JSON
225+
mkdir -p import/
226+
for asm_json_file in export/*.json
227+
do
228+
local bin_file_from_asm_import
229+
bin_file_from_asm_import="import/$(basename "${asm_json_file}" .json).bin"
230+
231+
local import_options=(--bin --import-asm-json "${asm_json_file}")
232+
run_solc_store_stdout "${bin_file_from_asm_import}" "${import_options[@]}"
233+
234+
stripCLIDecorations < "$bin_file_from_asm_import" > tmpfile
235+
mv tmpfile "$bin_file_from_asm_import"
236+
done
237+
238+
# Compare bytecode from compilation with bytecode from import
239+
for bin_file in export/*.bin
240+
do
241+
local bin_file_from_asm_import=${bin_file/export/import}
242+
if ! diff --strip-trailing-cr --ignore-all-space "${bin_file}" "${bin_file_from_asm_import}" > diff_error
243+
then
244+
printError "ERROR: Bytecode from compilation (${bin_file}) differs from bytecode from EVM asm import (${bin_file_from_asm_import}):"
245+
printError " $(cat diff_error)"
246+
if (( EXIT_ON_ERROR == 1 ))
247+
then
248+
# NOTE: The import_options we print here refers to the wrong file (the last one
249+
# processed by the previous loop) - but it's still a good starting point for debugging ;)
250+
print_used_commands "${PWD}" "${SOLC} ${export_options[*]}" "${SOLC} ${import_options[*]}"
251+
return 1
252+
fi
253+
FAILED=$((FAILED + 1))
254+
fi
255+
done
256+
TESTED=$((TESTED + 1))
257+
}
258+
156259
# function tests whether exporting and importing again is equivalent.
157260
# Results are recorded by incrementing the FAILED or UNCOMPILABLE global variable.
158261
# Also, in case of a mismatch a diff is printed
@@ -168,6 +271,7 @@ function test_import_export_equivalence {
168271

169272
case "$IMPORT_TEST_TYPE" in
170273
ast) compile_test="--ast-compact-json" ;;
274+
evm-assembly) compile_test="--bin" ;;
171275
*) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;;
172276
esac
173277

@@ -181,6 +285,7 @@ function test_import_export_equivalence {
181285
then
182286
case "$IMPORT_TEST_TYPE" in
183287
ast) test_ast_import_export_equivalence "${sol_file}" "${input_files[@]}" ;;
288+
evm-assembly) test_evmjson_import_export_equivalence "${sol_file}" "${input_files[@]}" ;;
184289
*) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;;
185290
esac
186291
else
@@ -191,16 +296,30 @@ function test_import_export_equivalence {
191296
# and print some details about the corresponding solc invocation.
192297
if (( solc_return_code == 2 ))
193298
then
194-
fail "\n\nERROR: Uncaught Exception while executing '$SOLC ${compile_test} ${input_files[*]}':\n${output}\n"
299+
# For the evm-assembly import/export tests, this script uses only the old code generator.
300+
# Some semantic tests can only be compiled with --via-ir (some need to be additionally
301+
# compiled with --optimize). The tests that are meant to be compiled with --via-ir are
302+
# throwing an UnimplementedFeatureError exception, e.g.:
303+
# "Copying of type struct C.S memory[] memory to storage not yet supported",
304+
# "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator".
305+
# We will just ignore these kind of exceptions for now. However, any other exception
306+
# will be treated as a fatal error and the script execution will be terminated with an error.
307+
if [[ "${output}" != *"UnimplementedFeatureError"* ]]
308+
then
309+
fail "\n\nERROR: Uncaught exception while executing '${SOLC} ${compile_test} ${input_files[*]}':\n${output}\n"
310+
fi
195311
fi
196312
fi
197313
}
198314

199315
command_available "$SOLC" --version
200316
command_available jq --version
317+
command_available "$EXPR" --version
318+
command_available "$READLINK" --version
201319

202320
case "$IMPORT_TEST_TYPE" in
203321
ast) TEST_DIRS=("${SYNTAXTESTS_DIR}" "${ASTJSONTESTS_DIR}") ;;
322+
evm-assembly) TEST_DIRS=("${SEMANTICTESTS_DIR}") ;;
204323
*) assertFail "Import test type not defined. $(print_usage)" ;;
205324
esac
206325

Diff for: test/cmdlineTests/~evmasm_import_export/test.sh

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# shellcheck source=scripts/common.sh
5+
source "${REPO_ROOT}/scripts/common.sh"
6+
7+
SOLTMPDIR=$(mktemp -d -t "cmdline-test-evmasm-import-export-XXXXXX")
8+
cd "$SOLTMPDIR"
9+
if ! "$REPO_ROOT/scripts/ASTImportTest.sh" evm-assembly
10+
then
11+
rm -r "$SOLTMPDIR"
12+
fail
13+
fi
14+
rm -r "$SOLTMPDIR"

Diff for: test/libsolidity/semanticTests/expressions/module_from_ternary_expression.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
==== Source: A ====
2-
contract C {
2+
contract D {
33
}
44
==== Source: B ====
55
import "A" as M;
66

77
contract C {
88
function f() public pure returns (bool) {
99
bool flag;
10-
((flag = true) ? M : M).C;
10+
((flag = true) ? M : M).D;
1111
return flag;
1212
}
1313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
contract C0 {}
2+
contract C1 {}
3+
contract C2 {}
4+
contract C3 {}
5+
contract C4 {}
6+
contract C5 {}
7+
contract C6 {}
8+
contract C7 {}
9+
contract C8 {}
10+
contract C9 {}
11+
contract C10 {}
12+
13+
contract D {
14+
function run() public {
15+
// This is primarily meant to test assembly import via --import-asm-json.
16+
// The exported JSON will fail the reimport unless the subassembly indices are parsed
17+
// correctly - as hex numbers.
18+
new C0();
19+
new C1();
20+
new C2();
21+
new C3();
22+
new C4();
23+
new C5();
24+
new C6();
25+
new C7();
26+
new C8();
27+
new C9();
28+
new C10();
29+
}
30+
}
31+
// ----
32+
// run() ->
33+
// gas irOptimized: 381615
34+
// gas legacy: 392719
35+
// gas legacyOptimized: 392719

0 commit comments

Comments
 (0)