cinderx 2026.1.16.2__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_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.
Files changed (68) hide show
  1. __static__/__init__.py +641 -0
  2. __static__/compiler_flags.py +8 -0
  3. __static__/enum.py +160 -0
  4. __static__/native_utils.py +77 -0
  5. __static__/type_code.py +48 -0
  6. __strict__/__init__.py +39 -0
  7. _cinderx.so +0 -0
  8. cinderx/__init__.py +577 -0
  9. cinderx/__pycache__/__init__.cpython-314.pyc +0 -0
  10. cinderx/_asyncio.py +156 -0
  11. cinderx/compileall.py +710 -0
  12. cinderx/compiler/__init__.py +40 -0
  13. cinderx/compiler/__main__.py +137 -0
  14. cinderx/compiler/config.py +7 -0
  15. cinderx/compiler/consts.py +72 -0
  16. cinderx/compiler/debug.py +70 -0
  17. cinderx/compiler/dis_stable.py +283 -0
  18. cinderx/compiler/errors.py +151 -0
  19. cinderx/compiler/flow_graph_optimizer.py +1287 -0
  20. cinderx/compiler/future.py +91 -0
  21. cinderx/compiler/misc.py +32 -0
  22. cinderx/compiler/opcode_cinder.py +18 -0
  23. cinderx/compiler/opcode_static.py +100 -0
  24. cinderx/compiler/opcodebase.py +158 -0
  25. cinderx/compiler/opcodes.py +991 -0
  26. cinderx/compiler/optimizer.py +547 -0
  27. cinderx/compiler/pyassem.py +3711 -0
  28. cinderx/compiler/pycodegen.py +7660 -0
  29. cinderx/compiler/pysourceloader.py +62 -0
  30. cinderx/compiler/static/__init__.py +1404 -0
  31. cinderx/compiler/static/compiler.py +629 -0
  32. cinderx/compiler/static/declaration_visitor.py +335 -0
  33. cinderx/compiler/static/definite_assignment_checker.py +280 -0
  34. cinderx/compiler/static/effects.py +160 -0
  35. cinderx/compiler/static/module_table.py +666 -0
  36. cinderx/compiler/static/type_binder.py +2176 -0
  37. cinderx/compiler/static/types.py +10580 -0
  38. cinderx/compiler/static/util.py +81 -0
  39. cinderx/compiler/static/visitor.py +91 -0
  40. cinderx/compiler/strict/__init__.py +69 -0
  41. cinderx/compiler/strict/class_conflict_checker.py +249 -0
  42. cinderx/compiler/strict/code_gen_base.py +409 -0
  43. cinderx/compiler/strict/common.py +507 -0
  44. cinderx/compiler/strict/compiler.py +352 -0
  45. cinderx/compiler/strict/feature_extractor.py +130 -0
  46. cinderx/compiler/strict/flag_extractor.py +97 -0
  47. cinderx/compiler/strict/loader.py +827 -0
  48. cinderx/compiler/strict/preprocessor.py +11 -0
  49. cinderx/compiler/strict/rewriter/__init__.py +5 -0
  50. cinderx/compiler/strict/rewriter/remove_annotations.py +84 -0
  51. cinderx/compiler/strict/rewriter/rewriter.py +975 -0
  52. cinderx/compiler/strict/runtime.py +77 -0
  53. cinderx/compiler/symbols.py +1754 -0
  54. cinderx/compiler/unparse.py +414 -0
  55. cinderx/compiler/visitor.py +194 -0
  56. cinderx/jit.py +230 -0
  57. cinderx/opcode.py +202 -0
  58. cinderx/static.py +113 -0
  59. cinderx/strictmodule.py +6 -0
  60. cinderx/test_support.py +341 -0
  61. cinderx-2026.1.16.2.dist-info/METADATA +15 -0
  62. cinderx-2026.1.16.2.dist-info/RECORD +68 -0
  63. cinderx-2026.1.16.2.dist-info/WHEEL +6 -0
  64. cinderx-2026.1.16.2.dist-info/licenses/LICENSE +21 -0
  65. cinderx-2026.1.16.2.dist-info/top_level.txt +5 -0
  66. opcodes/__init__.py +0 -0
  67. opcodes/assign_opcode_numbers.py +272 -0
  68. opcodes/cinderx_opcodes.py +121 -0
@@ -0,0 +1,409 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+
5
+ from __future__ import annotations
6
+
7
+ import ast
8
+ import builtins
9
+ import sys
10
+ from ast import (
11
+ AST,
12
+ AsyncFunctionDef,
13
+ Call,
14
+ ClassDef,
15
+ expr,
16
+ FunctionDef,
17
+ ImportFrom,
18
+ Name,
19
+ NodeVisitor,
20
+ stmt,
21
+ )
22
+ from typing import Any, cast, final, Mapping
23
+
24
+ from ..consts import CO_FUTURE_ANNOTATIONS
25
+ from ..pyassem import PyFlowGraph
26
+ from ..pycodegen import CinderCodeGenBase, CodeGenerator, find_futures, FOR_LOOP
27
+ from ..symbols import BaseSymbolVisitor, FunctionScope
28
+ from .common import FIXED_MODULES, lineinfo
29
+ from .feature_extractor import _IMPLICIT_GLOBALS, FeatureExtractor
30
+
31
+
32
+ def is_mutable(node: AST) -> bool:
33
+ return isinstance(node, Name) and node.id in ("mutable")
34
+
35
+
36
+ class FindClassDef(NodeVisitor):
37
+ def __init__(self) -> None:
38
+ self.has_class = False
39
+
40
+ def check(self, node: AST) -> None:
41
+ self.generic_visit(node)
42
+
43
+ def visit_ClassDef(self, node: ClassDef) -> None:
44
+ for n in node.decorator_list:
45
+ if is_mutable(n):
46
+ # do not consider this class for freezing
47
+ break
48
+ else:
49
+ self.has_class = True
50
+ for body_stmt in node.body:
51
+ # also consider inner classes
52
+ self.visit(body_stmt)
53
+
54
+ def visit_FunctionDef(self, node: FunctionDef) -> None:
55
+ # do not go into func body
56
+ pass
57
+
58
+ def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> None:
59
+ # do not go into func body
60
+ pass
61
+
62
+
63
+ class ForBodyHook(ast.stmt):
64
+ def __init__(self, node: list[ast.stmt], target: ast.expr) -> None:
65
+ self.body: list[ast.stmt] = node
66
+ self.target = target
67
+ lineinfo(self, target)
68
+
69
+
70
+ class TryFinallyHook(ast.stmt):
71
+ def __init__(self, finally_body: list[ast.stmt]) -> None:
72
+ self.finally_body = finally_body
73
+ # List of (handler, builtin_name)
74
+ self.handlers_to_restore: list[tuple[str, str]] = []
75
+ lineinfo(self, finally_body[0] if finally_body else None)
76
+
77
+
78
+ class TryHandlerBodyHook(ast.stmt):
79
+ def __init__(self, handler_body: list[ast.stmt], tracker_name: str) -> None:
80
+ self.handler_body = handler_body
81
+ self.tracker_name = tracker_name
82
+ lineinfo(self, handler_body[0] if handler_body else None)
83
+
84
+
85
+ class TryBodyHook(ast.stmt):
86
+ def __init__(self, body: list[ast.stmt]) -> None:
87
+ self.body = body
88
+ lineinfo(self, body[0] if body else None)
89
+ self.trackers: list[str] = []
90
+
91
+
92
+ class StrictCodeGenBase(CinderCodeGenBase):
93
+ flow_graph = PyFlowGraph
94
+ class_list_name: str = "<classes>"
95
+
96
+ def __init__(
97
+ self,
98
+ parent: CodeGenerator | None,
99
+ node: AST,
100
+ symbols: BaseSymbolVisitor,
101
+ graph: PyFlowGraph,
102
+ flags: int = 0,
103
+ optimization_lvl: int = 0,
104
+ builtins: dict[str, Any] = builtins.__dict__,
105
+ future_flags: int | None = None,
106
+ name: str | None = None,
107
+ ) -> None:
108
+ super().__init__(
109
+ parent,
110
+ node,
111
+ symbols,
112
+ graph,
113
+ flags=flags,
114
+ optimization_lvl=optimization_lvl,
115
+ future_flags=future_flags,
116
+ name=name,
117
+ )
118
+ self.has_class: bool = self.has_classDef(node)
119
+ self.made_class_list = False
120
+ self.builtins = builtins
121
+ # record the lineno of the first toplevel statement
122
+ # this will be the lineno for all extra generated code
123
+ # in the beginning of the strict module
124
+ self.first_body_node: ast.stmt | None = None
125
+ if not parent and isinstance(node, ast.Module) and len(node.body) > 0:
126
+ self.first_body_node = node.body[0]
127
+
128
+ if parent and isinstance(parent, StrictCodeGenBase):
129
+ self.feature_extractor: FeatureExtractor = parent.feature_extractor
130
+ else:
131
+ self.feature_extractor = FeatureExtractor(builtins, self.future_flags)
132
+ self.feature_extractor.visit(node)
133
+
134
+ @classmethod
135
+ def make_code_gen(
136
+ cls,
137
+ module_name: str,
138
+ tree: AST,
139
+ filename: str,
140
+ source: str | bytes | ast.Module | ast.Expression | ast.Interactive,
141
+ flags: int,
142
+ optimize: int,
143
+ ast_optimizer_enabled: bool = True,
144
+ builtins: dict[str, Any] = builtins.__dict__,
145
+ ) -> StrictCodeGenBase:
146
+ future_flags = find_futures(flags, tree)
147
+ if ast_optimizer_enabled:
148
+ tree = cls.optimize_tree(
149
+ optimize, tree, bool(future_flags & CO_FUTURE_ANNOTATIONS)
150
+ )
151
+ s = cls._SymbolVisitor(future_flags)
152
+ s.visit(tree)
153
+
154
+ graph = cls.flow_graph(module_name, filename, s.scopes[tree])
155
+ code_gen = cls(
156
+ None,
157
+ tree,
158
+ s,
159
+ graph,
160
+ flags=flags,
161
+ optimization_lvl=optimize,
162
+ builtins=builtins,
163
+ future_flags=future_flags,
164
+ )
165
+ code_gen._qual_name = module_name
166
+ code_gen.visit(tree)
167
+ return code_gen
168
+
169
+ def is_functionScope(self) -> bool:
170
+ """Whether the generator has any parent function scope"""
171
+ scope = self.scope
172
+ while scope is not None:
173
+ if isinstance(scope, FunctionScope):
174
+ return True
175
+ scope = scope.parent
176
+ return False
177
+
178
+ def has_classDef(self, node: AST) -> bool:
179
+ visitor = FindClassDef()
180
+ visitor.check(node)
181
+ return visitor.has_class
182
+
183
+ def emit_load_fixed_methods(self) -> None:
184
+ # load and store <strict-modules>
185
+ self.emit("LOAD_NAME", "<fixed-modules>")
186
+ self.emit("LOAD_CONST", "__strict__")
187
+ self.emit_binary_subscr()
188
+ self.emit_dup()
189
+ self.emit("STORE_GLOBAL", "<strict-modules>")
190
+ self.emit("LOAD_CONST", "freeze_type")
191
+ self.emit_binary_subscr()
192
+ self.emit("STORE_GLOBAL", "<freeze-type>")
193
+
194
+ def strictPreVisitCall(self, node: Call) -> None:
195
+ func = node.func
196
+ if isinstance(func, ast.Name):
197
+ # We don't currently allow aliasing or shadowing exec/eval
198
+ # so this check is currently sufficient.
199
+ if func.id == "exec" or func.id == "eval":
200
+ # exec and eval don't take keyword args, so we only need to
201
+ # inspect the normal arguments
202
+ if len(node.args) < 2:
203
+ # we're implicitly capturing globals, make it explicit so
204
+ # that we get our globals dictionary. exec/eval won't be
205
+ # able to mutate them though. We also need to explicitly
206
+ # call locals() here because the behavior of exec/eval is
207
+ # to use globals as locals if it is explicitly supplied.
208
+ def call_function(line_node: AST, name: str) -> expr:
209
+ load = lineinfo(ast.Name(name, ast.Load()))
210
+ call = lineinfo(ast.Call(load, [], []))
211
+ return call
212
+
213
+ node.args.append(call_function(node.args[0], "globals"))
214
+ node.args.append(call_function(node.args[0], "locals"))
215
+
216
+ def visitCall(self, node: Call) -> None:
217
+ self.strictPreVisitCall(node)
218
+
219
+ super().visitCall(node)
220
+
221
+ def visitForBodyHook(self, node: ForBodyHook) -> None:
222
+ self.visit_list(node.body)
223
+
224
+ def strictPreVisitFor(self, node: ast.For) -> None:
225
+ node.body = [ForBodyHook(node.body, node.target)]
226
+
227
+ def strictPostVisitFor(self, node: ast.For) -> None:
228
+ # And undo to keep any introspection happy
229
+ node.body = cast(ForBodyHook, node.body[0]).body
230
+
231
+ def visitFor(self, node: ast.For) -> None:
232
+ self.strictPreVisitFor(node)
233
+ super().visitFor(node)
234
+ self.strictPostVisitFor(node)
235
+
236
+ def make_function(
237
+ self, name: str, body: list[stmt], location_node: ast.AST | None = None
238
+ ) -> None:
239
+ # pyre-fixme[20]: Argument `args` expected.
240
+ func = lineinfo(ast.FunctionDef(), location_node)
241
+ func.name = name
242
+ func.decorator_list = []
243
+ func.returns = None
244
+ func.type_comment = ""
245
+ func.body = body
246
+ # pyre-fixme[20]: Argument `args` expected.
247
+ args = lineinfo(ast.arguments())
248
+ func.args = args
249
+
250
+ args.kwonlyargs = []
251
+ args.kw_defaults = []
252
+ args.defaults = []
253
+ args.args = []
254
+ args.posonlyargs = []
255
+ args.kwarg = None
256
+ args.vararg = None
257
+
258
+ scope = FunctionScope(name, self.scope.module, self.scope.klass)
259
+ scope.parent = self.scope
260
+ self.feature_extractor.scopes[func] = scope
261
+ self.scopes[func] = scope
262
+
263
+ self.visitFunctionDef(func)
264
+
265
+ def emit_append_class_list(self) -> None:
266
+ """
267
+ Assumes the class to append is TOS1.
268
+ Assumes `<classes>` is at TOS
269
+ Pops that class and append to `<classes>`
270
+ Do not Leave `<classes>` on the stack
271
+ """
272
+ self.emit_rotate_stack(2)
273
+ self.emit("LIST_APPEND", 1)
274
+ self.emit("POP_TOP")
275
+
276
+ def emit_create_class_list(self) -> None:
277
+ """create and store an empty list `<classes>`"""
278
+ self.emit("BUILD_LIST", 0)
279
+ op = "STORE_FAST" if self.is_functionScope() else "STORE_GLOBAL"
280
+ self.emit(op, self.class_list_name)
281
+ self.made_class_list = True
282
+
283
+ def emit_freeze_class_list(self) -> None:
284
+ """
285
+ create a for loop that iterates on all of `<classes>`
286
+ assign the value to `<class>`, and call
287
+ freeze_type on `<class>`
288
+ """
289
+
290
+ start = self.newBlock()
291
+ anchor = self.newBlock()
292
+ after = self.newBlock()
293
+ body = self.newBlock()
294
+
295
+ self.push_loop(FOR_LOOP, start, after)
296
+ self.emit_load_class_list()
297
+ self.emit("GET_ITER")
298
+
299
+ self.nextBlock(start)
300
+ # for <class> in <classes>
301
+ # we don't actually need to assign to <class>
302
+ self.emit("FOR_ITER", anchor)
303
+ self.nextBlock(body)
304
+ self.emit("LOAD_GLOBAL", "<freeze-type>")
305
+ # argument need to be top most
306
+ self.emit_rotate_stack(2)
307
+ self.emit_call_one_arg()
308
+ # discard the result of call
309
+ self.emit("POP_TOP")
310
+
311
+ self.emitJump(start)
312
+ self.nextBlock(anchor)
313
+ self.emit_end_for()
314
+ self.pop_loop()
315
+ self.nextBlock(after)
316
+
317
+ def emit_load_class_list(self) -> None:
318
+ """load `<classes>` on top of stack"""
319
+ op = "LOAD_FAST" if self.is_functionScope() else "LOAD_GLOBAL"
320
+ self.emit(op, self.class_list_name)
321
+
322
+ def find_immutability_flag(self, node: ClassDef) -> bool:
323
+ old_size = len(node.decorator_list)
324
+ node.decorator_list = [d for d in node.decorator_list if not is_mutable(d)]
325
+ return old_size == len(node.decorator_list)
326
+
327
+ def register_immutability(self, node: ClassDef, flag: bool) -> None:
328
+ if self.has_class and flag:
329
+ self.emit_dup()
330
+ self.emit_load_class_list()
331
+ self.emit_append_class_list()
332
+ super().register_immutability(node, flag)
333
+
334
+ def processBody(
335
+ self,
336
+ node: ast.FunctionDef | ast.AsyncFunctionDef | ast.Lambda,
337
+ body: list[ast.stmt],
338
+ gen: CodeGenerator,
339
+ ) -> None:
340
+ if (
341
+ isinstance(node, (FunctionDef, AsyncFunctionDef))
342
+ and isinstance(gen, StrictCodeGenBase)
343
+ and gen.has_class
344
+ ):
345
+ # initialize the <classes> list
346
+ if not gen.made_class_list:
347
+ gen.emit_create_class_list()
348
+ # create a try + finally structure where we freeze all classes
349
+ # in the finally block
350
+ gen.emit_try_finally(
351
+ None,
352
+ lambda: super(StrictCodeGenBase, self).processBody(node, body, gen),
353
+ lambda: gen.emit_freeze_class_list(),
354
+ )
355
+ else:
356
+ super().processBody(node, body, gen)
357
+
358
+ def startModule(self) -> None:
359
+ first_node = self.first_body_node
360
+ if first_node:
361
+ self.set_pos(first_node)
362
+ self.emit_load_fixed_methods()
363
+ if self.has_class and not self.made_class_list:
364
+ self.emit_create_class_list()
365
+ super().startModule()
366
+
367
+ def emit_module_return(self, node: ast.Module) -> None:
368
+ if self.has_class:
369
+ self.emit_freeze_class_list()
370
+ super().emit_module_return(node)
371
+
372
+ def emit_import_fixed_modules(
373
+ self, node: ImportFrom, mod_name: str, mod: Mapping[str, object]
374
+ ) -> None:
375
+ new_names = [n for n in node.names if n.name not in mod]
376
+ if new_names:
377
+ # We have names we don't know about, keep them around...
378
+ new_node = ImportFrom(mod_name, new_names, node.level)
379
+ super().visitImportFrom(new_node)
380
+
381
+ # Load the module into TOS...
382
+ self.emit("LOAD_NAME", "<fixed-modules>")
383
+ self.emit("LOAD_CONST", mod_name)
384
+ self.emit_binary_subscr() # TOS = mod
385
+
386
+ # Store all of the imported names from the module
387
+ for _i, name in enumerate(node.names):
388
+ var_name = name.name
389
+ asname = name.asname or var_name
390
+ value = mod.get(var_name)
391
+ if value is not None:
392
+ # duplicate TOS (mod)
393
+ self.emit_dup()
394
+ # var name
395
+ self.emit("LOAD_CONST", var_name)
396
+ self.emit_binary_subscr()
397
+ self.emit("STORE_GLOBAL", asname)
398
+ # remove TOS (mod)
399
+ self.emit("POP_TOP")
400
+
401
+ @final
402
+ def visitImportFrom(self, node: ImportFrom) -> None:
403
+ if node.level == 0 and node.module is not None and node.module in FIXED_MODULES:
404
+ module_name = node.module
405
+ assert module_name is not None
406
+ mod = FIXED_MODULES[module_name]
407
+ self.emit_import_fixed_modules(node, module_name, mod)
408
+ return
409
+ super().visitImportFrom(node)