Skip to content

Commit

Permalink
Merge pull request #1895 from crytic/ck-printer
Browse files Browse the repository at this point in the history
feat: ck printer
  • Loading branch information
montyly authored Sep 15, 2023
2 parents 46630b7 + bcbe4ff commit 1db0d01
Show file tree
Hide file tree
Showing 13 changed files with 1,145 additions and 186 deletions.
2 changes: 1 addition & 1 deletion scripts/ci_test_printers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
cd tests/e2e/solc_parsing/test_data/compile/ || exit

# Do not test the evm printer,as it needs a refactoring
ALL_PRINTERS="cfg,constructor-calls,contract-summary,data-dependency,echidna,function-id,function-summary,modifiers,call-graph,human-summary,inheritance,inheritance-graph,loc,slithir,slithir-ssa,vars-and-auth,require,variable-order,declaration"
ALL_PRINTERS="cfg,constructor-calls,contract-summary,data-dependency,echidna,function-id,function-summary,modifiers,call-graph,halstead,human-summary,inheritance,inheritance-graph,loc,martin,slithir,slithir-ssa,vars-and-auth,require,variable-order,declaration,ck"

# Only test 0.5.17 to limit test time
for file in *0.5.17-compact.zip; do
Expand Down
6 changes: 6 additions & 0 deletions slither/core/variables/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
from .state_variable import StateVariable
from .variable import Variable
from .local_variable_init_from_tuple import LocalVariableInitFromTuple
from .local_variable import LocalVariable
from .top_level_variable import TopLevelVariable
from .event_variable import EventVariable
from .function_type_variable import FunctionTypeVariable
from .structure_variable import StructureVariable
4 changes: 3 additions & 1 deletion slither/core/variables/local_variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from slither.core.variables.variable import Variable
from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
from slither.core.solidity_types.elementary_type import ElementaryType

Expand Down Expand Up @@ -51,6 +50,9 @@ def is_storage(self) -> bool:
Returns:
(bool)
"""
# pylint: disable=import-outside-toplevel
from slither.core.solidity_types.array_type import ArrayType

if self.location == "memory":
return False
if self.location == "calldata":
Expand Down
3 changes: 3 additions & 0 deletions slither/printers/all_printers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from .summary.slithir import PrinterSlithIR
from .summary.slithir_ssa import PrinterSlithIRSSA
from .summary.human_summary import PrinterHumanSummary
from .summary.ck import CK
from .summary.halstead import Halstead
from .functions.cfg import CFG
from .summary.function_ids import FunctionIds
from .summary.variable_order import VariableOrder
Expand All @@ -21,3 +23,4 @@
from .summary.when_not_paused import PrinterWhenNotPaused
from .summary.declaration import Declaration
from .functions.dominator import Dominator
from .summary.martin import Martin
58 changes: 58 additions & 0 deletions slither/printers/summary/ck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
CK Metrics are a suite of six software metrics proposed by Chidamber and Kemerer in 1994.
These metrics are used to measure the complexity of a class.
https://en.wikipedia.org/wiki/Programming_complexity
- Response For a Class (RFC) is a metric that measures the number of unique method calls within a class.
- Number of Children (NOC) is a metric that measures the number of children a class has.
- Depth of Inheritance Tree (DIT) is a metric that measures the number of parent classes a class has.
- Coupling Between Object Classes (CBO) is a metric that measures the number of classes a class is coupled to.
Not implemented:
- Lack of Cohesion of Methods (LCOM) is a metric that measures the lack of cohesion in methods.
- Weighted Methods per Class (WMC) is a metric that measures the complexity of a class.
During the calculation of the metrics above, there are a number of other intermediate metrics that are calculated.
These are also included in the output:
- State variables: total number of state variables
- Constants: total number of constants
- Immutables: total number of immutables
- Public: total number of public functions
- External: total number of external functions
- Internal: total number of internal functions
- Private: total number of private functions
- Mutating: total number of state mutating functions
- View: total number of view functions
- Pure: total number of pure functions
- External mutating: total number of external mutating functions
- No auth or onlyOwner: total number of functions without auth or onlyOwner modifiers
- No modifiers: total number of functions without modifiers
- Ext calls: total number of external calls
"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.ck import CKMetrics
from slither.utils.output import Output


class CK(AbstractPrinter):
ARGUMENT = "ck"
HELP = "Chidamber and Kemerer (CK) complexity metrics and related function attributes"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#ck"

def output(self, _filename: str) -> Output:
if len(self.contracts) == 0:
return self.generate_output("No contract found")

ck = CKMetrics(self.contracts)

res = self.generate_output(ck.full_text)
res.add_pretty_table(ck.auxiliary1.pretty_table, ck.auxiliary1.title)
res.add_pretty_table(ck.auxiliary2.pretty_table, ck.auxiliary2.title)
res.add_pretty_table(ck.auxiliary3.pretty_table, ck.auxiliary3.title)
res.add_pretty_table(ck.auxiliary4.pretty_table, ck.auxiliary4.title)
res.add_pretty_table(ck.core.pretty_table, ck.core.title)
self.info(ck.full_text)

return res
49 changes: 49 additions & 0 deletions slither/printers/summary/halstead.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Halstead complexity metrics
https://en.wikipedia.org/wiki/Halstead_complexity_measures
12 metrics based on the number of unique operators and operands:
Core metrics:
n1 = the number of distinct operators
n2 = the number of distinct operands
N1 = the total number of operators
N2 = the total number of operands
Extended metrics1:
n = n1 + n2 # Program vocabulary
N = N1 + N2 # Program length
S = n1 * log2(n1) + n2 * log2(n2) # Estimated program length
V = N * log2(n) # Volume
Extended metrics2:
D = (n1 / 2) * (N2 / n2) # Difficulty
E = D * V # Effort
T = E / 18 seconds # Time required to program
B = (E^(2/3)) / 3000 # Number of delivered bugs
"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.halstead import HalsteadMetrics
from slither.utils.output import Output


class Halstead(AbstractPrinter):
ARGUMENT = "halstead"
HELP = "Computes the Halstead complexity metrics for each contract"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#halstead"

def output(self, _filename: str) -> Output:
if len(self.contracts) == 0:
return self.generate_output("No contract found")

halstead = HalsteadMetrics(self.contracts)

res = self.generate_output(halstead.full_text)
res.add_pretty_table(halstead.core.pretty_table, halstead.core.title)
res.add_pretty_table(halstead.extended1.pretty_table, halstead.extended1.title)
res.add_pretty_table(halstead.extended2.pretty_table, halstead.extended2.title)
self.info(halstead.full_text)

return res
32 changes: 32 additions & 0 deletions slither/printers/summary/martin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""
Robert "Uncle Bob" Martin - Agile software metrics
https://en.wikipedia.org/wiki/Software_package_metrics
Efferent Coupling (Ce): Number of contracts that the contract depends on
Afferent Coupling (Ca): Number of contracts that depend on a contract
Instability (I): Ratio of efferent coupling to total coupling (Ce / (Ce + Ca))
Abstractness (A): Number of abstract contracts / total number of contracts
Distance from the Main Sequence (D): abs(A + I - 1)
"""
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.martin import MartinMetrics
from slither.utils.output import Output


class Martin(AbstractPrinter):
ARGUMENT = "martin"
HELP = "Martin agile software metrics (Ca, Ce, I, A, D)"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#martin"

def output(self, _filename: str) -> Output:
if len(self.contracts) == 0:
return self.generate_output("No contract found")

martin = MartinMetrics(self.contracts)

res = self.generate_output(martin.full_text)
res.add_pretty_table(martin.core.pretty_table, martin.core.title)
self.info(martin.full_text)
return res
Loading

0 comments on commit 1db0d01

Please sign in to comment.