• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CityOfZion / neo3-boa / 901be479-27a3-4d37-a8e9-d5889c82bd13

16 Jan 2024 04:06PM UTC coverage: 92.101% (+0.1%) from 91.967%
901be479-27a3-4d37-a8e9-d5889c82bd13

push

circleci

Mirella de Medeiros
CU-86a1n2upk - Notify is not being registered on events when using `notify` as a exported member of another import

20767 of 22548 relevant lines covered (92.1%)

1.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.98
/boa3/internal/compiler/codegenerator/codegeneratorvisitor.py
1
import ast
2✔
2
import os.path
2✔
3
from inspect import isclass
2✔
4
from typing import Dict, List, Optional
2✔
5

6
from boa3.internal import constants
2✔
7
from boa3.internal.analyser.astanalyser import IAstAnalyser
2✔
8
from boa3.internal.compiler.codegenerator.codegenerator import CodeGenerator
2✔
9
from boa3.internal.compiler.codegenerator.generatordata import GeneratorData
2✔
10
from boa3.internal.compiler.codegenerator.variablegenerationdata import VariableGenerationData
2✔
11
from boa3.internal.compiler.codegenerator.vmcodemapping import VMCodeMapping
2✔
12
from boa3.internal.model.builtin.builtin import Builtin
2✔
13
from boa3.internal.model.builtin.method.builtinmethod import IBuiltinMethod
2✔
14
from boa3.internal.model.expression import IExpression
2✔
15
from boa3.internal.model.imports.package import Package
2✔
16
from boa3.internal.model.method import Method
2✔
17
from boa3.internal.model.operation.binary.binaryoperation import BinaryOperation
2✔
18
from boa3.internal.model.operation.binaryop import BinaryOp
2✔
19
from boa3.internal.model.operation.operation import IOperation
2✔
20
from boa3.internal.model.operation.unary.unaryoperation import UnaryOperation
2✔
21
from boa3.internal.model.property import Property
2✔
22
from boa3.internal.model.symbol import ISymbol
2✔
23
from boa3.internal.model.type.classes.classtype import ClassType
2✔
24
from boa3.internal.model.type.classes.userclass import UserClass
2✔
25
from boa3.internal.model.type.collection.sequence.sequencetype import SequenceType
2✔
26
from boa3.internal.model.type.type import IType, Type
2✔
27
from boa3.internal.model.variable import Variable
2✔
28

29

30
class VisitorCodeGenerator(IAstAnalyser):
2✔
31
    """
32
    This class is responsible for walk through the ast.
33

34
    The methods with the name starting with 'visit_' are implementations of methods from the :class:`NodeVisitor` class.
35
    These methods are used to walk through the Python abstract syntax tree.
36

37
    :ivar generator:
38
    """
39

40
    def __init__(self, generator: CodeGenerator, filename: str = None, root: str = None):
2✔
41
        super().__init__(ast.parse(""), filename=filename, root_folder=root, log=True, fail_fast=True)
2✔
42

43
        self.generator = generator
2✔
44
        self.current_method: Optional[Method] = None
2✔
45
        self.current_class: Optional[UserClass] = None
2✔
46
        self.symbols = generator.symbol_table
2✔
47

48
        self.global_stmts: List[ast.AST] = []
2✔
49
        self._is_generating_initialize = False
2✔
50
        self._root_module: ast.AST = self._tree
2✔
51

52
    @property
2✔
53
    def _symbols(self) -> Dict[str, ISymbol]:
2✔
54
        symbol_table = self.symbols.copy()
2✔
55

56
        if isinstance(self.current_class, UserClass):
2✔
57
            symbol_table.update(self.current_class.symbols)
2✔
58

59
        return symbol_table
2✔
60

61
    def include_instruction(self, node: ast.AST, address: int):
2✔
62
        if self.current_method is not None and address in VMCodeMapping.instance().code_map:
2✔
63
            bytecode = VMCodeMapping.instance().code_map[address]
2✔
64
            from boa3.internal.model.debuginstruction import DebugInstruction
2✔
65
            self.current_method.include_instruction(DebugInstruction.build(node, bytecode))
2✔
66

67
    def build_data(self, origin_node: Optional[ast.AST],
2✔
68
                   symbol_id: Optional[str] = None,
69
                   symbol: Optional[ISymbol] = None,
70
                   result_type: Optional[IType] = None,
71
                   index: Optional[int] = None,
72
                   origin_object_type: Optional[ISymbol] = None,
73
                   already_generated: bool = False) -> GeneratorData:
74

75
        if isinstance(symbol, IType) and result_type is None:
2✔
76
            result_type = symbol
2✔
77
            symbol = None
2✔
78

79
        if symbol is None and symbol_id is not None:
2✔
80
            # try to find the symbol if the id is known
81
            if symbol_id in self._symbols:
2✔
82
                found_symbol = self._symbols[symbol_id]
2✔
83
            else:
84
                _, found_symbol = self.generator.get_symbol(symbol_id)
2✔
85
                if found_symbol is Type.none:
2✔
86
                    # generator get_symbol returns Type.none if the symbol is not found
87
                    found_symbol = None
2✔
88

89
            if found_symbol is not None:
2✔
90
                symbol = found_symbol
2✔
91

92
        return GeneratorData(origin_node, symbol_id, symbol, result_type, index, origin_object_type, already_generated)
2✔
93

94
    def visit_and_update_analyser(self, node: ast.AST, target_analyser) -> GeneratorData:
2✔
95
        result = self.visit(node)
2✔
96
        if hasattr(target_analyser, '_update_logs'):
2✔
97
            target_analyser._update_logs(self)
2✔
98
        return result
2✔
99

100
    def visit(self, node: ast.AST) -> GeneratorData:
2✔
101
        result = super().visit(node)
2✔
102
        if not isinstance(result, GeneratorData):
2✔
103
            result = self.build_data(node)
2✔
104
        return result
2✔
105

106
    def visit_to_map(self, node: ast.AST, generate: bool = False) -> GeneratorData:
2✔
107
        address: int = VMCodeMapping.instance().bytecode_size
2✔
108
        if isinstance(node, ast.Expr):
2✔
109
            value = self.visit_Expr(node, generate)
2✔
110
        elif generate:
2✔
111
            value = self.visit_to_generate(node)
2✔
112
        else:
113
            value = self.visit(node)
2✔
114

115
        if not isinstance(node, (ast.For, ast.While, ast.If)):
2✔
116
            # control flow nodes must map each of their instructions
117
            self.include_instruction(node, address)
2✔
118
        return value
2✔
119

120
    def visit_to_generate(self, node) -> GeneratorData:
2✔
121
        """
122
        Visitor to generate the nodes that the primary visitor is used to retrieve value
123

124
        :param node: an ast node
125
        """
126
        if isinstance(node, ast.AST):
2✔
127
            result = self.visit(node)
2✔
128

129
            if not result.already_generated and result.symbol_id is not None:
2✔
130
                if self.is_exception_name(result.symbol_id):
2✔
131
                    self.generator.convert_new_exception()
2✔
132
                else:
133
                    is_internal = hasattr(node, 'is_internal_call') and node.is_internal_call
2✔
134
                    class_type = result.type if isinstance(node, ast.Attribute) else None
2✔
135

136
                    if (self.is_implemented_class_type(result.type)
2✔
137
                            and len(result.symbol_id.split(constants.ATTRIBUTE_NAME_SEPARATOR)) > 1):
138
                        # if the symbol id has the attribute separator and the top item on the stack is a user class,
139
                        # then this value is an attribute from that class
140
                        # change the id for correct generation
141
                        result.symbol_id = result.symbol_id.split(constants.ATTRIBUTE_NAME_SEPARATOR)[-1]
2✔
142

143
                    elif isinstance(result.index, Package):
2✔
144
                        class_type = None
2✔
145

146
                    self.generator.convert_load_symbol(result.symbol_id, is_internal=is_internal, class_type=class_type)
2✔
147

148
                result.already_generated = True
2✔
149

150
            return result
2✔
151
        else:
152
            index = self.generator.convert_literal(node)
2✔
153
            return self.build_data(node, index=index)
2✔
154

155
    def is_exception_name(self, exc_id: str) -> bool:
2✔
156
        global_symbols = globals()
2✔
157
        if exc_id in global_symbols or exc_id in global_symbols['__builtins__']:
2✔
158
            symbol = (global_symbols[exc_id]
2✔
159
                      if exc_id in global_symbols
160
                      else global_symbols['__builtins__'][exc_id])
161
            if isclass(symbol) and issubclass(symbol, BaseException):
2✔
162
                return True
2✔
163
        return False
2✔
164

165
    def is_global_variable(self, variable_id: str) -> bool:
2✔
166
        if self.current_class and variable_id in self.current_class.variables:
2✔
167
            return False
2✔
168
        if self.current_method and (variable_id in self.current_method.args
2✔
169
                                    or variable_id in self.current_method.locals):
170
            return False
2✔
171

172
        return variable_id in self.symbols
2✔
173

174
    def _remove_inserted_opcodes_since(self, last_address: int, last_stack_size: Optional[int] = None):
2✔
175
        self.generator._remove_inserted_opcodes_since(last_address, last_stack_size)
2✔
176

177
    def _get_unique_name(self, name_id: str, node: ast.AST) -> str:
2✔
178
        return '{0}{2}{1}'.format(node.__hash__(), name_id, constants.VARIABLE_NAME_SEPARATOR)
2✔
179

180
    def set_filename(self, filename: str):
2✔
181
        if isinstance(filename, str) and os.path.isfile(filename):
2✔
182
            if constants.PATH_SEPARATOR != os.path.sep:
2✔
183
                self.filename = filename.replace(constants.PATH_SEPARATOR, os.path.sep)
×
184
            else:
185
                self.filename = filename
2✔
186

187
    def visit_Module(self, module: ast.Module) -> GeneratorData:
2✔
188
        """
189
        Visitor of the module node
190

191
        Fills module symbol table
192

193
        :param module:
194
        """
195
        global_stmts = [node for node in module.body if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
2✔
196
        function_stmts = module.body[len(global_stmts):]
2✔
197
        mandatory_global_stmts = []
2✔
198
        for stmt in global_stmts:
2✔
199
            if isinstance(stmt, ast.ClassDef):
2✔
200
                class_symbol = self.get_symbol(stmt.name)
2✔
201
                if isinstance(class_symbol, UserClass) and len(class_symbol.class_variables) > 0:
2✔
202
                    mandatory_global_stmts.append(stmt)
2✔
203
            elif not isinstance(stmt, (ast.Import, ast.ImportFrom)):
2✔
204
                mandatory_global_stmts.append(stmt)
2✔
205

206
        for stmt in function_stmts:
2✔
207
            self.visit(stmt)
2✔
208

209
        if self.generator.initialize_static_fields():
2✔
210
            last_symbols = self.symbols  # save to revert in the end and not compromise consequent visits
2✔
211
            class_non_static_stmts = []
2✔
212

213
            for node in global_stmts.copy():
2✔
214
                if isinstance(node, ast.ClassDef):
2✔
215
                    class_origin_module = None
2✔
216
                    if hasattr(node, 'origin'):
2✔
217
                        class_origin_module = node.origin
2✔
218
                        if (node.origin is not self._root_module
2✔
219
                                and hasattr(node.origin, 'symbols')):
220
                            # symbols unique to imports are not included in the symbols
221
                            self.symbols = node.origin.symbols
2✔
222

223
                    class_variables = []
2✔
224
                    class_functions = []
2✔
225
                    for stmt in node.body:
2✔
226
                        if isinstance(stmt, (ast.Assign, ast.AugAssign, ast.AnnAssign)):
2✔
227
                            class_variables.append(stmt)
2✔
228
                        else:
229
                            class_functions.append(stmt)
2✔
230

231
                    if len(class_functions) > 0:
2✔
232
                        if class_origin_module is not None and not hasattr(node, 'origin'):
2✔
233
                            node.origin = class_origin_module
×
234

235
                        cls_fun = node
2✔
236
                        if len(class_variables) > 0:
2✔
237
                            cls_var = node
2✔
238
                            cls_fun = self.clone(node)
2✔
239
                            cls_fun.body = class_functions
2✔
240
                            cls_var.body = class_variables
2✔
241
                        else:
242
                            global_stmts.remove(cls_fun)
2✔
243

244
                        class_non_static_stmts.append(cls_fun)
2✔
245
                    self.symbols = last_symbols  # don't use inner scopes to evaluate the other globals
2✔
246

247
            # to generate the 'initialize' method for Neo
248
            self._log_info(f"Compiling '{constants.INITIALIZE_METHOD_ID}' function")
2✔
249
            self._is_generating_initialize = True
2✔
250
            for stmt in global_stmts:
2✔
251
                cur_tree = self._tree
2✔
252
                cur_filename = self.filename
2✔
253
                if hasattr(stmt, 'origin'):
2✔
254
                    if hasattr(stmt.origin, 'filename'):
2✔
255
                        self.set_filename(stmt.origin.filename)
2✔
256
                    self._tree = stmt.origin
2✔
257

258
                self.visit(stmt)
2✔
259
                self.filename = cur_filename
2✔
260
                self._tree = cur_tree
2✔
261

262
            self._is_generating_initialize = False
2✔
263
            self.generator.end_initialize()
2✔
264

265
            # generate any symbol inside classes that's not variables AFTER generating 'initialize' method
266
            for stmt in class_non_static_stmts:
2✔
267
                cur_tree = self._tree
2✔
268
                if hasattr(stmt, 'origin'):
2✔
269
                    self._tree = stmt.origin
2✔
270
                self.visit(stmt)
2✔
271
                self._tree = cur_tree
2✔
272

273
            self.symbols = last_symbols  # revert in the end to not compromise consequent visits
2✔
274
            self.generator.additional_symbols = None
2✔
275

276
        elif len(function_stmts) > 0 or len(mandatory_global_stmts) > 0:
2✔
277
            # to organize syntax tree nodes from other modules
278
            for stmt in global_stmts:
2✔
279
                if not hasattr(stmt, 'origin'):
2✔
280
                    stmt.origin = module
2✔
281

282
            module.symbols = self._symbols
2✔
283
            self.global_stmts.extend(global_stmts)
2✔
284
        else:
285
            # to generate objects when there are no static variables to generate 'initialize'
286
            for stmt in global_stmts:
2✔
287
                self.visit(stmt)
2✔
288

289
        return self.build_data(module)
2✔
290

291
    def visit_ClassDef(self, node: ast.ClassDef) -> GeneratorData:
2✔
292
        if node.name in self.symbols:
2✔
293
            class_symbol = self.symbols[node.name]
2✔
294
            if isinstance(class_symbol, UserClass):
2✔
295
                self.current_class = class_symbol
2✔
296

297
            if self._is_generating_initialize:
2✔
298
                address = self.generator.bytecode_size
2✔
299
                self.generator.convert_new_empty_array(len(class_symbol.class_variables), class_symbol)
2✔
300
                self.generator.convert_store_variable(node.name, address)
2✔
301
            else:
302
                init_method = class_symbol.constructor_method()
2✔
303
                if isinstance(init_method, Method) and init_method.start_address is None:
2✔
304
                    self.generator.generate_implicit_init_user_class(init_method)
2✔
305

306
        for stmt in node.body:
2✔
307
            self.visit(stmt)
2✔
308

309
        self.current_class = None
2✔
310
        return self.build_data(node)
2✔
311

312
    def visit_FunctionDef(self, function: ast.FunctionDef) -> GeneratorData:
2✔
313
        """
314
        Visitor of the function definition node
315

316
        Generates the Neo VM code for the function
317

318
        :param function: the python ast function definition node
319
        """
320
        method = self._symbols[function.name]
2✔
321

322
        if isinstance(method, Property):
2✔
323
            method = method.getter
2✔
324

325
        if isinstance(method, Method):
2✔
326
            self.current_method = method
2✔
327
            if isinstance(self.current_class, ClassType):
2✔
328
                function_name = self.current_class.identifier + constants.ATTRIBUTE_NAME_SEPARATOR + function.name
2✔
329
            else:
330
                function_name = function.name
2✔
331

332
            self._log_info(f"Compiling '{function_name}' function")
2✔
333

334
            if method.is_public or method.is_called:
2✔
335
                if not isinstance(self.current_class, ClassType) or not self.current_class.is_interface:
2✔
336
                    self.generator.convert_begin_method(method)
2✔
337

338
                    for stmt in function.body:
2✔
339
                        self.visit_to_map(stmt)
2✔
340

341
                    self.generator.convert_end_method(function.name)
2✔
342

343
            self.current_method = None
2✔
344

345
        return self.build_data(function, symbol=method, symbol_id=function.name)
2✔
346

347
    def visit_Return(self, ret: ast.Return) -> GeneratorData:
2✔
348
        """
349
        Visitor of a function return node
350

351
        :param ret: the python ast return node
352
        """
353
        if self.generator.stack_size > 0:
2✔
354
            self.generator.clear_stack(True)
2✔
355

356
        if self.current_method.return_type is not Type.none:
2✔
357
            result = self.visit_to_generate(ret.value)
2✔
358
            if result.type is Type.none and not self.generator.is_none_inserted():
2✔
359
                self.generator.convert_literal(None)
2✔
360
        elif ret.value is not None:
2✔
361
            self.visit_to_generate(ret.value)
2✔
362
            if self.generator.stack_size > 0:
2✔
363
                self.generator.remove_stack_top_item()
2✔
364

365
        self.generator.insert_return()
2✔
366

367
        return self.build_data(ret)
2✔
368

369
    def store_variable(self, *var_ids: VariableGenerationData, value: ast.AST):
2✔
370
        # if the value is None, it is a variable declaration
371
        if value is not None:
2✔
372
            if len(var_ids) == 1:
2✔
373
                # it's a simple assignment
374
                var_id, index, address = var_ids[0]
2✔
375
                if index is None:
2✔
376
                    # if index is None, then it is a variable assignment
377
                    result_data = self.visit_to_generate(value)
2✔
378

379
                    if result_data.type is Type.none and not self.generator.is_none_inserted():
2✔
380
                        self.generator.convert_literal(None)
2✔
381
                    self.generator.convert_store_variable(var_id, address, self.current_class if self.current_method is None else None)
2✔
382
                else:
383
                    # if not, it is an array assignment
384
                    self.generator.convert_load_symbol(var_id)
2✔
385
                    self.visit_to_generate(index)
2✔
386

387
                    aux_index = VMCodeMapping.instance().bytecode_size
2✔
388
                    value_data = self.visit_to_generate(value)
2✔
389
                    value_address = value_data.index if value_data.index is not None else aux_index
2✔
390

391
                    self.generator.convert_set_item(value_address)
2✔
392

393
            elif len(var_ids) > 0:
2✔
394
                # it's a chained assignment
395
                self.visit_to_generate(value)
2✔
396
                for pos, (var_id, index, address) in enumerate(reversed(var_ids)):
2✔
397
                    if index is None:
2✔
398
                        # if index is None, then it is a variable assignment
399
                        if pos < len(var_ids) - 1:
2✔
400
                            self.generator.duplicate_stack_top_item()
2✔
401
                        self.generator.convert_store_variable(var_id, address)
2✔
402
                    else:
403
                        # if not, it is an array assignment
404
                        if pos < len(var_ids) - 1:
2✔
405
                            self.generator.convert_load_symbol(var_id)
2✔
406
                            self.visit_to_generate(index)
2✔
407
                            fix_index = VMCodeMapping.instance().bytecode_size
2✔
408
                            self.generator.duplicate_stack_item(3)
2✔
409
                        else:
410
                            self.visit_to_generate(index)
2✔
411
                            fix_index = VMCodeMapping.instance().bytecode_size
2✔
412
                            self.generator.convert_load_symbol(var_id)
2✔
413
                            self.generator.swap_reverse_stack_items(3)
2✔
414
                        self.generator.convert_set_item(fix_index)
2✔
415

416
    def visit_AnnAssign(self, ann_assign: ast.AnnAssign) -> GeneratorData:
2✔
417
        """
418
        Visitor of an annotated assignment node
419

420
        :param ann_assign: the python ast variable assignment node
421
        """
422
        var_value_address = self.generator.bytecode_size
2✔
423
        var_data = self.visit(ann_assign.target)
2✔
424
        var_id = var_data.symbol_id
2✔
425
        # filter to find the imported variables
426
        if (var_id not in self.generator.symbol_table
2✔
427
                and hasattr(ann_assign, 'origin')
428
                and isinstance(ann_assign.origin, ast.AST)):
429
            var_id = self._get_unique_name(var_id, ann_assign.origin)
×
430

431
        if not (isinstance(var_data.symbol, Variable) and
2✔
432
                self.is_global_variable(var_id) and
433
                not self.generator.store_constant_variable(var_data.symbol)
434
        ):
435
            self.store_variable(VariableGenerationData(var_id, None, var_value_address), value=ann_assign.value)
2✔
436

437
        return self.build_data(ann_assign)
2✔
438

439
    def visit_Assign(self, assign: ast.Assign) -> GeneratorData:
2✔
440
        """
441
        Visitor of an assignment node
442

443
        :param assign: the python ast variable assignment node
444
        """
445
        vars_ids: List[VariableGenerationData] = []
2✔
446
        for target in assign.targets:
2✔
447
            var_value_address = self.generator.bytecode_size
2✔
448
            target_data: GeneratorData = self.visit(target)
2✔
449

450
            var_id = target_data.symbol_id
2✔
451
            var_index = target_data.index
2✔
452

453
            if (
2✔
454
                    isinstance(target_data.symbol, Variable) and
455
                    self.is_global_variable(var_id) and
456
                    not self.generator.store_constant_variable(target_data.symbol)
457
            ):
458
                continue
2✔
459

460
            # filter to find the imported variables
461
            if (var_id not in self.generator.symbol_table
2✔
462
                    and hasattr(assign, 'origin')
463
                    and isinstance(assign.origin, ast.AST)):
464
                var_id = self._get_unique_name(var_id, assign.origin)
×
465

466
            vars_ids.append(VariableGenerationData(var_id, var_index, var_value_address))
2✔
467

468
        if vars_ids:
2✔
469
            self.store_variable(*vars_ids, value=assign.value)
2✔
470
        return self.build_data(assign)
2✔
471

472
    def visit_AugAssign(self, aug_assign: ast.AugAssign) -> GeneratorData:
2✔
473
        """
474
        Visitor of an augmented assignment node
475

476
        :param aug_assign: the python ast augmented assignment node
477
        """
478
        start_address = self.generator.bytecode_size
2✔
479
        var_data = self.visit(aug_assign.target)
2✔
480
        var_id = var_data.symbol_id
2✔
481
        # filter to find the imported variables
482
        if (var_id not in self.generator.symbol_table
2✔
483
                and hasattr(aug_assign, 'origin')
484
                and isinstance(aug_assign.origin, ast.AST)):
485
            var_id = self._get_unique_name(var_id, aug_assign.origin)
×
486

487
        if isinstance(var_data.type, UserClass):
2✔
488
            self.generator.duplicate_stack_top_item()
2✔
489
        elif hasattr(var_data.origin_object_type, 'identifier') and var_data.already_generated:
2✔
490
            VMCodeMapping.instance().remove_opcodes(start_address)
×
491
            self.generator.convert_load_symbol(var_data.origin_object_type.identifier)
×
492

493
        self.generator.convert_load_symbol(var_id, class_type=var_data.origin_object_type)
2✔
494
        value_address = self.generator.bytecode_size
2✔
495
        self.visit_to_generate(aug_assign.value)
2✔
496
        self.generator.convert_operation(aug_assign.op)
2✔
497
        self.generator.convert_store_variable(var_id, value_address, user_class=var_data.origin_object_type)
2✔
498
        return self.build_data(aug_assign)
2✔
499

500
    def visit_Subscript(self, subscript: ast.Subscript) -> GeneratorData:
2✔
501
        """
502
        Visitor of a subscript node
503

504
        :param subscript: the python ast subscript node
505
        """
506
        if isinstance(subscript.slice, ast.Slice):
2✔
507
            return self.visit_Subscript_Slice(subscript)
2✔
508

509
        return self.visit_Subscript_Index(subscript)
2✔
510

511
    def visit_Subscript_Index(self, subscript: ast.Subscript) -> GeneratorData:
2✔
512
        """
513
        Visitor of a subscript node with index
514

515
        :param subscript: the python ast subscript node
516
        """
517
        index = None
2✔
518
        symbol_id = None
2✔
519
        if isinstance(subscript.ctx, ast.Load):
2✔
520
            # get item
521
            value_data = self.visit_to_generate(subscript.value)
2✔
522
            slice = subscript.slice.value if isinstance(subscript.slice, ast.Index) else subscript.slice
2✔
523
            self.visit_to_generate(slice)
2✔
524

525
            index_is_constant_number = isinstance(slice, ast.Num) and isinstance(slice.n, int)
2✔
526
            self.generator.convert_get_item(index_is_positive=(index_is_constant_number and slice.n >= 0),
2✔
527
                                            test_is_negative_index=not (index_is_constant_number and slice.n < 0))
528

529
            value_type = value_data.type
2✔
530
        else:
531
            # set item
532
            var_data = self.visit(subscript.value)
2✔
533

534
            index = subscript.slice.value if isinstance(subscript.slice, ast.Index) else subscript.slice
2✔
535
            symbol_id = var_data.symbol_id
2✔
536
            value_type = var_data.type
2✔
537

538
        result_type = value_type.item_type if isinstance(value_type, SequenceType) else value_type
2✔
539
        return self.build_data(subscript, result_type=result_type,
2✔
540
                               symbol_id=symbol_id, index=index)
541

542
    def visit_Subscript_Slice(self, subscript: ast.Subscript) -> GeneratorData:
2✔
543
        """
544
        Visitor of a subscript node with slice
545

546
        :param subscript: the python ast subscript node
547
        """
548
        lower_omitted = subscript.slice.lower is None
2✔
549
        upper_omitted = subscript.slice.upper is None
2✔
550
        step_omitted = subscript.slice.step is None
2✔
551

552
        self.visit_to_generate(subscript.value)
2✔
553

554
        step_negative = True if not step_omitted and subscript.slice.step.n < 0 else False
2✔
555

556
        if step_negative:
2✔
557
            # reverse the ByteString or the list
558
            self.generator.convert_array_negative_stride()
2✔
559

560
        addresses = []
2✔
561

562
        for index_number, omitted in [(subscript.slice.lower, lower_omitted), (subscript.slice.upper, upper_omitted)]:
2✔
563
            if not omitted:
2✔
564
                addresses.append(VMCodeMapping.instance().bytecode_size)
2✔
565
                self.visit_to_generate(index_number)
2✔
566

567
                index_is_constant_number = isinstance(index_number, ast.Num) and isinstance(index_number.n, int)
2✔
568
                if index_is_constant_number and index_number.n < 0:
2✔
569
                    self.generator.fix_negative_index(test_is_negative=False)
2✔
570
                elif not index_is_constant_number:
2✔
571
                    self.generator.fix_negative_index()
2✔
572

573
                if step_negative:
2✔
574
                    self.generator.duplicate_stack_item(len(addresses) + 1)  # duplicates the subscript
2✔
575
                    self.generator.fix_index_negative_stride()
2✔
576

577
                self.generator.fix_index_out_of_range(has_another_index_in_stack=len(addresses) == 2)
2✔
578

579
        # if both are explicit
580
        if not lower_omitted and not upper_omitted:
2✔
581
            self.generator.convert_get_sub_sequence()
2✔
582

583
        # only one of them is omitted
584
        elif lower_omitted != upper_omitted:
2✔
585
            # start position is omitted
586
            if lower_omitted:
2✔
587
                self.generator.convert_get_sequence_beginning()
2✔
588
            # end position is omitted
589
            else:
590
                self.generator.convert_get_sequence_ending()
2✔
591
        else:
592
            self.generator.convert_copy()
2✔
593

594
        if not step_omitted:
2✔
595
            self.visit_to_generate(subscript.slice.step)
2✔
596
            self.generator.convert_get_stride()
2✔
597

598
        return self.build_data(subscript)
2✔
599

600
    def _convert_unary_operation(self, operand, op):
2✔
601
        self.visit_to_generate(operand)
2✔
602
        self.generator.convert_operation(op)
2✔
603

604
    def _convert_binary_operation(self, left, right, op):
2✔
605
        self.visit_to_generate(left)
2✔
606
        self.visit_to_generate(right)
2✔
607
        self.generator.convert_operation(op)
2✔
608

609
    def visit_BinOp(self, bin_op: ast.BinOp) -> GeneratorData:
2✔
610
        """
611
        Visitor of a binary operation node
612

613
        :param bin_op: the python ast binary operation node
614
        """
615
        if isinstance(bin_op.op, BinaryOperation):
2✔
616
            self._convert_binary_operation(bin_op.left, bin_op.right, bin_op.op)
2✔
617

618
        return self.build_data(bin_op)
2✔
619

620
    def visit_UnaryOp(self, un_op: ast.UnaryOp) -> GeneratorData:
2✔
621
        """
622
        Visitor of a binary operation node
623

624
        :param un_op: the python ast binary operation node
625
        """
626
        if isinstance(un_op.op, UnaryOperation):
2✔
627
            self._convert_unary_operation(un_op.operand, un_op.op)
2✔
628

629
        return self.build_data(un_op, result_type=un_op.op.result)
2✔
630

631
    def visit_Compare(self, compare: ast.Compare) -> GeneratorData:
2✔
632
        """
633
        Visitor of a compare operation node
634

635
        :param compare: the python ast compare operation node
636
        """
637
        operation_symbol: IOperation = BinaryOp.And
2✔
638
        converted: bool = False
2✔
639
        left = compare.left
2✔
640
        for index, op in enumerate(compare.ops):
2✔
641
            right = compare.comparators[index]
2✔
642
            if isinstance(op, IOperation):
2✔
643
                if isinstance(op, BinaryOperation):
2✔
644
                    self._convert_binary_operation(left, right, op)
2✔
645
                else:
646
                    operand = left
2✔
647
                    if isinstance(operand, ast.NameConstant) and operand.value is None:
2✔
648
                        operand = right
×
649
                    self._convert_unary_operation(operand, op)
2✔
650
                # if it's more than two comparators, must include AND between the operations
651
                if not converted:
2✔
652
                    converted = True
2✔
653
                    operation_symbol = op
2✔
654
                else:
655
                    operation_symbol = BinaryOp.And
2✔
656
                    self.generator.convert_operation(BinaryOp.And)
2✔
657
            left = right
2✔
658

659
        return self.build_data(compare, symbol=operation_symbol, result_type=operation_symbol.result)
2✔
660

661
    def visit_BoolOp(self, bool_op: ast.BoolOp) -> GeneratorData:
2✔
662
        """
663
        Visitor of a compare operation node
664

665
        :param bool_op: the python ast boolean operation node
666
        """
667
        if isinstance(bool_op.op, BinaryOperation):
2✔
668
            left = bool_op.values[0]
2✔
669
            self.visit_to_generate(left)
2✔
670
            for index, right in enumerate(bool_op.values[1:]):
2✔
671
                self.visit_to_generate(right)
2✔
672
                self.generator.convert_operation(bool_op.op)
2✔
673

674
        return self.build_data(bool_op)
2✔
675

676
    def visit_While(self, while_node: ast.While) -> GeneratorData:
2✔
677
        """
678
        Visitor of a while statement node
679

680
        :param while_node: the python ast while statement node
681
        """
682
        start_addr: int = self.generator.convert_begin_while()
2✔
683
        for stmt in while_node.body:
2✔
684
            self.visit_to_map(stmt, generate=True)
2✔
685

686
        test_address: int = VMCodeMapping.instance().bytecode_size
2✔
687
        test_data = self.visit_to_map(while_node.test, generate=True)
2✔
688
        self.generator.convert_end_while(start_addr, test_address)
2✔
689

690
        else_begin_address: int = self.generator.last_code_start_address
2✔
691
        for stmt in while_node.orelse:
2✔
692
            self.visit_to_map(stmt, generate=True)
2✔
693

694
        self.generator.convert_end_loop_else(start_addr, else_begin_address, len(while_node.orelse) > 0)
2✔
695
        return self.build_data(while_node, index=start_addr)
2✔
696

697
    def visit_Match(self, match_node: ast.Match) -> GeneratorData:
2✔
698
        case_addresses = []
2✔
699

700
        for index, case in enumerate(match_node.cases):
2✔
701
            # if it's the wildcard to accept all values
702
            if hasattr(case.pattern, 'pattern') and case.pattern.pattern is None:
2✔
703
                for stmt in case.body:
2✔
704
                    self.visit_to_map(stmt, generate=True)
2✔
705
                self.generator.convert_end_if(case_addresses[-1])
2✔
706
            else:
707
                subject = self.visit_to_map(match_node.subject, generate=True)
2✔
708
                if isinstance(case.pattern, ast.MatchSingleton):
2✔
709
                    self.generator.convert_literal(case.pattern.value)
2✔
710
                    pattern_type = self.get_type(case.pattern.value)
2✔
711
                else:
712
                    pattern = self.visit_to_generate(case.pattern.value)
2✔
713
                    pattern_type = pattern.type
2✔
714

715
                self.generator.duplicate_stack_item(2)
2✔
716
                pattern_type.generate_is_instance_type_check(self.generator)
2✔
717

718
                self.generator.swap_reverse_stack_items(3)
2✔
719
                self.generator.convert_operation(BinaryOp.NumEq)
2✔
720
                self.generator.convert_operation(BinaryOp.And)
2✔
721

722
                case_addresses.append(self.generator.convert_begin_if())
2✔
723
                for stmt in case.body:
2✔
724
                    self.visit_to_map(stmt, generate=True)
2✔
725

726
                ends_with_if = len(case.body) > 0 and isinstance(case.body[-1], ast.If)
2✔
727

728
                if index < len(match_node.cases) - 1:
2✔
729
                    case_addresses[index] = self.generator.convert_begin_else(case_addresses[index], ends_with_if)
2✔
730

731
        for case_addr in reversed(range(len(case_addresses))):
2✔
732
            self.generator.convert_end_if(case_addresses[case_addr])
2✔
733

734
        return self.build_data(match_node)
2✔
735

736
    def visit_For(self, for_node: ast.For) -> GeneratorData:
2✔
737
        """
738
        Visitor of for statement node
739

740
        :param for_node: the python ast for node
741
        """
742
        self.visit_to_generate(for_node.iter)
2✔
743
        start_address = self.generator.convert_begin_for()
2✔
744

745
        if isinstance(for_node.target, tuple):
2✔
746
            for target in for_node.target:
×
747
                var_data = self.visit_to_map(target)
×
748
                self.generator.convert_store_variable(var_data.symbol_id)
×
749
        else:
750
            var_data = self.visit(for_node.target)
2✔
751
            self.generator.convert_store_variable(var_data.symbol_id)
2✔
752

753
        for stmt in for_node.body:
2✔
754
            self.visit_to_map(stmt, generate=True)
2✔
755

756
        # TODO: remove when optimizing for generation #864e6me36
757
        if self.current_method is not None:
2✔
758
            self.current_method.remove_instruction(for_node.lineno, for_node.col_offset)
2✔
759

760
        condition_address = self.generator.convert_end_for(start_address)
2✔
761
        self.include_instruction(for_node, condition_address)
2✔
762
        else_begin = self.generator.last_code_start_address
2✔
763

764
        for stmt in for_node.orelse:
2✔
765
            self.visit_to_map(stmt, generate=True)
2✔
766

767
        self.generator.convert_end_loop_else(start_address,
2✔
768
                                             else_begin,
769
                                             has_else=len(for_node.orelse) > 0,
770
                                             is_for=True)
771
        return self.build_data(for_node)
2✔
772

773
    def visit_If(self, if_node: ast.If) -> GeneratorData:
2✔
774
        """
775
        Visitor of if statement node
776

777
        :param if_node: the python ast if statement node
778
        """
779
        test = self.visit_to_map(if_node.test, generate=True)
2✔
780

781
        if not Type.bool.is_type_of(test.type) and test.type is not None:
2✔
782
            self.generator.convert_builtin_method_call(Builtin.Bool)
2✔
783

784
        start_addr: int = self.generator.convert_begin_if()
2✔
785
        for stmt in if_node.body:
2✔
786
            self.visit_to_map(stmt, generate=True)
2✔
787

788
        ends_with_if = len(if_node.body) > 0 and isinstance(if_node.body[-1], ast.If)
2✔
789

790
        if len(if_node.orelse) > 0:
2✔
791
            start_addr = self.generator.convert_begin_else(start_addr, ends_with_if)
2✔
792
            for stmt in if_node.orelse:
2✔
793
                self.visit_to_map(stmt, generate=True)
2✔
794

795
        self.generator.convert_end_if(start_addr)
2✔
796
        return self.build_data(if_node)
2✔
797

798
    def visit_Expr(self, expr: ast.Expr, generate: bool = False) -> GeneratorData:
2✔
799
        """
800
        Visitor of an expression node
801

802
        :param expr: the python ast expression node
803
        :param generate: if it should convert the value
804
        """
805
        last_stack = self.generator.stack_size
2✔
806
        if generate:
2✔
807
            value = self.visit_to_generate(expr.value)
2✔
808
        else:
809
            value = self.visit(expr.value)
2✔
810

811
        new_stack = self.generator.stack_size
2✔
812
        for x in range(last_stack, new_stack):
2✔
813
            self.generator.remove_stack_top_item()
2✔
814

815
        return value
2✔
816

817
    def visit_IfExp(self, if_node: ast.IfExp) -> GeneratorData:
2✔
818
        """
819
        Visitor of if expression node
820

821
        :param if_node: the python ast if statement node
822
        """
823
        self.visit_to_map(if_node.test, generate=True)
2✔
824

825
        start_addr: int = self.generator.convert_begin_if()
2✔
826
        body_data = self.visit_to_map(if_node.body, generate=True)
2✔
827

828
        start_addr = self.generator.convert_begin_else(start_addr)
2✔
829
        else_data = self.visit_to_map(if_node.orelse, generate=True)
2✔
830

831
        self.generator.convert_end_if(start_addr)
2✔
832
        return self.build_data(if_node, result_type=Type.union.build([body_data.type, else_data.type]))
2✔
833

834
    def visit_Assert(self, assert_node: ast.Assert) -> GeneratorData:
2✔
835
        """
836
        Visitor of the assert node
837

838
        :param assert_node: the python ast assert node
839
        """
840
        self.visit_to_generate(assert_node.test)
2✔
841

842
        if assert_node.msg is not None:
2✔
843
            self.visit_to_generate(assert_node.msg)
2✔
844

845
        self.generator.convert_assert(has_message=assert_node.msg is not None)
2✔
846
        return self.build_data(assert_node)
2✔
847

848
    def visit_Call(self, call: ast.Call) -> GeneratorData:
2✔
849
        """
850
        Visitor of a function call node
851

852
        :param call: the python ast function call node
853
        :returns: The called function return type
854
        """
855
        # the parameters are included into the stack in the reversed order
856
        last_address = VMCodeMapping.instance().bytecode_size
2✔
857
        last_stack = self.generator.stack_size
2✔
858

859
        func_data = self.visit(call.func)
2✔
860
        function_id = func_data.symbol_id
2✔
861
        # if the symbol is not a method, check if it is a class method
862
        if (isinstance(func_data.type, ClassType) and func_data.symbol_id in func_data.type.symbols
2✔
863
                and isinstance(func_data.type.symbols[func_data.symbol_id], IExpression)):
864
            symbol = func_data.type.symbols[func_data.symbol_id]
2✔
865
        else:
866
            symbol = func_data.symbol
2✔
867

868
        if not isinstance(symbol, Method):
2✔
869
            is_internal = hasattr(call, 'is_internal_call') and call.is_internal_call
2✔
870
            _, symbol = self.generator.get_symbol(function_id, is_internal=is_internal)
2✔
871

872
        if self.is_implemented_class_type(symbol):
2✔
873
            self.generator.convert_init_user_class(symbol)
2✔
874
            symbol = symbol.constructor_method()
2✔
875
        args_addresses: List[int] = []
2✔
876

877
        has_cls_or_self_argument = isinstance(symbol, Method) and symbol.has_cls_or_self
2✔
878
        if not has_cls_or_self_argument:
2✔
879
            self._remove_inserted_opcodes_since(last_address, last_stack)
2✔
880

881
        if isinstance(symbol, Method):
2✔
882
            # self or cls is already generated
883
            call_args = (call.args[1:]
2✔
884
                         if has_cls_or_self_argument and len(call.args) == len(symbol.args)
885
                         else call.args)
886
            args_to_generate = [arg for index, arg in enumerate(call_args) if index in symbol.args_to_be_generated()]
2✔
887
            keywords_dict = {keyword.arg: keyword.value for keyword in call.keywords}
2✔
888
            keywords_with_index = {index: keywords_dict[arg_name]
2✔
889
                                   for index, arg_name in enumerate(symbol.args)
890
                                   if arg_name in keywords_dict}
891

892
            for index in keywords_with_index:
2✔
893
                if index < len(args_to_generate):
2✔
894
                    # override default values
895
                    args_to_generate[index] = keywords_with_index[index]
2✔
896
                else:
897
                    # put keywords
898
                    args_to_generate.append(keywords_with_index[index])
2✔
899

900
        else:
901
            args_to_generate = call.args
2✔
902

903
        if isinstance(symbol, IBuiltinMethod):
2✔
904
            reordered_args = []
2✔
905
            for index in symbol.generation_order:
2✔
906
                if 0 <= index < len(args_to_generate):
2✔
907
                    reordered_args.append(args_to_generate[index])
2✔
908

909
            args = reordered_args
2✔
910
        else:
911
            args = reversed(args_to_generate)
2✔
912

913
        args_begin_address = self.generator.last_code_start_address
2✔
914
        for arg in args:
2✔
915
            args_addresses.append(
2✔
916
                VMCodeMapping.instance().bytecode_size
917
            )
918
            self.visit_to_generate(arg)
2✔
919

920
        class_type = None
2✔
921
        if has_cls_or_self_argument:
2✔
922
            num_args = len(args_addresses)
2✔
923
            if self.generator.stack_size > num_args:
2✔
924
                value = self.generator._stack_pop(-num_args - 1)
2✔
925
                self.generator._stack_append(value)
2✔
926
                class_type = value if isinstance(value, UserClass) else None
2✔
927
            end_address = VMCodeMapping.instance().move_to_end(last_address, args_begin_address)
2✔
928
            if not symbol.is_init:
2✔
929
                args_addresses.append(end_address)
2✔
930

931
        if self.is_exception_name(function_id):
2✔
932
            self.generator.convert_new_exception(len(call.args))
2✔
933
        elif isinstance(symbol, type(Builtin.Super)) and len(args_to_generate) == 0:
2✔
934
            self_or_cls_id = list(self.current_method.args)[0]
2✔
935
            self.generator.convert_load_symbol(self_or_cls_id)
2✔
936
        elif isinstance(symbol, IBuiltinMethod):
2✔
937
            self.generator.convert_builtin_method_call(symbol, args_addresses)
2✔
938
        else:
939
            self.generator.convert_load_symbol(function_id, args_addresses,
2✔
940
                                               class_type=class_type)
941

942
        return self.build_data(call, symbol=symbol, symbol_id=function_id,
2✔
943
                               result_type=symbol.type if isinstance(symbol, IExpression) else symbol,
944
                               already_generated=True)
945

946
    def visit_Raise(self, raise_node: ast.Raise) -> GeneratorData:
2✔
947
        """
948
        Visitor of the raise node
949

950
        :param raise_node: the python ast raise node
951
        """
952
        self.visit_to_map(raise_node.exc, generate=True)
2✔
953
        self.generator.convert_raise_exception()
2✔
954
        return self.build_data(raise_node)
2✔
955

956
    def visit_Try(self, try_node: ast.Try) -> GeneratorData:
2✔
957
        """
958
        Visitor of the try node
959

960
        :param try_node: the python ast try node
961
        """
962
        try_address: int = self.generator.convert_begin_try()
2✔
963
        try_end: Optional[int] = None
2✔
964
        for stmt in try_node.body:
2✔
965
            self.visit_to_map(stmt, generate=True)
2✔
966

967
        if len(try_node.handlers) == 1:
2✔
968
            handler = try_node.handlers[0]
2✔
969
            try_end = self.generator.convert_try_except(handler.name)
2✔
970
            for stmt in handler.body:
2✔
971
                self.visit_to_map(stmt, generate=True)
2✔
972

973
        else_address = None
2✔
974
        if len(try_node.orelse) > 0:
2✔
975
            else_start_address = self.generator.convert_begin_else(try_end)
2✔
976
            else_address = self.generator.bytecode_size
2✔
977
            for stmt in try_node.orelse:
2✔
978
                self.visit_to_map(stmt, generate=True)
2✔
979
            self.generator.convert_end_if(else_start_address)
2✔
980

981
        except_end = self.generator.convert_end_try(try_address, try_end, else_address)
2✔
982
        for stmt in try_node.finalbody:
2✔
983
            self.visit_to_map(stmt, generate=True)
2✔
984
        self.generator.convert_end_try_finally(except_end, try_address, len(try_node.finalbody) > 0)
2✔
985

986
        return self.build_data(try_node)
2✔
987

988
    def visit_Name(self, name_node: ast.Name) -> GeneratorData:
2✔
989
        """
990
        Visitor of a name node
991

992
        :param name_node: the python ast name identifier node
993
        :return: the identifier of the name
994
        """
995
        name = name_node.id
2✔
996
        if name not in self._symbols:
2✔
997
            hashed_name = self._get_unique_name(name, self._tree)
2✔
998
            if hashed_name not in self._symbols and hasattr(name_node, 'origin'):
2✔
999
                hashed_name = self._get_unique_name(name, name_node.origin)
2✔
1000

1001
            if hashed_name in self._symbols:
2✔
1002
                name = hashed_name
2✔
1003

1004
        else:
1005
            if name not in self.generator.symbol_table:
2✔
1006
                symbol = self._symbols[name]
2✔
1007
                for symbol_id, internal_symbol in self.generator.symbol_table.items():
2✔
1008
                    if internal_symbol == symbol:
2✔
1009
                        name = symbol_id
×
1010
                        break
×
1011

1012
        return self.build_data(name_node, name)
2✔
1013

1014
    def visit_Starred(self, starred: ast.Starred) -> GeneratorData:
2✔
1015
        value_data = self.visit_to_generate(starred.value)
2✔
1016
        self.generator.convert_starred_variable()
2✔
1017

1018
        starred_data = value_data.copy(starred)
2✔
1019
        starred_data.already_generated = True
2✔
1020
        return starred_data
2✔
1021

1022
    def visit_Attribute(self, attribute: ast.Attribute) -> GeneratorData:
2✔
1023
        """
1024
        Visitor of a attribute node
1025

1026
        :param attribute: the python ast attribute node
1027
        :return: the identifier of the attribute
1028
        """
1029
        last_address = VMCodeMapping.instance().bytecode_size
2✔
1030
        last_stack = self.generator.stack_size
2✔
1031

1032
        _attr, attr = self.generator.get_symbol(attribute.attr)
2✔
1033
        value = attribute.value
2✔
1034
        value_symbol = None
2✔
1035
        value_type = None
2✔
1036
        value_data = self.visit(value)
2✔
1037
        need_to_visit_again = True
2✔
1038

1039
        if value_data.symbol_id is not None and not value_data.already_generated:
2✔
1040
            value_id = value_data.symbol_id
2✔
1041
            if value_data.symbol is not None:
2✔
1042
                value_symbol = value_data.symbol
2✔
1043
            else:
1044
                _, value_symbol = self.generator.get_symbol(value_id)
2✔
1045
            value_type = value_symbol.type if hasattr(value_symbol, 'type') else value_symbol
2✔
1046

1047
            if hasattr(value_type, 'symbols') and attribute.attr in value_type.symbols:
2✔
1048
                attr = value_type.symbols[attribute.attr]
2✔
1049
            elif isinstance(value_type, Package) and attribute.attr in value_type.inner_packages:
2✔
1050
                attr = value_type.inner_packages[attribute.attr]
2✔
1051

1052
            if isinstance(value_symbol, UserClass):
2✔
1053
                if isinstance(attr, Method) and attr.has_cls_or_self:
2✔
1054
                    self.generator.convert_load_symbol(value_id)
2✔
1055
                    need_to_visit_again = False
2✔
1056

1057
                if isinstance(attr, Variable):
2✔
1058
                    cur_bytesize = self.generator.bytecode_size
2✔
1059
                    self.visit_to_generate(attribute.value)
2✔
1060
                    self.generator.convert_load_symbol(attribute.attr, class_type=value_symbol)
2✔
1061

1062
                    symbol_id = _attr if isinstance(_attr, str) else None
2✔
1063
                    return self.build_data(attribute,
2✔
1064
                                           already_generated=self.generator.bytecode_size > cur_bytesize,
1065
                                           symbol=attr, symbol_id=symbol_id,
1066
                                           origin_object_type=value_symbol)
1067
        else:
1068
            if isinstance(value, ast.Attribute) and value_data.already_generated:
2✔
1069
                need_to_visit_again = False
2✔
1070
            else:
1071
                need_to_visit_again = value_data.already_generated
2✔
1072
                self._remove_inserted_opcodes_since(last_address, last_stack)
2✔
1073

1074
        # the verification above only verify variables, this one will should work with literals and constants
1075
        if isinstance(value, ast.Constant) and len(attr.args) > 0 and isinstance(attr, IBuiltinMethod) and attr.has_self_argument:
2✔
1076
            attr = attr.build(value_data.type)
×
1077

1078
        if attr is not Type.none and not hasattr(attribute, 'generate_value'):
2✔
1079
            value_symbol_id = (value_symbol.identifier
2✔
1080
                               if self.is_implemented_class_type(value_symbol)
1081
                               else value_data.symbol_id)
1082
            attribute_id = f'{value_symbol_id}{constants.ATTRIBUTE_NAME_SEPARATOR}{attribute.attr}'
2✔
1083
            index = value_type if isinstance(value_type, Package) else None
2✔
1084
            return self.build_data(attribute, symbol_id=attribute_id, symbol=attr, index=index)
2✔
1085

1086
        if isinstance(value, ast.Attribute) and need_to_visit_again:
2✔
1087
            value_data = self.visit(value)
2✔
1088
        elif hasattr(attribute, 'generate_value') and attribute.generate_value:
2✔
1089
            current_bytecode_size = self.generator.bytecode_size
2✔
1090
            if need_to_visit_again:
2✔
1091
                value_data = self.visit_to_generate(attribute.value)
2✔
1092

1093
            result = value_data.type
2✔
1094
            generation_result = value_data.symbol
2✔
1095
            if result is None and value_data.symbol_id is not None:
2✔
1096
                _, result = self.generator.get_symbol(value_data.symbol_id)
2✔
1097
                if isinstance(result, IExpression):
2✔
1098
                    generation_result = result
×
1099
                    result = result.type
×
1100
            elif isinstance(attribute.value, ast.Attribute) and isinstance(value_data.index, int):
2✔
1101
                result = self.get_type(generation_result)
2✔
1102

1103
            if self.is_implemented_class_type(result):
2✔
1104
                class_attr_id = f'{result.identifier}.{attribute.attr}'
2✔
1105
                symbol_id = class_attr_id
2✔
1106
                symbol = None
2✔
1107
                result_type = None
2✔
1108
                symbol_index = None
2✔
1109
                is_load_context_variable_from_class = (isinstance(attribute.ctx, ast.Load) and
2✔
1110
                                                       isinstance(attr, Variable) and
1111
                                                       isinstance(result, UserClass))
1112

1113
                if self.generator.bytecode_size > current_bytecode_size and isinstance(result, UserClass) and not is_load_context_variable_from_class:
2✔
1114
                    # it was generated already, don't convert again
1115
                    generated = False
2✔
1116
                    symbol_id = attribute.attr if isinstance(generation_result, Variable) else class_attr_id
2✔
1117
                    result_type = result
2✔
1118
                else:
1119
                    current_bytecode_size = self.generator.bytecode_size
2✔
1120
                    index = self.generator.convert_class_symbol(result,
2✔
1121
                                                                attribute.attr,
1122
                                                                isinstance(attribute.ctx, ast.Load))
1123
                    generated = self.generator.bytecode_size > current_bytecode_size
2✔
1124
                    symbol = result
2✔
1125
                    if not isinstance(result, UserClass):
2✔
1126
                        if isinstance(index, int):
2✔
1127
                            symbol_index = index
2✔
1128
                        else:
1129
                            symbol_id = index
2✔
1130

1131
                return self.build_data(attribute,
2✔
1132
                                       symbol_id=symbol_id, symbol=symbol,
1133
                                       result_type=result_type, index=symbol_index,
1134
                                       origin_object_type=value_type,
1135
                                       already_generated=generated)
1136

1137
        if value_data is not None and value_symbol is None:
2✔
1138
            value_symbol = value_data.symbol_id
×
1139

1140
        if value_data is not None and value_data.symbol_id is not None:
2✔
1141
            value_id = f'{value_data.symbol_id}{constants.ATTRIBUTE_NAME_SEPARATOR}{attribute.attr}'
2✔
1142
        else:
1143
            value_id = attribute.attr
×
1144

1145
        return self.build_data(attribute, symbol_id=value_id, symbol=value_symbol)
2✔
1146

1147
    def visit_Continue(self, continue_node: ast.Continue) -> GeneratorData:
2✔
1148
        """
1149
        :param continue_node: the python ast continue statement node
1150
        """
1151
        self.generator.convert_loop_continue()
2✔
1152
        return self.build_data(continue_node)
2✔
1153

1154
    def visit_Break(self, break_node: ast.Break) -> GeneratorData:
2✔
1155
        """
1156
        :param break_node: the python ast break statement node
1157
        """
1158
        self.generator.convert_loop_break()
2✔
1159
        return self.build_data(break_node)
2✔
1160

1161
    def visit_Constant(self, constant: ast.Constant) -> GeneratorData:
2✔
1162
        """
1163
        Visitor of constant values node
1164

1165
        :param constant: the python ast constant value node
1166
        """
1167
        index = self.generator.convert_literal(constant.value)
2✔
1168
        result_type = self.get_type(constant.value)
2✔
1169
        return self.build_data(constant, result_type=result_type, index=index, already_generated=True)
2✔
1170

1171
    def visit_NameConstant(self, constant: ast.NameConstant) -> GeneratorData:
2✔
1172
        """
1173
        Visitor of constant names node
1174

1175
        :param constant: the python ast name constant node
1176
        """
1177
        index = self.generator.convert_literal(constant.value)
×
1178
        result_type = self.get_type(constant.value)
×
1179
        return self.build_data(constant, result_type=result_type, index=index, already_generated=True)
×
1180

1181
    def visit_Num(self, num: ast.Num) -> GeneratorData:
2✔
1182
        """
1183
        Visitor of literal number node
1184

1185
        :param num: the python ast number node
1186
        """
1187
        index = self.generator.convert_literal(num.n)
×
1188
        result_type = self.get_type(num.n)
×
1189
        return self.build_data(num, result_type=result_type, index=index, already_generated=True)
×
1190

1191
    def visit_Str(self, string: ast.Str) -> GeneratorData:
2✔
1192
        """
1193
        Visitor of literal string node
1194

1195
        :param string: the python ast string node
1196
        """
1197
        index = self.generator.convert_literal(string.s)
×
1198
        result_type = self.get_type(string.s)
×
1199
        return self.build_data(string, result_type=result_type, index=index, already_generated=True)
×
1200

1201
    def visit_Bytes(self, bts: ast.Bytes) -> GeneratorData:
2✔
1202
        """
1203
        Visitor of literal bytes node
1204

1205
        :param bts: the python ast bytes node
1206
        """
1207
        index = self.generator.convert_literal(bts.s)
×
1208
        result_type = self.get_type(bts.s)
×
1209
        return self.build_data(bts, result_type=result_type, index=index, already_generated=True)
×
1210

1211
    def visit_Tuple(self, tup_node: ast.Tuple) -> GeneratorData:
2✔
1212
        """
1213
        Visitor of literal tuple node
1214

1215
        :param tup_node: the python ast tuple node
1216
        """
1217
        result_type = Type.tuple
2✔
1218
        self._create_array(tup_node.elts, result_type)
2✔
1219
        return self.build_data(tup_node, result_type=result_type, already_generated=True)
2✔
1220

1221
    def visit_List(self, list_node: ast.List) -> GeneratorData:
2✔
1222
        """
1223
        Visitor of literal list node
1224

1225
        :param list_node: the python ast list node
1226
        """
1227
        result_type = Type.list
2✔
1228
        self._create_array(list_node.elts, result_type)
2✔
1229
        return self.build_data(list_node, result_type=result_type, already_generated=True)
2✔
1230

1231
    def visit_Dict(self, dict_node: ast.Dict) -> GeneratorData:
2✔
1232
        """
1233
        Visitor of literal dict node
1234

1235
        :param dict_node: the python ast dict node
1236
        """
1237
        result_type = Type.dict
2✔
1238
        length = min(len(dict_node.keys), len(dict_node.values))
2✔
1239
        self.generator.convert_new_map(result_type)
2✔
1240
        for key_value in range(length):
2✔
1241
            self.generator.duplicate_stack_top_item()
2✔
1242
            self.visit_to_generate(dict_node.keys[key_value])
2✔
1243
            value_data = self.visit_to_generate(dict_node.values[key_value])
2✔
1244
            self.generator.convert_set_item(value_data.index)
2✔
1245

1246
        return self.build_data(dict_node, result_type=result_type, already_generated=True)
2✔
1247

1248
    def visit_JoinedStr(self, fstring: ast.JoinedStr) -> GeneratorData:
2✔
1249
        """
1250
        Visitor of an f-string node
1251

1252
        :param string: the python ast string node
1253
        """
1254
        result_type = Type.str
2✔
1255
        index = self.generator.bytecode_size
2✔
1256
        for index, value in enumerate(fstring.values):
2✔
1257
            self.visit_to_generate(value)
2✔
1258
            if index != 0:
2✔
1259
                self.generator.convert_operation(BinaryOp.Concat)
2✔
1260
                if index < len(fstring.values) - 1:
2✔
1261
                    self._remove_inserted_opcodes_since(self.generator.last_code_start_address)
×
1262

1263
        return self.build_data(fstring, result_type=result_type, index=index, already_generated=True)
2✔
1264

1265
    def visit_FormattedValue(self, formatted_value: ast.FormattedValue) -> GeneratorData:
2✔
1266
        """
1267
        Visitor of a formatted_value node
1268

1269
        :param formatted_value: the python ast string node
1270
        """
1271
        generated_value = self.visit_to_generate(formatted_value.value)
2✔
1272
        if generated_value.type == Type.str:
2✔
1273
            return generated_value
2✔
1274

1275
        else:
1276
            match generated_value.type:
2✔
1277
                case Type.bool:
2✔
1278
                    self.generator.convert_builtin_method_call(Builtin.StrBool)
2✔
1279
                case Type.int:
2✔
1280
                    self.generator.convert_builtin_method_call(Builtin.StrInt)
2✔
1281
                case Type.bytes:
2✔
1282
                    self.generator.convert_builtin_method_call(Builtin.StrBytes)
2✔
1283
                case x if Type.sequence.is_type_of(x):
2✔
1284
                    self.generator.convert_builtin_method_call(Builtin.StrSequence)
2✔
1285
                case x if isinstance(x, UserClass):
2✔
1286
                    self.generator.convert_builtin_method_call(Builtin.StrClass.build(x))
2✔
1287

1288
            return self.build_data(formatted_value, result_type=Type.str, already_generated=True)
2✔
1289

1290
    def visit_Pass(self, pass_node: ast.Pass) -> GeneratorData:
2✔
1291
        """
1292
        Visitor of pass node
1293

1294
        :param pass_node: the python ast dict node
1295
        """
1296
        # only generates if the scope is a function
1297
        result_type = None
2✔
1298
        generated = False
2✔
1299

1300
        if isinstance(self.current_method, Method):
2✔
1301
            self.generator.insert_nop()
2✔
1302
            generated = True
2✔
1303

1304
        return self.build_data(pass_node, result_type=result_type, already_generated=generated)
2✔
1305

1306
    def _create_array(self, values: List[ast.AST], array_type: IType):
2✔
1307
        """
1308
        Creates a new array from a literal sequence
1309

1310
        :param values: list of values of the new array items
1311
        """
1312
        length = len(values)
2✔
1313
        if length > 0:
2✔
1314
            for value in reversed(values):
2✔
1315
                self.visit_to_generate(value)
2✔
1316
        self.generator.convert_new_array(length, array_type)
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc