Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make type information of NewArray more precise #1784

Merged
merged 9 commits into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 10 additions & 19 deletions slither/core/expressions/new_array.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,23 @@
from typing import Union, TYPE_CHECKING

from typing import TYPE_CHECKING

from slither.core.expressions.expression import Expression
from slither.core.solidity_types.type import Type

if TYPE_CHECKING:
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
from slither.core.solidity_types.array_type import ArrayType


class NewArray(Expression):

# note: dont conserve the size of the array if provided
def __init__(
self, depth: int, array_type: Union["TypeAliasTopLevel", "ElementaryType"]
) -> None:
def __init__(self, array_type: "ArrayType") -> None:
super().__init__()
assert isinstance(array_type, Type)
self._depth: int = depth
self._array_type: Type = array_type
# pylint: disable=import-outside-toplevel
from slither.core.solidity_types.array_type import ArrayType

@property
def array_type(self) -> Type:
return self._array_type
assert isinstance(array_type, ArrayType)
self._array_type = array_type

@property
def depth(self) -> int:
return self._depth
def array_type(self) -> "ArrayType":
return self._array_type

def __str__(self):
return "new " + str(self._array_type) + "[]" * self._depth
return "new " + str(self._array_type)
2 changes: 1 addition & 1 deletion slither/slithir/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]) -> Union[Call,
return op

if isinstance(ins.ori, TmpNewArray):
n = NewArray(ins.ori.depth, ins.ori.array_type, ins.lvalue)
n = NewArray(ins.ori.array_type, ins.lvalue)
n.set_expression(ins.expression)
return n

Expand Down
2 changes: 1 addition & 1 deletion slither/slithir/operations/init_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __str__(self):
def convert(elem):
if isinstance(elem, (list,)):
return str([convert(x) for x in elem])
return str(elem)
return f"{elem}({elem.type})"

init_values = convert(self.init_values)
return f"{self.lvalue}({self.lvalue.type}) = {init_values}"
20 changes: 7 additions & 13 deletions slither/slithir/operations/new_array.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from typing import List, Union, TYPE_CHECKING
from slither.slithir.operations.lvalue import OperationWithLValue

from slither.core.solidity_types.array_type import ArrayType
from slither.slithir.operations.call import Call
from slither.core.solidity_types.type import Type
from slither.slithir.operations.lvalue import OperationWithLValue

if TYPE_CHECKING:
from slither.core.solidity_types.type_alias import TypeAliasTopLevel
from slither.slithir.variables.constant import Constant
from slither.slithir.variables.temporary import TemporaryVariable
from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA
Expand All @@ -13,29 +13,23 @@
class NewArray(Call, OperationWithLValue):
def __init__(
self,
depth: int,
array_type: "TypeAliasTopLevel",
array_type: "ArrayType",
lvalue: Union["TemporaryVariableSSA", "TemporaryVariable"],
) -> None:
super().__init__()
assert isinstance(array_type, Type)
self._depth = depth
assert isinstance(array_type, ArrayType)
self._array_type = array_type

self._lvalue = lvalue

@property
def array_type(self) -> "TypeAliasTopLevel":
def array_type(self) -> "ArrayType":
return self._array_type

@property
def read(self) -> List["Constant"]:
return self._unroll(self.arguments)

@property
def depth(self) -> int:
return self._depth

def __str__(self):
args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.array_type}{'[]' * self.depth}({','.join(args)})"
return f"{self.lvalue} = new {self.array_type}({','.join(args)})"
8 changes: 1 addition & 7 deletions slither/slithir/tmp_operations/tmp_new_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
class TmpNewArray(OperationWithLValue):
def __init__(
self,
depth: int,
array_type: Type,
lvalue: TemporaryVariable,
) -> None:
super().__init__()
assert isinstance(array_type, Type)
self._depth = depth
self._array_type = array_type
self._lvalue = lvalue

Expand All @@ -24,9 +22,5 @@ def array_type(self) -> Type:
def read(self):
return []

@property
def depth(self) -> int:
return self._depth

def __str__(self):
return f"{self.lvalue} = new {self.array_type}{'[]' * self._depth}"
return f"{self.lvalue} = new {self.array_type}"
3 changes: 1 addition & 2 deletions slither/slithir/utils/ssa.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,10 +789,9 @@ def copy_ir(ir: Operation, *instances) -> Operation:
variable_right = get_variable(ir, lambda x: x.variable_right, *instances)
return Member(variable_left, variable_right, lvalue)
if isinstance(ir, NewArray):
depth = ir.depth
array_type = ir.array_type
lvalue = get_variable(ir, lambda x: x.lvalue, *instances)
new_ir = NewArray(depth, array_type, lvalue)
new_ir = NewArray(array_type, lvalue)
new_ir.arguments = get_rec_values(ir, lambda x: x.arguments, *instances)
return new_ir
if isinstance(ir, NewElementaryType):
Expand Down
34 changes: 3 additions & 31 deletions slither/solc_parsing/expressions/expression_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,37 +559,9 @@ def parse_expression(expression: Dict, caller_context: CallerContextExpression)
type_name = children[0]

if type_name[caller_context.get_key()] == "ArrayTypeName":
depth = 0
while type_name[caller_context.get_key()] == "ArrayTypeName":
# Note: dont conserve the size of the array if provided
# We compute it directly
if is_compact_ast:
type_name = type_name["baseType"]
else:
type_name = type_name["children"][0]
depth += 1
if type_name[caller_context.get_key()] == "ElementaryTypeName":
if is_compact_ast:
array_type = ElementaryType(type_name["name"])
else:
array_type = ElementaryType(type_name["attributes"]["name"])
elif type_name[caller_context.get_key()] == "UserDefinedTypeName":
if is_compact_ast:
if "name" not in type_name:
name_type = type_name["pathNode"]["name"]
else:
name_type = type_name["name"]

array_type = parse_type(UnknownType(name_type), caller_context)
else:
array_type = parse_type(
UnknownType(type_name["attributes"]["name"]), caller_context
)
elif type_name[caller_context.get_key()] == "FunctionTypeName":
array_type = parse_type(type_name, caller_context)
else:
raise ParsingError(f"Incorrect type array {type_name}")
array = NewArray(depth, array_type)
array_type = parse_type(type_name, caller_context)
assert isinstance(array_type, ArrayType)
array = NewArray(array_type)
array.set_offset(src, caller_context.compilation_unit)
return array

Expand Down
3 changes: 1 addition & 2 deletions slither/visitors/expression/expression_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ def _post_member_access(self, expression: expressions.MemberAccess) -> None:

def _post_new_array(self, expression: expressions.NewArray) -> None:
array = str(expression.array_type)
depth = expression.depth
val = f"new {array}{'[]' * depth}"
val = f"new {array}"
set_val(expression, val)

def _post_new_contract(self, expression: expressions.NewContract) -> None:
Expand Down
2 changes: 1 addition & 1 deletion slither/visitors/slithir/expression_to_slithir.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ def _post_member_access(self, expression: MemberAccess) -> None:

def _post_new_array(self, expression: NewArray) -> None:
val = TemporaryVariable(self._node)
operation = TmpNewArray(expression.depth, expression.array_type, val)
operation = TmpNewArray(expression.array_type, val)
operation.set_expression(expression)
self._result.append(operation)
set_val(expression, val)
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/slithir/test_ssa_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
TemporaryVariableSSA,
)

from slither.core.solidity_types import ArrayType

# Directory of currently executing script. Will be used as basis for temporary file names.
SCRIPT_DIR = pathlib.Path(getsourcefile(lambda: 0)).parent # type:ignore

Expand Down Expand Up @@ -1048,3 +1050,31 @@ def test_issue_1748(slither_from_source):
operations = f.slithir_operations
assign_op = operations[0]
assert isinstance(assign_op, InitArray)


def test_issue_1776(slither_from_source):
source = """
contract Contract {
function foo() public returns (uint) {
uint[5][10][] memory arr = new uint[5][10][](2);
return 0;
}
}
"""
with slither_from_source(source) as slither:
c = slither.get_contract_from_name("Contract")[0]
f = c.functions[0]
operations = f.slithir_operations
new_op = operations[0]
lvalue = new_op.lvalue
lvalue_type = lvalue.type
assert isinstance(lvalue_type, ArrayType)
assert lvalue_type.is_dynamic
lvalue_type1 = lvalue_type.type
assert isinstance(lvalue_type1, ArrayType)
assert not lvalue_type1.is_dynamic
assert lvalue_type1.length_value.value == "10"
lvalue_type2 = lvalue_type1.type
assert isinstance(lvalue_type2, ArrayType)
assert not lvalue_type2.is_dynamic
assert lvalue_type2.length_value.value == "5"