[libevmasm] Add support to import evm assembly json.

This commit is contained in:
Alexander Arlt
2023-05-16 19:57:31 -05:00
parent aa9e25021d
commit fd137e2929
59 changed files with 1110 additions and 61 deletions
+151 -2
View File
@@ -23,6 +23,18 @@
# ast import/export tests:
# - first exporting a .sol file to JSON, then loading it into the compiler
# and exporting it again. The second JSON should be identical to the first.
#
# evm-assembly import/export tests:
# - first a .sol file will be exported to a combined json file, containing outputs
# for "asm" "bin" "bin-runtime" "opcodes" "srcmap" and "srcmap-runtime" (expected output).
# The "asm" output will then be used as import, where its output "bin" "bin-runtime"
# "opcodes" "srcmap" "srcmap-runtime" (obtained output) will be compared with the expected output.
# The expected output needs to be identical with the obtained output.
#
# Additionally to this, the direct import/export is tested by importing an
# evm-assembly json with --import-asm-json and directly exporting it again with
# --asm-json using the same solc invocation. The original asm json file used for the
# import and the resulting exported asm json file need to be identical.
set -euo pipefail
@@ -40,7 +52,7 @@ source "${REPO_ROOT}/scripts/common.sh"
function print_usage
{
echo "Usage: ${0} ast [--exit-on-error|--help]."
echo "Usage: ${0} ast|evm-assembly [--exit-on-error|--help]."
}
function print_used_commands
@@ -81,6 +93,7 @@ for PARAM in "$@"
do
case "$PARAM" in
ast) check_import_test_type_unset ; IMPORT_TEST_TYPE="ast" ;;
evm-assembly) check_import_test_type_unset ; IMPORT_TEST_TYPE="evm-assembly" ;;
--help) print_usage ; exit 0 ;;
--exit-on-error) EXIT_ON_ERROR=1 ;;
*) fail "Unknown option '$PARAM'. Aborting. $(print_usage)" ;;
@@ -89,6 +102,7 @@ done
SYNTAXTESTS_DIR="${REPO_ROOT}/test/libsolidity/syntaxTests"
ASTJSONTESTS_DIR="${REPO_ROOT}/test/libsolidity/ASTJSON"
SEMANTICTESTS_DIR="${REPO_ROOT}/test/libsolidity/semanticTests"
FAILED=0
UNCOMPILABLE=0
@@ -153,6 +167,125 @@ function test_ast_import_export_equivalence
TESTED=$((TESTED + 1))
}
function test_evmjson_import_export_equivalence
{
local sol_file="$1"
local input_files=( "${@:2}" )
local outputs=( "asm" "bin" "bin-runtime" "opcodes" "srcmap" "srcmap-runtime" )
local export_command=("$SOLC" --combined-json "$(IFS=, ; echo "${outputs[*]}")" --pretty-json --json-indent 4 "${input_files[@]}")
local success=1
if ! "${export_command[@]}" > expected.json 2> expected.error
then
success=0
printError "ERROR: (export) EVM Assembly JSON reimport failed for ${sol_file}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "$(pwd)" "${export_command[*]}" ""
return 1
fi
fi
if ! "${export_command[@]}" "--optimize" > expected.optimize.json 2> expected.optimize.error
then
success=0
printError "ERROR: (export with --optimize) EVM Assembly JSON reimport failed for ${sol_file}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "$(pwd)" "${export_command[*]} --optimize" ""
return 1
fi
fi
for input_json in "expected.json" "expected.optimize.json"
do
local optimize_param=""
if [[ "$input_json" == "expected.optimize.json" ]]
then
optimize_param="--optimize"
fi
# Note that we have some test files, that only consist of free functions.
# Those files don't define any contracts, so the resulting JSON does not have any
# keys. In this case `jq` returns an error like `jq: error: null (null) has no keys`.
# To not get spammed by these errors, errors are redirected to /dev/null.
for contract in $(jq '.contracts | keys | .[]' "$input_json" 2> /dev/null)
do
for output in "${outputs[@]}"
do
jq --raw-output ".contracts.${contract}.\"${output}\"" "$input_json" > "expected.${output}.json"
done
assembly=$(cat expected.asm.json)
[[ $assembly != "" && $assembly != "null" ]] || continue
local import_command=("${SOLC}" --combined-json "bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime" --pretty-json --json-indent 4 --import-asm-json expected.asm.json)
if ! "${import_command[@]}" > obtained.json 2> obtained.error
then
success=0
printError "ERROR: (import) EVM Assembly JSON reimport failed for ${sol_file}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "$(pwd)" "${export_command[*]} ${optimize_param}" "${import_command[*]}"
return 1
fi
fi
for output in "${outputs[@]}"
do
for obtained_contract in $(jq '.contracts | keys | .[]' obtained.json 2> /dev/null)
do
jq --raw-output ".contracts.${obtained_contract}.\"${output}\"" obtained.json > "obtained.${output}.json"
# compare expected and obtained evm assembly json
if ! diff_files "expected.${output}.json" "obtained.${output}.json"
then
success=0
printError "ERROR: (${output}) EVM Assembly JSON reimport failed for ${sol_file}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "$(pwd)" "${export_command[*]} ${optimize_param}" "${import_command[*]}"
return 1
fi
fi
done
done
# direct export via --asm-json, if imported with --import-asm-json.
if ! "${SOLC}" --asm-json --import-asm-json expected.asm.json --pretty-json --json-indent 4 | tail -n+2 > obtained_direct_import_export.json 2> obtained_direct_import_export.error
then
success=0
printError "ERROR: (direct) EVM Assembly JSON reimport failed for ${sol_file}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "$(pwd)" "${SOLC} --asm-json --import-asm-json expected.asm.json --pretty-json --json-indent 4 | tail -n+4" ""
return 1
fi
fi
# reformat json files using jq.
jq . expected.asm.json > expected.asm.json.pretty
jq . obtained_direct_import_export.json > obtained_direct_import_export.json.pretty
# compare expected and obtained evm assembly.
if ! diff_files expected.asm.json.pretty obtained_direct_import_export.json.pretty
then
success=0
printError "ERROR: EVM Assembly JSON reimport failed for ${sol_file}"
if (( EXIT_ON_ERROR == 1 ))
then
print_used_commands "$(pwd)" "${export_command[*]} ${optimize_param}" "${import_command[*]}"
return 1
fi
fi
done
done
if (( success == 1 ))
then
TESTED=$((TESTED + 1))
else
FAILED=$((FAILED + 1))
fi
}
# function tests whether exporting and importing again is equivalent.
# Results are recorded by incrementing the FAILED or UNCOMPILABLE global variable.
# Also, in case of a mismatch a diff is printed
@@ -168,6 +301,7 @@ function test_import_export_equivalence {
case "$IMPORT_TEST_TYPE" in
ast) compile_test="--ast-compact-json" ;;
evm-assembly) compile_test="--bin" ;;
*) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;;
esac
@@ -181,6 +315,7 @@ function test_import_export_equivalence {
then
case "$IMPORT_TEST_TYPE" in
ast) test_ast_import_export_equivalence "${sol_file}" "${input_files[@]}" ;;
evm-assembly) test_evmjson_import_export_equivalence "${sol_file}" "${input_files[@]}" ;;
*) assertFail "Unknown import test type '${IMPORT_TEST_TYPE}'. Aborting." ;;
esac
else
@@ -191,7 +326,20 @@ function test_import_export_equivalence {
# and print some details about the corresponding solc invocation.
if (( solc_return_code == 2 ))
then
fail "\n\nERROR: Uncaught Exception while executing '$SOLC ${compile_test} ${input_files[*]}':\n${output}\n"
# For the evm-assembly import/export tests, this script uses only the
# old code generator. Some semantic test can only be compiled with
# --via-ir (some need to be additionally compiled with --optimize).
# The tests that are meant to be compiled with --via-ir are throwing
# an UnimplementedFeatureError exception (e.g. Copying of type struct C.S
# memory[] memory to storage not yet supported, Copying nested calldata
# dynamic arrays to storage is not implemented in the old code generator.)
# We will just ignore these kind of exceptions for now.
# However, any other exception will be treated as a fatal error and the
# script execution will be terminated with an error.
if [[ "$output" != *"UnimplementedFeatureError"* ]]
then
fail "\n\nERROR: Uncaught Exception while executing '$SOLC ${compile_test} ${input_files[*]}':\n${output}\n"
fi
fi
fi
}
@@ -203,6 +351,7 @@ command_available jq --version
case "$IMPORT_TEST_TYPE" in
ast) TEST_DIRS=("${SYNTAXTESTS_DIR}" "${ASTJSONTESTS_DIR}") ;;
evm-assembly) TEST_DIRS=("${SEMANTICTESTS_DIR}") ;;
*) assertFail "Import test type not defined. $(print_usage)" ;;
esac