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

CityOfZion / neo3-boa / 9a535731-11a4-476e-ac4c-f4b1ec2d8bb7

25 Sep 2023 08:49PM UTC coverage: 91.739% (+0.001%) from 91.738%
9a535731-11a4-476e-ac4c-f4b1ec2d8bb7

push

circleci

Mirella de Medeiros
CU-864ea8yf8 - Create functional tests for Neo 3.6 features

4 of 4 new or added lines in 4 files covered. (100.0%)

19967 of 21765 relevant lines covered (91.74%)

3.62 hits per line

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

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

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

30

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

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

38
    :ivar generator:
39
    """
40

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

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

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

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

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

60
        return symbol_table
4✔
61

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

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

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

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

90
            if found_symbol is not None:
4✔
91
                symbol = found_symbol
4✔
92

93
        return GeneratorData(origin_node, symbol_id, symbol, result_type, index, origin_object_type, already_generated)
4✔
94

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

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

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

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

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

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

130
            if not result.already_generated and result.symbol_id is not None:
4✔
131
                if self.is_exception_name(result.symbol_id):
4✔
132
                    self.generator.convert_new_exception()
4✔
133
                else:
134
                    # TODO: validate function calls
135
                    is_internal = hasattr(node, 'is_internal_call') and node.is_internal_call
4✔
136
                    class_type = result.type if isinstance(node, ast.Attribute) else None
4✔
137

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

145
                    elif isinstance(result.index, Package):
4✔
146
                        class_type = None
4✔
147

148
                    self.generator.convert_load_symbol(result.symbol_id, is_internal=is_internal, class_type=class_type)
4✔
149

150
                result.already_generated = True
4✔
151

152
            return result
4✔
153
        else:
154
            index = self.generator.convert_literal(node)
4✔
155
            return self.build_data(node, index=index)
4✔
156

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

167
    def _remove_inserted_opcodes_since(self, last_address: int, last_stack_size: Optional[int] = None):
4✔
168
        self.generator._remove_inserted_opcodes_since(last_address, last_stack_size)
4✔
169

170
    def _get_unique_name(self, name_id: str, node: ast.AST) -> str:
4✔
171
        return '{0}{2}{1}'.format(node.__hash__(), name_id, constants.VARIABLE_NAME_SEPARATOR)
4✔
172

173
    def set_filename(self, filename: str):
4✔
174
        if isinstance(filename, str) and os.path.isfile(filename):
4✔
175
            if constants.PATH_SEPARATOR != os.path.sep:
4✔
176
                self.filename = filename.replace(constants.PATH_SEPARATOR, os.path.sep)
×
177
            else:
178
                self.filename = filename
4✔
179

180
    def visit_Module(self, module: ast.Module) -> GeneratorData:
4✔
181
        """
182
        Visitor of the module node
183

184
        Fills module symbol table
185

186
        :param module:
187
        """
188
        global_stmts = [node for node in module.body if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
4✔
189
        function_stmts = module.body[len(global_stmts):]
4✔
190
        mandatory_global_stmts = []
4✔
191
        for stmt in global_stmts:
4✔
192
            if isinstance(stmt, ast.ClassDef):
4✔
193
                class_symbol = self.get_symbol(stmt.name)
4✔
194
                if isinstance(class_symbol, UserClass) and len(class_symbol.class_variables) > 0:
4✔
195
                    mandatory_global_stmts.append(stmt)
4✔
196
            elif not isinstance(stmt, (ast.Import, ast.ImportFrom)):
4✔
197
                mandatory_global_stmts.append(stmt)
4✔
198

199
        for stmt in function_stmts:
4✔
200
            self.visit(stmt)
4✔
201

202
        if self.generator.initialize_static_fields():
4✔
203
            last_symbols = self.symbols  # save to revert in the end and not compromise consequent visits
4✔
204
            class_non_static_stmts = []
4✔
205

206
            for node in global_stmts.copy():
4✔
207
                if isinstance(node, ast.ClassDef):
4✔
208
                    class_origin_module = None
4✔
209
                    if hasattr(node, 'origin'):
4✔
210
                        class_origin_module = node.origin
4✔
211
                        if (node.origin is not self._root_module
4✔
212
                                and hasattr(node.origin, 'symbols')):
213
                            # symbols unique to imports are not included in the symbols
214
                            self.symbols = node.origin.symbols
×
215

216
                    class_variables = []
4✔
217
                    class_functions = []
4✔
218
                    for stmt in node.body:
4✔
219
                        if isinstance(stmt, (ast.Assign, ast.AugAssign, ast.AnnAssign)):
4✔
220
                            class_variables.append(stmt)
4✔
221
                        else:
222
                            class_functions.append(stmt)
4✔
223

224
                    if len(class_functions) > 0:
4✔
225
                        if class_origin_module is not None and not hasattr(node, 'origin'):
4✔
226
                            node.origin = class_origin_module
×
227

228
                        cls_fun = node
4✔
229
                        if len(class_variables) > 0:
4✔
230
                            cls_var = node
4✔
231
                            cls_fun = self.clone(node)
4✔
232
                            cls_fun.body = class_functions
4✔
233
                            cls_var.body = class_variables
4✔
234
                        else:
235
                            global_stmts.remove(cls_fun)
4✔
236

237
                        class_non_static_stmts.append(cls_fun)
4✔
238

239
            # to generate the 'initialize' method for Neo
240
            self._log_info(f"Compiling '{constants.INITIALIZE_METHOD_ID}' function")
4✔
241
            self._is_generating_initialize = True
4✔
242
            for stmt in global_stmts:
4✔
243
                cur_tree = self._tree
4✔
244
                cur_filename = self.filename
4✔
245
                if hasattr(stmt, 'origin'):
4✔
246
                    if hasattr(stmt.origin, 'filename'):
4✔
247
                        self.set_filename(stmt.origin.filename)
4✔
248
                    self._tree = stmt.origin
4✔
249

250
                self.visit(stmt)
4✔
251
                self.filename = cur_filename
4✔
252
                self._tree = cur_tree
4✔
253

254
            self._is_generating_initialize = False
4✔
255
            self.generator.end_initialize()
4✔
256

257
            # generate any symbol inside classes that's not variables AFTER generating 'initialize' method
258
            for stmt in class_non_static_stmts:
4✔
259
                cur_tree = self._tree
4✔
260
                if hasattr(stmt, 'origin'):
4✔
261
                    self._tree = stmt.origin
4✔
262
                self.visit(stmt)
4✔
263
                self._tree = cur_tree
4✔
264

265
            self.symbols = last_symbols  # revert in the end to not compromise consequent visits
4✔
266
            self.generator.additional_symbols = None
4✔
267

268
        elif len(function_stmts) > 0 or len(mandatory_global_stmts) > 0:
4✔
269
            # to organize syntax tree nodes from other modules
270
            for stmt in global_stmts:
4✔
271
                if not hasattr(stmt, 'origin'):
4✔
272
                    stmt.origin = module
4✔
273

274
            module.symbols = self._symbols
4✔
275
            self.global_stmts.extend(global_stmts)
4✔
276
        else:
277
            # to generate objects when there are no static variables to generate 'initialize'
278
            for stmt in global_stmts:
4✔
279
                self.visit(stmt)
4✔
280

281
        return self.build_data(module)
4✔
282

283
    def visit_ClassDef(self, node: ast.ClassDef) -> GeneratorData:
4✔
284
        if node.name in self.symbols:
4✔
285
            class_symbol = self.symbols[node.name]
4✔
286
            if isinstance(class_symbol, UserClass):
4✔
287
                self.current_class = class_symbol
4✔
288

289
            if self._is_generating_initialize:
4✔
290
                address = self.generator.bytecode_size
4✔
291
                self.generator.convert_new_empty_array(len(class_symbol.class_variables), class_symbol)
4✔
292
                self.generator.convert_store_variable(node.name, address)
4✔
293
            else:
294
                init_method = class_symbol.constructor_method()
4✔
295
                if isinstance(init_method, Method) and init_method.start_address is None:
4✔
296
                    self.generator.generate_implicit_init_user_class(init_method)
4✔
297

298
        for stmt in node.body:
4✔
299
            self.visit(stmt)
4✔
300

301
        self.current_class = None
4✔
302
        return self.build_data(node)
4✔
303

304
    def visit_FunctionDef(self, function: ast.FunctionDef) -> GeneratorData:
4✔
305
        """
306
        Visitor of the function definition node
307

308
        Generates the Neo VM code for the function
309

310
        :param function: the python ast function definition node
311
        """
312
        method = self._symbols[function.name]
4✔
313

314
        if isinstance(method, Property):
4✔
315
            method = method.getter
4✔
316

317
        if isinstance(method, Method):
4✔
318
            self.current_method = method
4✔
319
            if isinstance(self.current_class, ClassType):
4✔
320
                function_name = self.current_class.identifier + constants.ATTRIBUTE_NAME_SEPARATOR + function.name
4✔
321
            else:
322
                function_name = function.name
4✔
323

324
            self._log_info(f"Compiling '{function_name}' function")
4✔
325

326
            if method.is_public or method.is_called:
4✔
327
                if not isinstance(self.current_class, ClassType) or not self.current_class.is_interface:
4✔
328
                    self.generator.convert_begin_method(method)
4✔
329

330
                    for stmt in function.body:
4✔
331
                        self.visit_to_map(stmt)
4✔
332

333
                    self.generator.convert_end_method(function.name)
4✔
334

335
            self.current_method = None
4✔
336

337
        return self.build_data(function, symbol=method, symbol_id=function.name)
4✔
338

339
    def visit_Return(self, ret: ast.Return) -> GeneratorData:
4✔
340
        """
341
        Visitor of a function return node
342

343
        :param ret: the python ast return node
344
        """
345
        if self.generator.stack_size > 0:
4✔
346
            self.generator.clear_stack(True)
4✔
347

348
        if self.current_method.return_type is not Type.none:
4✔
349
            result = self.visit_to_generate(ret.value)
4✔
350
            if result.type is Type.none and not self.generator.is_none_inserted():
4✔
351
                self.generator.convert_literal(None)
4✔
352
        elif ret.value is not None:
4✔
353
            self.visit_to_generate(ret.value)
4✔
354
            if self.generator.stack_size > 0:
4✔
355
                self.generator.remove_stack_top_item()
4✔
356

357
        self.generator.insert_return()
4✔
358

359
        return self.build_data(ret)
4✔
360

361
    def store_variable(self, *var_ids: VariableGenerationData, value: ast.AST):
4✔
362
        # if the value is None, it is a variable declaration
363
        if value is not None:
4✔
364
            if len(var_ids) == 1:
4✔
365
                # it's a simple assignment
366
                var_id, index, address = var_ids[0]
4✔
367
                if index is None:
4✔
368
                    # if index is None, then it is a variable assignment
369
                    result_data = self.visit_to_generate(value)
4✔
370

371
                    if result_data.type is Type.none and not self.generator.is_none_inserted():
4✔
372
                        self.generator.convert_literal(None)
4✔
373
                    self.generator.convert_store_variable(var_id, address, self.current_class if self.current_method is None else None)
4✔
374
                else:
375
                    # if not, it is an array assignment
376
                    self.generator.convert_load_symbol(var_id)
4✔
377
                    self.visit_to_generate(index)
4✔
378

379
                    aux_index = VMCodeMapping.instance().bytecode_size
4✔
380
                    value_data = self.visit_to_generate(value)
4✔
381
                    value_address = value_data.index if value_data.index is not None else aux_index
4✔
382

383
                    self.generator.convert_set_item(value_address)
4✔
384

385
            elif len(var_ids) > 0:
4✔
386
                # it's a chained assignment
387
                self.visit_to_generate(value)
4✔
388
                for pos, (var_id, index, address) in enumerate(reversed(var_ids)):
4✔
389
                    if index is None:
4✔
390
                        # if index is None, then it is a variable assignment
391
                        if pos < len(var_ids) - 1:
4✔
392
                            self.generator.duplicate_stack_top_item()
4✔
393
                        self.generator.convert_store_variable(var_id, address)
4✔
394
                    else:
395
                        # if not, it is an array assignment
396
                        if pos < len(var_ids) - 1:
4✔
397
                            self.generator.convert_load_symbol(var_id)
4✔
398
                            self.visit_to_generate(index)
4✔
399
                            fix_index = VMCodeMapping.instance().bytecode_size
4✔
400
                            self.generator.duplicate_stack_item(3)
4✔
401
                        else:
402
                            self.visit_to_generate(index)
4✔
403
                            fix_index = VMCodeMapping.instance().bytecode_size
4✔
404
                            self.generator.convert_load_symbol(var_id)
4✔
405
                            self.generator.swap_reverse_stack_items(3)
4✔
406
                        self.generator.convert_set_item(fix_index)
4✔
407

408
    def visit_AnnAssign(self, ann_assign: ast.AnnAssign) -> GeneratorData:
4✔
409
        """
410
        Visitor of an annotated assignment node
411

412
        :param ann_assign: the python ast variable assignment node
413
        """
414
        var_value_address = self.generator.bytecode_size
4✔
415
        var_data = self.visit(ann_assign.target)
4✔
416
        var_id = var_data.symbol_id
4✔
417
        # filter to find the imported variables
418
        if (var_id not in self.generator.symbol_table
4✔
419
                and hasattr(ann_assign, 'origin')
420
                and isinstance(ann_assign.origin, ast.AST)):
421
            var_id = self._get_unique_name(var_id, ann_assign.origin)
×
422
        self.store_variable(VariableGenerationData(var_id, None, var_value_address), value=ann_assign.value)
4✔
423
        return self.build_data(ann_assign)
4✔
424

425
    def visit_Assign(self, assign: ast.Assign) -> GeneratorData:
4✔
426
        """
427
        Visitor of an assignment node
428

429
        :param assign: the python ast variable assignment node
430
        """
431
        vars_ids: List[VariableGenerationData] = []
4✔
432
        for target in assign.targets:
4✔
433
            var_value_address = self.generator.bytecode_size
4✔
434
            target_data: GeneratorData = self.visit(target)
4✔
435

436
            var_id = target_data.symbol_id
4✔
437
            var_index = target_data.index
4✔
438

439
            # filter to find the imported variables
440
            if (var_id not in self.generator.symbol_table
4✔
441
                    and hasattr(assign, 'origin')
442
                    and isinstance(assign.origin, ast.AST)):
443
                var_id = self._get_unique_name(var_id, assign.origin)
×
444

445
            vars_ids.append(VariableGenerationData(var_id, var_index, var_value_address))
4✔
446

447
        self.store_variable(*vars_ids, value=assign.value)
4✔
448
        return self.build_data(assign)
4✔
449

450
    def visit_AugAssign(self, aug_assign: ast.AugAssign) -> GeneratorData:
4✔
451
        """
452
        Visitor of an augmented assignment node
453

454
        :param aug_assign: the python ast augmented assignment node
455
        """
456
        start_address = self.generator.bytecode_size
4✔
457
        var_data = self.visit(aug_assign.target)
4✔
458
        var_id = var_data.symbol_id
4✔
459
        # filter to find the imported variables
460
        if (var_id not in self.generator.symbol_table
4✔
461
                and hasattr(aug_assign, 'origin')
462
                and isinstance(aug_assign.origin, ast.AST)):
463
            var_id = self._get_unique_name(var_id, aug_assign.origin)
×
464

465
        if isinstance(var_data.type, UserClass):
4✔
466
            self.generator.duplicate_stack_top_item()
4✔
467
        elif hasattr(var_data.origin_object_type, 'identifier') and var_data.already_generated:
4✔
468
            VMCodeMapping.instance().remove_opcodes(start_address)
×
469
            self.generator.convert_load_symbol(var_data.origin_object_type.identifier)
×
470

471
        self.generator.convert_load_symbol(var_id, class_type=var_data.origin_object_type)
4✔
472
        value_address = self.generator.bytecode_size
4✔
473
        self.visit_to_generate(aug_assign.value)
4✔
474
        self.generator.convert_operation(aug_assign.op)
4✔
475
        self.generator.convert_store_variable(var_id, value_address, user_class=var_data.origin_object_type)
4✔
476
        return self.build_data(aug_assign)
4✔
477

478
    def visit_Subscript(self, subscript: ast.Subscript) -> GeneratorData:
4✔
479
        """
480
        Visitor of a subscript node
481

482
        :param subscript: the python ast subscript node
483
        """
484
        if isinstance(subscript.slice, ast.Slice):
4✔
485
            return self.visit_Subscript_Slice(subscript)
4✔
486

487
        return self.visit_Subscript_Index(subscript)
4✔
488

489
    def visit_Subscript_Index(self, subscript: ast.Subscript) -> GeneratorData:
4✔
490
        """
491
        Visitor of a subscript node with index
492

493
        :param subscript: the python ast subscript node
494
        """
495
        index = None
4✔
496
        symbol_id = None
4✔
497
        if isinstance(subscript.ctx, ast.Load):
4✔
498
            # get item
499
            value_data = self.visit_to_generate(subscript.value)
4✔
500
            slice = subscript.slice.value if isinstance(subscript.slice, ast.Index) else subscript.slice
4✔
501
            self.visit_to_generate(slice)
4✔
502

503
            index_is_constant_number = isinstance(slice, ast.Num) and isinstance(slice.n, int)
4✔
504
            self.generator.convert_get_item(index_is_positive=(index_is_constant_number and slice.n >= 0),
4✔
505
                                            test_is_negative_index=not (index_is_constant_number and slice.n < 0))
506

507
            value_type = value_data.type
4✔
508
        else:
509
            # set item
510
            var_data = self.visit(subscript.value)
4✔
511

512
            index = subscript.slice.value if isinstance(subscript.slice, ast.Index) else subscript.slice
4✔
513
            symbol_id = var_data.symbol_id
4✔
514
            value_type = var_data.type
4✔
515

516
        result_type = value_type.item_type if isinstance(value_type, SequenceType) else value_type
4✔
517
        return self.build_data(subscript, result_type=result_type,
4✔
518
                               symbol_id=symbol_id, index=index)
519

520
    def visit_Subscript_Slice(self, subscript: ast.Subscript) -> GeneratorData:
4✔
521
        """
522
        Visitor of a subscript node with slice
523

524
        :param subscript: the python ast subscript node
525
        """
526
        lower_omitted = subscript.slice.lower is None
4✔
527
        upper_omitted = subscript.slice.upper is None
4✔
528
        step_omitted = subscript.slice.step is None
4✔
529

530
        self.visit_to_generate(subscript.value)
4✔
531

532
        step_negative = True if not step_omitted and subscript.slice.step.n < 0 else False
4✔
533

534
        if step_negative:
4✔
535
            # reverse the ByteString or the list
536
            self.generator.convert_array_negative_stride()
4✔
537

538
        addresses = []
4✔
539

540
        for index_number, omitted in [(subscript.slice.lower, lower_omitted), (subscript.slice.upper, upper_omitted)]:
4✔
541
            if not omitted:
4✔
542
                addresses.append(VMCodeMapping.instance().bytecode_size)
4✔
543
                self.visit_to_generate(index_number)
4✔
544

545
                index_is_constant_number = isinstance(index_number, ast.Num) and isinstance(index_number.n, int)
4✔
546
                if index_is_constant_number and index_number.n < 0:
4✔
547
                    self.generator.fix_negative_index(test_is_negative=False)
4✔
548
                elif not index_is_constant_number:
4✔
549
                    self.generator.fix_negative_index()
4✔
550

551
                if step_negative:
4✔
552
                    self.generator.duplicate_stack_item(len(addresses) + 1)  # duplicates the subscript
4✔
553
                    self.generator.fix_index_negative_stride()
4✔
554

555
                self.generator.fix_index_out_of_range(has_another_index_in_stack=len(addresses) == 2)
4✔
556

557
        # if both are explicit
558
        if not lower_omitted and not upper_omitted:
4✔
559
            self.generator.convert_get_sub_sequence()
4✔
560

561
        # only one of them is omitted
562
        elif lower_omitted != upper_omitted:
4✔
563
            # start position is omitted
564
            if lower_omitted:
4✔
565
                self.generator.convert_get_sequence_beginning()
4✔
566
            # end position is omitted
567
            else:
568
                self.generator.convert_get_sequence_ending()
4✔
569
        else:
570
            self.generator.convert_copy()
4✔
571

572
        if not step_omitted:
4✔
573
            self.visit_to_generate(subscript.slice.step)
4✔
574
            self.generator.convert_get_stride()
4✔
575

576
        return self.build_data(subscript)
4✔
577

578
    def _convert_unary_operation(self, operand, op):
4✔
579
        self.visit_to_generate(operand)
4✔
580
        self.generator.convert_operation(op)
4✔
581

582
    def _convert_binary_operation(self, left, right, op):
4✔
583
        self.visit_to_generate(left)
4✔
584
        self.visit_to_generate(right)
4✔
585
        self.generator.convert_operation(op)
4✔
586

587
    def visit_BinOp(self, bin_op: ast.BinOp) -> GeneratorData:
4✔
588
        """
589
        Visitor of a binary operation node
590

591
        :param bin_op: the python ast binary operation node
592
        """
593
        if isinstance(bin_op.op, BinaryOperation):
4✔
594
            self._convert_binary_operation(bin_op.left, bin_op.right, bin_op.op)
4✔
595

596
        return self.build_data(bin_op)
4✔
597

598
    def visit_UnaryOp(self, un_op: ast.UnaryOp) -> GeneratorData:
4✔
599
        """
600
        Visitor of a binary operation node
601

602
        :param un_op: the python ast binary operation node
603
        """
604
        if isinstance(un_op.op, UnaryOperation):
4✔
605
            self._convert_unary_operation(un_op.operand, un_op.op)
4✔
606

607
        return self.build_data(un_op)
4✔
608

609
    def visit_Compare(self, compare: ast.Compare) -> GeneratorData:
4✔
610
        """
611
        Visitor of a compare operation node
612

613
        :param compare: the python ast compare operation node
614
        """
615
        operation_symbol: IOperation = BinaryOp.And
4✔
616
        converted: bool = False
4✔
617
        left = compare.left
4✔
618
        for index, op in enumerate(compare.ops):
4✔
619
            right = compare.comparators[index]
4✔
620
            if isinstance(op, IOperation):
4✔
621
                if isinstance(op, BinaryOperation):
4✔
622
                    self._convert_binary_operation(left, right, op)
4✔
623
                else:
624
                    operand = left
4✔
625
                    if isinstance(operand, ast.NameConstant) and operand.value is None:
4✔
626
                        operand = right
×
627
                    self._convert_unary_operation(operand, op)
4✔
628
                # if it's more than two comparators, must include AND between the operations
629
                if not converted:
4✔
630
                    converted = True
4✔
631
                    operation_symbol = op
4✔
632
                else:
633
                    operation_symbol = BinaryOp.And
4✔
634
                    self.generator.convert_operation(BinaryOp.And)
4✔
635
            left = right
4✔
636

637
        return self.build_data(compare, symbol=operation_symbol, result_type=operation_symbol.result)
4✔
638

639
    def visit_BoolOp(self, bool_op: ast.BoolOp) -> GeneratorData:
4✔
640
        """
641
        Visitor of a compare operation node
642

643
        :param bool_op: the python ast boolean operation node
644
        """
645
        if isinstance(bool_op.op, BinaryOperation):
4✔
646
            left = bool_op.values[0]
4✔
647
            self.visit_to_generate(left)
4✔
648
            for index, right in enumerate(bool_op.values[1:]):
4✔
649
                self.visit_to_generate(right)
4✔
650
                self.generator.convert_operation(bool_op.op)
4✔
651

652
        return self.build_data(bool_op)
4✔
653

654
    def visit_While(self, while_node: ast.While) -> GeneratorData:
4✔
655
        """
656
        Visitor of a while statement node
657

658
        :param while_node: the python ast while statement node
659
        """
660
        start_addr: int = self.generator.convert_begin_while()
4✔
661
        for stmt in while_node.body:
4✔
662
            self.visit_to_map(stmt, generate=True)
4✔
663

664
        test_address: int = VMCodeMapping.instance().bytecode_size
4✔
665
        test_data = self.visit_to_map(while_node.test, generate=True)
4✔
666
        self.generator.convert_end_while(start_addr, test_address)
4✔
667

668
        else_begin_address: int = self.generator.last_code_start_address
4✔
669
        for stmt in while_node.orelse:
4✔
670
            self.visit_to_map(stmt, generate=True)
4✔
671

672
        self.generator.convert_end_loop_else(start_addr, else_begin_address, len(while_node.orelse) > 0)
4✔
673
        return self.build_data(while_node, index=start_addr)
4✔
674

675
    def visit_For(self, for_node: ast.For) -> GeneratorData:
4✔
676
        """
677
        Visitor of for statement node
678

679
        :param for_node: the python ast for node
680
        """
681
        self.visit_to_generate(for_node.iter)
4✔
682
        start_address = self.generator.convert_begin_for()
4✔
683

684
        if isinstance(for_node.target, tuple):
4✔
685
            for target in for_node.target:
×
686
                var_data = self.visit_to_map(target)
×
687
                self.generator.convert_store_variable(var_data.symbol_id)
×
688
        else:
689
            var_data = self.visit(for_node.target)
4✔
690
            self.generator.convert_store_variable(var_data.symbol_id)
4✔
691

692
        for stmt in for_node.body:
4✔
693
            self.visit_to_map(stmt, generate=True)
4✔
694

695
        # TODO: remove when optimizing for generation
696
        if self.current_method is not None:
4✔
697
            self.current_method.remove_instruction(for_node.lineno, for_node.col_offset)
4✔
698

699
        condition_address = self.generator.convert_end_for(start_address)
4✔
700
        self.include_instruction(for_node, condition_address)
4✔
701
        else_begin = self.generator.last_code_start_address
4✔
702

703
        for stmt in for_node.orelse:
4✔
704
            self.visit_to_map(stmt, generate=True)
4✔
705

706
        self.generator.convert_end_loop_else(start_address,
4✔
707
                                             else_begin,
708
                                             has_else=len(for_node.orelse) > 0,
709
                                             is_for=True)
710
        return self.build_data(for_node)
4✔
711

712
    def visit_If(self, if_node: ast.If) -> GeneratorData:
4✔
713
        """
714
        Visitor of if statement node
715

716
        :param if_node: the python ast if statement node
717
        """
718
        test = self.visit_to_map(if_node.test, generate=True)
4✔
719

720
        if not Type.bool.is_type_of(test.type) and test.type is not None:
4✔
721
            self.generator.convert_builtin_method_call(Builtin.Bool)
4✔
722

723
        start_addr: int = self.generator.convert_begin_if()
4✔
724
        for stmt in if_node.body:
4✔
725
            self.visit_to_map(stmt, generate=True)
4✔
726

727
        ends_with_if = len(if_node.body) > 0 and isinstance(if_node.body[-1], ast.If)
4✔
728

729
        if len(if_node.orelse) > 0:
4✔
730
            start_addr = self.generator.convert_begin_else(start_addr, ends_with_if)
4✔
731
            for stmt in if_node.orelse:
4✔
732
                self.visit_to_map(stmt, generate=True)
4✔
733

734
        self.generator.convert_end_if(start_addr)
4✔
735
        return self.build_data(if_node)
4✔
736

737
    def visit_Expr(self, expr: ast.Expr, generate: bool = False) -> GeneratorData:
4✔
738
        """
739
        Visitor of an expression node
740

741
        :param expr: the python ast expression node
742
        :param generate: if it should convert the value
743
        """
744
        last_stack = self.generator.stack_size
4✔
745
        if generate:
4✔
746
            value = self.visit_to_generate(expr.value)
4✔
747
        else:
748
            value = self.visit(expr.value)
4✔
749

750
        new_stack = self.generator.stack_size
4✔
751
        for x in range(last_stack, new_stack):
4✔
752
            self.generator.remove_stack_top_item()
4✔
753

754
        return value
4✔
755

756
    def visit_IfExp(self, if_node: ast.IfExp) -> GeneratorData:
4✔
757
        """
758
        Visitor of if expression node
759

760
        :param if_node: the python ast if statement node
761
        """
762
        self.visit_to_map(if_node.test, generate=True)
4✔
763

764
        start_addr: int = self.generator.convert_begin_if()
4✔
765
        body_data = self.visit_to_map(if_node.body, generate=True)
4✔
766

767
        start_addr = self.generator.convert_begin_else(start_addr)
4✔
768
        else_data = self.visit_to_map(if_node.orelse, generate=True)
4✔
769

770
        self.generator.convert_end_if(start_addr)
4✔
771
        return self.build_data(if_node, result_type=Type.union.build([body_data.type, else_data.type]))
4✔
772

773
    def visit_Assert(self, assert_node: ast.Assert) -> GeneratorData:
4✔
774
        """
775
        Visitor of the assert node
776

777
        :param assert_node: the python ast assert node
778
        """
779
        self.visit_to_generate(assert_node.test)
4✔
780

781
        if assert_node.msg is not None:
4✔
782
            self.visit_to_generate(assert_node.msg)
4✔
783

784
        self.generator.convert_assert(has_message=assert_node.msg is not None)
4✔
785
        return self.build_data(assert_node)
4✔
786

787
    def visit_Call(self, call: ast.Call) -> GeneratorData:
4✔
788
        """
789
        Visitor of a function call node
790

791
        :param call: the python ast function call node
792
        :returns: The called function return type
793
        """
794
        # the parameters are included into the stack in the reversed order
795
        last_address = VMCodeMapping.instance().bytecode_size
4✔
796
        last_stack = self.generator.stack_size
4✔
797

798
        func_data = self.visit(call.func)
4✔
799
        function_id = func_data.symbol_id
4✔
800
        # if the symbol is not a method, check if it is a class method
801
        if (isinstance(func_data.type, ClassType) and func_data.symbol_id in func_data.type.symbols
4✔
802
                and isinstance(func_data.type.symbols[func_data.symbol_id], IExpression)):
803
            symbol = func_data.type.symbols[func_data.symbol_id]
4✔
804
        else:
805
            symbol = func_data.symbol
4✔
806

807
        if not isinstance(symbol, Method):
4✔
808
            is_internal = hasattr(call, 'is_internal_call') and call.is_internal_call
4✔
809
            _, symbol = self.generator.get_symbol(function_id, is_internal=is_internal)
4✔
810

811
        if self.is_implemented_class_type(symbol):
4✔
812
            self.generator.convert_init_user_class(symbol)
4✔
813
            symbol = symbol.constructor_method()
4✔
814
        args_addresses: List[int] = []
4✔
815

816
        has_cls_or_self_argument = isinstance(symbol, Method) and symbol.has_cls_or_self
4✔
817
        if not has_cls_or_self_argument:
4✔
818
            self._remove_inserted_opcodes_since(last_address, last_stack)
4✔
819

820
        if isinstance(symbol, Method):
4✔
821
            # self or cls is already generated
822
            call_args = (call.args[1:]
4✔
823
                         if has_cls_or_self_argument and len(call.args) == len(symbol.args)
824
                         else call.args)
825
            args_to_generate = [arg for index, arg in enumerate(call_args) if index in symbol.args_to_be_generated()]
4✔
826
            keywords_dict = {keyword.arg: keyword.value for keyword in call.keywords}
4✔
827
            keywords_with_index = {index: keywords_dict[arg_name]
4✔
828
                                   for index, arg_name in enumerate(symbol.args)
829
                                   if arg_name in keywords_dict}
830

831
            for index in keywords_with_index:
4✔
832
                if index < len(args_to_generate):
4✔
833
                    # override default values
834
                    args_to_generate[index] = keywords_with_index[index]
4✔
835
                else:
836
                    # put keywords
837
                    args_to_generate.append(keywords_with_index[index])
4✔
838

839
        else:
840
            args_to_generate = call.args
4✔
841

842
        if isinstance(symbol, IBuiltinMethod):
4✔
843
            reordered_args = []
4✔
844
            for index in symbol.generation_order:
4✔
845
                if 0 <= index < len(args_to_generate):
4✔
846
                    reordered_args.append(args_to_generate[index])
4✔
847

848
            args = reordered_args
4✔
849
        else:
850
            args = reversed(args_to_generate)
4✔
851

852
        args_begin_address = self.generator.last_code_start_address
4✔
853
        for arg in args:
4✔
854
            args_addresses.append(
4✔
855
                VMCodeMapping.instance().bytecode_size
856
            )
857
            self.visit_to_generate(arg)
4✔
858

859
        class_type = None
4✔
860
        if has_cls_or_self_argument:
4✔
861
            num_args = len(args_addresses)
4✔
862
            if self.generator.stack_size > num_args:
4✔
863
                value = self.generator._stack_pop(-num_args - 1)
4✔
864
                self.generator._stack_append(value)
4✔
865
                class_type = value if isinstance(value, UserClass) else None
4✔
866
            end_address = VMCodeMapping.instance().move_to_end(last_address, args_begin_address)
4✔
867
            if not symbol.is_init:
4✔
868
                args_addresses.append(end_address)
4✔
869

870
        if self.is_exception_name(function_id):
4✔
871
            self.generator.convert_new_exception(len(call.args))
4✔
872
        elif isinstance(symbol, type(Builtin.Super)) and len(args_to_generate) == 0:
4✔
873
            self_or_cls_id = list(self.current_method.args)[0]
4✔
874
            self.generator.convert_load_symbol(self_or_cls_id)
4✔
875
        elif isinstance(symbol, IBuiltinMethod):
4✔
876
            self.generator.convert_builtin_method_call(symbol, args_addresses)
4✔
877
        else:
878
            self.generator.convert_load_symbol(function_id, args_addresses,
4✔
879
                                               class_type=class_type)
880

881
        return self.build_data(call, symbol=symbol, symbol_id=function_id,
4✔
882
                               result_type=symbol.type if isinstance(symbol, IExpression) else symbol,
883
                               already_generated=True)
884

885
    def visit_Raise(self, raise_node: ast.Raise) -> GeneratorData:
4✔
886
        """
887
        Visitor of the raise node
888

889
        :param raise_node: the python ast raise node
890
        """
891
        self.visit_to_map(raise_node.exc, generate=True)
4✔
892
        self.generator.convert_raise_exception()
4✔
893
        return self.build_data(raise_node)
4✔
894

895
    def visit_Try(self, try_node: ast.Try) -> GeneratorData:
4✔
896
        """
897
        Visitor of the try node
898

899
        :param try_node: the python ast try node
900
        """
901
        try_address: int = self.generator.convert_begin_try()
4✔
902
        try_end: Optional[int] = None
4✔
903
        for stmt in try_node.body:
4✔
904
            self.visit_to_map(stmt, generate=True)
4✔
905

906
        if len(try_node.handlers) == 1:
4✔
907
            handler = try_node.handlers[0]
4✔
908
            try_end = self.generator.convert_try_except(handler.name)
4✔
909
            for stmt in handler.body:
4✔
910
                self.visit_to_map(stmt, generate=True)
4✔
911

912
        else_address = None
4✔
913
        if len(try_node.orelse) > 0:
4✔
914
            else_start_address = self.generator.convert_begin_else(try_end)
4✔
915
            else_address = self.generator.bytecode_size
4✔
916
            for stmt in try_node.orelse:
4✔
917
                self.visit_to_map(stmt, generate=True)
4✔
918
            self.generator.convert_end_if(else_start_address)
4✔
919

920
        except_end = self.generator.convert_end_try(try_address, try_end, else_address)
4✔
921
        for stmt in try_node.finalbody:
4✔
922
            self.visit_to_map(stmt, generate=True)
4✔
923
        self.generator.convert_end_try_finally(except_end, try_address, len(try_node.finalbody) > 0)
4✔
924

925
        return self.build_data(try_node)
4✔
926

927
    def visit_Name(self, name_node: ast.Name) -> GeneratorData:
4✔
928
        """
929
        Visitor of a name node
930

931
        :param name_node: the python ast name identifier node
932
        :return: the identifier of the name
933
        """
934
        name = name_node.id
4✔
935
        if name not in self._symbols:
4✔
936
            hashed_name = self._get_unique_name(name, self._tree)
4✔
937
            if hashed_name not in self._symbols and hasattr(name_node, 'origin'):
4✔
938
                hashed_name = self._get_unique_name(name, name_node.origin)
4✔
939

940
            if hashed_name in self._symbols:
4✔
941
                name = hashed_name
4✔
942

943
        else:
944
            if name not in self.generator.symbol_table:
4✔
945
                symbol = self._symbols[name]
4✔
946
                for symbol_id, internal_symbol in self.generator.symbol_table.items():
4✔
947
                    if internal_symbol == symbol:
4✔
948
                        name = symbol_id
×
949
                        break
×
950

951
        return self.build_data(name_node, name)
4✔
952

953
    def visit_Starred(self, starred: ast.Starred) -> GeneratorData:
4✔
954
        value_data = self.visit_to_generate(starred.value)
4✔
955
        self.generator.convert_starred_variable()
4✔
956

957
        starred_data = value_data.copy(starred)
4✔
958
        starred_data.already_generated = True
4✔
959
        return starred_data
4✔
960

961
    def visit_Attribute(self, attribute: ast.Attribute) -> GeneratorData:
4✔
962
        """
963
        Visitor of a attribute node
964

965
        :param attribute: the python ast attribute node
966
        :return: the identifier of the attribute
967
        """
968
        last_address = VMCodeMapping.instance().bytecode_size
4✔
969
        last_stack = self.generator.stack_size
4✔
970

971
        _attr, attr = self.generator.get_symbol(attribute.attr)
4✔
972
        value = attribute.value
4✔
973
        value_symbol = None
4✔
974
        value_type = None
4✔
975
        value_data = self.visit(value)
4✔
976
        need_to_visit_again = True
4✔
977

978
        if value_data.symbol_id is not None and not value_data.already_generated:
4✔
979
            value_id = value_data.symbol_id
4✔
980
            if value_data.symbol is not None:
4✔
981
                value_symbol = value_data.symbol
4✔
982
            else:
983
                _, value_symbol = self.generator.get_symbol(value_id)
4✔
984
            value_type = value_symbol.type if hasattr(value_symbol, 'type') else value_symbol
4✔
985

986
            if hasattr(value_type, 'symbols') and attribute.attr in value_type.symbols:
4✔
987
                attr = value_type.symbols[attribute.attr]
4✔
988
            elif isinstance(value_type, Package) and attribute.attr in value_type.inner_packages:
4✔
989
                attr = value_type.inner_packages[attribute.attr]
4✔
990

991
            if isinstance(value_symbol, UserClass):
4✔
992
                if isinstance(attr, Method) and attr.has_cls_or_self:
4✔
993
                    self.generator.convert_load_symbol(value_id)
4✔
994
                    need_to_visit_again = False
4✔
995

996
                if isinstance(attr, Variable):
4✔
997
                    cur_bytesize = self.generator.bytecode_size
4✔
998
                    self.visit_to_generate(attribute.value)
4✔
999
                    self.generator.convert_load_symbol(attribute.attr, class_type=value_symbol)
4✔
1000

1001
                    symbol_id = _attr if isinstance(_attr, str) else None
4✔
1002
                    return self.build_data(attribute,
4✔
1003
                                           already_generated=self.generator.bytecode_size > cur_bytesize,
1004
                                           symbol=attr, symbol_id=symbol_id,
1005
                                           origin_object_type=value_symbol)
1006
        else:
1007
            if isinstance(value, ast.Attribute) and value_data.already_generated:
4✔
1008
                need_to_visit_again = False
4✔
1009
            else:
1010
                need_to_visit_again = value_data.already_generated
4✔
1011
                self._remove_inserted_opcodes_since(last_address, last_stack)
4✔
1012

1013
        # the verification above only verify variables, this one will should work with literals and constants
1014
        if isinstance(value, (ast.Constant if SYS_VERSION_INFO >= (3, 8) else (ast.Num, ast.Str, ast.Bytes))) \
4✔
1015
                and len(attr.args) > 0 and isinstance(attr, IBuiltinMethod) and attr.has_self_argument:
1016
            attr = attr.build(value_data.type)
×
1017

1018
        if attr is not Type.none and not hasattr(attribute, 'generate_value'):
4✔
1019
            value_symbol_id = (value_symbol.identifier
4✔
1020
                               if self.is_implemented_class_type(value_symbol)
1021
                               else value_data.symbol_id)
1022
            attribute_id = f'{value_symbol_id}{constants.ATTRIBUTE_NAME_SEPARATOR}{attribute.attr}'
4✔
1023
            index = value_type if isinstance(value_type, Package) else None
4✔
1024
            return self.build_data(attribute, symbol_id=attribute_id, symbol=attr, index=index)
4✔
1025

1026
        if isinstance(value, ast.Attribute) and need_to_visit_again:
4✔
1027
            value_data = self.visit(value)
4✔
1028
        elif hasattr(attribute, 'generate_value') and attribute.generate_value:
4✔
1029
            current_bytecode_size = self.generator.bytecode_size
4✔
1030
            if need_to_visit_again:
4✔
1031
                value_data = self.visit_to_generate(attribute.value)
4✔
1032

1033
            result = value_data.type
4✔
1034
            generation_result = value_data.symbol
4✔
1035
            if result is None and value_data.symbol_id is not None:
4✔
1036
                _, result = self.generator.get_symbol(value_data.symbol_id)
4✔
1037
                if isinstance(result, IExpression):
4✔
1038
                    generation_result = result
×
1039
                    result = result.type
×
1040
            elif isinstance(attribute.value, ast.Attribute) and isinstance(value_data.index, int):
4✔
1041
                result = self.get_type(generation_result)
4✔
1042

1043
            if self.is_implemented_class_type(result):
4✔
1044
                class_attr_id = f'{result.identifier}.{attribute.attr}'
4✔
1045
                symbol_id = class_attr_id
4✔
1046
                symbol = None
4✔
1047
                result_type = None
4✔
1048
                symbol_index = None
4✔
1049
                is_load_context_variable_from_class = (isinstance(attribute.ctx, ast.Load) and
4✔
1050
                                                       isinstance(attr, Variable) and
1051
                                                       isinstance(result, UserClass))
1052

1053
                if self.generator.bytecode_size > current_bytecode_size and isinstance(result, UserClass) and not is_load_context_variable_from_class:
4✔
1054
                    # it was generated already, don't convert again
1055
                    generated = False
4✔
1056
                    symbol_id = attribute.attr if isinstance(generation_result, Variable) else class_attr_id
4✔
1057
                    result_type = result
4✔
1058
                else:
1059
                    current_bytecode_size = self.generator.bytecode_size
4✔
1060
                    index = self.generator.convert_class_symbol(result,
4✔
1061
                                                                attribute.attr,
1062
                                                                isinstance(attribute.ctx, ast.Load))
1063
                    generated = self.generator.bytecode_size > current_bytecode_size
4✔
1064
                    symbol = result
4✔
1065
                    if not isinstance(result, UserClass):
4✔
1066
                        if isinstance(index, int):
4✔
1067
                            symbol_index = index
4✔
1068
                        else:
1069
                            symbol_id = index
4✔
1070

1071
                return self.build_data(attribute,
4✔
1072
                                       symbol_id=symbol_id, symbol=symbol,
1073
                                       result_type=result_type, index=symbol_index,
1074
                                       origin_object_type=value_type,
1075
                                       already_generated=generated)
1076

1077
        if value_data is not None and value_symbol is None:
4✔
1078
            value_symbol = value_data.symbol_id
×
1079

1080
        if value_data is not None and value_data.symbol_id is not None:
4✔
1081
            value_id = f'{value_data.symbol_id}{constants.ATTRIBUTE_NAME_SEPARATOR}{attribute.attr}'
4✔
1082
        else:
1083
            value_id = attribute.attr
×
1084

1085
        return self.build_data(attribute, symbol_id=value_id, symbol=value_symbol)
4✔
1086

1087
    def visit_Continue(self, continue_node: ast.Continue) -> GeneratorData:
4✔
1088
        """
1089
        :param continue_node: the python ast continue statement node
1090
        """
1091
        self.generator.convert_loop_continue()
4✔
1092
        return self.build_data(continue_node)
4✔
1093

1094
    def visit_Break(self, break_node: ast.Break) -> GeneratorData:
4✔
1095
        """
1096
        :param break_node: the python ast break statement node
1097
        """
1098
        self.generator.convert_loop_break()
4✔
1099
        return self.build_data(break_node)
4✔
1100

1101
    def visit_Constant(self, constant: ast.Constant) -> GeneratorData:
4✔
1102
        """
1103
        Visitor of constant values node
1104

1105
        :param constant: the python ast constant value node
1106
        """
1107
        index = self.generator.convert_literal(constant.value)
3✔
1108
        result_type = self.get_type(constant.value)
3✔
1109
        return self.build_data(constant, result_type=result_type, index=index, already_generated=True)
3✔
1110

1111
    def visit_NameConstant(self, constant: ast.NameConstant) -> GeneratorData:
4✔
1112
        """
1113
        Visitor of constant names node
1114

1115
        :param constant: the python ast name constant node
1116
        """
1117
        index = self.generator.convert_literal(constant.value)
1✔
1118
        result_type = self.get_type(constant.value)
1✔
1119
        return self.build_data(constant, result_type=result_type, index=index, already_generated=True)
1✔
1120

1121
    def visit_Num(self, num: ast.Num) -> GeneratorData:
4✔
1122
        """
1123
        Visitor of literal number node
1124

1125
        :param num: the python ast number node
1126
        """
1127
        index = self.generator.convert_literal(num.n)
1✔
1128
        result_type = self.get_type(num.n)
1✔
1129
        return self.build_data(num, result_type=result_type, index=index, already_generated=True)
1✔
1130

1131
    def visit_Str(self, string: ast.Str) -> GeneratorData:
4✔
1132
        """
1133
        Visitor of literal string node
1134

1135
        :param string: the python ast string node
1136
        """
1137
        index = self.generator.convert_literal(string.s)
1✔
1138
        result_type = self.get_type(string.s)
1✔
1139
        return self.build_data(string, result_type=result_type, index=index, already_generated=True)
1✔
1140

1141
    def visit_Bytes(self, bts: ast.Bytes) -> GeneratorData:
4✔
1142
        """
1143
        Visitor of literal bytes node
1144

1145
        :param bts: the python ast bytes node
1146
        """
1147
        index = self.generator.convert_literal(bts.s)
1✔
1148
        result_type = self.get_type(bts.s)
1✔
1149
        return self.build_data(bts, result_type=result_type, index=index, already_generated=True)
1✔
1150

1151
    def visit_Tuple(self, tup_node: ast.Tuple) -> GeneratorData:
4✔
1152
        """
1153
        Visitor of literal tuple node
1154

1155
        :param tup_node: the python ast tuple node
1156
        """
1157
        result_type = Type.tuple
4✔
1158
        self._create_array(tup_node.elts, result_type)
4✔
1159
        return self.build_data(tup_node, result_type=result_type, already_generated=True)
4✔
1160

1161
    def visit_List(self, list_node: ast.List) -> GeneratorData:
4✔
1162
        """
1163
        Visitor of literal list node
1164

1165
        :param list_node: the python ast list node
1166
        """
1167
        result_type = Type.list
4✔
1168
        self._create_array(list_node.elts, result_type)
4✔
1169
        return self.build_data(list_node, result_type=result_type, already_generated=True)
4✔
1170

1171
    def visit_Dict(self, dict_node: ast.Dict) -> GeneratorData:
4✔
1172
        """
1173
        Visitor of literal dict node
1174

1175
        :param dict_node: the python ast dict node
1176
        """
1177
        result_type = Type.dict
4✔
1178
        length = min(len(dict_node.keys), len(dict_node.values))
4✔
1179
        self.generator.convert_new_map(result_type)
4✔
1180
        for key_value in range(length):
4✔
1181
            self.generator.duplicate_stack_top_item()
4✔
1182
            self.visit_to_generate(dict_node.keys[key_value])
4✔
1183
            value_data = self.visit_to_generate(dict_node.values[key_value])
4✔
1184
            self.generator.convert_set_item(value_data.index)
4✔
1185

1186
        return self.build_data(dict_node, result_type=result_type, already_generated=True)
4✔
1187

1188
    def visit_Pass(self, pass_node: ast.Pass) -> GeneratorData:
4✔
1189
        """
1190
        Visitor of pass node
1191

1192
        :param pass_node: the python ast dict node
1193
        """
1194
        # only generates if the scope is a function
1195
        result_type = None
4✔
1196
        generated = False
4✔
1197

1198
        if isinstance(self.current_method, Method):
4✔
1199
            self.generator.insert_nop()
4✔
1200
            generated = True
4✔
1201

1202
        return self.build_data(pass_node, result_type=result_type, already_generated=generated)
4✔
1203

1204
    def _create_array(self, values: List[ast.AST], array_type: IType):
4✔
1205
        """
1206
        Creates a new array from a literal sequence
1207

1208
        :param values: list of values of the new array items
1209
        """
1210
        length = len(values)
4✔
1211
        if length > 0:
4✔
1212
            for value in reversed(values):
4✔
1213
                self.visit_to_generate(value)
4✔
1214
        self.generator.convert_new_array(length, array_type)
4✔
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