Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions xdis/codetype/code312rust.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# (C) Copyright 2025 by Rocky Bernstein
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

from dataclasses import dataclass
from typing import Any, Dict, Tuple, Union

from xdis.codetype.code311 import Code311


@dataclass
class SourceLocation:
# 1-based integer line number
line_number: int
# column offset in line: 1-based int (constructed from a zero-indexed stored value)
column_offset: int


class Code312Rust(Code311):
"""Class for a RustPython 3.12 code object used when a Python
interpreter is not RustPython 3.12 but working on RustPython 3.12 bytecode. It
also functions as an object that can be used to build or write a
Python3 code object, since we allow mutable structures.

When done mutating, call method to_native().

For convenience in generating code objects, fields like
`co_consts`, co_names which are (immutable) tuples in the end-result can be stored
instead as (mutable) lists. Likewise, the line number table `co_lnotab`
can be stored as a simple list of offset, line_number tuples.

"""
def __init__(
self,
co_argcount: int,
co_posonlyargcount: int,
co_kwonlyargcount: int,
co_stacksize: int,
co_flags: int,
co_code: bytes,
co_consts: tuple,
co_names: tuple[str],
co_varnames: tuple[str],
co_filename: str,
co_name: str,
co_firstlineno: int,
co_freevars: tuple,
co_cellvars: tuple,
version_triple: Tuple[int, int, int],
locations: tuple,
collection_order: Dict[Union[set, frozenset, dict], Tuple[Any]] = {},
) -> None:
self.co_argcount = co_argcount
self.co_posonlyargcount = co_posonlyargcount
self.co_kwonlyargcount = co_kwonlyargcount
self.co_lnotab={} # FIXME: compute this from locations.
self.co_stacksize = co_stacksize
self.co_flags = co_flags
self.co_code = co_code
self.co_consts = co_consts
self.co_names = co_names
self.co_varnames = co_varnames
self.co_filename = co_filename
self.co_name = co_name
self.co_firstlineno = co_firstlineno # None if 0 in serialized form
self.co_cellvars = co_cellvars
self.co_freevars= co_freevars
self.co_collection_order = collection_order
self.version_triple = version_triple
self.locations = locations
1 change: 0 additions & 1 deletion xdis/codetype/code313rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ def __init__(
self.co_linetable = co_linetable
self.co_qualname = co_qualname
self.co_cellvars = co_cellvars
self.co_linetable = co_linetable
self.co_freevars= co_freevars
self.co_exceptiontable = co_exceptiontable
self.co_collection_order = collection_order
Expand Down
4 changes: 2 additions & 2 deletions xdis/cross_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def findlinestarts(code, dup_lines: bool = False):
Generate pairs (offset, lineno) as described in Python/compile.c.
"""

if hasattr(code, "co_lines"):
if hasattr(code, "co_lines") and hasattr(code, "co_linetable"):
# Taken from 3.10 findlinestarts
lastline = None
for start, _, line in code.co_lines():
Expand Down Expand Up @@ -325,7 +325,7 @@ def format_code_info(
lines.append("# Keyword-only arguments: %s" % co.co_kwonlyargcount)

pos_argc = co.co_argcount
if version_tuple >= (1, 3):
if hasattr(co, "co_nlocals"):
lines.append("# Number of locals: %s" % co.co_nlocals)
if version_tuple >= (1, 5):
lines.append("# Stack size: %s" % co.co_stacksize)
Expand Down
21 changes: 15 additions & 6 deletions xdis/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,24 @@ def load_module_from_file_object(
magic = fp.read(4)
magic_int = magic2int(magic)

if magic_int == 3531:
# this magic int is used for both 3.12 and 3.13Rust!
# Disambiguate using the fact that CPython 3.13 stores 0xe3
# "c" | 0x80 at offoset 0x10 while RustPython uses "c" (no 0x80).
fp.seek(0x10)
code_type = fp.read(1)
if code_type == b'c':
# Is RustPython 3.13 using CPython's 3.12 magic number.
magic_int = 35310
else:
assert code_type == b'\xe3', "Expecting magic int 3531 to have a code type b'0x63 or b'0x33' at offset 0x10"
fp.seek(0x04)

# For reasons I don't understand, PyPy 3.2 stores a magic
# of '0'... The two values below are for Python 2.x and 3.x respectively
if magic[0:1] in ["0", b"0"]:
magic = int2magic(3180 + 7)
magic_int = magic2int(magic)

try:
# FIXME: use the internal routine below
Expand All @@ -245,10 +259,7 @@ def load_module_from_file_object(
else:
raise ImportError(f"Bad magic number: '{magic}'")

if magic_int in [2657, 22138] + list(
RUSTPYTHON_MAGICS
) + list(JYTHON_MAGICS):
version = magicint2version.get(magic_int, "")
version = magic_int2tuple(magic_int)

if magic_int in INTERIM_MAGIC_INTS:
raise ImportError(
Expand All @@ -268,8 +279,6 @@ def load_module_from_file_object(

try:
my_magic_int = PYTHON_MAGIC_INT
magic_int = magic2int(magic)
version = magic_int2tuple(magic_int)

timestamp = None
source_size = None
Expand Down
15 changes: 10 additions & 5 deletions xdis/magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,13 @@
# See below for mapping to version numbers.
PYPY3_MAGICS = (48, 64, 112, 160, 192, 240, 244, 256, 320, 336, 384, 416)

# Rust Magics is a git commit number!
RUSTPYTHON_MAGICS = (
12641, # RustPython 3.12
12897, # RustPython 3.12
12897, # RustPython 3.12.0 0.4.0
13413, # RustPython 3.13
24881, # RustPython 3.13
24881, # RustPython 3.13 0.4.0
35310, # RustPython 3.13 0.4.0 later version! Actually 3531 is stored in file
)

# A list of interim Python version magic numbers used, but were not
Expand Down Expand Up @@ -515,7 +517,7 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict:
add_magic_from_int(3512, "3.12a2c")
add_magic_from_int(3513, "3.12a4a")
add_magic_from_int(3514, "3.12a4b")
add_magic_from_int(3515, "3.12a5a")
# add_magic_from_int(3515, "3.12a5a") # Rust Python 3.13 0.40 uses this too!
add_magic_from_int(3516, "3.12a5b")
add_magic_from_int(3517, "3.12a5c")
add_magic_from_int(3518, "3.12a6a")
Expand Down Expand Up @@ -695,9 +697,12 @@ def __by_version(magic_versions: Dict[bytes, str]) -> dict:
add_magic_from_int(416, "3.11.13PyPy") # PyPy 3.11.13 or pypy3.11-7.3.20

add_magic_from_int(12641, "3.12.0a.rust") # RustPython 3.12.0
add_magic_from_int(12897, "3.13.0b.rust") # RustPython 3.12.0
add_magic_from_int(12897, "3.12.0.rust") # RustPython 3.12.0 0.4.0
add_magic_from_int(13413, "3.13.0a.rust") # RustPython 3.13.0
add_magic_from_int(24881, "3.13.0b.rust") # RustPython 3.13.0
add_magic_from_int(24881, "3.13.0b.rust") # RustPython 3.13.0 0.4.0

# Actually we should add 3531, but that already means CPython 3.12!
add_magic_from_int(35310, "3.13.1.rust") # RustPython 3.13.0 0.4.0

# Graal Python. Graal uses its own JVM-ish CPython bytecode, not
# true CPython or PyPy bytecode.
Expand Down
12 changes: 8 additions & 4 deletions xdis/op_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@
opcode_311pypy,
opcode_312,
opcode_313,
opcode_313rust,
opcode_314,
opcode_3531rust,
opcode_12897rust,
opcode_24481rust,
)
from xdis.opcodes.opcode_graal import (
opcode_38graal,
Expand Down Expand Up @@ -187,10 +189,12 @@
3.11: opcode_311,
"3.12.7Graal": opcode_312graal, # this right?
"3.12.8Graal": opcode_312graal, # this right?
"3.12.0Rust": opcode_12897rust,
"3.12.0rc2": opcode_312,
"3.12.0": opcode_312,
"3.13.0rc3": opcode_313,
"3.13.0Rust": opcode_313rust,
"3.13.0Rust": opcode_24481rust,
"3.13.1Rust": opcode_3531rust,
"3.14b3": opcode_314,
"3.14.0": opcode_314,
"3.14": opcode_314,
Expand All @@ -211,7 +215,7 @@ def get_opcode_module(version_info: Tuple[int, ...], implementation: PythonImple
if vers_str not in canonic_python_version:
vers_str = version_tuple_to_str(version_info[:2])

if implementation != PythonImplementation.CPython:
if str(implementation) != str(PythonImplementation.CPython):
vers_str += str(implementation)

return op_imports[canonic_python_version[vers_str]]
Expand Down Expand Up @@ -330,4 +334,4 @@ def remap_opcodes(op_obj, alternate_opmap):

if __name__ == "__main__":
from version_info import PYTHON_IMPLEMENTATION, PYTHON_VERSION_TRIPLE
print(get_opcode_module(PYTHON_VERSION_TRIPLE, PYTHON_IMPLEMENTATION))
print(get_opcode_module(PYTHON_VERSION_TRIPLE[:2], PYTHON_IMPLEMENTATION))
10 changes: 4 additions & 6 deletions xdis/opcodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
opcode_310pypy,
opcode_311pypy,
)
from xdis.opcodes.opcode_rust import opcode_313rust # opcode_312rust,
from xdis.opcodes.opcode_rust import opcode_3531rust, opcode_12897rust, opcode_24481rust

# from xdis.opcodes.opcode_graal import (
# opcode_310graal,
Expand All @@ -78,7 +78,6 @@
# )



__all__ = [
"opcode_10",
"opcode_11",
Expand Down Expand Up @@ -123,9 +122,8 @@
"opcode_39pypy",
# "opcode_310graal",
# "opcode_311graal",
# "opcode_312graal",
"opcode_38graal",
# "opcode_312rust",
"opcode_313rust",

"opcode_3531rust",
"opcode_12897rust",
"opcode_24481rust",
]
Loading