|
|
@ -6,18 +6,19 @@ |
|
|
|
- Response For a Class (RFC) is a metric that measures the number of unique method calls within a class. |
|
|
|
- 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. |
|
|
|
- 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. |
|
|
|
- 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: |
|
|
|
Not implemented: |
|
|
|
- Lack of Cohesion of Methods (LCOM) is a metric that measures the lack of cohesion in methods. |
|
|
|
- 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. |
|
|
|
- Weighted Methods per Class (WMC) is a metric that measures the complexity of a class. |
|
|
|
- Coupling Between Object Classes (CBO) is a metric that measures the number of classes a class is coupled to. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
|
""" |
|
|
|
from typing import Tuple |
|
|
|
from typing import Tuple |
|
|
|
from slither.printers.abstract_printer import AbstractPrinter |
|
|
|
from slither.utils.colors import bold |
|
|
|
from slither.utils.myprettytable import make_pretty_table |
|
|
|
from slither.utils.myprettytable import make_pretty_table |
|
|
|
from slither.slithir.operations.high_level_call import HighLevelCall |
|
|
|
from slither.slithir.operations.high_level_call import HighLevelCall |
|
|
|
from slither.utils.colors import bold |
|
|
|
from slither.printers.abstract_printer import AbstractPrinter |
|
|
|
|
|
|
|
from slither.printers.summary.martin import compute_coupling |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compute_dit(contract, depth=0): |
|
|
|
def compute_dit(contract, depth=0): |
|
|
@ -95,37 +96,41 @@ def compute_metrics(contracts): |
|
|
|
for inherited in contracts |
|
|
|
for inherited in contracts |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for c in contracts: |
|
|
|
# We pass 0 for the 2nd arg (abstractness) because we only care about the coupling metrics (Ca and Ce) |
|
|
|
(state_variables, constants, immutables, public_getters) = count_variables(c) |
|
|
|
coupling = compute_coupling(contracts, 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for contract in contracts: |
|
|
|
|
|
|
|
(state_variables, constants, immutables, public_getters) = count_variables(contract) |
|
|
|
rfc = public_getters # add 1 for each public getter |
|
|
|
rfc = public_getters # add 1 for each public getter |
|
|
|
metrics1[c.name] = { |
|
|
|
metrics1[contract.name] = { |
|
|
|
"State variables": state_variables, |
|
|
|
"State variables": state_variables, |
|
|
|
"Constants": constants, |
|
|
|
"Constants": constants, |
|
|
|
"Immutables": immutables, |
|
|
|
"Immutables": immutables, |
|
|
|
} |
|
|
|
} |
|
|
|
metrics2[c.name] = { |
|
|
|
metrics2[contract.name] = { |
|
|
|
"Public": 0, |
|
|
|
"Public": 0, |
|
|
|
"External": 0, |
|
|
|
"External": 0, |
|
|
|
"Internal": 0, |
|
|
|
"Internal": 0, |
|
|
|
"Private": 0, |
|
|
|
"Private": 0, |
|
|
|
} |
|
|
|
} |
|
|
|
metrics3[c.name] = { |
|
|
|
metrics3[contract.name] = { |
|
|
|
"Mutating": 0, |
|
|
|
"Mutating": 0, |
|
|
|
"View": 0, |
|
|
|
"View": 0, |
|
|
|
"Pure": 0, |
|
|
|
"Pure": 0, |
|
|
|
} |
|
|
|
} |
|
|
|
metrics4[c.name] = { |
|
|
|
metrics4[contract.name] = { |
|
|
|
"External mutating": 0, |
|
|
|
"External mutating": 0, |
|
|
|
"No auth or onlyOwner": 0, |
|
|
|
"No auth or onlyOwner": 0, |
|
|
|
"No modifiers": 0, |
|
|
|
"No modifiers": 0, |
|
|
|
} |
|
|
|
} |
|
|
|
metrics5[c.name] = { |
|
|
|
metrics5[contract.name] = { |
|
|
|
"Ext calls": 0, |
|
|
|
"Ext calls": 0, |
|
|
|
"RFC": 0, |
|
|
|
"RFC": 0, |
|
|
|
"NOC": len(dependents[c.name]), |
|
|
|
"NOC": len(dependents[contract.name]), |
|
|
|
"DIT": compute_dit(c), |
|
|
|
"DIT": compute_dit(contract), |
|
|
|
|
|
|
|
"CBO": coupling[contract.name]["Dependents"] + coupling[contract.name]["Dependencies"], |
|
|
|
} |
|
|
|
} |
|
|
|
for func in c.functions: |
|
|
|
for func in contract.functions: |
|
|
|
if func.name == "constructor": |
|
|
|
if func.name == "constructor": |
|
|
|
continue |
|
|
|
continue |
|
|
|
pure = func.pure |
|
|
|
pure = func.pure |
|
|
@ -147,29 +152,33 @@ def compute_metrics(contracts): |
|
|
|
|
|
|
|
|
|
|
|
# convert irs to string with target function and contract name |
|
|
|
# convert irs to string with target function and contract name |
|
|
|
external_calls = [] |
|
|
|
external_calls = [] |
|
|
|
for h in high_level_calls: |
|
|
|
for high_level_call in high_level_calls: |
|
|
|
if hasattr(h.destination, "name"): |
|
|
|
if hasattr(high_level_call.destination, "name"): |
|
|
|
external_calls.append(f"{h.function_name}{h.destination.name}") |
|
|
|
external_calls.append( |
|
|
|
|
|
|
|
f"{high_level_call.function_name}{high_level_call.destination.name}" |
|
|
|
|
|
|
|
) |
|
|
|
else: |
|
|
|
else: |
|
|
|
external_calls.append(f"{h.function_name}{h.destination.type.type.name}") |
|
|
|
external_calls.append( |
|
|
|
|
|
|
|
f"{high_level_call.function_name}{high_level_call.destination.type.type.name}" |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
rfc += len(set(external_calls)) |
|
|
|
rfc += len(set(external_calls)) |
|
|
|
|
|
|
|
|
|
|
|
metrics2[c.name]["Public"] += 1 if public else 0 |
|
|
|
metrics2[contract.name]["Public"] += 1 if public else 0 |
|
|
|
metrics2[c.name]["External"] += 1 if external else 0 |
|
|
|
metrics2[contract.name]["External"] += 1 if external else 0 |
|
|
|
metrics2[c.name]["Internal"] += 1 if internal else 0 |
|
|
|
metrics2[contract.name]["Internal"] += 1 if internal else 0 |
|
|
|
metrics2[c.name]["Private"] += 1 if private else 0 |
|
|
|
metrics2[contract.name]["Private"] += 1 if private else 0 |
|
|
|
|
|
|
|
|
|
|
|
metrics3[c.name]["Mutating"] += 1 if mutating else 0 |
|
|
|
metrics3[contract.name]["Mutating"] += 1 if mutating else 0 |
|
|
|
metrics3[c.name]["View"] += 1 if view else 0 |
|
|
|
metrics3[contract.name]["View"] += 1 if view else 0 |
|
|
|
metrics3[c.name]["Pure"] += 1 if pure else 0 |
|
|
|
metrics3[contract.name]["Pure"] += 1 if pure else 0 |
|
|
|
|
|
|
|
|
|
|
|
metrics4[c.name]["External mutating"] += 1 if external_public_mutating else 0 |
|
|
|
metrics4[contract.name]["External mutating"] += 1 if external_public_mutating else 0 |
|
|
|
metrics4[c.name]["No auth or onlyOwner"] += 1 if external_no_auth else 0 |
|
|
|
metrics4[contract.name]["No auth or onlyOwner"] += 1 if external_no_auth else 0 |
|
|
|
metrics4[c.name]["No modifiers"] += 1 if external_no_modifiers else 0 |
|
|
|
metrics4[contract.name]["No modifiers"] += 1 if external_no_modifiers else 0 |
|
|
|
|
|
|
|
|
|
|
|
metrics5[c.name]["Ext calls"] += len(external_calls) |
|
|
|
metrics5[contract.name]["Ext calls"] += len(external_calls) |
|
|
|
metrics5[c.name]["RFC"] = rfc |
|
|
|
metrics5[contract.name]["RFC"] = rfc |
|
|
|
|
|
|
|
|
|
|
|
return metrics1, metrics2, metrics3, metrics4, metrics5 |
|
|
|
return metrics1, metrics2, metrics3, metrics4, metrics5 |
|
|
|
|
|
|
|
|
|
|
@ -213,7 +222,7 @@ def no_auth(func) -> bool: |
|
|
|
|
|
|
|
|
|
|
|
class CKMetrics(AbstractPrinter): |
|
|
|
class CKMetrics(AbstractPrinter): |
|
|
|
ARGUMENT = "ck" |
|
|
|
ARGUMENT = "ck" |
|
|
|
HELP = "Computes the CK complexity metrics for each contract" |
|
|
|
HELP = "Chidamber and Kemerer (CK) complexity metrics and related function attributes" |
|
|
|
|
|
|
|
|
|
|
|
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#ck" |
|
|
|
WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#ck" |
|
|
|
|
|
|
|
|
|
|
@ -246,8 +255,12 @@ class CKMetrics(AbstractPrinter): |
|
|
|
table3 = make_pretty_table(["Contract", *keys], metrics4, True) |
|
|
|
table3 = make_pretty_table(["Contract", *keys], metrics4, True) |
|
|
|
txt += str(table3) + "\n" |
|
|
|
txt += str(table3) + "\n" |
|
|
|
|
|
|
|
|
|
|
|
# metrics5: ext calls and rfc |
|
|
|
# metrics5: ext calls and ck metrics |
|
|
|
txt += bold("\nExt calls and RFC\n") |
|
|
|
txt += bold("\nExternal calls and CK Metrics:\n") |
|
|
|
|
|
|
|
txt += bold("Response For a Class (RFC)\n") |
|
|
|
|
|
|
|
txt += bold("Number of Children (NOC)\n") |
|
|
|
|
|
|
|
txt += bold("Depth of Inheritance Tree (DIT)\n") |
|
|
|
|
|
|
|
txt += bold("Coupling Between Object Classes (CBO)\n") |
|
|
|
keys = list(metrics5[self.contracts[0].name].keys()) |
|
|
|
keys = list(metrics5[self.contracts[0].name].keys()) |
|
|
|
table4 = make_pretty_table(["Contract", *keys], metrics5, False) |
|
|
|
table4 = make_pretty_table(["Contract", *keys], metrics5, False) |
|
|
|
txt += str(table4) + "\n" |
|
|
|
txt += str(table4) + "\n" |
|
|
|