unidecompiler-plugin-python-pyc 0.1.5__tar.gz → 0.2.0__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.5 → unidecompiler_plugin_python_pyc-0.2.0}/PKG-INFO +2 -2
  2. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/pyproject.toml +2 -2
  3. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc/lifter.py +76 -6
  4. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc/plugin.py +6 -0
  5. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc.egg-info/PKG-INFO +2 -2
  6. unidecompiler_plugin_python_pyc-0.2.0/src/unidecompiler_plugin_python_pyc.egg-info/requires.txt +1 -0
  7. unidecompiler_plugin_python_pyc-0.1.5/src/unidecompiler_plugin_python_pyc.egg-info/requires.txt +0 -1
  8. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/README.md +0 -0
  9. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/setup.cfg +0 -0
  10. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc/__init__.py +0 -0
  11. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc/pyc.py +0 -0
  12. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc/simulation.py +0 -0
  13. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc/support.py +0 -0
  14. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc.egg-info/SOURCES.txt +0 -0
  15. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc.egg-info/dependency_links.txt +0 -0
  16. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/src/unidecompiler_plugin_python_pyc.egg-info/entry_points.txt +0 -0
  17. {unidecompiler_plugin_python_pyc-0.1.5 → unidecompiler_plugin_python_pyc-0.2.0}/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.5
3
+ Version: 0.2.0
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.10
12
+ Requires-Dist: unidecompiler<0.3.0,>=0.2.0
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.5"
7
+ version = "0.2.0"
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.10,<0.2.0"]
13
+ dependencies = ["unidecompiler>=0.2.0,<0.3.0"]
14
14
 
15
15
  [project.urls]
16
16
  Homepage = "https://github.com/Wker666/unidecompiler"
@@ -42,6 +42,7 @@ from unidecompiler.core.effects import (
42
42
  RaiseTop,
43
43
  RaiseWithCause,
44
44
  ReraiseTop,
45
+ RewriteException,
45
46
  ResumeValue,
46
47
  ReturnTop,
47
48
  ReturnVoid,
@@ -96,6 +97,7 @@ from unidecompiler.core.vm_region import (
96
97
  build_hint_region_profile,
97
98
  lift_control_region as lift_vm_control_region,
98
99
  )
100
+ from unidecompiler.progress import ProgressReporter, report_progress
99
101
  from unidecompiler_plugin_python_pyc.pyc import PycCodeObject, PycExceptionRegion, PycModule
100
102
 
101
103
 
@@ -261,7 +263,29 @@ def _load_from_dict_or_globals(_context, instruction, source: SourceRef) -> tupl
261
263
 
262
264
 
263
265
  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")),)
266
+ name = str(instruction.argrepr or instruction.argval or "intrinsic")
267
+ if name == "INTRINSIC_STOPITERATION_ERROR":
268
+ # CPython conditionally replaces StopIteration-family exceptions and
269
+ # retains the original as cause/context. This remains a generic
270
+ # value rewrite; core owns its stack lift and conservative rendering.
271
+ return (
272
+ RewriteException(
273
+ source=source,
274
+ predicate=Const(value="stop-iteration-family", source=source),
275
+ replacement=Const(value="runtime-error-from-input", source=source),
276
+ retain_input_as_cause=True,
277
+ ),
278
+ )
279
+ # ``arg``/``argval`` is an implementation ordinal. The decoded text is
280
+ # the only presentation-safe identity for an opaque intrinsic operation;
281
+ # retain it as data and leave any execution semantics outside this thin
282
+ # effect table.
283
+ return (
284
+ CallTopAs(
285
+ source=source,
286
+ callee_name=name,
287
+ ),
288
+ )
265
289
 
266
290
 
267
291
  def _before_with(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -527,6 +551,17 @@ def _raise_varargs(_context, instruction, source: SourceRef) -> tuple[Effect, ..
527
551
  return (RaiseTop(source=source),)
528
552
 
529
553
 
554
+ def _reraise(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
555
+ count = _instruction_count(instruction)
556
+ return (
557
+ ReraiseTop(
558
+ source=source,
559
+ consume_stack_exception=True,
560
+ resume_slot_count=count,
561
+ ),
562
+ )
563
+
564
+
530
565
  def _yield_value(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
531
566
  if instruction.arg in {1, 2}:
532
567
  return ()
@@ -704,7 +739,7 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
704
739
  "RETURN_CONST": _return_const,
705
740
  "INSTRUMENTED_RETURN_CONST": _return_const,
706
741
  "RAISE_VARARGS": _raise_varargs,
707
- "RERAISE": lambda _context, _instruction, source: (ReraiseTop(source=source),),
742
+ "RERAISE": _reraise,
708
743
  "YIELD_VALUE": _yield_value,
709
744
  "INSTRUMENTED_YIELD_VALUE": _yield_value,
710
745
  "SEND": _send,
@@ -722,8 +757,15 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
722
757
  )
723
758
 
724
759
 
725
- def lift_pyc_module(pyc_module: PycModule, metadata: dict) -> ModuleIR:
726
- functions = list(_lift_code_object_tree(pyc_module.code))
760
+ def lift_pyc_module(
761
+ pyc_module: PycModule,
762
+ metadata: dict,
763
+ *,
764
+ reporter: ProgressReporter | None = None,
765
+ ) -> ModuleIR:
766
+ total = _code_object_count(pyc_module.code)
767
+ report_progress(reporter, phase="lift", status="started", completed=0, total=total, unit="function", message="lifting Python code objects")
768
+ functions = list(_lift_code_object_tree(pyc_module.code, reporter=reporter, total=total, progress=[0]))
727
769
  return assemble_vm_module(
728
770
  name=pyc_module.filename or "<python-pyc>",
729
771
  source_language="python",
@@ -750,7 +792,13 @@ def lift_code_object(code: PycCodeObject) -> FunctionIR:
750
792
  return function
751
793
 
752
794
 
753
- def _lift_code_object_tree(code: PycCodeObject) -> tuple[FunctionIR, ...]:
795
+ def _lift_code_object_tree(
796
+ code: PycCodeObject,
797
+ *,
798
+ reporter: ProgressReporter | None = None,
799
+ total: int | None = None,
800
+ progress: list[int] | None = None,
801
+ ) -> tuple[FunctionIR, ...]:
754
802
  spec = _python_function_spec(code)
755
803
  functions = [
756
804
  recover_vm_function(
@@ -759,11 +807,25 @@ def _lift_code_object_tree(code: PycCodeObject) -> tuple[FunctionIR, ...]:
759
807
  raw=tuple(_raw_instruction_line(instruction) for instruction in code.instructions),
760
808
  )
761
809
  ]
810
+ if reporter is not None and total is not None and progress is not None:
811
+ progress[0] += 1
812
+ report_progress(reporter, phase="lift", completed=progress[0], total=total, unit="function", message=f"lifting code object {progress[0]}/{total}")
762
813
  for child in code.children:
763
- functions.extend(_lift_code_object_tree(child))
814
+ functions.extend(
815
+ _lift_code_object_tree(
816
+ child,
817
+ reporter=reporter,
818
+ total=total,
819
+ progress=progress,
820
+ )
821
+ )
764
822
  return tuple(functions)
765
823
 
766
824
 
825
+ def _code_object_count(code: PycCodeObject) -> int:
826
+ return 1 + sum(_code_object_count(child) for child in code.children)
827
+
828
+
767
829
  def _python_low_effects(instruction, source: SourceRef) -> tuple[Effect, ...] | None:
768
830
  return PYTHON_EFFECT_TABLE.effects_for(None, instruction, source)
769
831
 
@@ -808,6 +870,14 @@ def _python_bytecode_steps(
808
870
  "target": region.target,
809
871
  "depth": region.depth,
810
872
  "lasti": region.lasti,
873
+ # The exception table declares the handler's input stack
874
+ # layout. These are VM-neutral stack facts; core owns
875
+ # all CFG construction and recovery decisions.
876
+ "stack_depth": region.depth,
877
+ "stack_suffix": (
878
+ *(("resume-position",) if region.lasti else ()),
879
+ "exception",
880
+ ),
811
881
  },
812
882
  label="protected-region",
813
883
  )
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from unidecompiler.core.ir import ModuleIR
4
4
  from unidecompiler.plugins import FrontendModule
5
+ from unidecompiler.progress import ProgressReporter
5
6
  from unidecompiler_plugin_python_pyc.lifter import lift_pyc_module
6
7
  from unidecompiler_plugin_python_pyc.pyc import decode_pyc, looks_like_pyc
7
8
  from unidecompiler_plugin_python_pyc.simulation import PythonPycSimulationAdapter
@@ -45,3 +46,8 @@ class PythonPycFrontendPlugin:
45
46
  )
46
47
 
47
48
  return lift_pyc_module(module.payload, module.metadata)
49
+
50
+ def lift_with_progress(self, module: FrontendModule, reporter: ProgressReporter) -> ModuleIR:
51
+ if module.frontend_id != self.id:
52
+ raise TypeError(f"Python pyc frontend cannot lift module from {module.frontend_id!r}")
53
+ return lift_pyc_module(module.payload, module.metadata, reporter=reporter)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unidecompiler-plugin-python-pyc
3
- Version: 0.1.5
3
+ Version: 0.2.0
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.10
12
+ Requires-Dist: unidecompiler<0.3.0,>=0.2.0
13
13
 
14
14
  # unidecompiler-plugin-python-pyc
15
15