unidecompiler-plugin-python-pyc 0.1.2__tar.gz → 0.1.4__tar.gz

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.
Files changed (17) hide show
  1. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/PKG-INFO +2 -2
  2. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/pyproject.toml +2 -2
  3. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc/lifter.py +146 -145
  4. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc.egg-info/PKG-INFO +2 -2
  5. unidecompiler_plugin_python_pyc-0.1.4/src/unidecompiler_plugin_python_pyc.egg-info/requires.txt +1 -0
  6. unidecompiler_plugin_python_pyc-0.1.2/src/unidecompiler_plugin_python_pyc.egg-info/requires.txt +0 -1
  7. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/README.md +0 -0
  8. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/setup.cfg +0 -0
  9. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc/__init__.py +0 -0
  10. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc/plugin.py +0 -0
  11. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc/pyc.py +0 -0
  12. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc/simulation.py +0 -0
  13. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc/support.py +0 -0
  14. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc.egg-info/SOURCES.txt +0 -0
  15. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc.egg-info/dependency_links.txt +0 -0
  16. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc.egg-info/entry_points.txt +0 -0
  17. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.4}/src/unidecompiler_plugin_python_pyc.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unidecompiler-plugin-python-pyc
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python bytecode frontend plugin for unidecompiler
5
5
  Author-email: Wker <1670133844@qq.com>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/Wker666/unidecompiler
9
9
  Project-URL: Issues, https://github.com/Wker666/unidecompiler/issues
10
10
  Requires-Python: >=3.11
11
11
  Description-Content-Type: text/markdown
12
- Requires-Dist: unidecompiler<0.2.0,>=0.1.2
12
+ Requires-Dist: unidecompiler<0.2.0,>=0.1.9
13
13
 
14
14
  # unidecompiler-plugin-python-pyc
15
15
 
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unidecompiler-plugin-python-pyc"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Python bytecode frontend plugin for unidecompiler"
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
11
11
  authors = [{ name = "Wker", email = "1670133844@qq.com" }]
12
12
  requires-python = ">=3.11"
13
- dependencies = ["unidecompiler>=0.1.2,<0.2.0"]
13
+ dependencies = ["unidecompiler>=0.1.9,<0.2.0"]
14
14
 
15
15
  [project.urls]
16
16
  Homepage = "https://github.com/Wker666/unidecompiler"
@@ -13,6 +13,10 @@ from unidecompiler.core.effects import (
13
13
  BuildArrayCall,
14
14
  CallTopAs,
15
15
  Copy,
16
+ DeleteAttr,
17
+ DeleteGlobal,
18
+ DeleteItem,
19
+ DeleteLocal,
16
20
  DropBelowTop,
17
21
  Effect,
18
22
  Compare,
@@ -32,14 +36,17 @@ from unidecompiler.core.effects import (
32
36
  Iterate,
33
37
  MakeFunctionValue,
34
38
  MergeMap,
39
+ OfferImplicitCallArguments,
35
40
  Pop,
36
41
  Push,
37
42
  RaiseTop,
38
43
  RaiseWithCause,
39
44
  ReraiseTop,
45
+ ResumeValue,
40
46
  ReturnTop,
41
47
  ReturnVoid,
42
48
  StoreAttr,
49
+ StoreGlobal,
43
50
  StoreLocal,
44
51
  StoreMany,
45
52
  StoreManyFromPopOrder,
@@ -55,7 +62,6 @@ from unidecompiler.core.effects import (
55
62
  )
56
63
  from unidecompiler.core.vm_module import assemble_vm_module
57
64
  from unidecompiler.core.ir import (
58
- Assign,
59
65
  BinaryOp,
60
66
  CapturedVar,
61
67
  Const,
@@ -83,15 +89,13 @@ from unidecompiler.provenance import ByteRange
83
89
  from unidecompiler.core.vm_region import (
84
90
  VMLinearState,
85
91
  VMRegionCallbacks,
92
+ VMRegionSlice,
86
93
  VMRegionOpcodeClasses,
87
94
  VMRegionProfile,
88
95
  VMStatefulCallbacks,
89
96
  build_hint_region_profile,
90
97
  lift_control_region as lift_vm_control_region,
91
98
  )
92
- from unidecompiler.core.vm_structures import (
93
- vm_unsupported,
94
- )
95
99
  from unidecompiler_plugin_python_pyc.pyc import PycCodeObject, PycExceptionRegion, PycModule
96
100
 
97
101
 
@@ -102,6 +106,10 @@ BINARY_SYMBOLS = {
102
106
  "-=": "-",
103
107
  "*": "*",
104
108
  "*=": "*",
109
+ "**": "**",
110
+ "**=": "**",
111
+ "@": "@",
112
+ "@=": "@",
105
113
  "/": "/",
106
114
  "/=": "/",
107
115
  "//": "//",
@@ -122,7 +130,6 @@ BINARY_SYMBOLS = {
122
130
  COMPLEX_OPS = {
123
131
  }
124
132
  IGNORED_OPS = {
125
- "RESUME",
126
133
  "CACHE",
127
134
  "NOP",
128
135
  "EXTENDED_ARG",
@@ -130,7 +137,7 @@ IGNORED_OPS = {
130
137
  "COPY_FREE_VARS",
131
138
  }
132
139
  PYTHON_REGION_OPCODE_CLASSES = VMRegionOpcodeClasses(
133
- noise=frozenset({"END_FOR", "POP_TOP", "NOP", "RESUME"}),
140
+ noise=frozenset({"END_FOR", "POP_TOP", "NOP"}),
134
141
  control=frozenset(
135
142
  {
136
143
  "POP_JUMP_IF_FALSE",
@@ -190,8 +197,7 @@ def _load_build_class(_context, _instruction, source: SourceRef) -> tuple[Effect
190
197
 
191
198
  def _import_name(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
192
199
  return (
193
- Pop(source=source, count=2, allow_missing=True),
194
- Push(source=source, value=Global(name=str(instruction.argval), source=source)),
200
+ BuildCall(source=source, callee=Global(name=f"__import__:{instruction.argval}", source=source), arg_count=2, returns=1),
195
201
  )
196
202
 
197
203
 
@@ -219,6 +225,14 @@ def _load_local(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
219
225
  return (LoadLocal(source=source, name=name, fallback=Var(name=name, source=source)),)
220
226
 
221
227
 
228
+ def _load_and_clear_local(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
229
+ name = str(instruction.argval)
230
+ return (
231
+ LoadLocal(source=source, name=name, fallback=Var(name=name, source=source)),
232
+ DeleteLocal(source=source, name=name),
233
+ )
234
+
235
+
222
236
  def _load_closure(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
223
237
  name = str(instruction.argval)
224
238
  return (Push(source=source, value=CapturedVar(name=name, source=source)),)
@@ -243,12 +257,25 @@ def _load_global(_context, instruction, source: SourceRef) -> tuple[Effect, ...]
243
257
 
244
258
  def _load_from_dict_or_globals(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
245
259
  name = str(instruction.argval)
260
+ return (BuildCall(source=source, callee=Global(name=f"load_from_dict_or_globals:{name}", source=source), arg_count=1, returns=1),)
261
+
262
+
263
+ def _call_intrinsic_1(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
264
+ return (CallTopAs(source=source, callee_name=str(instruction.argval or instruction.argrepr or "intrinsic")),)
265
+
266
+
267
+ def _before_with(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
268
+ name = "before_async_with" if "ASYNC" in instruction.opname else "before_with"
246
269
  return (
247
- Pop(source=source, count=1, allow_missing=True),
248
- Push(source=source, value=Global(name=name, source=source)),
270
+ BuildCall(source=source, callee=Global(name=name, source=source), arg_count=1, returns=1),
271
+ Unpack(source=source, count=2),
249
272
  )
250
273
 
251
274
 
275
+ def _set_function_attribute(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
276
+ return (BuildCall(source=source, callee=Global(name=f"set_function_attribute:{instruction.argval or instruction.argrepr}", source=source), arg_count=2, returns=1),)
277
+
278
+
252
279
  def _make_function(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
253
280
  return (MakeFunctionValue(source=source, fallback_name=str(instruction.argval or instruction.argrepr or "<function>")),)
254
281
 
@@ -283,7 +310,10 @@ def _build_set(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
283
310
 
284
311
 
285
312
  def _get_iter(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
286
- return (Iterate(source=source),)
313
+ return (
314
+ Iterate(source=source),
315
+ OfferImplicitCallArguments(source=source, max_explicit_arg_count=0),
316
+ )
287
317
 
288
318
 
289
319
  def _unpack_sequence(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -321,7 +351,7 @@ def _unary(op: str):
321
351
 
322
352
  def _store_global(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
323
353
  name = str(instruction.argval)
324
- return (StoreLocal(source=source, name=name, target=Var(name=name, source=source), materialize=False),)
354
+ return (StoreGlobal(source=source, name=name),)
325
355
 
326
356
 
327
357
  def _store_deref(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -336,16 +366,32 @@ def _store_deref(_context, instruction, source: SourceRef) -> tuple[Effect, ...]
336
366
  )
337
367
 
338
368
 
339
- def _delete_global(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
340
- return (Pop(source=source, count=1, allow_missing=True),)
369
+ def _delete_local(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
370
+ name = str(instruction.argval)
371
+ return (DeleteLocal(source=source, name=name),)
341
372
 
342
373
 
343
- def _delete_attr(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
344
- return (Pop(source=source, count=1, allow_missing=True),)
374
+ def _delete_deref(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
375
+ name = str(instruction.argval)
376
+ return (
377
+ DeleteLocal(
378
+ source=source,
379
+ name=name,
380
+ target=CapturedVar(name=name, source=source),
381
+ ),
382
+ )
383
+
384
+
385
+ def _delete_global(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
386
+ return (DeleteGlobal(source=source, name=str(instruction.argval)),)
387
+
388
+
389
+ def _delete_attr(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
390
+ return (DeleteAttr(source=source, attr=str(instruction.argval)),)
345
391
 
346
392
 
347
393
  def _delete_subscr(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
348
- return (Pop(source=source, count=2, allow_missing=True),)
394
+ return (DeleteItem(source=source),)
349
395
 
350
396
 
351
397
  def _build_slice(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -360,7 +406,10 @@ def _binary_slice(_context, _instruction, source: SourceRef) -> tuple[Effect, ..
360
406
 
361
407
 
362
408
  def _store_slice(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
363
- return (StoreItemEffect(source=source),)
409
+ return (
410
+ BuildCall(source=source, arg_count=2, callee=Global(name="slice", source=source)),
411
+ StoreItemEffect(source=source, order="value-obj-key"),
412
+ )
364
413
 
365
414
 
366
415
  def _list_extend(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -443,8 +492,8 @@ def _map_add(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
443
492
  return (StoreItemAtDepth(source=source, depth=_instruction_count(instruction)),)
444
493
 
445
494
 
446
- def _set_add(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
447
- return (SetAdd(source=source),)
495
+ def _set_add(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
496
+ return (SetAdd(source=source, depth=_instruction_count(instruction)),)
448
497
 
449
498
 
450
499
  def _store_local(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -484,6 +533,13 @@ def _yield_value(_context, instruction, source: SourceRef) -> tuple[Effect, ...]
484
533
  return (YieldTop(source=source, default=Const(value=None, source=source)),)
485
534
 
486
535
 
536
+ def _resume(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
537
+ # CPython's resume mode 5 continues a ``yield`` expression, whose value
538
+ # is supplied by the caller. Await/send modes manage their result in the
539
+ # surrounding protocol and must not manufacture an extra stack value.
540
+ return (ResumeValue(source=source),) if _instruction_count(instruction) == 5 else ()
541
+
542
+
487
543
  def _send(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
488
544
  return (Pop(source=source, count=1, allow_missing=True),)
489
545
 
@@ -521,24 +577,17 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
521
577
  "JUMP_NO_INTERRUPT",
522
578
  "INSTRUMENTED_JUMP_BACKWARD",
523
579
  "INSTRUMENTED_JUMP_FORWARD",
524
- "CALL_INTRINSIC_1",
525
- "BEFORE_WITH",
526
580
  "CLEANUP_THROW",
527
581
  "CHECK_EG_MATCH",
528
582
  "END_ASYNC_FOR",
529
583
  "LOOKUP_METHOD",
530
584
  "PUSH_EXC_INFO",
531
585
  "WITH_EXCEPT_START",
532
- "DELETE_FAST",
533
- "DELETE_NAME",
534
- "DELETE_DEREF",
535
- "DELETE_GLOBAL",
536
586
  "ENTER_EXECUTOR",
537
587
  "EXIT_INIT_CHECK",
538
588
  "INSTRUMENTED_INSTRUCTION",
539
589
  "INSTRUMENTED_LINE",
540
590
  "INSTRUMENTED_RESUME",
541
- "INTERPRETER_EXIT",
542
591
  "POP_BLOCK",
543
592
  "RESERVED",
544
593
  "SETUP_CLEANUP",
@@ -559,12 +608,15 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
559
608
  "LOAD_CLOSURE": _load_closure,
560
609
  "LOAD_FAST": _load_local,
561
610
  "LOAD_FAST_CHECK": _load_local,
562
- "LOAD_FAST_AND_CLEAR": _load_local,
611
+ "LOAD_FAST_AND_CLEAR": _load_and_clear_local,
563
612
  "LOAD_FAST_LOAD_FAST": _load_fast_pair,
564
613
  "LOAD_FROM_DICT_OR_GLOBALS": _load_from_dict_or_globals,
565
614
  "LOAD_GLOBAL": _load_global,
566
615
  "MAKE_FUNCTION": _make_function,
567
616
  "STORE_GLOBAL": _store_global,
617
+ "DELETE_FAST": _delete_local,
618
+ "DELETE_NAME": _delete_local,
619
+ "DELETE_DEREF": _delete_deref,
568
620
  "DELETE_GLOBAL": _delete_global,
569
621
  "DELETE_ATTR": _delete_attr,
570
622
  "DELETE_SUBSCR": _delete_subscr,
@@ -577,7 +629,12 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
577
629
  "CALL_FUNCTION_EX": _call_function_ex,
578
630
  "INSTRUMENTED_CALL_FUNCTION_EX": _call_function_ex,
579
631
  "LOAD_LOCALS": _load_locals,
580
- "SET_FUNCTION_ATTRIBUTE": lambda _context, _instruction, source: (DropBelowTop(source=source, count=1),),
632
+ "SET_FUNCTION_ATTRIBUTE": _set_function_attribute,
633
+ "CALL_INTRINSIC_1": _call_intrinsic_1,
634
+ "BEFORE_WITH": _before_with,
635
+ "BEFORE_ASYNC_WITH": _before_with,
636
+ "EXIT_INIT_CHECK": lambda _context, _instruction, source: (BuildCall(source=source, callee=Global(name="exit_init_check", source=source), arg_count=1, returns=0),),
637
+ "INTERPRETER_EXIT": lambda _context, _instruction, source: (ReturnTop(source=source),),
581
638
  "COPY": _copy,
582
639
  "SWAP": _swap,
583
640
  "LOAD_ATTR": _load_attr,
@@ -605,6 +662,7 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
605
662
  "BUILD_TUPLE": _build_tuple,
606
663
  "BUILD_SET": _build_set,
607
664
  "SET_ADD": _set_add,
665
+ "RESUME": _resume,
608
666
  "GET_ITER": _get_iter,
609
667
  "GET_YIELD_FROM_ITER": _get_iter,
610
668
  "UNPACK_SEQUENCE": _unpack_sequence,
@@ -684,8 +742,8 @@ def lift_code_object(code: PycCodeObject) -> FunctionIR:
684
742
  _python_function_spec(code),
685
743
  steps,
686
744
  profile=_python_region_profile(steps, instructions),
687
- callbacks=_python_region_callbacks(code, instructions),
688
- stateful_callbacks=_python_stateful_callbacks(code, instructions),
745
+ callbacks_factory=lambda normalized_steps: _python_region_callbacks(code, normalized_steps),
746
+ stateful_callbacks_factory=lambda normalized_steps: _python_stateful_callbacks(code, normalized_steps),
689
747
  initial_locals=_python_initial_locals(code),
690
748
  raw_window=lambda index: _raw_instruction_window(instructions, index),
691
749
  )
@@ -884,21 +942,40 @@ def _python_initial_locals(code: PycCodeObject) -> dict[str, Expr]:
884
942
 
885
943
  def _python_region_callbacks(
886
944
  code: PycCodeObject,
887
- instructions: tuple[object, ...],
945
+ prepared_steps: tuple[VMBytecodeStep, ...],
888
946
  ) -> VMRegionCallbacks[VMBytecodeStep]:
947
+ def lift_slice(slice_start, slice_end, stack):
948
+ slice_steps = prepared_steps[slice_start:slice_end]
949
+ result = lift_steps(
950
+ slice_steps,
951
+ initial_locals=_python_initial_locals(code),
952
+ initial_stack=stack,
953
+ )
954
+ if result.state.diagnostics:
955
+ return VMRegionSlice(stopped_at=slice_start)
956
+ output: list[object] = list(result.state.statements)
957
+ if result.stopped_at is not None and result.state.terminator is None:
958
+ return VMRegionSlice(
959
+ statements=tuple(output),
960
+ stopped_at=slice_start + slice_steps.index(result.stopped_at),
961
+ )
962
+ if result.state.terminator is not None:
963
+ output.append(result.state.terminator)
964
+ return VMRegionSlice(statements=tuple(output))
965
+
966
+ def lift_expr(slice_start, slice_end, stack):
967
+ result = lift_steps(
968
+ prepared_steps[slice_start:slice_end],
969
+ initial_locals=_python_initial_locals(code),
970
+ initial_stack=stack,
971
+ )
972
+ if result.state.diagnostics or not result.state.stack:
973
+ return None
974
+ return result.state.stack[-1]
975
+
889
976
  return VMRegionCallbacks(
890
- lift_slice=lambda slice_start, slice_end, stack: _lift_instruction_slice(
891
- code,
892
- instructions[slice_start:slice_end],
893
- [],
894
- stack,
895
- ),
896
- lift_expr=lambda slice_start, slice_end, stack: _lift_stack_expr(
897
- code,
898
- instructions[slice_start:slice_end],
899
- [],
900
- stack,
901
- ),
977
+ lift_slice=lift_slice,
978
+ lift_expr=lift_expr,
902
979
  lift_iter_loop=lambda _get_iter_index, _iterable: None,
903
980
  lift_async_iter_loop=lambda _prefix_start, _get_aiter_index, _region_end: None,
904
981
  lift_comprehension=lambda _prefix_start, _get_iter_index, _region_end, _iterable: None,
@@ -907,45 +984,38 @@ def _python_region_callbacks(
907
984
 
908
985
  def _python_stateful_callbacks(
909
986
  code: PycCodeObject,
910
- instructions: tuple[object, ...],
987
+ prepared_steps: tuple[VMBytecodeStep, ...],
911
988
  ) -> VMStatefulCallbacks[VMBytecodeStep]:
989
+ def lift_linear(start, end, locals_, stack):
990
+ slice_steps = prepared_steps[start:end]
991
+ result = lift_steps(
992
+ slice_steps,
993
+ initial_locals=locals_,
994
+ initial_stack=stack,
995
+ )
996
+ stopped_at_terminal_end = (
997
+ result.stopped_at is slice_steps[-1]
998
+ if slice_steps and result.state.terminator is not None
999
+ else False
1000
+ )
1001
+ if result.state.diagnostics or (
1002
+ result.stopped_at is not None and not stopped_at_terminal_end
1003
+ ):
1004
+ return None
1005
+ return VMLinearState(
1006
+ locals=result.state.locals,
1007
+ stack=tuple(result.state.stack),
1008
+ statements=tuple(result.state.statements),
1009
+ terminator=result.state.terminator,
1010
+ )
1011
+
912
1012
  return VMStatefulCallbacks(
913
1013
  initial_locals=lambda: _python_initial_locals(code),
914
- lift_linear=lambda start, end, locals, stack: _python_linear_state(
915
- instructions[start:end],
916
- locals,
917
- stack,
918
- ),
1014
+ lift_linear=lift_linear,
919
1015
  branch_condition=_python_branch_condition,
920
1016
  )
921
1017
 
922
1018
 
923
- def _python_linear_state(
924
- instructions: tuple[object, ...],
925
- initial_locals: dict[str, Expr],
926
- initial_stack: tuple[Expr, ...],
927
- ) -> VMLinearState | None:
928
- result = _run_python_stack_slice(
929
- _PycLinearLocals(tuple(initial_locals)),
930
- instructions,
931
- dict(initial_locals),
932
- initial_stack,
933
- )
934
- stopped_at_terminal_end = (
935
- getattr(result.stopped_at, "source", None) == _python_source(instructions[-1])
936
- if instructions and result.state.terminator is not None
937
- else False
938
- )
939
- if result.state.diagnostics or (result.stopped_at is not None and not stopped_at_terminal_end):
940
- return None
941
- return VMLinearState(
942
- locals=result.state.locals,
943
- stack=tuple(result.state.stack),
944
- statements=tuple(result.state.statements),
945
- terminator=result.state.terminator,
946
- )
947
-
948
-
949
1019
  def _python_branch_condition(branch: VMBytecodeStep, stack: tuple[Expr, ...]) -> Expr | None:
950
1020
  if not stack:
951
1021
  return None
@@ -962,11 +1032,6 @@ def _python_branch_condition(branch: VMBytecodeStep, stack: tuple[Expr, ...]) ->
962
1032
  return condition
963
1033
 
964
1034
 
965
- class _PycLinearLocals:
966
- def __init__(self, varnames: tuple[str, ...]) -> None:
967
- self.varnames = varnames
968
-
969
-
970
1035
  def _python_region_profile(
971
1036
  steps: tuple[VMBytecodeStep, ...],
972
1037
  instructions: tuple[object, ...],
@@ -999,70 +1064,6 @@ def _await_region_end(instructions: tuple[object, ...], start: int, end: int) ->
999
1064
  return None
1000
1065
 
1001
1066
 
1002
- def _lift_instruction_slice(
1003
- code: PycCodeObject,
1004
- instructions: tuple[object, ...],
1005
- assignments: list[Assign],
1006
- preloaded_stack: tuple[Expr, ...] = (),
1007
- ) -> tuple[object, ...]:
1008
- if not instructions:
1009
- return ()
1010
- initial_locals = {
1011
- name: Var(name=name, source=SourceRef(frontend="python-pyc", detail=f"local:{name}"))
1012
- for name in code.varnames
1013
- }
1014
- for assignment in assignments:
1015
- initial_locals[assignment.target.name] = assignment.target
1016
- result = _run_python_stack_slice(code, instructions, initial_locals, preloaded_stack)
1017
- if result.state.diagnostics:
1018
- return ()
1019
- output: list[object] = list(result.state.statements)
1020
- if result.stopped_at is not None and result.state.terminator is None:
1021
- try:
1022
- stopped_index = instructions.index(result.stopped_at)
1023
- except ValueError:
1024
- stopped_index = 0
1025
- output.append(
1026
- vm_unsupported(
1027
- source=result.stopped_at.source,
1028
- message="unsupported region",
1029
- detail=f"stopped at {result.stopped_at.opcode}",
1030
- raw=(result.stopped_at.raw,) if result.stopped_at.raw else _raw_instruction_window(instructions, stopped_index),
1031
- )
1032
- )
1033
- if result.state.terminator is not None:
1034
- output.append(result.state.terminator)
1035
- return tuple(output)
1036
-
1037
-
1038
- def _lift_stack_expr(
1039
- code: PycCodeObject,
1040
- instructions: tuple[object, ...],
1041
- assignments: list[Assign],
1042
- preloaded_stack: tuple[Expr, ...] = (),
1043
- ) -> Expr | None:
1044
- initial_locals = {
1045
- name: Var(name=name, source=SourceRef(frontend="python-pyc", detail=f"local:{name}"))
1046
- for name in code.varnames
1047
- }
1048
- for assignment in assignments:
1049
- initial_locals[assignment.target.name] = assignment.target
1050
- result = _run_python_stack_slice(code, instructions, initial_locals, preloaded_stack)
1051
- if result.state.diagnostics or not result.state.stack:
1052
- return None
1053
- return result.state.stack[-1]
1054
-
1055
-
1056
- def _run_python_stack_slice(
1057
- code: PycCodeObject,
1058
- instructions: tuple[object, ...],
1059
- initial_locals: dict[str, Expr],
1060
- initial_stack: tuple[Expr, ...] = (),
1061
- ):
1062
- steps = _python_bytecode_steps(instructions)
1063
- return lift_steps(steps, initial_locals=initial_locals, initial_stack=initial_stack)
1064
-
1065
-
1066
1067
  def _instruction_count(instruction) -> int:
1067
1068
  return instruction.arg if isinstance(instruction.arg, int) else 0
1068
1069
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unidecompiler-plugin-python-pyc
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python bytecode frontend plugin for unidecompiler
5
5
  Author-email: Wker <1670133844@qq.com>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/Wker666/unidecompiler
9
9
  Project-URL: Issues, https://github.com/Wker666/unidecompiler/issues
10
10
  Requires-Python: >=3.11
11
11
  Description-Content-Type: text/markdown
12
- Requires-Dist: unidecompiler<0.2.0,>=0.1.2
12
+ Requires-Dist: unidecompiler<0.2.0,>=0.1.9
13
13
 
14
14
  # unidecompiler-plugin-python-pyc
15
15