Improve YUL parsing (#987)

* Improve YUL parsing
- Add support for YUL top level function
(fix 899)
- Add function_language in the function definition, to determine if the
function is written in YUL or Solidity. This is now used when the
function of the contract are propagated in the inheritance (fix 969)
pull/992/head
Feist Josselin 3 years ago committed by GitHub
parent 1548c8217e
commit 95019e30b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      slither/core/declarations/contract.py
  2. 9
      slither/core/declarations/function.py
  3. 31
      slither/solc_parsing/yul/parse_yul.py
  4. BIN
      tests/ast-parsing/compile/yul-0.8.0-compact.zip
  5. BIN
      tests/ast-parsing/compile/yul-0.8.1-compact.zip
  6. BIN
      tests/ast-parsing/compile/yul-0.8.10-compact.zip
  7. BIN
      tests/ast-parsing/compile/yul-0.8.2-compact.zip
  8. BIN
      tests/ast-parsing/compile/yul-0.8.3-compact.zip
  9. BIN
      tests/ast-parsing/compile/yul-0.8.4-compact.zip
  10. BIN
      tests/ast-parsing/compile/yul-0.8.5-compact.zip
  11. BIN
      tests/ast-parsing/compile/yul-0.8.6-compact.zip
  12. BIN
      tests/ast-parsing/compile/yul-0.8.7-compact.zip
  13. BIN
      tests/ast-parsing/compile/yul-0.8.8-compact.zip
  14. BIN
      tests/ast-parsing/compile/yul-0.8.9-compact.zip
  15. 17
      tests/ast-parsing/yul-0.8.0.sol

@ -11,7 +11,7 @@ from slither.core.cfg.scope import Scope
from slither.core.solidity_types.type import Type
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.declarations.function import Function, FunctionType
from slither.core.declarations.function import Function, FunctionType, FunctionLanguage
from slither.utils.erc import (
ERC20_signatures,
ERC165_signatures,
@ -540,7 +540,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def available_elements_from_inheritances(
self,
elements: Dict[str, "Function"],
getter_available: Callable[["Contract"], List["Function"]],
getter_available: Callable[["Contract"], List["FunctionContract"]],
) -> Dict[str, "Function"]:
"""
@ -551,14 +551,16 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
# keep track of the contracts visited
# to prevent an ovveride due to multiple inheritance of the same contract
# A is B, C, D is C, --> the second C was already seen
inherited_elements: Dict[str, "Function"] = {}
inherited_elements: Dict[str, "FunctionContract"] = {}
accessible_elements = {}
contracts_visited = []
for father in self.inheritance_reverse:
functions: Dict[str, "Function"] = {
functions: Dict[str, "FunctionContract"] = {
v.full_name: v
for v in getter_available(father)
if v.contract not in contracts_visited
and v.function_language
!= FunctionLanguage.Yul # Yul functions are not propagated in the inheritance
}
contracts_visited.append(father)
inherited_elements.update(functions)

@ -104,6 +104,12 @@ def _filter_state_variables_written(expressions: List["Expression"]):
return ret
class FunctionLanguage(Enum):
Solidity = 0
Yul = 1
Vyper = 2
class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-public-methods
"""
Function class
@ -207,6 +213,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
# Assume we are analyzing Solidty by default
self.function_language: FunctionLanguage = FunctionLanguage.Solidity
###################################################################################
###################################################################################
# region General properties

@ -10,7 +10,9 @@ from slither.core.declarations import (
SolidityFunction,
Contract,
)
from slither.core.declarations.function import FunctionLanguage
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.expressions import (
Literal,
AssignmentOperation,
@ -353,7 +355,13 @@ def convert_yul_block(
def convert_yul_function_definition(
root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]
) -> YulNode:
func = FunctionContract(root.compilation_unit)
top_node_scope = node_scope
while not isinstance(top_node_scope, Function):
top_node_scope = top_node_scope.father
assert isinstance(top_node_scope, (FunctionTopLevel, FunctionContract))
func = type(top_node_scope)(root.compilation_unit)
func.function_language = FunctionLanguage.Yul
yul_function = YulFunction(func, root, ast, node_scope)
root.contract.add_function(func)
@ -707,6 +715,15 @@ def parse_yul_function_call(root: YulScope, node: YulNode, ast: Dict) -> Optiona
raise SlitherException(f"unexpected function call target type {str(type(ident.value))}")
def _check_for_state_variable_name(root: YulScope, potential_name: str) -> Optional[Identifier]:
root_function = root.function
if isinstance(root_function, FunctionContract):
var = root_function.contract.get_state_variable_from_name(potential_name)
if var:
return Identifier(var)
return None
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]:
name = ast["name"]
@ -739,17 +756,17 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
# check for magic suffixes
if name.endswith("_slot") or name.endswith(".slot"):
potential_name = name[:-5]
var = root.function.contract.get_state_variable_from_name(potential_name)
if var:
return Identifier(var)
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
var = root.function.get_local_variable_from_name(potential_name)
if var and var.is_storage:
return Identifier(var)
if name.endswith("_offset") or name.endswith(".offset"):
potential_name = name[:-7]
var = root.function.contract.get_state_variable_from_name(potential_name)
if var:
return Identifier(var)
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
raise SlitherException(f"unresolved reference to identifier {name}")

@ -50,3 +50,20 @@ contract C {
}
}
// function from https://docs.soliditylang.org/en/v0.8.0/assembly.html
function at(address _addr) view returns (bytes memory o_code) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(o_code, 0x20), 0, size)
}
}

Loading…
Cancel
Save