Skip to content

Commit 4e9c217

Browse files
committed
Improve ir hashability and type correctness
1 parent f4575cf commit 4e9c217

File tree

6 files changed

+61
-62
lines changed

6 files changed

+61
-62
lines changed

pysoot/sootir/soot_class.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from dataclasses import dataclass
44

5+
from frozendict import frozendict
6+
57
from .soot_method import SootMethod
68
from . import convert_soot_attributes
79

@@ -18,10 +20,10 @@ class SootClass:
1820
] # TODO: replace with dataclass in Python 3.10
1921
name: str
2022
super_class: str
21-
interfaces: list[str]
22-
attrs: list[str]
23-
methods: list[SootMethod]
24-
fields: dict[str, tuple[list[str], str]]
23+
interfaces: tuple[str, ...]
24+
attrs: tuple[str, ...]
25+
methods: tuple[SootMethod, ...]
26+
fields: frozendict[str, tuple[tuple[str], str]]
2527

2628
def __str__(self):
2729
tstr = "//" + repr(self) + "\n"
@@ -73,7 +75,7 @@ def from_ir(ir_class):
7375
fields = {}
7476
for field in ir_class.getFields():
7577
fields[str(field.getName())] = (
76-
convert_soot_attributes(field.getModifiers()),
78+
tuple(convert_soot_attributes(field.getModifiers())),
7779
str(field.getType()),
7880
)
7981

@@ -83,5 +85,5 @@ def from_ir(ir_class):
8385
else:
8486
super_class = ""
8587
return SootClass(
86-
class_name, super_class, interface_names, attrs, methods, fields
88+
class_name, super_class, tuple(interface_names), tuple(attrs), tuple(methods), frozendict(fields)
8789
)

pysoot/sootir/soot_expr.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass
4-
from typing import Any
54

65
from .soot_value import SootValue
76

@@ -152,7 +151,7 @@ def from_ir(type_, expr_name, ir_subvalue):
152151
class SootNewMultiArrayExpr(SootExpr):
153152
__slots__ = ["base_type", "sizes"] # TODO: replace with dataclass in Python 3.10
154153
base_type: str
155-
sizes: Any
154+
sizes: tuple[SootValue, ...]
156155

157156
def __str__(self):
158157
return "new %s%s" % (
@@ -165,7 +164,7 @@ def from_ir(type_, expr_name, ir_subvalue):
165164
return SootNewMultiArrayExpr(
166165
type_,
167166
str(ir_subvalue.getBaseType()),
168-
(SootValue.from_ir(size) for size in ir_subvalue.getSizes()),
167+
tuple(SootValue.from_ir(size) for size in ir_subvalue.getSizes()),
169168
)
170169

171170

@@ -195,7 +194,7 @@ def __str__(self):
195194
@staticmethod
196195
def from_ir(type_, expr_name, ir_subvalue):
197196
return SootPhiExpr(
198-
type_, (SootValue.from_ir(v.getValue()) for v in ir_subvalue.getArgs())
197+
type_, tuple(SootValue.from_ir(v.getValue()) for v in ir_subvalue.getArgs())
199198
)
200199

201200

@@ -211,8 +210,8 @@ class SootInvokeExpr(SootExpr):
211210
] # TODO: replace with dataclass in Python 3.10
212211
class_name: str
213212
method_name: str
214-
method_params: Any
215-
args: Any
213+
method_params: tuple[str, ...]
214+
args: tuple[SootValue, ...]
216215

217216
def __str__(self):
218217
return "%s.%s(%s)]" % (
@@ -229,7 +228,7 @@ def list_to_arg_str(args):
229228
@dataclass(unsafe_hash=True)
230229
class SootVirtualInvokeExpr(SootInvokeExpr):
231230
__slots__ = ["base"] # TODO: replace with dataclass in Python 3.10
232-
base: Any
231+
base: SootValue
233232

234233
def __str__(self):
235234
return "%s.%s(%s) [virtualinvoke %s" % (
@@ -261,8 +260,9 @@ class SootDynamicInvokeExpr(SootInvokeExpr):
261260
"bootstrap_method",
262261
"bootstrap_args",
263262
] # TODO: replace with dataclass in Python 3.10
264-
bootstrap_method: Any
265-
bootstrap_args: Any
263+
# TODO: bootstrap_method and bootstrap_args are not implemented yet
264+
bootstrap_method: None
265+
bootstrap_args: None
266266

267267
@staticmethod
268268
def from_ir(type_, expr_name, ir_expr):
@@ -280,7 +280,7 @@ def from_ir(type_, expr_name, ir_expr):
280280
class_name=class_name,
281281
method_name=method_name,
282282
method_params=method_params,
283-
args=method_args,
283+
args=args,
284284
bootstrap_method=bootstrap_method,
285285
bootstrap_args=bootstrap_args,
286286
)
@@ -289,7 +289,7 @@ def from_ir(type_, expr_name, ir_expr):
289289
@dataclass(unsafe_hash=True)
290290
class SootInterfaceInvokeExpr(SootInvokeExpr):
291291
__slots__ = ["base"] # TODO: replace with dataclass in Python 3.10
292-
base: Any
292+
base: SootValue
293293

294294
def __str__(self):
295295
return "%s.%s(%s) [interfaceinvoke %s" % (
@@ -303,7 +303,7 @@ def __str__(self):
303303
def from_ir(type_, expr_name, ir_expr):
304304
args = tuple([SootValue.from_ir(arg) for arg in ir_expr.getArgs()])
305305
called_method = ir_expr.getMethod()
306-
params = tuple([str(param) for param in called_method.getParameterTypes()])
306+
params = tuple(str(param) for param in called_method.getParameterTypes())
307307

308308
return SootInterfaceInvokeExpr(
309309
type=type_,
@@ -318,7 +318,7 @@ def from_ir(type_, expr_name, ir_expr):
318318
@dataclass(unsafe_hash=True)
319319
class SootSpecialInvokeExpr(SootInvokeExpr):
320320
__slots__ = ["base"] # TODO: replace with dataclass in Python 3.10
321-
base: Any
321+
base: SootValue
322322

323323
def __str__(self):
324324
return "%s.%s(%s) [specialinvoke %s" % (
@@ -330,9 +330,9 @@ def __str__(self):
330330

331331
@staticmethod
332332
def from_ir(type_, expr_name, ir_expr):
333-
args = tuple([SootValue.from_ir(arg) for arg in ir_expr.getArgs()])
333+
args = tuple(SootValue.from_ir(arg) for arg in ir_expr.getArgs())
334334
called_method = ir_expr.getMethod()
335-
params = tuple([str(param) for param in called_method.getParameterTypes()])
335+
params = tuple(str(param) for param in called_method.getParameterTypes())
336336

337337
return SootSpecialInvokeExpr(
338338
type=type_,
@@ -357,9 +357,9 @@ def __str__(self):
357357

358358
@staticmethod
359359
def from_ir(type_, expr_name, ir_expr):
360-
args = tuple([SootValue.from_ir(arg) for arg in ir_expr.getArgs()])
360+
args = tuple(SootValue.from_ir(arg) for arg in ir_expr.getArgs())
361361
called_method = ir_expr.getMethod()
362-
params = tuple([str(param) for param in called_method.getParameterTypes()])
362+
params = tuple(str(param) for param in called_method.getParameterTypes())
363363

364364
return SootStaticInvokeExpr(
365365
type=type_,

pysoot/sootir/soot_method.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from dataclasses import dataclass
55
from functools import lru_cache
66

7+
from frozendict import frozendict
78
from jpype.types import JClass
89

910
from .soot_block import SootBlock
@@ -31,11 +32,11 @@ class SootMethod:
3132
exceptions: tuple[str, ...]
3233
blocks: tuple[SootBlock, ...]
3334
params: tuple[str, ...]
34-
basic_cfg: defaultdict[SootBlock, list[SootBlock]]
35-
exceptional_preds: defaultdict[SootBlock, list[SootBlock]]
35+
basic_cfg: frozendict[SootBlock, tuple[SootBlock]]
36+
exceptional_preds: frozendict[SootBlock, tuple[SootBlock]]
3637

3738
@property
38-
# @lru_cache(maxsize=1)
39+
@lru_cache(maxsize=1)
3940
def block_by_label(self):
4041
return {b.label: b for b in self.blocks}
4142

@@ -109,13 +110,13 @@ def from_ir(class_name, ir_method):
109110
if "Assign" in ir_stmt.getClass().getSimpleName():
110111
ir_expr = ir_stmt.getRightOp()
111112
if "Phi" in ir_expr.getClass().getSimpleName():
112-
values = [
113+
values = tuple(
113114
(
114115
SootValue.from_ir(v.getValue()),
115116
stmt_to_block_idx[v.getUnit()],
116117
)
117118
for v in ir_expr.getArgs()
118-
]
119+
)
119120

120121
phi_expr = SootValue.IREXPR_TO_EXPR[ir_expr]
121122
phi_expr.values = values
@@ -136,6 +137,6 @@ def from_ir(class_name, ir_method):
136137
attrs=tuple(attrs),
137138
exceptions=tuple(exceptions),
138139
blocks=tuple(blocks),
139-
basic_cfg=basic_cfg,
140-
exceptional_preds=exceptional_preds,
140+
basic_cfg=frozendict({k: tuple(v) for k, v in basic_cfg.items()}),
141+
exceptional_preds=frozendict({k: tuple(v) for k, v in exceptional_preds.items()}),
141142
)

pysoot/sootir/soot_statement.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass
4-
from typing import Any
54

65
from frozendict import frozendict
76

@@ -197,7 +196,7 @@ class LookupSwitchStmt(SootStmt):
197196
"default_target",
198197
] # TODO: replace with dataclass in Python 3.10
199198
key: SootValue
200-
lookup_values_and_targets: Any
199+
lookup_values_and_targets: frozendict[int, SootStmt]
201200
default_target: SootStmt
202201

203202
def __str__(self):
@@ -214,11 +213,11 @@ def from_ir(label, offset, ir_stmt, stmt_map=None):
214213
lookup_values_and_targets = frozendict({k: v for k, v in zip(lookup_values, targets)})
215214

216215
return LookupSwitchStmt(
217-
label,
218-
offset,
219-
SootValue.from_ir(ir_stmt.getKey()),
220-
lookup_values_and_targets,
221-
stmt_map[ir_stmt.getDefaultTarget()],
216+
label=label,
217+
offset=offset,
218+
key=SootValue.from_ir(ir_stmt.getKey()),
219+
lookup_values_and_targets=lookup_values_and_targets,
220+
default_target=stmt_map[ir_stmt.getDefaultTarget()],
222221
)
223222

224223

@@ -233,10 +232,10 @@ class TableSwitchStmt(SootStmt):
233232
"default_target",
234233
]
235234
key: SootValue
236-
low_index: Any
237-
high_index: Any
238-
targets: Any
239-
lookup_values_and_targets: Any
235+
low_index: int
236+
high_index: int
237+
targets: tuple[SootStmt, ...]
238+
lookup_values_and_targets: frozendict[int, SootStmt]
240239
default_target: SootStmt
241240

242241
def __str__(self):
@@ -248,21 +247,21 @@ def __str__(self):
248247

249248
@staticmethod
250249
def from_ir(label, offset, ir_stmt, stmt_map=None):
251-
targets = [stmt_map[t] for t in ir_stmt.getTargets()]
250+
targets = tuple(stmt_map[t] for t in ir_stmt.getTargets())
252251
dict_iter = zip(
253252
range(ir_stmt.getLowIndex(), ir_stmt.getHighIndex() + 1), targets
254253
)
255254
lookup_values_and_targets = {k: v for k, v in dict_iter}
256255

257256
return TableSwitchStmt(
258-
label,
259-
offset,
260-
SootValue.from_ir(ir_stmt.getKey()),
261-
ir_stmt.getLowIndex(),
262-
ir_stmt.getHighIndex(),
263-
tuple(targets),
264-
stmt_map[ir_stmt.getDefaultTarget()],
265-
frozendict(lookup_values_and_targets),
257+
label=label,
258+
offset=offset,
259+
key=SootValue.from_ir(ir_stmt.getKey()),
260+
low_index=int(ir_stmt.getLowIndex()),
261+
high_index=int(ir_stmt.getHighIndex()),
262+
targets=tuple(targets),
263+
default_target=stmt_map[ir_stmt.getDefaultTarget()],
264+
lookup_values_and_targets=frozendict(lookup_values_and_targets),
266265
)
267266

268267

pysoot/sootir/soot_value.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass
4-
from typing import Any
5-
6-
from jpype.types import JDouble, JFloat, JInt, JLong, JString
74

85

96
@dataclass(unsafe_hash=True)
@@ -53,8 +50,8 @@ def from_ir(type_, ir_value):
5350
@dataclass(unsafe_hash=True)
5451
class SootArrayRef(SootValue):
5552
__slots__ = ["base", "index"] # TODO: replace with dataclass in Python 3.10
56-
base: Any
57-
index: Any
53+
base: SootValue
54+
index: SootValue
5855

5956
def __str__(self):
6057
return "%s[%s]" % (self.base, self.index)
@@ -83,14 +80,14 @@ def from_ir(type_, ir_value):
8380
@dataclass(unsafe_hash=True)
8481
class SootParamRef(SootValue):
8582
__slots__ = ["index"] # TODO: replace with dataclass in Python 3.10
86-
index: Any
83+
index: int
8784

8885
def __str__(self):
8986
return "@parameter%d[%s]" % (self.index, self.type)
9087

9188
@staticmethod
9289
def from_ir(type_, ir_value):
93-
return SootParamRef(type_, ir_value.getIndex())
90+
return SootParamRef(type_, int(ir_value.getIndex()))
9491

9592

9693
@dataclass(unsafe_hash=True)
@@ -108,7 +105,7 @@ def from_ir(type_, ir_value):
108105
@dataclass(unsafe_hash=True)
109106
class SootStaticFieldRef(SootValue):
110107
__slots__ = ["field"] # TODO: replace with dataclass in Python 3.10
111-
field: Any
108+
field: tuple[str, str]
112109

113110
def __str__(self):
114111
return "StaticFieldRef %s" % (self.field,)
@@ -122,7 +119,7 @@ def from_ir(type_, ir_value):
122119
@dataclass(unsafe_hash=True)
123120
class SootInstanceFieldRef(SootValue):
124121
__slots__ = ["base", "field"] # TODO: replace with dataclass in Python 3.10
125-
base: Any
122+
base: SootValue
126123
field: tuple[str, str]
127124

128125
def __str__(self):
@@ -141,14 +138,14 @@ def from_ir(type_, ir_value):
141138
@dataclass(unsafe_hash=True)
142139
class SootClassConstant(SootValue):
143140
__slots__ = ["value"] # TODO: replace with dataclass in Python 3.10
144-
value: Any
141+
value: str
145142

146143
def __str__(self):
147144
return str(self.value)
148145

149146
@staticmethod
150147
def from_ir(type_, ir_value):
151-
return SootClassConstant(type_, ir_value)
148+
return SootClassConstant(type_, str(ir_value.getValue()))
152149

153150

154151
@dataclass(unsafe_hash=True)

tests/test_pysoot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def test_exceptions1(self):
8181
lifter = Lifter(jar)
8282

8383
mm = lifter.classes["exceptions1.Main"].methods[1]
84-
assert mm.basic_cfg[mm.blocks[0]] == [mm.blocks[1], mm.blocks[2]]
84+
assert mm.basic_cfg[mm.blocks[0]] == (mm.blocks[1], mm.blocks[2])
8585
assert len(mm.exceptional_preds) == 1
8686

8787
preds = mm.exceptional_preds[mm.blocks[18]]

0 commit comments

Comments
 (0)