unidecompiler-plugin-python-pyc 0.1.2__tar.gz → 0.1.3__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.3}/PKG-INFO +2 -2
  2. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/pyproject.toml +2 -2
  3. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc/lifter.py +81 -126
  4. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc.egg-info/PKG-INFO +2 -2
  5. unidecompiler_plugin_python_pyc-0.1.3/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.3}/README.md +0 -0
  8. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/setup.cfg +0 -0
  9. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc/__init__.py +0 -0
  10. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc/plugin.py +0 -0
  11. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc/pyc.py +0 -0
  12. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc/simulation.py +0 -0
  13. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/src/unidecompiler_plugin_python_pyc/support.py +0 -0
  14. {unidecompiler_plugin_python_pyc-0.1.2 → unidecompiler_plugin_python_pyc-0.1.3}/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.3}/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.3}/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.3}/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.3
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.6
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.3"
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.6,<0.2.0"]
14
14
 
15
15
  [project.urls]
16
16
  Homepage = "https://github.com/Wker666/unidecompiler"
@@ -32,11 +32,13 @@ from unidecompiler.core.effects import (
32
32
  Iterate,
33
33
  MakeFunctionValue,
34
34
  MergeMap,
35
+ OfferImplicitCallArguments,
35
36
  Pop,
36
37
  Push,
37
38
  RaiseTop,
38
39
  RaiseWithCause,
39
40
  ReraiseTop,
41
+ ResumeValue,
40
42
  ReturnTop,
41
43
  ReturnVoid,
42
44
  StoreAttr,
@@ -55,7 +57,6 @@ from unidecompiler.core.effects import (
55
57
  )
56
58
  from unidecompiler.core.vm_module import assemble_vm_module
57
59
  from unidecompiler.core.ir import (
58
- Assign,
59
60
  BinaryOp,
60
61
  CapturedVar,
61
62
  Const,
@@ -83,15 +84,13 @@ from unidecompiler.provenance import ByteRange
83
84
  from unidecompiler.core.vm_region import (
84
85
  VMLinearState,
85
86
  VMRegionCallbacks,
87
+ VMRegionSlice,
86
88
  VMRegionOpcodeClasses,
87
89
  VMRegionProfile,
88
90
  VMStatefulCallbacks,
89
91
  build_hint_region_profile,
90
92
  lift_control_region as lift_vm_control_region,
91
93
  )
92
- from unidecompiler.core.vm_structures import (
93
- vm_unsupported,
94
- )
95
94
  from unidecompiler_plugin_python_pyc.pyc import PycCodeObject, PycExceptionRegion, PycModule
96
95
 
97
96
 
@@ -122,7 +121,6 @@ BINARY_SYMBOLS = {
122
121
  COMPLEX_OPS = {
123
122
  }
124
123
  IGNORED_OPS = {
125
- "RESUME",
126
124
  "CACHE",
127
125
  "NOP",
128
126
  "EXTENDED_ARG",
@@ -130,7 +128,7 @@ IGNORED_OPS = {
130
128
  "COPY_FREE_VARS",
131
129
  }
132
130
  PYTHON_REGION_OPCODE_CLASSES = VMRegionOpcodeClasses(
133
- noise=frozenset({"END_FOR", "POP_TOP", "NOP", "RESUME"}),
131
+ noise=frozenset({"END_FOR", "POP_TOP", "NOP"}),
134
132
  control=frozenset(
135
133
  {
136
134
  "POP_JUMP_IF_FALSE",
@@ -283,7 +281,10 @@ def _build_set(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
283
281
 
284
282
 
285
283
  def _get_iter(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
286
- return (Iterate(source=source),)
284
+ return (
285
+ Iterate(source=source),
286
+ OfferImplicitCallArguments(source=source, max_explicit_arg_count=0),
287
+ )
287
288
 
288
289
 
289
290
  def _unpack_sequence(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -360,7 +361,10 @@ def _binary_slice(_context, _instruction, source: SourceRef) -> tuple[Effect, ..
360
361
 
361
362
 
362
363
  def _store_slice(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
363
- return (StoreItemEffect(source=source),)
364
+ return (
365
+ BuildCall(source=source, arg_count=2, callee=Global(name="slice", source=source)),
366
+ StoreItemEffect(source=source, order="value-obj-key"),
367
+ )
364
368
 
365
369
 
366
370
  def _list_extend(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -443,8 +447,8 @@ def _map_add(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
443
447
  return (StoreItemAtDepth(source=source, depth=_instruction_count(instruction)),)
444
448
 
445
449
 
446
- def _set_add(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
447
- return (SetAdd(source=source),)
450
+ def _set_add(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
451
+ return (SetAdd(source=source, depth=_instruction_count(instruction)),)
448
452
 
449
453
 
450
454
  def _store_local(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
@@ -484,6 +488,13 @@ def _yield_value(_context, instruction, source: SourceRef) -> tuple[Effect, ...]
484
488
  return (YieldTop(source=source, default=Const(value=None, source=source)),)
485
489
 
486
490
 
491
+ def _resume(_context, instruction, source: SourceRef) -> tuple[Effect, ...]:
492
+ # CPython's resume mode 5 continues a ``yield`` expression, whose value
493
+ # is supplied by the caller. Await/send modes manage their result in the
494
+ # surrounding protocol and must not manufacture an extra stack value.
495
+ return (ResumeValue(source=source),) if _instruction_count(instruction) == 5 else ()
496
+
497
+
487
498
  def _send(_context, _instruction, source: SourceRef) -> tuple[Effect, ...]:
488
499
  return (Pop(source=source, count=1, allow_missing=True),)
489
500
 
@@ -605,6 +616,7 @@ PYTHON_EFFECT_TABLE = VMEffectTable(
605
616
  "BUILD_TUPLE": _build_tuple,
606
617
  "BUILD_SET": _build_set,
607
618
  "SET_ADD": _set_add,
619
+ "RESUME": _resume,
608
620
  "GET_ITER": _get_iter,
609
621
  "GET_YIELD_FROM_ITER": _get_iter,
610
622
  "UNPACK_SEQUENCE": _unpack_sequence,
@@ -684,8 +696,8 @@ def lift_code_object(code: PycCodeObject) -> FunctionIR:
684
696
  _python_function_spec(code),
685
697
  steps,
686
698
  profile=_python_region_profile(steps, instructions),
687
- callbacks=_python_region_callbacks(code, instructions),
688
- stateful_callbacks=_python_stateful_callbacks(code, instructions),
699
+ callbacks_factory=lambda normalized_steps: _python_region_callbacks(code, normalized_steps),
700
+ stateful_callbacks_factory=lambda normalized_steps: _python_stateful_callbacks(code, normalized_steps),
689
701
  initial_locals=_python_initial_locals(code),
690
702
  raw_window=lambda index: _raw_instruction_window(instructions, index),
691
703
  )
@@ -884,21 +896,40 @@ def _python_initial_locals(code: PycCodeObject) -> dict[str, Expr]:
884
896
 
885
897
  def _python_region_callbacks(
886
898
  code: PycCodeObject,
887
- instructions: tuple[object, ...],
899
+ prepared_steps: tuple[VMBytecodeStep, ...],
888
900
  ) -> VMRegionCallbacks[VMBytecodeStep]:
901
+ def lift_slice(slice_start, slice_end, stack):
902
+ slice_steps = prepared_steps[slice_start:slice_end]
903
+ result = lift_steps(
904
+ slice_steps,
905
+ initial_locals=_python_initial_locals(code),
906
+ initial_stack=stack,
907
+ )
908
+ if result.state.diagnostics:
909
+ return VMRegionSlice(stopped_at=slice_start)
910
+ output: list[object] = list(result.state.statements)
911
+ if result.stopped_at is not None and result.state.terminator is None:
912
+ return VMRegionSlice(
913
+ statements=tuple(output),
914
+ stopped_at=slice_start + slice_steps.index(result.stopped_at),
915
+ )
916
+ if result.state.terminator is not None:
917
+ output.append(result.state.terminator)
918
+ return VMRegionSlice(statements=tuple(output))
919
+
920
+ def lift_expr(slice_start, slice_end, stack):
921
+ result = lift_steps(
922
+ prepared_steps[slice_start:slice_end],
923
+ initial_locals=_python_initial_locals(code),
924
+ initial_stack=stack,
925
+ )
926
+ if result.state.diagnostics or not result.state.stack:
927
+ return None
928
+ return result.state.stack[-1]
929
+
889
930
  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
- ),
931
+ lift_slice=lift_slice,
932
+ lift_expr=lift_expr,
902
933
  lift_iter_loop=lambda _get_iter_index, _iterable: None,
903
934
  lift_async_iter_loop=lambda _prefix_start, _get_aiter_index, _region_end: None,
904
935
  lift_comprehension=lambda _prefix_start, _get_iter_index, _region_end, _iterable: None,
@@ -907,45 +938,38 @@ def _python_region_callbacks(
907
938
 
908
939
  def _python_stateful_callbacks(
909
940
  code: PycCodeObject,
910
- instructions: tuple[object, ...],
941
+ prepared_steps: tuple[VMBytecodeStep, ...],
911
942
  ) -> VMStatefulCallbacks[VMBytecodeStep]:
943
+ def lift_linear(start, end, locals_, stack):
944
+ slice_steps = prepared_steps[start:end]
945
+ result = lift_steps(
946
+ slice_steps,
947
+ initial_locals=locals_,
948
+ initial_stack=stack,
949
+ )
950
+ stopped_at_terminal_end = (
951
+ result.stopped_at is slice_steps[-1]
952
+ if slice_steps and result.state.terminator is not None
953
+ else False
954
+ )
955
+ if result.state.diagnostics or (
956
+ result.stopped_at is not None and not stopped_at_terminal_end
957
+ ):
958
+ return None
959
+ return VMLinearState(
960
+ locals=result.state.locals,
961
+ stack=tuple(result.state.stack),
962
+ statements=tuple(result.state.statements),
963
+ terminator=result.state.terminator,
964
+ )
965
+
912
966
  return VMStatefulCallbacks(
913
967
  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
- ),
968
+ lift_linear=lift_linear,
919
969
  branch_condition=_python_branch_condition,
920
970
  )
921
971
 
922
972
 
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
973
  def _python_branch_condition(branch: VMBytecodeStep, stack: tuple[Expr, ...]) -> Expr | None:
950
974
  if not stack:
951
975
  return None
@@ -962,11 +986,6 @@ def _python_branch_condition(branch: VMBytecodeStep, stack: tuple[Expr, ...]) ->
962
986
  return condition
963
987
 
964
988
 
965
- class _PycLinearLocals:
966
- def __init__(self, varnames: tuple[str, ...]) -> None:
967
- self.varnames = varnames
968
-
969
-
970
989
  def _python_region_profile(
971
990
  steps: tuple[VMBytecodeStep, ...],
972
991
  instructions: tuple[object, ...],
@@ -999,70 +1018,6 @@ def _await_region_end(instructions: tuple[object, ...], start: int, end: int) ->
999
1018
  return None
1000
1019
 
1001
1020
 
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
1021
  def _instruction_count(instruction) -> int:
1067
1022
  return instruction.arg if isinstance(instruction.arg, int) else 0
1068
1023
 
@@ -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.3
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.6
13
13
 
14
14
  # unidecompiler-plugin-python-pyc
15
15