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.
- __static__/__init__.py +641 -0
- __static__/compiler_flags.py +8 -0
- __static__/enum.py +160 -0
- __static__/native_utils.py +77 -0
- __static__/type_code.py +48 -0
- __strict__/__init__.py +39 -0
- _cinderx.so +0 -0
- cinderx/__init__.py +577 -0
- cinderx/__pycache__/__init__.cpython-314.pyc +0 -0
- cinderx/_asyncio.py +156 -0
- cinderx/compileall.py +710 -0
- cinderx/compiler/__init__.py +40 -0
- cinderx/compiler/__main__.py +137 -0
- cinderx/compiler/config.py +7 -0
- cinderx/compiler/consts.py +72 -0
- cinderx/compiler/debug.py +70 -0
- cinderx/compiler/dis_stable.py +283 -0
- cinderx/compiler/errors.py +151 -0
- cinderx/compiler/flow_graph_optimizer.py +1287 -0
- cinderx/compiler/future.py +91 -0
- cinderx/compiler/misc.py +32 -0
- cinderx/compiler/opcode_cinder.py +18 -0
- cinderx/compiler/opcode_static.py +100 -0
- cinderx/compiler/opcodebase.py +158 -0
- cinderx/compiler/opcodes.py +991 -0
- cinderx/compiler/optimizer.py +547 -0
- cinderx/compiler/pyassem.py +3711 -0
- cinderx/compiler/pycodegen.py +7660 -0
- cinderx/compiler/pysourceloader.py +62 -0
- cinderx/compiler/static/__init__.py +1404 -0
- cinderx/compiler/static/compiler.py +629 -0
- cinderx/compiler/static/declaration_visitor.py +335 -0
- cinderx/compiler/static/definite_assignment_checker.py +280 -0
- cinderx/compiler/static/effects.py +160 -0
- cinderx/compiler/static/module_table.py +666 -0
- cinderx/compiler/static/type_binder.py +2176 -0
- cinderx/compiler/static/types.py +10580 -0
- cinderx/compiler/static/util.py +81 -0
- cinderx/compiler/static/visitor.py +91 -0
- cinderx/compiler/strict/__init__.py +69 -0
- cinderx/compiler/strict/class_conflict_checker.py +249 -0
- cinderx/compiler/strict/code_gen_base.py +409 -0
- cinderx/compiler/strict/common.py +507 -0
- cinderx/compiler/strict/compiler.py +352 -0
- cinderx/compiler/strict/feature_extractor.py +130 -0
- cinderx/compiler/strict/flag_extractor.py +97 -0
- cinderx/compiler/strict/loader.py +827 -0
- cinderx/compiler/strict/preprocessor.py +11 -0
- cinderx/compiler/strict/rewriter/__init__.py +5 -0
- cinderx/compiler/strict/rewriter/remove_annotations.py +84 -0
- cinderx/compiler/strict/rewriter/rewriter.py +975 -0
- cinderx/compiler/strict/runtime.py +77 -0
- cinderx/compiler/symbols.py +1754 -0
- cinderx/compiler/unparse.py +414 -0
- cinderx/compiler/visitor.py +194 -0
- cinderx/jit.py +230 -0
- cinderx/opcode.py +202 -0
- cinderx/static.py +113 -0
- cinderx/strictmodule.py +6 -0
- cinderx/test_support.py +341 -0
- cinderx-2026.1.16.2.dist-info/METADATA +15 -0
- cinderx-2026.1.16.2.dist-info/RECORD +68 -0
- cinderx-2026.1.16.2.dist-info/WHEEL +6 -0
- cinderx-2026.1.16.2.dist-info/licenses/LICENSE +21 -0
- cinderx-2026.1.16.2.dist-info/top_level.txt +5 -0
- opcodes/__init__.py +0 -0
- opcodes/assign_opcode_numbers.py +272 -0
- opcodes/cinderx_opcodes.py +121 -0
|
@@ -0,0 +1,1404 @@
|
|
|
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
|
+
Assign,
|
|
12
|
+
AST,
|
|
13
|
+
AsyncFunctionDef,
|
|
14
|
+
Attribute,
|
|
15
|
+
BinOp,
|
|
16
|
+
BoolOp,
|
|
17
|
+
Call,
|
|
18
|
+
ClassDef,
|
|
19
|
+
cmpop,
|
|
20
|
+
Compare,
|
|
21
|
+
Constant,
|
|
22
|
+
DictComp,
|
|
23
|
+
expr,
|
|
24
|
+
FunctionDef,
|
|
25
|
+
Module,
|
|
26
|
+
Name,
|
|
27
|
+
UnaryOp,
|
|
28
|
+
)
|
|
29
|
+
from collections.abc import Callable as typingCallable, Generator
|
|
30
|
+
from contextlib import contextmanager
|
|
31
|
+
from types import CodeType
|
|
32
|
+
from typing import Any, cast
|
|
33
|
+
|
|
34
|
+
from .. import consts, opcode_static
|
|
35
|
+
from ..opcodebase import Opcode
|
|
36
|
+
from ..pyassem import (
|
|
37
|
+
Block,
|
|
38
|
+
IndexedSet,
|
|
39
|
+
PyFlowGraph,
|
|
40
|
+
PyFlowGraph312,
|
|
41
|
+
PyFlowGraph314,
|
|
42
|
+
PyFlowGraph315,
|
|
43
|
+
PyFlowGraphCinder310,
|
|
44
|
+
)
|
|
45
|
+
from ..pycodegen import (
|
|
46
|
+
CinderCodeGenerator310,
|
|
47
|
+
CinderCodeGenerator312,
|
|
48
|
+
CinderCodeGenerator314,
|
|
49
|
+
CinderCodeGenerator315,
|
|
50
|
+
CodeGenerator,
|
|
51
|
+
CodeGenTree,
|
|
52
|
+
compile_code,
|
|
53
|
+
CompNode,
|
|
54
|
+
FuncOrLambda,
|
|
55
|
+
PatternContext,
|
|
56
|
+
)
|
|
57
|
+
from ..strict import (
|
|
58
|
+
StrictCodeGenerator310,
|
|
59
|
+
StrictCodeGenerator312,
|
|
60
|
+
StrictCodeGenerator314,
|
|
61
|
+
)
|
|
62
|
+
from ..strict.code_gen_base import StrictCodeGenBase
|
|
63
|
+
from ..strict.common import FIXED_MODULES
|
|
64
|
+
from ..symbols import BaseSymbolVisitor, ClassScope, ModuleScope, Scope
|
|
65
|
+
from .compiler import Compiler
|
|
66
|
+
from .definite_assignment_checker import DefiniteAssignmentVisitor
|
|
67
|
+
from .effects import NarrowingEffect, TypeState
|
|
68
|
+
from .module_table import ModuleFlag, ModuleTable
|
|
69
|
+
from .type_binder import UsedRefinementField
|
|
70
|
+
from .types import (
|
|
71
|
+
ASYNC_CACHED_PROPERTY_IMPL_PREFIX,
|
|
72
|
+
AsyncCachedPropertyMethod,
|
|
73
|
+
AwaitableType,
|
|
74
|
+
CACHED_PROPERTY_IMPL_PREFIX,
|
|
75
|
+
CachedPropertyMethod,
|
|
76
|
+
CInstance,
|
|
77
|
+
Class,
|
|
78
|
+
CType,
|
|
79
|
+
Dataclass,
|
|
80
|
+
DataclassDecorator,
|
|
81
|
+
DataclassField,
|
|
82
|
+
DecoratedMethod,
|
|
83
|
+
DynamicInstance,
|
|
84
|
+
EAGER_IMPORT_NAME,
|
|
85
|
+
Function,
|
|
86
|
+
FunctionContainer,
|
|
87
|
+
GenericClass,
|
|
88
|
+
KnownBoolean,
|
|
89
|
+
Slot,
|
|
90
|
+
TMP_VAR_PREFIX,
|
|
91
|
+
TType,
|
|
92
|
+
TypeDescr,
|
|
93
|
+
Value,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
from cinder import _set_qualname
|
|
98
|
+
except ImportError:
|
|
99
|
+
|
|
100
|
+
def _set_qualname(code: CodeType, qualname: str) -> None:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def exec_static(
|
|
105
|
+
source: str,
|
|
106
|
+
locals: dict[str, object],
|
|
107
|
+
globals: dict[str, object],
|
|
108
|
+
modname: str = "<module>",
|
|
109
|
+
) -> None:
|
|
110
|
+
code = compile_code(
|
|
111
|
+
source, "<module>", "exec", compiler=StaticCodeGenerator, modname=modname
|
|
112
|
+
)
|
|
113
|
+
if "<fixed-modules>" not in globals:
|
|
114
|
+
globals["<fixed-modules>"] = FIXED_MODULES
|
|
115
|
+
if "<builtins>" not in globals:
|
|
116
|
+
globals["<builtins>"] = builtins.__dict__
|
|
117
|
+
|
|
118
|
+
exec(code, locals, globals)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class StaticPatternContext(PatternContext):
|
|
122
|
+
def __init__(self) -> None:
|
|
123
|
+
super().__init__()
|
|
124
|
+
self.type_dict: dict[str, Class] = {}
|
|
125
|
+
|
|
126
|
+
def clone(self) -> StaticPatternContext:
|
|
127
|
+
pc = StaticPatternContext()
|
|
128
|
+
pc.stores = list(self.stores)
|
|
129
|
+
pc.allow_irrefutable = self.allow_irrefutable
|
|
130
|
+
pc.fail_pop = list(self.fail_pop)
|
|
131
|
+
pc.on_top = self.on_top
|
|
132
|
+
pc.type_dict = self.type_dict.copy()
|
|
133
|
+
return pc
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class PyFlowGraphStatic310(PyFlowGraphCinder310):
|
|
137
|
+
opcode: Opcode = opcode_static.opcode
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class PyFlowGraphStatic312(PyFlowGraph312):
|
|
141
|
+
opcode: Opcode = opcode_static.opcode
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class InitSubClassGenerator:
|
|
145
|
+
def __init__(self, flow_graph: PyFlowGraph, qualname: str) -> None:
|
|
146
|
+
self.flow_graph = flow_graph
|
|
147
|
+
self.qualname = qualname
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def scope(self) -> Scope:
|
|
151
|
+
raise NotImplementedError()
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def name(self) -> str:
|
|
155
|
+
return self.qualname
|
|
156
|
+
|
|
157
|
+
def getCode(self) -> CodeType:
|
|
158
|
+
code = self.flow_graph.getCode()
|
|
159
|
+
_set_qualname(code, self.qualname)
|
|
160
|
+
return code
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class StaticCodeGenBase(StrictCodeGenBase):
|
|
164
|
+
flow_graph = PyFlowGraph
|
|
165
|
+
_default_cache: dict[type[ast.AST], typingCallable[..., None]] = {}
|
|
166
|
+
pattern_context = StaticPatternContext
|
|
167
|
+
# Defined in subclasses; this is an explicit receiver class for
|
|
168
|
+
# self.defaultCall() and self.make_child_codegen to dispatch to
|
|
169
|
+
parent_impl: type[CodeGenerator] = CodeGenerator
|
|
170
|
+
|
|
171
|
+
def __init__(
|
|
172
|
+
self,
|
|
173
|
+
parent: CodeGenerator | None,
|
|
174
|
+
node: AST,
|
|
175
|
+
symbols: BaseSymbolVisitor,
|
|
176
|
+
graph: PyFlowGraph,
|
|
177
|
+
compiler: Compiler,
|
|
178
|
+
modname: str,
|
|
179
|
+
flags: int = 0,
|
|
180
|
+
optimization_lvl: int = 0,
|
|
181
|
+
enable_patching: bool = False,
|
|
182
|
+
builtins: dict[str, Any] = builtins.__dict__,
|
|
183
|
+
future_flags: int | None = None,
|
|
184
|
+
) -> None:
|
|
185
|
+
super().__init__(
|
|
186
|
+
parent,
|
|
187
|
+
node,
|
|
188
|
+
symbols,
|
|
189
|
+
graph,
|
|
190
|
+
flags=flags,
|
|
191
|
+
optimization_lvl=optimization_lvl,
|
|
192
|
+
builtins=builtins,
|
|
193
|
+
future_flags=future_flags,
|
|
194
|
+
)
|
|
195
|
+
self.compiler = compiler
|
|
196
|
+
self.modname = modname
|
|
197
|
+
# Use this counter to allocate temporaries for loop indices
|
|
198
|
+
self._tmpvar_loopidx_count = 0
|
|
199
|
+
self.cur_mod: ModuleTable = self.compiler.modules[modname]
|
|
200
|
+
self.enable_patching = enable_patching
|
|
201
|
+
self.scope_to_node: dict[Scope, AST] = {
|
|
202
|
+
scope: node for node, scope in self.scopes.items()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
def _is_static_compiler_disabled(self, node: AST) -> bool:
|
|
206
|
+
if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef)):
|
|
207
|
+
# Static compilation can only be disabled for functions and classes.
|
|
208
|
+
return False
|
|
209
|
+
if node in self.cur_mod.compile_non_static:
|
|
210
|
+
return True
|
|
211
|
+
scope_node = self.scope_to_node[self.scope]
|
|
212
|
+
fn = None
|
|
213
|
+
if isinstance(scope_node, ClassDef):
|
|
214
|
+
klass = self.get_type(scope_node)
|
|
215
|
+
if klass:
|
|
216
|
+
assert isinstance(klass, Class)
|
|
217
|
+
if klass.donotcompile:
|
|
218
|
+
# If static compilation is disabled on the entire class, it's skipped for all contained
|
|
219
|
+
# methods too.
|
|
220
|
+
return True
|
|
221
|
+
fn = klass.get_own_member(node.name)
|
|
222
|
+
|
|
223
|
+
if fn is None:
|
|
224
|
+
# Wasn't a method, let's check if it's a module level function
|
|
225
|
+
fn = self.get_type(node)
|
|
226
|
+
|
|
227
|
+
if isinstance(fn, (Function, DecoratedMethod)):
|
|
228
|
+
return fn.donotcompile
|
|
229
|
+
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
def _is_type_checked(self, t: Class) -> bool:
|
|
233
|
+
return t is not self.compiler.type_env.object and t.is_nominal_type
|
|
234
|
+
|
|
235
|
+
def _get_arg_types(
|
|
236
|
+
self,
|
|
237
|
+
func: FuncOrLambda | CompNode,
|
|
238
|
+
args: ast.arguments,
|
|
239
|
+
graph: PyFlowGraph,
|
|
240
|
+
) -> tuple[object, ...]:
|
|
241
|
+
arg_checks = []
|
|
242
|
+
cellvars = graph.cellvars
|
|
243
|
+
is_comprehension = not isinstance(
|
|
244
|
+
func, (ast.AsyncFunctionDef, ast.FunctionDef, ast.Lambda)
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
for i, arg in enumerate(args.posonlyargs):
|
|
248
|
+
t = self.get_type(arg)
|
|
249
|
+
if self._is_type_checked(t.klass):
|
|
250
|
+
arg_checks.append(self._calculate_idx(arg.arg, i, cellvars))
|
|
251
|
+
arg_checks.append(t.klass.type_descr)
|
|
252
|
+
|
|
253
|
+
for i, arg in enumerate(args.args):
|
|
254
|
+
# Comprehension nodes don't have arguments when they're typed; make
|
|
255
|
+
# up for that here.
|
|
256
|
+
t = (
|
|
257
|
+
self.compiler.type_env.DYNAMIC
|
|
258
|
+
if is_comprehension
|
|
259
|
+
else self.get_type(arg)
|
|
260
|
+
)
|
|
261
|
+
if self._is_type_checked(t.klass):
|
|
262
|
+
arg_checks.append(
|
|
263
|
+
self._calculate_idx(arg.arg, i + len(args.posonlyargs), cellvars)
|
|
264
|
+
)
|
|
265
|
+
arg_checks.append(t.klass.type_descr)
|
|
266
|
+
|
|
267
|
+
for i, arg in enumerate(args.kwonlyargs):
|
|
268
|
+
t = self.get_type(arg)
|
|
269
|
+
if self._is_type_checked(t.klass):
|
|
270
|
+
arg_checks.append(
|
|
271
|
+
self._calculate_idx(
|
|
272
|
+
arg.arg,
|
|
273
|
+
i + len(args.posonlyargs) + len(args.args),
|
|
274
|
+
cellvars,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
arg_checks.append(t.klass.type_descr)
|
|
278
|
+
|
|
279
|
+
# we should never emit arg checks for object
|
|
280
|
+
assert not any(td == ("builtins", "object") for td in arg_checks[1::2])
|
|
281
|
+
|
|
282
|
+
return tuple(arg_checks)
|
|
283
|
+
|
|
284
|
+
def get_type(self, node: AST) -> Value:
|
|
285
|
+
return self.cur_mod.types[node]
|
|
286
|
+
|
|
287
|
+
def get_node_data(self, key: AST, data_type: type[TType]) -> TType:
|
|
288
|
+
return self.cur_mod.get_node_data(key, data_type)
|
|
289
|
+
|
|
290
|
+
def get_opt_node_data(self, key: AST, data_type: type[TType]) -> TType | None:
|
|
291
|
+
return self.cur_mod.get_opt_node_data(key, data_type)
|
|
292
|
+
|
|
293
|
+
def set_node_data(self, key: AST, data_type: type[TType], value: TType) -> None:
|
|
294
|
+
self.cur_mod.set_node_data(key, data_type, value)
|
|
295
|
+
|
|
296
|
+
@classmethod
|
|
297
|
+
def make_code_gen(
|
|
298
|
+
cls,
|
|
299
|
+
module_name: str,
|
|
300
|
+
tree: AST,
|
|
301
|
+
filename: str,
|
|
302
|
+
source: str | bytes | ast.Module | ast.Expression | ast.Interactive,
|
|
303
|
+
flags: int,
|
|
304
|
+
optimize: int,
|
|
305
|
+
ast_optimizer_enabled: bool = True,
|
|
306
|
+
enable_patching: bool = False,
|
|
307
|
+
builtins: dict[str, Any] = builtins.__dict__,
|
|
308
|
+
) -> StaticCodeGenBase:
|
|
309
|
+
assert ast_optimizer_enabled
|
|
310
|
+
|
|
311
|
+
compiler = Compiler(cls)
|
|
312
|
+
code_gen = compiler.code_gen(
|
|
313
|
+
module_name, filename, tree, source, optimize, enable_patching, builtins
|
|
314
|
+
)
|
|
315
|
+
return code_gen
|
|
316
|
+
|
|
317
|
+
def make_child_codegen(
|
|
318
|
+
self,
|
|
319
|
+
tree: CodeGenTree,
|
|
320
|
+
graph: PyFlowGraph,
|
|
321
|
+
name: str | None = None,
|
|
322
|
+
) -> CodeGenerator:
|
|
323
|
+
if self._is_static_compiler_disabled(tree):
|
|
324
|
+
return self.parent_impl(
|
|
325
|
+
self,
|
|
326
|
+
tree,
|
|
327
|
+
self.symbols,
|
|
328
|
+
graph,
|
|
329
|
+
flags=self.flags,
|
|
330
|
+
optimization_lvl=self.optimization_lvl,
|
|
331
|
+
)
|
|
332
|
+
return self.__class__(
|
|
333
|
+
self,
|
|
334
|
+
tree,
|
|
335
|
+
self.symbols,
|
|
336
|
+
graph,
|
|
337
|
+
compiler=self.compiler,
|
|
338
|
+
modname=self.modname,
|
|
339
|
+
optimization_lvl=self.optimization_lvl,
|
|
340
|
+
enable_patching=self.enable_patching,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
def make_function_graph(
|
|
344
|
+
self,
|
|
345
|
+
func: FuncOrLambda | CompNode,
|
|
346
|
+
func_args: ast.arguments,
|
|
347
|
+
filename: str,
|
|
348
|
+
scopes: dict[AST, Scope],
|
|
349
|
+
class_name: str | None,
|
|
350
|
+
name: str,
|
|
351
|
+
first_lineno: int,
|
|
352
|
+
) -> PyFlowGraph:
|
|
353
|
+
graph = super().make_function_graph(
|
|
354
|
+
func, func_args, filename, scopes, class_name, name, first_lineno
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
if self._is_static_compiler_disabled(func):
|
|
358
|
+
return graph
|
|
359
|
+
|
|
360
|
+
graph.setFlag(consts.CI_CO_STATICALLY_COMPILED)
|
|
361
|
+
arg_types = self._get_arg_types(func, func_args, graph)
|
|
362
|
+
ret_type = self._get_return_type(func)
|
|
363
|
+
graph.extra_consts.append((arg_types, ret_type.type_descr))
|
|
364
|
+
return graph
|
|
365
|
+
|
|
366
|
+
def _get_return_type(self, func: FuncOrLambda | CompNode) -> Class:
|
|
367
|
+
if isinstance(func, (FunctionDef, AsyncFunctionDef)):
|
|
368
|
+
function = self.get_func_container(func)
|
|
369
|
+
klass = function.return_type.resolved()
|
|
370
|
+
if not self._is_type_checked(klass):
|
|
371
|
+
return self.compiler.type_env.object
|
|
372
|
+
else:
|
|
373
|
+
klass = self.get_type(func).klass
|
|
374
|
+
if isinstance(klass, AwaitableType):
|
|
375
|
+
klass = klass.type_args[0]
|
|
376
|
+
return klass
|
|
377
|
+
|
|
378
|
+
@contextmanager
|
|
379
|
+
def new_loopidx(self) -> Generator[str, None, None]:
|
|
380
|
+
self._tmpvar_loopidx_count += 1
|
|
381
|
+
try:
|
|
382
|
+
yield f"{TMP_VAR_PREFIX}.{self._tmpvar_loopidx_count}"
|
|
383
|
+
finally:
|
|
384
|
+
self._tmpvar_loopidx_count -= 1
|
|
385
|
+
|
|
386
|
+
def _resolve_class(self, node: ClassDef) -> Class | None:
|
|
387
|
+
klass = self.get_type(node)
|
|
388
|
+
if not isinstance(klass, Class) or klass is self.compiler.type_env.dynamic:
|
|
389
|
+
return
|
|
390
|
+
return klass
|
|
391
|
+
|
|
392
|
+
def emit_build_class(
|
|
393
|
+
self,
|
|
394
|
+
node: ast.ClassDef,
|
|
395
|
+
class_body: CodeGenerator,
|
|
396
|
+
outer_scope: CodeGenerator | None = None,
|
|
397
|
+
) -> None:
|
|
398
|
+
if (outer_scope is not None and outer_scope is not self) or getattr(
|
|
399
|
+
node, "type_params", None
|
|
400
|
+
):
|
|
401
|
+
# TASK(T210915794): We don't support type params, we need to pass the
|
|
402
|
+
# .generic_base as part of the bases
|
|
403
|
+
raise NotImplementedError("Type params not supported in static Python yet")
|
|
404
|
+
|
|
405
|
+
klass = self._resolve_class(node)
|
|
406
|
+
if not isinstance(self.scope, ModuleScope) or klass is None:
|
|
407
|
+
# If a class isn't top-level then it's not going to be using static
|
|
408
|
+
# features (we could relax this in the future for classes nested in classes).
|
|
409
|
+
return super().emit_build_class(node, class_body)
|
|
410
|
+
|
|
411
|
+
self.emit_closure(class_body, 0)
|
|
412
|
+
self.emit("LOAD_CONST", node.name)
|
|
413
|
+
|
|
414
|
+
if node.keywords:
|
|
415
|
+
for keyword in node.keywords:
|
|
416
|
+
self.emit("LOAD_CONST", keyword.arg)
|
|
417
|
+
self.visit(keyword.value)
|
|
418
|
+
|
|
419
|
+
self.emit("BUILD_MAP", len(node.keywords))
|
|
420
|
+
else:
|
|
421
|
+
self.emit("LOAD_CONST", None)
|
|
422
|
+
|
|
423
|
+
class_scope = class_body.scope
|
|
424
|
+
assert isinstance(class_scope, ClassScope)
|
|
425
|
+
self.emit("LOAD_CONST", class_scope.needs_class_closure)
|
|
426
|
+
|
|
427
|
+
assert klass is not None
|
|
428
|
+
final_methods: list[str] = []
|
|
429
|
+
for class_or_subclass in klass.mro:
|
|
430
|
+
final_methods.extend(class_or_subclass.get_own_final_method_names())
|
|
431
|
+
self.emit("LOAD_CONST", tuple(sorted(final_methods)))
|
|
432
|
+
|
|
433
|
+
if klass.is_final:
|
|
434
|
+
self.emit("LOAD_CONST", True)
|
|
435
|
+
else:
|
|
436
|
+
self.emit("LOAD_CONST", False)
|
|
437
|
+
|
|
438
|
+
count = 0
|
|
439
|
+
for name, value in klass.members.items():
|
|
440
|
+
if isinstance(value, CachedPropertyMethod):
|
|
441
|
+
self.emit("LOAD_CONST", CACHED_PROPERTY_IMPL_PREFIX + name)
|
|
442
|
+
count += 1
|
|
443
|
+
elif isinstance(value, AsyncCachedPropertyMethod):
|
|
444
|
+
self.emit("LOAD_CONST", ASYNC_CACHED_PROPERTY_IMPL_PREFIX + name)
|
|
445
|
+
count += 1
|
|
446
|
+
|
|
447
|
+
self.emit("BUILD_TUPLE", count)
|
|
448
|
+
|
|
449
|
+
for base in node.bases:
|
|
450
|
+
self.visit(base)
|
|
451
|
+
|
|
452
|
+
self.emit(
|
|
453
|
+
"INVOKE_FUNCTION",
|
|
454
|
+
((("_static",), "__build_cinder_class__"), 7 + len(node.bases)),
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
def processBody(
|
|
458
|
+
self, node: FuncOrLambda, body: list[ast.stmt], gen: CodeGenerator
|
|
459
|
+
) -> None:
|
|
460
|
+
if isinstance(node, (ast.FunctionDef | ast.AsyncFunctionDef)):
|
|
461
|
+
# check for any unassigned primitive values and force them to be
|
|
462
|
+
# assigned.
|
|
463
|
+
visitor = DefiniteAssignmentVisitor(self.symbols.scopes[node])
|
|
464
|
+
visitor.analyzeFunction(node)
|
|
465
|
+
for unassigned in visitor.unassigned:
|
|
466
|
+
node_type = self.get_type(unassigned)
|
|
467
|
+
if isinstance(node_type, CInstance):
|
|
468
|
+
assert isinstance(gen, StaticCodeGenBase)
|
|
469
|
+
node_type.emit_init(unassigned, gen)
|
|
470
|
+
|
|
471
|
+
super().processBody(node, body, gen)
|
|
472
|
+
|
|
473
|
+
def visitFunctionOrLambda(
|
|
474
|
+
self, node: ast.FunctionDef | ast.AsyncFunctionDef | ast.Lambda
|
|
475
|
+
) -> None:
|
|
476
|
+
if isinstance(node, ast.Lambda):
|
|
477
|
+
return super().visitFunctionOrLambda(node)
|
|
478
|
+
|
|
479
|
+
function = self.get_func_container(node)
|
|
480
|
+
name = function.emit_function(node, self)
|
|
481
|
+
self.storeName(name)
|
|
482
|
+
|
|
483
|
+
def get_func_container(self, node: ast.AST) -> FunctionContainer:
|
|
484
|
+
function = self.get_type(node)
|
|
485
|
+
if not isinstance(function, FunctionContainer):
|
|
486
|
+
raise RuntimeError("bad value for function")
|
|
487
|
+
|
|
488
|
+
return function
|
|
489
|
+
|
|
490
|
+
def walkClassBody(self, node: ClassDef, gen: CodeGenerator) -> None:
|
|
491
|
+
klass = self._resolve_class(node)
|
|
492
|
+
super().walkClassBody(node, gen)
|
|
493
|
+
if not klass:
|
|
494
|
+
return
|
|
495
|
+
|
|
496
|
+
if not klass.has_init_subclass:
|
|
497
|
+
gen_scope = gen.scope
|
|
498
|
+
assert isinstance(gen_scope, ClassScope)
|
|
499
|
+
|
|
500
|
+
# we define a custom override of __init_subclass__
|
|
501
|
+
if not gen_scope.needs_class_closure:
|
|
502
|
+
# we need to call super(), which will need to have
|
|
503
|
+
# __class__ available if it's not already...
|
|
504
|
+
gen.graph.cellvars.get_index("__class__")
|
|
505
|
+
gen_scope.cells["__class__"] = 1
|
|
506
|
+
gen_scope.needs_class_closure = True
|
|
507
|
+
|
|
508
|
+
init_graph = self.flow_graph(
|
|
509
|
+
"__init_subclass__",
|
|
510
|
+
self.graph.filename,
|
|
511
|
+
None,
|
|
512
|
+
0,
|
|
513
|
+
("cls",),
|
|
514
|
+
(),
|
|
515
|
+
(),
|
|
516
|
+
optimized=1,
|
|
517
|
+
docstring=None,
|
|
518
|
+
)
|
|
519
|
+
init_graph.freevars.get_index("__class__")
|
|
520
|
+
init_graph.emit_prologue()
|
|
521
|
+
init_graph.emit("LOAD_GLOBAL", "super")
|
|
522
|
+
init_graph.emit("LOAD_DEREF", "__class__")
|
|
523
|
+
init_graph.emit("LOAD_FAST", "cls")
|
|
524
|
+
init_graph.emit_super_call("__init_subclass__", True)
|
|
525
|
+
init_graph.emit_call_method(0)
|
|
526
|
+
init_graph.emit("POP_TOP")
|
|
527
|
+
|
|
528
|
+
init_graph.emit("LOAD_FAST", "cls")
|
|
529
|
+
init_graph.emit("INVOKE_FUNCTION", ((("_static",), "init_subclass"), 1))
|
|
530
|
+
init_graph.emit("RETURN_VALUE")
|
|
531
|
+
|
|
532
|
+
gen.emit("LOAD_CLOSURE", "__class__")
|
|
533
|
+
gen.emit("BUILD_TUPLE", 1)
|
|
534
|
+
init_gen = InitSubClassGenerator(
|
|
535
|
+
init_graph, self.get_qual_prefix(gen) + ".__init_subclass__"
|
|
536
|
+
)
|
|
537
|
+
gen.emit_make_function(
|
|
538
|
+
init_gen, self.get_qual_prefix(gen) + ".__init_subclass__", 8
|
|
539
|
+
)
|
|
540
|
+
gen.emit("STORE_NAME", "__init_subclass__")
|
|
541
|
+
|
|
542
|
+
assert isinstance(gen, StaticCodeGenBase)
|
|
543
|
+
klass.emit_extra_members(node, gen)
|
|
544
|
+
|
|
545
|
+
class_mems_with_overrides = [
|
|
546
|
+
name
|
|
547
|
+
for name, value in klass.members.items()
|
|
548
|
+
if (isinstance(value, Slot) and not value.is_classvar)
|
|
549
|
+
or isinstance(value, CachedPropertyMethod)
|
|
550
|
+
or isinstance(value, AsyncCachedPropertyMethod)
|
|
551
|
+
]
|
|
552
|
+
|
|
553
|
+
class_mems = [
|
|
554
|
+
name
|
|
555
|
+
for name in class_mems_with_overrides
|
|
556
|
+
if not isinstance(mem := klass.members[name], Slot) or not mem.override
|
|
557
|
+
]
|
|
558
|
+
|
|
559
|
+
if klass.allow_weakrefs:
|
|
560
|
+
class_mems.append("__weakref__")
|
|
561
|
+
|
|
562
|
+
# In the future we may want a compatibility mode where we add
|
|
563
|
+
# __dict__ and __weakref__
|
|
564
|
+
gen.emit("LOAD_CONST", tuple(class_mems))
|
|
565
|
+
gen.emit("STORE_NAME", "__slots__")
|
|
566
|
+
|
|
567
|
+
slots_with_default = [
|
|
568
|
+
name
|
|
569
|
+
for name in class_mems_with_overrides
|
|
570
|
+
if name in klass.members
|
|
571
|
+
and isinstance(klass.members[name], Slot)
|
|
572
|
+
and cast(
|
|
573
|
+
Slot[Class], klass.members[name]
|
|
574
|
+
).is_typed_descriptor_with_default_value()
|
|
575
|
+
]
|
|
576
|
+
|
|
577
|
+
for name in slots_with_default:
|
|
578
|
+
gen.emit("LOAD_CONST", name)
|
|
579
|
+
gen.emit("LOAD_NAME", name)
|
|
580
|
+
gen.emit("DELETE_NAME", name)
|
|
581
|
+
|
|
582
|
+
gen.emit("BUILD_MAP", len(slots_with_default))
|
|
583
|
+
gen.emit("STORE_NAME", "__slots_with_default__")
|
|
584
|
+
|
|
585
|
+
count = 0
|
|
586
|
+
for name, value in klass.members.items():
|
|
587
|
+
if not isinstance(value, Slot):
|
|
588
|
+
continue
|
|
589
|
+
|
|
590
|
+
if value.is_classvar:
|
|
591
|
+
continue
|
|
592
|
+
|
|
593
|
+
value_type = value.decl_type
|
|
594
|
+
if value.decl_type is self.compiler.type_env.dynamic:
|
|
595
|
+
if value.is_typed_descriptor_with_default_value():
|
|
596
|
+
value_type = self.compiler.type_env.object
|
|
597
|
+
else:
|
|
598
|
+
continue
|
|
599
|
+
|
|
600
|
+
gen.emit("LOAD_CONST", name)
|
|
601
|
+
gen.emit("LOAD_CONST", value_type.type_descr)
|
|
602
|
+
count += 1
|
|
603
|
+
|
|
604
|
+
if count:
|
|
605
|
+
gen.emit("BUILD_MAP", count)
|
|
606
|
+
gen.emit("STORE_NAME", "__slot_types__")
|
|
607
|
+
|
|
608
|
+
def visit_decorator(self, decorator: AST, class_def: ClassDef) -> None:
|
|
609
|
+
d = self.get_type(decorator)
|
|
610
|
+
if (
|
|
611
|
+
isinstance(d, DataclassDecorator)
|
|
612
|
+
and self.get_type(class_def) is not self.compiler.type_env.dynamic
|
|
613
|
+
):
|
|
614
|
+
return
|
|
615
|
+
|
|
616
|
+
super().visit_decorator(decorator, class_def)
|
|
617
|
+
|
|
618
|
+
def emit_decorator_call(self, decorator: AST, class_def: ClassDef) -> None:
|
|
619
|
+
self.get_type(decorator).emit_decorator_call(class_def, self)
|
|
620
|
+
|
|
621
|
+
def emit_load_builtin(self, name: str) -> None:
|
|
622
|
+
is_checked_dict = (
|
|
623
|
+
name == "dict" and ModuleFlag.CHECKED_DICTS in self.cur_mod.flags
|
|
624
|
+
)
|
|
625
|
+
is_checked_list = (
|
|
626
|
+
name == "list" and ModuleFlag.CHECKED_LISTS in self.cur_mod.flags
|
|
627
|
+
)
|
|
628
|
+
if is_checked_dict or is_checked_list:
|
|
629
|
+
chkname = f"chk{name}"
|
|
630
|
+
self.emit("LOAD_CONST", 0)
|
|
631
|
+
self.emit("LOAD_CONST", (chkname,))
|
|
632
|
+
self.emit(EAGER_IMPORT_NAME, "_static")
|
|
633
|
+
self.emit("IMPORT_FROM", chkname)
|
|
634
|
+
self.emit_rotate_stack(2)
|
|
635
|
+
self.emit("POP_TOP")
|
|
636
|
+
|
|
637
|
+
def visitModule(self, node: Module) -> None:
|
|
638
|
+
if ModuleFlag.CHECKED_DICTS in self.cur_mod.flags:
|
|
639
|
+
self.emit_load_builtin("dict")
|
|
640
|
+
self.storeName("dict")
|
|
641
|
+
|
|
642
|
+
if ModuleFlag.CHECKED_LISTS in self.cur_mod.flags:
|
|
643
|
+
self.emit_load_builtin("list")
|
|
644
|
+
self.storeName("list")
|
|
645
|
+
|
|
646
|
+
super().visitModule(node)
|
|
647
|
+
|
|
648
|
+
def emit_module_return(self, node: ast.Module) -> None:
|
|
649
|
+
self.emit("LOAD_CONST", tuple(self.cur_mod.named_finals.keys()))
|
|
650
|
+
self.emit("STORE_NAME", "__final_constants__")
|
|
651
|
+
super().emit_module_return(node)
|
|
652
|
+
|
|
653
|
+
def visitAssert(self, node: ast.Assert) -> None:
|
|
654
|
+
super().visitAssert(node)
|
|
655
|
+
# Only add casts when the assert is optimized out.
|
|
656
|
+
if not self.optimization_lvl:
|
|
657
|
+
return
|
|
658
|
+
# Since we're narrowing types in asserts, we need to ensure we cast
|
|
659
|
+
# all narrowed locals when asserts are optimized away.
|
|
660
|
+
effect = self.get_node_data(node, NarrowingEffect)
|
|
661
|
+
# As all of our effects store the final type, we can apply the effects on
|
|
662
|
+
# an empty dictionary to get an overapproximation of what we need to cast.
|
|
663
|
+
type_state = TypeState()
|
|
664
|
+
effect_nodes: dict[str, ast.AST] = {}
|
|
665
|
+
effect.apply(type_state, effect_nodes)
|
|
666
|
+
for key, value in type_state.local_types.items():
|
|
667
|
+
if value.klass.is_nominal_type:
|
|
668
|
+
self.visit(effect_nodes[key])
|
|
669
|
+
self.emit("CAST", value.klass.type_descr)
|
|
670
|
+
self.emit("POP_TOP")
|
|
671
|
+
for base, refinement_dict in type_state.refined_fields.items():
|
|
672
|
+
for attr, (value, _, _) in refinement_dict.items():
|
|
673
|
+
if value.klass.is_nominal_type:
|
|
674
|
+
key = f"{base}.{attr}"
|
|
675
|
+
self.visit(effect_nodes[key])
|
|
676
|
+
self.emit("CAST", value.klass.type_descr)
|
|
677
|
+
self.emit("POP_TOP")
|
|
678
|
+
|
|
679
|
+
def visitAttribute(self, node: Attribute) -> None:
|
|
680
|
+
self.set_pos(node)
|
|
681
|
+
data = self.get_opt_node_data(node, UsedRefinementField)
|
|
682
|
+
if data is not None and not data.is_source:
|
|
683
|
+
self.emit("LOAD_FAST", data.name)
|
|
684
|
+
return
|
|
685
|
+
|
|
686
|
+
if isinstance(node.ctx, ast.Store) and data is not None and data.is_used:
|
|
687
|
+
self.emit_dup()
|
|
688
|
+
self.emit("STORE_FAST", data.name)
|
|
689
|
+
|
|
690
|
+
self.get_type(node.value).emit_attr(node, self)
|
|
691
|
+
|
|
692
|
+
if (
|
|
693
|
+
isinstance(node.ctx, ast.Load)
|
|
694
|
+
and data is not None
|
|
695
|
+
and data.is_source
|
|
696
|
+
and data.is_used
|
|
697
|
+
):
|
|
698
|
+
self.emit_dup()
|
|
699
|
+
self.emit("STORE_FAST", data.name)
|
|
700
|
+
|
|
701
|
+
def visitAssignTarget(
|
|
702
|
+
self, elt: expr, stmt: AST, value: expr | None = None
|
|
703
|
+
) -> None:
|
|
704
|
+
if isinstance(elt, (ast.Tuple, ast.List)):
|
|
705
|
+
self._visitUnpack(elt)
|
|
706
|
+
if isinstance(value, ast.Tuple) and len(value.elts) == len(elt.elts):
|
|
707
|
+
for target, inner_value in zip(elt.elts, value.elts):
|
|
708
|
+
self.visitAssignTarget(target, stmt, inner_value)
|
|
709
|
+
else:
|
|
710
|
+
for target in elt.elts:
|
|
711
|
+
self.visitAssignTarget(target, stmt, None)
|
|
712
|
+
else:
|
|
713
|
+
elt_type = self.get_type(elt).klass
|
|
714
|
+
value_type = (
|
|
715
|
+
self.compiler.type_env.dynamic
|
|
716
|
+
if value is None
|
|
717
|
+
else self.get_type(value).klass
|
|
718
|
+
)
|
|
719
|
+
elt_type.emit_type_check(value_type, self)
|
|
720
|
+
self.visit(elt)
|
|
721
|
+
|
|
722
|
+
def visitAssign(self, node: Assign) -> None:
|
|
723
|
+
self.set_pos(node)
|
|
724
|
+
self.visit(node.value)
|
|
725
|
+
dups = len(node.targets) - 1
|
|
726
|
+
for i in range(len(node.targets)):
|
|
727
|
+
elt = node.targets[i]
|
|
728
|
+
if i < dups:
|
|
729
|
+
self.emit_dup()
|
|
730
|
+
if isinstance(elt, ast.AST):
|
|
731
|
+
self.visitAssignTarget(elt, node, node.value)
|
|
732
|
+
|
|
733
|
+
def visitAnnAssign(self, node: ast.AnnAssign) -> None:
|
|
734
|
+
self.set_pos(node)
|
|
735
|
+
value = node.value
|
|
736
|
+
if value:
|
|
737
|
+
value_type = self.get_type(value)
|
|
738
|
+
if (
|
|
739
|
+
isinstance(value_type, DataclassField)
|
|
740
|
+
and isinstance(self.tree, ast.ClassDef)
|
|
741
|
+
and isinstance(self.get_type(self.tree), Dataclass)
|
|
742
|
+
):
|
|
743
|
+
value_type.emit_field(node.target, self)
|
|
744
|
+
else:
|
|
745
|
+
self.visit(value)
|
|
746
|
+
self.get_type(node.target).klass.emit_type_check(value_type.klass, self)
|
|
747
|
+
self.visit(node.target)
|
|
748
|
+
target = node.target
|
|
749
|
+
if isinstance(target, ast.Name):
|
|
750
|
+
self.emit_simple_ann_assign(node)
|
|
751
|
+
elif isinstance(target, ast.Attribute):
|
|
752
|
+
if not node.value:
|
|
753
|
+
self.checkAnnExpr(target.value)
|
|
754
|
+
elif isinstance(target, ast.Subscript):
|
|
755
|
+
if not node.value:
|
|
756
|
+
self.checkAnnExpr(target.value)
|
|
757
|
+
self.checkAnnSubscr(target.slice)
|
|
758
|
+
else:
|
|
759
|
+
raise SystemError(
|
|
760
|
+
f"invalid node type {type(node).__name__} for annotated assignment"
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
if not node.simple:
|
|
764
|
+
self.checkAnnotation(node)
|
|
765
|
+
|
|
766
|
+
def visitConstant(self, node: Constant) -> None:
|
|
767
|
+
self.get_type(node).emit_constant(node, self)
|
|
768
|
+
|
|
769
|
+
def visitDefault(self, node: expr) -> None:
|
|
770
|
+
self.visit(node)
|
|
771
|
+
node_type = self.get_type(node)
|
|
772
|
+
if isinstance(node_type, CInstance):
|
|
773
|
+
node_type.emit_box(self)
|
|
774
|
+
|
|
775
|
+
def get_final_literal(self, node: AST) -> ast.Constant | None:
|
|
776
|
+
return self.cur_mod.get_final_literal(node, self.scope)
|
|
777
|
+
|
|
778
|
+
def visitName(self, node: Name) -> None:
|
|
779
|
+
final_val = self.get_final_literal(node)
|
|
780
|
+
if final_val is not None:
|
|
781
|
+
# visit the constant directly
|
|
782
|
+
return self.defaultVisit(final_val)
|
|
783
|
+
self.get_type(node).emit_name(node, self)
|
|
784
|
+
|
|
785
|
+
def emitAugAttribute(self, node: ast.AugAssign) -> None:
|
|
786
|
+
target = node.target
|
|
787
|
+
assert isinstance(target, ast.Attribute)
|
|
788
|
+
self.visit(target.value)
|
|
789
|
+
typ = self.get_type(target.value)
|
|
790
|
+
self.emit_dup()
|
|
791
|
+
typ.emit_load_attr(target, self)
|
|
792
|
+
self.emitAugRHS(node)
|
|
793
|
+
self.emit_rotate_stack(2)
|
|
794
|
+
typ.emit_store_attr(target, self)
|
|
795
|
+
|
|
796
|
+
def emitAugName(self, node: ast.AugAssign) -> None:
|
|
797
|
+
target = node.target
|
|
798
|
+
assert isinstance(target, ast.Name)
|
|
799
|
+
typ = self.get_type(target)
|
|
800
|
+
typ.emit_load_name(target, self)
|
|
801
|
+
self.emitAugRHS(node)
|
|
802
|
+
typ.emit_store_name(target, self)
|
|
803
|
+
|
|
804
|
+
def emitAugSubscript(self, node: ast.AugAssign) -> None:
|
|
805
|
+
target = node.target
|
|
806
|
+
assert isinstance(target, ast.Subscript)
|
|
807
|
+
self.visit(target.value)
|
|
808
|
+
self.visit(target.slice)
|
|
809
|
+
typ = self.get_type(target.value)
|
|
810
|
+
self.emit_dup(2)
|
|
811
|
+
typ.emit_load_subscr(target, self)
|
|
812
|
+
self.emitAugRHS(node)
|
|
813
|
+
self.emit_rotate_stack(3)
|
|
814
|
+
typ.emit_store_subscr(target, self)
|
|
815
|
+
|
|
816
|
+
def emitAugRHS(self, node: ast.AugAssign) -> None:
|
|
817
|
+
self.get_type(node.target).emit_aug_rhs(node, self)
|
|
818
|
+
|
|
819
|
+
def visitBinOp(self, node: BinOp) -> None:
|
|
820
|
+
self.get_type(node).emit_binop(node, self)
|
|
821
|
+
|
|
822
|
+
def visitUnaryOp(self, node: UnaryOp, type_ctx: Class | None = None) -> None:
|
|
823
|
+
self.get_type(node).emit_unaryop(node, self)
|
|
824
|
+
|
|
825
|
+
def visitCall(self, node: Call) -> None:
|
|
826
|
+
self.strictPreVisitCall(node)
|
|
827
|
+
self.get_type(node.func).emit_call(node, self)
|
|
828
|
+
|
|
829
|
+
def visitSubscript(self, node: ast.Subscript, aug_flag: bool = False) -> None:
|
|
830
|
+
# aug_flag is unused in static compiler; we have our own
|
|
831
|
+
# emitAugSubscript that doesn't call visitSubscript
|
|
832
|
+
self.get_type(node.value).emit_subscr(node, self)
|
|
833
|
+
|
|
834
|
+
def _visitReturnValue(self, value: ast.AST, expected: Class) -> None:
|
|
835
|
+
self.visit(value)
|
|
836
|
+
expected.emit_type_check(self.get_type(value).klass, self)
|
|
837
|
+
|
|
838
|
+
def visitReturn(self, node: ast.Return) -> None:
|
|
839
|
+
self.checkReturn(node)
|
|
840
|
+
function = self.get_func_container(self.tree)
|
|
841
|
+
|
|
842
|
+
expected = function.return_type.resolved()
|
|
843
|
+
if isinstance(self.tree, AsyncFunctionDef):
|
|
844
|
+
assert isinstance(expected, AwaitableType)
|
|
845
|
+
expected = expected.type_args[0]
|
|
846
|
+
self.set_pos(node)
|
|
847
|
+
value = node.value
|
|
848
|
+
is_return_constant = isinstance(value, ast.Constant)
|
|
849
|
+
opcode = "RETURN_VALUE"
|
|
850
|
+
oparg = 0
|
|
851
|
+
if value:
|
|
852
|
+
if not is_return_constant:
|
|
853
|
+
self._visitReturnValue(value, expected)
|
|
854
|
+
self.unwind_setup_entries(preserve_tos=True)
|
|
855
|
+
else:
|
|
856
|
+
self.unwind_setup_entries(preserve_tos=False)
|
|
857
|
+
self._visitReturnValue(value, expected)
|
|
858
|
+
if isinstance(expected, CType):
|
|
859
|
+
opcode = "RETURN_PRIMITIVE"
|
|
860
|
+
oparg = expected.instance.as_oparg()
|
|
861
|
+
else:
|
|
862
|
+
self.unwind_setup_entries(preserve_tos=False)
|
|
863
|
+
self.emit("LOAD_CONST", None)
|
|
864
|
+
|
|
865
|
+
self.emit(opcode, oparg)
|
|
866
|
+
self.nextBlock()
|
|
867
|
+
|
|
868
|
+
def visitDictComp(self, node: DictComp) -> None:
|
|
869
|
+
dict_type = self.get_type(node)
|
|
870
|
+
if dict_type in (
|
|
871
|
+
self.compiler.type_env.dict.instance,
|
|
872
|
+
self.compiler.type_env.dict.exact_type().instance,
|
|
873
|
+
):
|
|
874
|
+
return super().visitDictComp(node)
|
|
875
|
+
klass = dict_type.klass
|
|
876
|
+
|
|
877
|
+
assert (
|
|
878
|
+
isinstance(klass, GenericClass)
|
|
879
|
+
and klass.type_def is self.compiler.type_env.checked_dict
|
|
880
|
+
), dict_type
|
|
881
|
+
self.compile_comprehension(
|
|
882
|
+
node,
|
|
883
|
+
sys.intern("<dictcomp>"),
|
|
884
|
+
node.key,
|
|
885
|
+
node.value,
|
|
886
|
+
"BUILD_CHECKED_MAP",
|
|
887
|
+
(dict_type.klass.type_descr, 0),
|
|
888
|
+
)
|
|
889
|
+
|
|
890
|
+
def compile_subgendict(
|
|
891
|
+
self, node: ast.Dict, begin: int, end: int, dict_descr: TypeDescr
|
|
892
|
+
) -> None:
|
|
893
|
+
n = end - begin
|
|
894
|
+
for i in range(begin, end):
|
|
895
|
+
k = node.keys[i]
|
|
896
|
+
assert k is not None
|
|
897
|
+
self.visit(k)
|
|
898
|
+
self.visit(node.values[i])
|
|
899
|
+
|
|
900
|
+
self.emit("BUILD_CHECKED_MAP", (dict_descr, n))
|
|
901
|
+
|
|
902
|
+
def visitDict(self, node: ast.Dict) -> None:
|
|
903
|
+
dict_type = self.get_type(node)
|
|
904
|
+
if dict_type in (
|
|
905
|
+
self.compiler.type_env.dict.instance,
|
|
906
|
+
self.compiler.type_env.dict.exact_type().instance,
|
|
907
|
+
):
|
|
908
|
+
return super().visitDict(node)
|
|
909
|
+
klass = dict_type.klass
|
|
910
|
+
|
|
911
|
+
assert (
|
|
912
|
+
isinstance(klass, GenericClass)
|
|
913
|
+
and klass.type_def is self.compiler.type_env.checked_dict
|
|
914
|
+
), dict_type
|
|
915
|
+
|
|
916
|
+
self.set_pos(node)
|
|
917
|
+
elements = 0
|
|
918
|
+
is_unpacking = False
|
|
919
|
+
built_final_dict = False
|
|
920
|
+
|
|
921
|
+
# This is similar to the normal dict code generation, but instead of relying
|
|
922
|
+
# upon an opcode for BUILD_MAP_UNPACK we invoke the update method on the
|
|
923
|
+
# underlying dict type. Therefore the first dict that we create becomes
|
|
924
|
+
# the final dict. This allows us to not introduce a new opcode, but we should
|
|
925
|
+
# also be able to dispatch the INVOKE_METHOD rather efficiently.
|
|
926
|
+
dict_descr = dict_type.klass.type_descr
|
|
927
|
+
update_descr = dict_descr + ("update",)
|
|
928
|
+
for i, (k, v) in enumerate(zip(node.keys, node.values)):
|
|
929
|
+
is_unpacking = k is None
|
|
930
|
+
if elements == 0xFFFF or (elements and is_unpacking):
|
|
931
|
+
self.compile_subgendict(node, i - elements, i, dict_descr)
|
|
932
|
+
built_final_dict = True
|
|
933
|
+
elements = 0
|
|
934
|
+
|
|
935
|
+
if is_unpacking:
|
|
936
|
+
if not built_final_dict:
|
|
937
|
+
# {**foo, ...}, we need to generate the empty dict
|
|
938
|
+
self.emit("BUILD_CHECKED_MAP", (dict_descr, 0))
|
|
939
|
+
built_final_dict = True
|
|
940
|
+
self.emit_dup()
|
|
941
|
+
self.visit(v)
|
|
942
|
+
|
|
943
|
+
self.emit_invoke_method(update_descr, 1)
|
|
944
|
+
self.emit("POP_TOP")
|
|
945
|
+
else:
|
|
946
|
+
elements += 1
|
|
947
|
+
|
|
948
|
+
if elements or not built_final_dict:
|
|
949
|
+
if built_final_dict:
|
|
950
|
+
self.emit_dup()
|
|
951
|
+
self.compile_subgendict(
|
|
952
|
+
node, len(node.keys) - elements, len(node.keys), dict_descr
|
|
953
|
+
)
|
|
954
|
+
if built_final_dict:
|
|
955
|
+
self.emit_invoke_method(update_descr, 1)
|
|
956
|
+
self.emit("POP_TOP")
|
|
957
|
+
|
|
958
|
+
def compile_sub_checked_list(
|
|
959
|
+
self, node: ast.List, begin: int, end: int, type_descr: TypeDescr
|
|
960
|
+
) -> None:
|
|
961
|
+
n = end - begin
|
|
962
|
+
for i in range(begin, end):
|
|
963
|
+
elt = node.elts[i]
|
|
964
|
+
assert not isinstance(elt, ast.Starred)
|
|
965
|
+
self.visit(elt)
|
|
966
|
+
|
|
967
|
+
self.emit("BUILD_CHECKED_LIST", (type_descr, n))
|
|
968
|
+
|
|
969
|
+
def visitListComp(self, node: ast.ListComp) -> None:
|
|
970
|
+
list_type = self.get_type(node)
|
|
971
|
+
if list_type in (
|
|
972
|
+
self.compiler.type_env.list.instance,
|
|
973
|
+
self.compiler.type_env.list.exact_type().instance,
|
|
974
|
+
):
|
|
975
|
+
return super().visitListComp(node)
|
|
976
|
+
klass = list_type.klass
|
|
977
|
+
|
|
978
|
+
assert (
|
|
979
|
+
isinstance(klass, GenericClass)
|
|
980
|
+
and klass.type_def is self.compiler.type_env.checked_list
|
|
981
|
+
), list_type
|
|
982
|
+
self.compile_comprehension(
|
|
983
|
+
node,
|
|
984
|
+
sys.intern("<listcomp>"),
|
|
985
|
+
node.elt,
|
|
986
|
+
None,
|
|
987
|
+
"BUILD_CHECKED_LIST",
|
|
988
|
+
(list_type.klass.type_descr, 0),
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
def visitList(self, node: ast.List) -> None:
|
|
992
|
+
list_type = self.get_type(node)
|
|
993
|
+
if list_type in (
|
|
994
|
+
self.compiler.type_env.list.instance,
|
|
995
|
+
self.compiler.type_env.list.exact_type().instance,
|
|
996
|
+
):
|
|
997
|
+
return super().visitList(node)
|
|
998
|
+
klass = list_type.klass
|
|
999
|
+
|
|
1000
|
+
assert (
|
|
1001
|
+
isinstance(klass, GenericClass)
|
|
1002
|
+
and klass.type_def is self.compiler.type_env.checked_list
|
|
1003
|
+
), list_type
|
|
1004
|
+
|
|
1005
|
+
self.set_pos(node)
|
|
1006
|
+
list_descr = list_type.klass.type_descr
|
|
1007
|
+
extend_descr = (list_descr, "extend")
|
|
1008
|
+
built_final_list = False
|
|
1009
|
+
elements = 0
|
|
1010
|
+
|
|
1011
|
+
for i, elt in enumerate(node.elts):
|
|
1012
|
+
if isinstance(elt, ast.Starred):
|
|
1013
|
+
if elements:
|
|
1014
|
+
self.compile_sub_checked_list(node, i - elements, i, list_descr)
|
|
1015
|
+
built_final_list = True
|
|
1016
|
+
elements = 0
|
|
1017
|
+
if not built_final_list:
|
|
1018
|
+
# We need to generate the empty list to extend in the case of [*foo, ...].
|
|
1019
|
+
self.emit("BUILD_CHECKED_LIST", (list_descr, 0))
|
|
1020
|
+
built_final_list = True
|
|
1021
|
+
self.emit_dup()
|
|
1022
|
+
self.emit_load_static_method(extend_descr)
|
|
1023
|
+
self.visit(elt.value)
|
|
1024
|
+
self.emit_invoke_method(extend_descr, 1)
|
|
1025
|
+
self.emit("POP_TOP")
|
|
1026
|
+
else:
|
|
1027
|
+
elements += 1
|
|
1028
|
+
|
|
1029
|
+
if elements or not built_final_list:
|
|
1030
|
+
if built_final_list:
|
|
1031
|
+
self.emit_dup()
|
|
1032
|
+
self.compile_sub_checked_list(
|
|
1033
|
+
node, len(node.elts) - elements, len(node.elts), list_descr
|
|
1034
|
+
)
|
|
1035
|
+
if built_final_list:
|
|
1036
|
+
self.emit_invoke_method(extend_descr, 1)
|
|
1037
|
+
self.emit("POP_TOP")
|
|
1038
|
+
|
|
1039
|
+
def visitContinue(self, node: ast.Continue) -> None:
|
|
1040
|
+
loop_node = self.get_opt_node_data(node, AST)
|
|
1041
|
+
if isinstance(loop_node, ast.For):
|
|
1042
|
+
# Since we support primitive int for loops with crange()
|
|
1043
|
+
# we have some special handling to deal with the "continue"
|
|
1044
|
+
# statement.
|
|
1045
|
+
iter_type = self.get_type(loop_node.iter)
|
|
1046
|
+
iter_type.emit_continue(node, self)
|
|
1047
|
+
else:
|
|
1048
|
+
super().visitContinue(node)
|
|
1049
|
+
|
|
1050
|
+
def visitFor(self, node: ast.For) -> None:
|
|
1051
|
+
self.strictPreVisitFor(node)
|
|
1052
|
+
iter_type = self.get_type(node.iter)
|
|
1053
|
+
iter_type.emit_forloop(node, self)
|
|
1054
|
+
self.strictPostVisitFor(node)
|
|
1055
|
+
|
|
1056
|
+
def emit_invoke_function(self, descr: TypeDescr, arg_count: int) -> None:
|
|
1057
|
+
# Emit a zero EXTENDED_ARG before so that we can optimize and insert the
|
|
1058
|
+
# arg count
|
|
1059
|
+
self.emit("EXTENDED_ARG", 0)
|
|
1060
|
+
self.emit("INVOKE_FUNCTION", (descr, arg_count))
|
|
1061
|
+
|
|
1062
|
+
def emit_invoke_method(
|
|
1063
|
+
self, descr: TypeDescr, arg_count: int, is_classmethod: bool = False
|
|
1064
|
+
) -> None:
|
|
1065
|
+
# Emit a zero EXTENDED_ARG before so that we can optimize and insert the
|
|
1066
|
+
# arg count
|
|
1067
|
+
self.emit("EXTENDED_ARG", 0)
|
|
1068
|
+
self.emit(
|
|
1069
|
+
"INVOKE_METHOD",
|
|
1070
|
+
(descr, arg_count, True) if is_classmethod else (descr, arg_count),
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
def emit_load_static_method(
|
|
1074
|
+
self, descr: TypeDescr, is_classmethod: bool = False
|
|
1075
|
+
) -> None:
|
|
1076
|
+
# Emit a zero EXTENDED_ARG before so that we can optimize and insert the
|
|
1077
|
+
# arg count
|
|
1078
|
+
self.emit("EXTENDED_ARG", 0)
|
|
1079
|
+
self.emit(
|
|
1080
|
+
"LOAD_METHOD_STATIC",
|
|
1081
|
+
(descr, True) if is_classmethod else (descr,),
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
def defaultCall(self, node: object, name: str, *args: object) -> None:
|
|
1085
|
+
meth = getattr(self.parent_impl, name)
|
|
1086
|
+
return meth(self, node, *args)
|
|
1087
|
+
|
|
1088
|
+
def defaultVisit(self, node: AST, *args: object) -> None:
|
|
1089
|
+
klass = node.__class__
|
|
1090
|
+
meth = self._default_cache.get(klass, None)
|
|
1091
|
+
if meth is None:
|
|
1092
|
+
className = klass.__name__
|
|
1093
|
+
meth = getattr(
|
|
1094
|
+
super(StaticCodeGenBase, StaticCodeGenBase),
|
|
1095
|
+
"visit" + className,
|
|
1096
|
+
StaticCodeGenBase.generic_visit,
|
|
1097
|
+
)
|
|
1098
|
+
self._default_cache[klass] = meth
|
|
1099
|
+
return meth(self, node, *args)
|
|
1100
|
+
|
|
1101
|
+
def get_bool_const(self, node: AST) -> bool | None:
|
|
1102
|
+
if isinstance(node, ast.Constant):
|
|
1103
|
+
return bool(node.value)
|
|
1104
|
+
|
|
1105
|
+
kb = self.get_opt_node_data(node, KnownBoolean)
|
|
1106
|
+
if kb is not None:
|
|
1107
|
+
return True if kb == KnownBoolean.TRUE else False
|
|
1108
|
+
|
|
1109
|
+
def visitIf(self, node: ast.If) -> None:
|
|
1110
|
+
self.get_type(node.test)
|
|
1111
|
+
|
|
1112
|
+
test_const = self.get_bool_const(node.test)
|
|
1113
|
+
|
|
1114
|
+
end = self.newBlock("if_end")
|
|
1115
|
+
orelse = None
|
|
1116
|
+
if node.orelse:
|
|
1117
|
+
orelse = self.newBlock("if_else")
|
|
1118
|
+
|
|
1119
|
+
self.compileJumpIf(node.test, orelse or end, False)
|
|
1120
|
+
if test_const is not False:
|
|
1121
|
+
self.visitStatements(node.body)
|
|
1122
|
+
|
|
1123
|
+
if node.orelse:
|
|
1124
|
+
self.emit_jump_forward_noline(end)
|
|
1125
|
+
self.nextBlock(orelse)
|
|
1126
|
+
if test_const is not True:
|
|
1127
|
+
self.visitStatements(node.orelse)
|
|
1128
|
+
|
|
1129
|
+
self.nextBlock(end)
|
|
1130
|
+
|
|
1131
|
+
def compileJumpIf(self, test: AST, next: Block, is_if_true: bool) -> None:
|
|
1132
|
+
if isinstance(test, ast.UnaryOp):
|
|
1133
|
+
if isinstance(test.op, ast.Not):
|
|
1134
|
+
# Compile to remove not operation
|
|
1135
|
+
return self.compileJumpIf(test.operand, next, not is_if_true)
|
|
1136
|
+
elif isinstance(test, ast.BoolOp):
|
|
1137
|
+
is_or = isinstance(test.op, ast.Or)
|
|
1138
|
+
skip_jump = next
|
|
1139
|
+
if is_if_true != is_or:
|
|
1140
|
+
skip_jump = self.newBlock()
|
|
1141
|
+
|
|
1142
|
+
for node in test.values[:-1]:
|
|
1143
|
+
self.get_type(node).emit_jumpif(node, skip_jump, is_or, self)
|
|
1144
|
+
self.nextBlock()
|
|
1145
|
+
|
|
1146
|
+
self.get_type(test.values[-1]).emit_jumpif(
|
|
1147
|
+
test.values[-1], next, is_if_true, self
|
|
1148
|
+
)
|
|
1149
|
+
self.nextBlock()
|
|
1150
|
+
|
|
1151
|
+
if skip_jump is not next:
|
|
1152
|
+
self.nextBlock(skip_jump)
|
|
1153
|
+
return
|
|
1154
|
+
elif isinstance(test, ast.IfExp):
|
|
1155
|
+
end = self.newBlock("end")
|
|
1156
|
+
orelse = self.newBlock("orelse")
|
|
1157
|
+
# Jump directly to orelse if test matches
|
|
1158
|
+
self.get_type(test.test).emit_jumpif(test.test, orelse, False, self)
|
|
1159
|
+
# Jump directly to target if test is true and body is matches
|
|
1160
|
+
self.get_type(test.body).emit_jumpif(test.body, next, is_if_true, self)
|
|
1161
|
+
self.emit_jump_forward_noline(end)
|
|
1162
|
+
# Jump directly to target if test is true and orelse matches
|
|
1163
|
+
self.nextBlock(orelse)
|
|
1164
|
+
self.get_type(test.orelse).emit_jumpif(test.orelse, next, is_if_true, self)
|
|
1165
|
+
self.nextBlock(end)
|
|
1166
|
+
return
|
|
1167
|
+
|
|
1168
|
+
self.get_type(test).emit_jumpif(test, next, is_if_true, self)
|
|
1169
|
+
self.nextBlock()
|
|
1170
|
+
|
|
1171
|
+
def _calculate_idx(
|
|
1172
|
+
self, arg_name: str, non_cellvar_pos: int, cellvars: IndexedSet
|
|
1173
|
+
) -> int:
|
|
1174
|
+
if sys.version_info >= (3, 12):
|
|
1175
|
+
return non_cellvar_pos
|
|
1176
|
+
try:
|
|
1177
|
+
offset = cellvars.index(arg_name)
|
|
1178
|
+
except ValueError:
|
|
1179
|
+
return non_cellvar_pos
|
|
1180
|
+
else:
|
|
1181
|
+
# the negative sign indicates to the runtime/JIT that this is a cellvar
|
|
1182
|
+
return -(offset + 1)
|
|
1183
|
+
|
|
1184
|
+
def perf_warning(self, msg: str, node: AST) -> None:
|
|
1185
|
+
return self.compiler.error_sink.perf_warning(msg, self.graph.filename, node)
|
|
1186
|
+
|
|
1187
|
+
def storePatternName(self, name: str, pc: PatternContext) -> None:
|
|
1188
|
+
pc = cast(StaticPatternContext, pc)
|
|
1189
|
+
pc.type_dict[name].emit_type_check(self.compiler.type_env.DYNAMIC.klass, self)
|
|
1190
|
+
self.storeName(name)
|
|
1191
|
+
|
|
1192
|
+
def _pattern_helper_store_name(
|
|
1193
|
+
self, name: str | None, pc: PatternContext, loc: ast.AST
|
|
1194
|
+
) -> None:
|
|
1195
|
+
super()._pattern_helper_store_name(name, pc, loc)
|
|
1196
|
+
pc = cast(StaticPatternContext, pc)
|
|
1197
|
+
if name is not None:
|
|
1198
|
+
pc.type_dict[name] = self.get_type(loc).klass
|
|
1199
|
+
|
|
1200
|
+
def emit_convert_compare_arg(self, op: AST, optype: Value, ltype: Value) -> None:
|
|
1201
|
+
"""Convert `ltype` to be compatible with `optype`."""
|
|
1202
|
+
# Helper function for visitCompare; checks if ltype needs to be boxed.
|
|
1203
|
+
optype.emit_convert(ltype, self)
|
|
1204
|
+
if (
|
|
1205
|
+
isinstance(ltype, CInstance)
|
|
1206
|
+
and isinstance(optype, DynamicInstance)
|
|
1207
|
+
and isinstance(op, ast.In)
|
|
1208
|
+
):
|
|
1209
|
+
# We don't support static containers yet, so for something
|
|
1210
|
+
# like `x: int64 in y: list` we set the optype to dynamic
|
|
1211
|
+
# in static.types.CIntInstance.bind_compare, and we need to
|
|
1212
|
+
# box `x` here before the COMPARE opcode is emitted.
|
|
1213
|
+
ltype.emit_box(self)
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
class Static310CodeGenerator(StaticCodeGenBase, CinderCodeGenerator310):
|
|
1217
|
+
flow_graph = PyFlowGraphStatic310
|
|
1218
|
+
parent_impl = StrictCodeGenerator310
|
|
1219
|
+
|
|
1220
|
+
def visitBoolOp(self, node: BoolOp) -> None:
|
|
1221
|
+
end = self.newBlock()
|
|
1222
|
+
for child in node.values[:-1]:
|
|
1223
|
+
self.get_type(child).emit_jumpif_pop(
|
|
1224
|
+
child, end, type(node.op) is ast.Or, self
|
|
1225
|
+
)
|
|
1226
|
+
self.nextBlock()
|
|
1227
|
+
self.visit(node.values[-1])
|
|
1228
|
+
self.nextBlock(end)
|
|
1229
|
+
|
|
1230
|
+
def visitCompare(self, node: Compare) -> None:
|
|
1231
|
+
self.set_pos(node)
|
|
1232
|
+
self.visit(node.left)
|
|
1233
|
+
cleanup = self.newBlock("cleanup")
|
|
1234
|
+
left = node.left
|
|
1235
|
+
for op, code in zip(node.ops[:-1], node.comparators[:-1]):
|
|
1236
|
+
optype = self.get_type(op)
|
|
1237
|
+
ltype = self.get_type(left)
|
|
1238
|
+
if ltype != optype:
|
|
1239
|
+
optype.emit_convert(ltype, self)
|
|
1240
|
+
self.emitChainedCompareStep(op, code, cleanup)
|
|
1241
|
+
left = code
|
|
1242
|
+
# now do the last comparison
|
|
1243
|
+
if node.ops:
|
|
1244
|
+
op = node.ops[-1]
|
|
1245
|
+
optype = self.get_type(op)
|
|
1246
|
+
ltype = self.get_type(left)
|
|
1247
|
+
if ltype != optype:
|
|
1248
|
+
self.emit_convert_compare_arg(op, optype, ltype)
|
|
1249
|
+
code = node.comparators[-1]
|
|
1250
|
+
self.visit(code)
|
|
1251
|
+
rtype = self.get_type(code)
|
|
1252
|
+
if rtype != optype:
|
|
1253
|
+
optype.emit_convert(rtype, self)
|
|
1254
|
+
optype.emit_compare(op, self)
|
|
1255
|
+
if len(node.ops) > 1:
|
|
1256
|
+
end = self.newBlock("end")
|
|
1257
|
+
self.emit_jump_forward(end)
|
|
1258
|
+
self.nextBlock(cleanup)
|
|
1259
|
+
self.emit_rotate_stack(2)
|
|
1260
|
+
self.emit("POP_TOP")
|
|
1261
|
+
self.nextBlock(end)
|
|
1262
|
+
|
|
1263
|
+
def emitChainedCompareStep(
|
|
1264
|
+
self, op: cmpop, value: AST, cleanup: Block, always_pop: bool = False
|
|
1265
|
+
) -> None:
|
|
1266
|
+
optype = self.get_type(op)
|
|
1267
|
+
self.visit(value)
|
|
1268
|
+
rtype = self.get_type(value)
|
|
1269
|
+
if rtype != optype:
|
|
1270
|
+
optype.emit_convert(rtype, self)
|
|
1271
|
+
self.emit_dup()
|
|
1272
|
+
self.emit_rotate_stack(3)
|
|
1273
|
+
optype.emit_compare(op, self)
|
|
1274
|
+
method = optype.emit_jumpif_only if always_pop else optype.emit_jumpif_pop_only
|
|
1275
|
+
method(cleanup, False, self)
|
|
1276
|
+
self.nextBlock(label="compare_or_cleanup")
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
class Static312CodeGenerator(StaticCodeGenBase, CinderCodeGenerator312):
|
|
1280
|
+
flow_graph = PyFlowGraph312
|
|
1281
|
+
parent_impl = StrictCodeGenerator312
|
|
1282
|
+
|
|
1283
|
+
def visitBoolOp(self, node: ast.BoolOp) -> None:
|
|
1284
|
+
is_if_true = type(node.op) is not ast.And
|
|
1285
|
+
end = self.newBlock()
|
|
1286
|
+
for value in node.values[:-1]:
|
|
1287
|
+
self.visit(value)
|
|
1288
|
+
self.emit("COPY", 1)
|
|
1289
|
+
self.get_type(value).emit_jumpif_only(end, is_if_true, self)
|
|
1290
|
+
self.nextBlock()
|
|
1291
|
+
self.emit("POP_TOP")
|
|
1292
|
+
self.visit(node.values[-1])
|
|
1293
|
+
self.nextBlock(end)
|
|
1294
|
+
|
|
1295
|
+
def visitCompare(self, node: Compare) -> None:
|
|
1296
|
+
self.set_pos(node)
|
|
1297
|
+
left = node.left
|
|
1298
|
+
self.visit(left)
|
|
1299
|
+
ltype = self.get_type(left)
|
|
1300
|
+
|
|
1301
|
+
if len(node.comparators) == 1:
|
|
1302
|
+
op = node.ops[0]
|
|
1303
|
+
optype = self.get_type(op)
|
|
1304
|
+
if ltype != optype:
|
|
1305
|
+
self.emit_convert_compare_arg(op, optype, ltype)
|
|
1306
|
+
|
|
1307
|
+
self.visit(node.comparators[0])
|
|
1308
|
+
rtype = self.get_type(node.comparators[0])
|
|
1309
|
+
if rtype != optype:
|
|
1310
|
+
optype.emit_convert(rtype, self)
|
|
1311
|
+
|
|
1312
|
+
optype.emit_compare(op, self)
|
|
1313
|
+
return
|
|
1314
|
+
|
|
1315
|
+
cleanup = self.newBlock("cleanup")
|
|
1316
|
+
for i in range(len(node.comparators) - 1):
|
|
1317
|
+
self.set_pos(node)
|
|
1318
|
+
op = node.ops[i]
|
|
1319
|
+
optype = self.get_type(op)
|
|
1320
|
+
if ltype != optype:
|
|
1321
|
+
self.emit_convert_compare_arg(op, optype, ltype)
|
|
1322
|
+
|
|
1323
|
+
self.emitChainedCompareStep(
|
|
1324
|
+
node.ops[i], node.comparators[i], cleanup, False
|
|
1325
|
+
)
|
|
1326
|
+
left = node.comparators[i]
|
|
1327
|
+
self.emit("COPY", 1)
|
|
1328
|
+
optype = self.get_type(node.ops[i])
|
|
1329
|
+
optype.emit_jumpif_only(cleanup, False, self)
|
|
1330
|
+
self.nextBlock(label="compare_or_cleanup")
|
|
1331
|
+
self.emit("POP_TOP")
|
|
1332
|
+
|
|
1333
|
+
op = node.ops[-1]
|
|
1334
|
+
optype = self.get_type(op)
|
|
1335
|
+
ltype = self.get_type(left)
|
|
1336
|
+
if ltype != optype:
|
|
1337
|
+
optype.emit_convert(ltype, self)
|
|
1338
|
+
|
|
1339
|
+
self.visit(node.comparators[-1])
|
|
1340
|
+
|
|
1341
|
+
rtype = self.get_type(node.comparators[-1])
|
|
1342
|
+
optype.emit_compare(op, self)
|
|
1343
|
+
end = self.newBlock("end")
|
|
1344
|
+
self.emit_jump_forward(end)
|
|
1345
|
+
|
|
1346
|
+
self.nextBlock(cleanup)
|
|
1347
|
+
self.emit("SWAP", 2)
|
|
1348
|
+
self.emit("POP_TOP")
|
|
1349
|
+
self.nextBlock(end)
|
|
1350
|
+
|
|
1351
|
+
def emitChainedCompareStep(
|
|
1352
|
+
self,
|
|
1353
|
+
op: ast.cmpop,
|
|
1354
|
+
value: ast.expr,
|
|
1355
|
+
cleanup: Block,
|
|
1356
|
+
always_pop: bool = False,
|
|
1357
|
+
) -> None:
|
|
1358
|
+
optype = self.get_type(op)
|
|
1359
|
+
self.visit(value)
|
|
1360
|
+
rtype = self.get_type(value)
|
|
1361
|
+
if rtype != optype:
|
|
1362
|
+
optype.emit_convert(rtype, self)
|
|
1363
|
+
self.emit("SWAP", 2)
|
|
1364
|
+
self.emit("COPY", 2)
|
|
1365
|
+
optype = self.get_type(op)
|
|
1366
|
+
optype.emit_compare(op, self)
|
|
1367
|
+
|
|
1368
|
+
|
|
1369
|
+
class Static314CodeGenerator(Static312CodeGenerator, CinderCodeGenerator314):
|
|
1370
|
+
flow_graph = PyFlowGraph314
|
|
1371
|
+
parent_impl = StrictCodeGenerator314
|
|
1372
|
+
|
|
1373
|
+
def emit_invoke_function(self, descr: TypeDescr, arg_count: int) -> None:
|
|
1374
|
+
self.emit("INVOKE_FUNCTION", (descr, arg_count))
|
|
1375
|
+
|
|
1376
|
+
def emit_invoke_method(
|
|
1377
|
+
self, descr: TypeDescr, arg_count: int, is_classmethod: bool = False
|
|
1378
|
+
) -> None:
|
|
1379
|
+
self.emit(
|
|
1380
|
+
"INVOKE_METHOD",
|
|
1381
|
+
(descr, arg_count, True) if is_classmethod else (descr, arg_count),
|
|
1382
|
+
)
|
|
1383
|
+
|
|
1384
|
+
def emit_load_static_method(
|
|
1385
|
+
self, descr: TypeDescr, is_classmethod: bool = False
|
|
1386
|
+
) -> None:
|
|
1387
|
+
self.emit(
|
|
1388
|
+
"LOAD_METHOD_STATIC",
|
|
1389
|
+
(descr, True) if is_classmethod else (descr,),
|
|
1390
|
+
)
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
class Static315CodeGenerator(Static314CodeGenerator, CinderCodeGenerator315):
|
|
1394
|
+
pass
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
if sys.version_info >= (3, 15):
|
|
1398
|
+
StaticCodeGenerator = Static315CodeGenerator
|
|
1399
|
+
elif sys.version_info >= (3, 14):
|
|
1400
|
+
StaticCodeGenerator = Static314CodeGenerator
|
|
1401
|
+
elif sys.version_info >= (3, 12):
|
|
1402
|
+
StaticCodeGenerator = Static312CodeGenerator
|
|
1403
|
+
else:
|
|
1404
|
+
StaticCodeGenerator = Static310CodeGenerator
|