Hbrodin/ssa array tests (#1205)

* Initial array test cases. Not asserting anything yet.
* Test cases for SSA IR related to arrays
pull/1204/head
Henrik Brodin 3 years ago committed by GitHub
parent 33922541bb
commit fdf346f530
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 131
      tests/test_ssa_generation.py

@ -1,3 +1,4 @@
# pylint: disable=too-many-lines
import os import os
import pathlib import pathlib
from argparse import ArgumentTypeError from argparse import ArgumentTypeError
@ -25,10 +26,15 @@ from slither.slithir.operations import (
Binary, Binary,
BinaryType, BinaryType,
InternalCall, InternalCall,
Index,
) )
from slither.slithir.utils.ssa import is_used_later from slither.slithir.utils.ssa import is_used_later
from slither.slithir.variables import Constant, ReferenceVariable, LocalIRVariable, StateIRVariable from slither.slithir.variables import (
Constant,
ReferenceVariable,
LocalIRVariable,
StateIRVariable,
)
# Directory of currently executing script. Will be used as basis for temporary file names. # Directory of currently executing script. Will be used as basis for temporary file names.
SCRIPT_DIR = pathlib.Path(getsourcefile(lambda: 0)).parent SCRIPT_DIR = pathlib.Path(getsourcefile(lambda: 0)).parent
@ -782,6 +788,127 @@ def test_multiple_named_args_returns():
) )
@pytest.mark.xfail(reason="Tests for wanted state of SSA IR, not current.")
def test_memory_array():
src = """
contract MemArray {
struct A {
uint val1;
uint val2;
}
function test_array() internal {
A[] memory a= new A[](4);
// Create REF_0 -> a_1[2]
accept_array_entry(a[2]);
// Create REF_1 -> a_1[3]
accept_array_entry(a[3]);
A memory alocal;
accept_array_entry(alocal);
}
// val_1 = ϕ(val_0, REF_0, REF_1, alocal_1)
// val_0 is an unknown external value
function accept_array_entry(A memory val) public returns (uint) {
uint zero = 0;
b(zero);
// Create REF_2 -> val_1.val1
return b(val.val1);
}
function b(uint arg) public returns (uint){
// arg_1 = ϕ(arg_0, zero_1, REF_2)
return arg + 1;
}
}"""
with slither_from_source(src) as slither:
c = slither.contracts[0]
ftest_array, faccept, fb = c.functions
# Locate REF_0/REF_1/alocal (they are all args to the call)
accept_args = [x.arguments[0] for x in get_ssa_of_type(ftest_array, InternalCall)]
# Check entrypoint of accept_array_entry, it should contain a phi-node
# of expected rvalues
[phi_entry_accept] = get_ssa_of_type(faccept.entry_point, Phi)
for arg in accept_args:
assert arg in phi_entry_accept.rvalues
# NOTE(hbrodin): There should be an additional val_0 in the phi-node.
# That additional val_0 indicates an external caller of this function.
assert len(phi_entry_accept.rvalues) == len(accept_args) + 1
# Args used to invoke b
b_args = [x.arguments[0] for x in get_ssa_of_type(faccept, InternalCall)]
# Check entrypoint of B, it should contain a phi-node of expected
# rvalues
[phi_entry_b] = get_ssa_of_type(fb.entry_point, Phi)
for arg in b_args:
assert arg in phi_entry_b.rvalues
# NOTE(hbrodin): There should be an additional arg_0 (see comment about phi_entry_accept).
assert len(phi_entry_b.rvalues) == len(b_args) + 1
@pytest.mark.xfail(reason="Tests for wanted state of SSA IR, not current.")
def test_storage_array():
src = """
contract StorageArray {
struct A {
uint val1;
uint val2;
}
// NOTE(hbrodin): a is never written, should only become a_0. Same for astorage (astorage_0). Phi-nodes at entry
// should only add new versions of a state variable if it is actually written.
A[] a;
A astorage;
function test_array() internal {
accept_array_entry(a[2]);
accept_array_entry(a[3]);
accept_array_entry(astorage);
}
function accept_array_entry(A storage val) internal returns (uint) {
// val is either a[2], a[3] or astorage_0. Ideally this could be identified.
uint five = 5;
// NOTE(hbrodin): If the following line is enabled, there would ideally be a phi-node representing writes
// to either a or astorage.
//val.val2 = 4;
b(five);
return b(val.val1);
}
function b(uint value) public returns (uint){
// Expect a phi-node at the entrypoint
// value_1 = ϕ(value_0, five_0, REF_x), where REF_x is the reference to val.val1 in accept_array_entry.
return value + 1;
}
}"""
with slither_from_source(src) as slither:
c = slither.contracts[0]
_dump_functions(c)
ftest, faccept, fb = c.functions
# None of a/astorage is written so expect that there are no phi-nodes at entrypoint.
assert len(get_ssa_of_type(ftest.entry_point, Phi)) == 0
# Expect all references to start from index 0 (no writes)
assert all(x.variable_left.index == 0 for x in get_ssa_of_type(ftest, Index))
[phi_entry_accept] = get_ssa_of_type(faccept.entry_point, Phi)
assert len(phi_entry_accept.rvalues) == 3 # See comment in b above
[phi_entry_b] = get_ssa_of_type(fb.entry_point, Phi)
assert len(phi_entry_b.rvalues) == 3 # See comment in b above
@pytest.mark.skip(reason="Fails in current slither version. Fix in #1102.") @pytest.mark.skip(reason="Fails in current slither version. Fix in #1102.")
def test_issue_468(): def test_issue_468():
""" """

Loading…
Cancel
Save