angr 9.2.138__py3-none-manylinux2014_x86_64.whl → 9.2.140__py3-none-manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (100) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +48 -21
  3. angr/analyses/calling_convention/fact_collector.py +59 -12
  4. angr/analyses/calling_convention/utils.py +2 -2
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +23 -4
  7. angr/analyses/decompiler/ail_simplifier.py +79 -53
  8. angr/analyses/decompiler/block_simplifier.py +0 -2
  9. angr/analyses/decompiler/callsite_maker.py +80 -14
  10. angr/analyses/decompiler/clinic.py +99 -80
  11. angr/analyses/decompiler/condition_processor.py +2 -2
  12. angr/analyses/decompiler/decompiler.py +19 -7
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  14. angr/analyses/decompiler/expression_narrower.py +1 -1
  15. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  16. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  17. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  18. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  19. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  20. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  21. angr/analyses/decompiler/optimization_passes/optimization_pass.py +21 -12
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +17 -9
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  24. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  25. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  26. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  27. angr/analyses/decompiler/presets/fast.py +2 -0
  28. angr/analyses/decompiler/presets/full.py +2 -0
  29. angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
  30. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +28 -9
  31. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  32. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  33. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  34. angr/analyses/decompiler/structuring/dream.py +28 -19
  35. angr/analyses/decompiler/structuring/phoenix.py +253 -89
  36. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  37. angr/analyses/decompiler/structuring/structurer_base.py +121 -46
  38. angr/analyses/decompiler/structuring/structurer_nodes.py +6 -1
  39. angr/analyses/decompiler/utils.py +60 -1
  40. angr/analyses/deobfuscator/api_obf_finder.py +13 -5
  41. angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
  42. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  43. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  44. angr/analyses/propagator/top_checker_mixin.py +6 -6
  45. angr/analyses/reaching_definitions/__init__.py +2 -1
  46. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  47. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  48. angr/analyses/reaching_definitions/function_handler.py +15 -2
  49. angr/analyses/reaching_definitions/rd_state.py +1 -37
  50. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  51. angr/analyses/s_propagator.py +129 -87
  52. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  53. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  54. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  55. angr/analyses/stack_pointer_tracker.py +36 -22
  56. angr/analyses/typehoon/simple_solver.py +45 -7
  57. angr/analyses/typehoon/typeconsts.py +18 -5
  58. angr/analyses/variable_recovery/engine_ail.py +1 -1
  59. angr/analyses/variable_recovery/engine_base.py +62 -67
  60. angr/analyses/variable_recovery/engine_vex.py +1 -1
  61. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  62. angr/block.py +69 -107
  63. angr/callable.py +14 -7
  64. angr/calling_conventions.py +81 -10
  65. angr/distributed/__init__.py +1 -1
  66. angr/engines/__init__.py +7 -8
  67. angr/engines/engine.py +3 -138
  68. angr/engines/failure.py +2 -2
  69. angr/engines/hook.py +2 -2
  70. angr/engines/light/engine.py +5 -10
  71. angr/engines/pcode/emulate.py +2 -2
  72. angr/engines/pcode/engine.py +2 -14
  73. angr/engines/pcode/lifter.py +2 -2
  74. angr/engines/procedure.py +2 -2
  75. angr/engines/soot/engine.py +2 -2
  76. angr/engines/soot/statements/switch.py +1 -1
  77. angr/engines/successors.py +123 -17
  78. angr/engines/syscall.py +2 -2
  79. angr/engines/unicorn.py +3 -3
  80. angr/engines/vex/heavy/heavy.py +3 -15
  81. angr/engines/vex/lifter.py +2 -2
  82. angr/engines/vex/light/light.py +2 -2
  83. angr/factory.py +4 -19
  84. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  85. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  86. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  87. angr/knowledge_plugins/labels.py +2 -2
  88. angr/knowledge_plugins/obfuscations.py +1 -0
  89. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  90. angr/sim_type.py +19 -17
  91. angr/state_plugins/plugin.py +19 -4
  92. angr/storage/memory_mixins/memory_mixin.py +1 -1
  93. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  94. angr/utils/ssa/__init__.py +119 -4
  95. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
  96. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/RECORD +100 -98
  97. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
  98. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
  99. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
  100. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,13 @@ from .if_ import IfSimplifier
12
12
  from .cascading_ifs import CascadingIfsRemover
13
13
  from .ifelse import IfElseFlattener
14
14
  from .loop import LoopSimplifier
15
- from .expr_folding import ExpressionCounter, ExpressionFolder, StoreStatementFinder, ExpressionLocation
15
+ from .expr_folding import (
16
+ ExpressionCounter,
17
+ ExpressionFolder,
18
+ StoreStatementFinder,
19
+ ExpressionLocation,
20
+ InterferenceChecker,
21
+ )
16
22
  from .cascading_cond_transformer import CascadingConditionTransformer
17
23
  from .switch_expr_simplifier import SwitchExpressionSimplifier
18
24
  from .switch_cluster_simplifier import SwitchClusterFinder, simplify_switch_clusters, simplify_lowered_switches
@@ -23,10 +29,17 @@ class RegionSimplifier(Analysis):
23
29
  Simplifies a given region.
24
30
  """
25
31
 
26
- def __init__(self, func, region, variable_kb=None, simplify_switches: bool = True, simplify_ifelse: bool = True):
32
+ def __init__(
33
+ self,
34
+ func,
35
+ region,
36
+ arg_vvars: set[int] | None = None,
37
+ simplify_switches: bool = True,
38
+ simplify_ifelse: bool = True,
39
+ ):
27
40
  self.func = func
28
41
  self.region = region
29
- self.variable_kb = variable_kb
42
+ self.arg_vvars = arg_vvars
30
43
  self._simplify_switches = simplify_switches
31
44
  self._should_simplify_ifelses = simplify_ifelse
32
45
 
@@ -54,7 +67,7 @@ class RegionSimplifier(Analysis):
54
67
  # Remove empty nodes again
55
68
  r = self._remove_empty_nodes(r)
56
69
 
57
- if self.variable_kb is not None:
70
+ if self.arg_vvars is not None:
58
71
  # Fold expressions that are only used once into their use sites
59
72
  r = self._fold_oneuse_expressions(r)
60
73
  r = self._remove_empty_nodes(r)
@@ -88,8 +101,8 @@ class RegionSimplifier(Analysis):
88
101
  #
89
102
 
90
103
  def _fold_oneuse_expressions(self, region):
91
- variable_manager = self.variable_kb.variables[self.func.addr]
92
- expr_counter = ExpressionCounter(region, variable_manager)
104
+ # pylint:disable=unreachable
105
+ expr_counter = ExpressionCounter(region)
93
106
 
94
107
  variable_assignments = {}
95
108
  variable_uses = {}
@@ -126,7 +139,7 @@ class RegionSimplifier(Analysis):
126
139
  # make sure all variables that var depends on has been assigned at most once
127
140
  fail = False
128
141
  for dep_var in deps:
129
- if dep_var.is_function_argument:
142
+ if self.arg_vvars is not None and dep_var in self.arg_vvars:
130
143
  continue
131
144
  if dep_var in expr_counter.assignments and len(expr_counter.assignments[dep_var]) > 1:
132
145
  fail = True
@@ -151,8 +164,14 @@ class RegionSimplifier(Analysis):
151
164
  del variable_assignments[var]
152
165
  del variable_uses[var]
153
166
 
154
- # replace them
155
- ExpressionFolder(variable_assignments, variable_uses, region, variable_manager)
167
+ # ensure there is no interference between the call site and the use site
168
+ checker = InterferenceChecker(variable_assignments, variable_uses, region)
169
+ for varid in checker.interfered_assignments:
170
+ if varid in variable_assignments:
171
+ del variable_assignments[varid]
172
+ del variable_uses[varid]
173
+ # fold these expressions if possible
174
+ ExpressionFolder(variable_assignments, variable_uses, region)
156
175
  return region
157
176
 
158
177
  @staticmethod
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
 
5
5
  from ailment.manager import Manager
6
- from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement
6
+ from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement, Jump
7
7
  from ailment.expression import (
8
8
  Expression,
9
9
  Register,
@@ -19,6 +19,7 @@ from ailment.expression import (
19
19
  ITE,
20
20
  Tmp,
21
21
  DirtyExpression,
22
+ Reinterpret,
22
23
  )
23
24
 
24
25
  from angr.engines.light.engine import SimEngineNostmtAIL
@@ -183,6 +184,12 @@ class SimEngineSSARewriting(
183
184
 
184
185
  return None
185
186
 
187
+ def _handle_stmt_Jump(self, stmt: Jump) -> Jump | None:
188
+ new_target = self._expr(stmt.target)
189
+ if new_target is not None:
190
+ return Jump(stmt.idx, new_target, stmt.target_idx, **stmt.tags)
191
+ return None
192
+
186
193
  def _handle_stmt_ConditionalJump(self, stmt: ConditionalJump) -> ConditionalJump | None:
187
194
  new_cond = self._expr(stmt.condition)
188
195
  new_true_target = self._expr(stmt.true_target) if stmt.true_target is not None else None
@@ -429,7 +436,18 @@ class SimEngineSSARewriting(
429
436
  def _handle_expr_Phi(self, expr):
430
437
  return None
431
438
 
432
- def _handle_expr_Reinterpret(self, expr):
439
+ def _handle_expr_Reinterpret(self, expr: Reinterpret) -> Reinterpret | None:
440
+ new_operand = self._expr(expr.operand)
441
+ if new_operand is not None:
442
+ return Reinterpret(
443
+ expr.idx,
444
+ expr.from_bits,
445
+ expr.from_type,
446
+ expr.to_bits,
447
+ expr.to_type,
448
+ new_operand,
449
+ **expr.tags,
450
+ )
433
451
  return None
434
452
 
435
453
  def _handle_expr_StackBaseOffset(self, expr):
@@ -205,7 +205,7 @@ class SimEngineSSATraversal(SimEngineLightAIL[TraversalState, None, None, None])
205
205
  _handle_binop_Set = _handle_binop_Default
206
206
 
207
207
  def _handle_unop_Default(self, expr):
208
- self._expr(expr.operands[0])
208
+ self._expr(expr.operand)
209
209
 
210
210
  _handle_unop_BitwiseNeg = _handle_unop_Default
211
211
  _handle_unop_Dereference = _handle_unop_Default
@@ -243,16 +243,17 @@ class SimEngineSSATraversal(SimEngineLightAIL[TraversalState, None, None, None])
243
243
  if expr.maddr is not None:
244
244
  self._expr(expr.maddr)
245
245
 
246
+ _handle_expr_Convert = _handle_unop_Default
247
+ _handle_expr_Reinterpret = _handle_unop_Default
248
+
246
249
  def _handle_Dummy(self, expr):
247
250
  pass
248
251
 
249
252
  _handle_expr_VirtualVariable = _handle_Dummy
250
253
  _handle_expr_Phi = _handle_Dummy
251
254
  _handle_expr_Load = _handle_Dummy
252
- _handle_expr_Convert = _handle_Dummy
253
255
  _handle_expr_Const = _handle_Dummy
254
256
  _handle_expr_MultiStatementExpression = _handle_Dummy
255
- _handle_expr_Reinterpret = _handle_Dummy
256
257
  _handle_expr_StackBaseOffset = _handle_Dummy
257
258
  _handle_expr_BasePointerOffset = _handle_Dummy
258
259
  _handle_expr_Call = _handle_Dummy
@@ -2504,6 +2504,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
2504
2504
  omit_func_header=False,
2505
2505
  display_block_addrs=False,
2506
2506
  display_vvar_ids=False,
2507
+ min_data_addr: int = 0x400_000,
2507
2508
  ):
2508
2509
  super().__init__(flavor=flavor)
2509
2510
 
@@ -2578,6 +2579,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
2578
2579
  self.omit_func_header = omit_func_header
2579
2580
  self.display_block_addrs = display_block_addrs
2580
2581
  self.display_vvar_ids = display_vvar_ids
2582
+ self.min_data_addr = min_data_addr
2581
2583
  self.text = None
2582
2584
  self.map_pos_to_node = None
2583
2585
  self.map_pos_to_addr = None
@@ -3519,8 +3521,8 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3519
3521
  expr.value
3520
3522
  ]
3521
3523
  inline_string = True
3522
- elif isinstance(type_, SimTypePointer) and isinstance(type_.pts_to, SimTypeChar):
3523
- # char*
3524
+ elif isinstance(type_, SimTypePointer) and isinstance(type_.pts_to, (SimTypeChar, SimTypeBottom)):
3525
+ # char* or void*
3524
3526
  # Try to get a string
3525
3527
  if (
3526
3528
  self._cfg is not None
@@ -3583,11 +3585,16 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3583
3585
  elif function_pointer:
3584
3586
  self._function_pointers.add(expr.reference_variable)
3585
3587
 
3588
+ var_access = None
3586
3589
  if variable is not None and not reference_values:
3587
3590
  cvar = self._variable(variable, None)
3588
3591
  offset = getattr(expr, "reference_variable_offset", 0)
3589
- return self._access_constant_offset_reference(self._get_variable_reference(cvar), offset, None)
3592
+ var_access = self._access_constant_offset_reference(self._get_variable_reference(cvar), offset, None)
3590
3593
 
3594
+ if var_access is not None and expr.value >= self.min_data_addr:
3595
+ return var_access
3596
+
3597
+ reference_values["offset"] = var_access
3591
3598
  return CConstant(expr.value, type_, reference_values=reference_values, tags=expr.tags, codegen=self)
3592
3599
 
3593
3600
  def _handle_Expr_UnaryOp(self, expr, **kwargs):
@@ -10,7 +10,7 @@ import claripy
10
10
  import ailment
11
11
 
12
12
  from angr.utils.graph import GraphUtils
13
- from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
13
+ from angr.knowledge_plugins.cfg import IndirectJumpType
14
14
  from angr.analyses.decompiler.graph_region import GraphRegion
15
15
  from angr.analyses.decompiler.empty_node_remover import EmptyNodeRemover
16
16
  from angr.analyses.decompiler.jumptable_entry_condition_rewriter import JumpTableEntryConditionRewriter
@@ -112,6 +112,7 @@ class DreamStructurer(StructurerBase):
112
112
 
113
113
  loop_subgraph = self._region.graph
114
114
  successors = self._region.successors
115
+ assert successors is not None
115
116
 
116
117
  assert len(successors) <= 1
117
118
 
@@ -267,6 +268,7 @@ class DreamStructurer(StructurerBase):
267
268
 
268
269
  def _to_loop_body_sequence(self, loop_head, loop_subgraph, loop_successors):
269
270
  graph = self._region.graph_with_successors
271
+ assert graph is not None
270
272
  loop_region_graph = networkx.DiGraph()
271
273
 
272
274
  # TODO: Make sure the loop body has been structured
@@ -372,7 +374,7 @@ class DreamStructurer(StructurerBase):
372
374
  node.condition_and_nodes = new_cond_and_nodes
373
375
 
374
376
  if node.else_node is not None:
375
- node.else_node = walker._handle(node.else_node)
377
+ node.else_node = walker._handle(node.else_node) # type: ignore
376
378
  return node
377
379
 
378
380
  def _handle_SwitchCaseNode(node, **kwargs): # pylint:disable=unused-argument
@@ -480,8 +482,6 @@ class DreamStructurer(StructurerBase):
480
482
  :return: None
481
483
  """
482
484
 
483
- jump_tables = self.kb.cfgs["CFGFast"].jump_tables
484
-
485
485
  addr2nodes: dict[int, set[CodeNode]] = defaultdict(set)
486
486
  for node in seq.nodes:
487
487
  addr2nodes[node.addr].add(node)
@@ -491,14 +491,14 @@ class DreamStructurer(StructurerBase):
491
491
  node = seq.nodes[i]
492
492
 
493
493
  # Jumptable_AddressLoadedFromMemory
494
- r = self._make_switch_cases_address_loaded_from_memory(seq, i, node, addr2nodes, jump_tables)
494
+ r = self._make_switch_cases_address_loaded_from_memory(seq, i, node, addr2nodes)
495
495
  if r:
496
496
  # we found a node that looks like a switch-case. seq.nodes are changed. resume to find the next such
497
497
  # case
498
498
  break
499
499
 
500
500
  # Jumptable_AddressComputed
501
- r = self._make_switch_cases_address_computed(seq, i, node, addr2nodes, jump_tables)
501
+ r = self._make_switch_cases_address_computed(seq, i, node, addr2nodes)
502
502
  if r:
503
503
  break
504
504
 
@@ -506,9 +506,7 @@ class DreamStructurer(StructurerBase):
506
506
  # we did not find any node that looks like a switch-case. exit.
507
507
  break
508
508
 
509
- def _make_switch_cases_address_loaded_from_memory(
510
- self, seq, i, node, addr2nodes: dict[int, set[CodeNode]], jump_tables: dict[int, IndirectJump]
511
- ) -> bool:
509
+ def _make_switch_cases_address_loaded_from_memory(self, seq, i, node, addr2nodes: dict[int, set[CodeNode]]) -> bool:
512
510
  """
513
511
  A typical jump table involves multiple nodes, which look like the following:
514
512
 
@@ -526,6 +524,8 @@ class DreamStructurer(StructurerBase):
526
524
 
527
525
  try:
528
526
  last_stmt = self.cond_proc.get_last_statement(node)
527
+ if not isinstance(last_stmt, ailment.Stmt.ConditionalJump):
528
+ return False
529
529
  except EmptyBlockNotice:
530
530
  return False
531
531
  successor_addrs = extract_jump_targets(last_stmt)
@@ -533,14 +533,14 @@ class DreamStructurer(StructurerBase):
533
533
  return False
534
534
 
535
535
  for t in successor_addrs:
536
- if t in addr2nodes and t in jump_tables:
536
+ if t in addr2nodes and t in self.jump_tables:
537
537
  # this is a candidate!
538
538
  target = t
539
539
  break
540
540
  else:
541
541
  return False
542
542
 
543
- jump_table = jump_tables[target]
543
+ jump_table = self.jump_tables[target]
544
544
  if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
545
545
  return False
546
546
 
@@ -563,13 +563,20 @@ class DreamStructurer(StructurerBase):
563
563
  return False
564
564
 
565
565
  # build switch-cases
566
+ assert jump_table.jumptable_entries is not None
566
567
  cases, node_default, to_remove = self._switch_build_cases(
567
568
  seq, cmp_lb, jump_table.jumptable_entries, i, node_b_addr, addr2nodes
568
569
  )
569
570
  # if we don't know what the end address of this switch-case structure is, let's figure it out
570
- switch_end_addr = node_b_addr if node_default is None else None
571
- self._switch_handle_gotos(cases, node_default, switch_end_addr)
571
+ switch_end_addr = (
572
+ node_b_addr
573
+ if node_default is None
574
+ else self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
575
+ )
576
+ if switch_end_addr is not None:
577
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
572
578
 
579
+ assert last_stmt.ins_addr is not None
573
580
  self._make_switch_cases_core(
574
581
  seq,
575
582
  i,
@@ -586,12 +593,10 @@ class DreamStructurer(StructurerBase):
586
593
 
587
594
  return True
588
595
 
589
- def _make_switch_cases_address_computed(
590
- self, seq, i, node, addr2nodes: dict[int, set[CodeNode]], jump_tables: dict[int, IndirectJump]
591
- ) -> bool:
592
- if node.addr not in jump_tables:
596
+ def _make_switch_cases_address_computed(self, seq, i, node, addr2nodes: dict[int, set[CodeNode]]) -> bool:
597
+ if node.addr not in self.jump_tables:
593
598
  return False
594
- jump_table = jump_tables[node.addr]
599
+ jump_table = self.jump_tables[node.addr]
595
600
  if jump_table.type != IndirectJumpType.Jumptable_AddressComputed:
596
601
  return False
597
602
 
@@ -617,9 +622,11 @@ class DreamStructurer(StructurerBase):
617
622
  cmp_expr, cmp_lb, cmp_ub = cmp # pylint:disable=unused-variable
618
623
 
619
624
  jumptable_entries = jump_table.jumptable_entries
625
+ assert jumptable_entries is not None
620
626
 
621
627
  if isinstance(last_stmt.false_target, ailment.Expr.Const):
622
628
  default_addr = last_stmt.false_target.value
629
+ assert isinstance(default_addr, int)
623
630
  else:
624
631
  return False
625
632
 
@@ -656,8 +663,9 @@ class DreamStructurer(StructurerBase):
656
663
  addr,
657
664
  addr2nodes,
658
665
  to_remove,
666
+ *,
667
+ jumptable_addr: int,
659
668
  node_a=None,
660
- jumptable_addr=None,
661
669
  ):
662
670
  scnode = SwitchCaseNode(cmp_expr, cases, node_default, addr=addr)
663
671
  scnode = CodeNode(scnode, node.reaching_condition)
@@ -715,6 +723,7 @@ class DreamStructurer(StructurerBase):
715
723
  ):
716
724
  # unpacking is needed
717
725
  for n in node_a.node.nodes:
726
+ assert n.addr is not None
718
727
  if isinstance(n, ConditionNode):
719
728
  unpacked = self._switch_unpack_condition_node(n, jumptable)
720
729
  if unpacked is None: