Skip to content

Commit

Permalink
refactor: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
devtooligan committed Jul 6, 2023
1 parent c175d52 commit 92b6b88
Showing 1 changed file with 121 additions and 4 deletions.
125 changes: 121 additions & 4 deletions slither/printers/summary/halstead.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,136 @@
"""
import math
from dataclasses import dataclass, field
from typing import Tuple, List, Dict
from collections import OrderedDict
from slither.core.declarations import (
Contract,
Pragma,
Import,
Function,
Modifier,
)
from slither.printers.abstract_printer import AbstractPrinter
from slither.slithir.variables.temporary import TemporaryVariable
from slither.utils.myprettytable import make_pretty_table
from slither.utils.upgradeability import encode_ir_for_halstead
from slither.utils.myprettytable import make_pretty_table, MyPrettyTable
from slither.utils.upgradeability import encode_ir_for_halstead # TODO: Add to slither/utils/halstead

class HalsteadContractMetrics:
"""Class to hold the Halstead metrics for a single contract."""
# TODO: Add to slither/utils/halstead
contract: Contract
all_operators: List[str] = []
all_operands: List[str] = []
n1: int
n2: int
N1: int
N2: int
n: int
N: int
S: float
V: float
D: float
E: float
T: float
B: float

def compute_halstead(contracts: list) -> tuple:
def __post_init__(self):
if (len(self.all_operators == 0)):
self.populate_operators_and_operands()
self.compute_metrics()

def populate_operators_and_operands(self):
"""Populate the operators and operands lists."""
operators = []
operands = []
for func in self.contract.functions:
for node in func.nodes:
for operation in node.irs:
# use operation.expression.type to get the unique operator type
encoded_operator = encode_ir_for_halstead(operation)
operators.append(encoded_operator)

# use operation.used to get the operands of the operation ignoring the temporary variables
operands.extend([
op for op in operation.used if not isinstance(op, TemporaryVariable)
])
self.all_operators.extend(operators)
self.all_operands.extend(operands)

def compute_metrics(self, all_operators):
"""Compute the Halstead metrics."""
self.n1 = len(set(self.all_operators))
self.n2 = len(set(self.all_operands))
self.N1 = len(self.all_operators)
self.N2 = len(self.all_operands)
self.n = self.n1 + self.n2
self.N = self.N1 + self.N2
self.S = self.n1 * math.log2(self.n1) + self.n2 * math.log2(self.n2)
self.V = self.N * math.log2(self.n)
self.D = (self.n1 / 2) * (self.N2 / self.n2)
self.E = self.D * self.V
self.T = self.E / 18
self.B = (self.E ** (2 / 3)) / 3000

@dataclass
class MetricsInfo:
title: str
keys: List[str]
pretty_table: MyPrettyTable
txt: str


class HalsteadMetrics:
"""Class to hold the Halstead metrics for all contracts and methods for reporting."""
contracts: List[Contract]
contract_metrics: Dict[Contract, HalsteadContractMetrics]
title: str = "Halstead complexity metrics"
core: MetricsInfo
extended1: MetricsInfo
extended2: MetricsInfo

def __post_init__(self):
for contract in self.contracts:
self.contract_metrics[contract.name] = HalsteadContractMetrics(contract)

if len(self.contracts) > 1:
self.contract_metrics["ALL CONTRACTS"] = self.get_combined_metrics()


self.update_core_metrics(contract)
extended1 = self.update_extended_metrics1(contract)
extended2 = self.update_extended_metrics2(contract)
self.update_extended_metrics1(contract)
self.update_extended_metrics2(contract)

pretty_table = make_pretty_table(
["Contract", *keys],
data=all_data,

)

txt = "f"

def update_core_metrics(self):
"""Return the core metrics section."""
title = "Core metrics"
keys = [
"Total Operators",
"Unique Operators",
"Total Operands",
"Unique Operands",
]

return MetricsInfo(title=title, keys=keys, pretty_table=pretty_table, txt=txt)


def compute_halstead(contracts: list) -> Tuple[Dict, Dict, Dict]:
"""Used to compute the Halstead complexity metrics for a list of contracts.
Args:
contracts: list of contracts.
Returns:
Halstead metrics as a tuple of two OrderedDicts (core_metrics, extended_metrics)
Halstead metrics as a tuple of three OrderedDicts (core_metrics, extended_metrics1, extended_metrics2)
which each contain one key per contract. The value of each key is a dict of metrics.
In addition to one key per contract, there is a key for "ALL CONTRACTS" that contains
Expand Down

0 comments on commit 92b6b88

Please sign in to comment.