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

CityOfZion / neo3-boa / 017024aa-199d-4642-8e92-e534b6134c2e

pending completion
017024aa-199d-4642-8e92-e534b6134c2e

Pull #1096

circleci

meevee98
#8678c423x - Update NeoTestRunner documentation on Neo3Boa
Pull Request #1096: Update NeoTestRunner usage in the documentation

19588 of 21344 relevant lines covered (91.77%)

2.73 hits per line

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

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

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

31

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

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

39
    :ivar generator:
40
    """
41

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

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

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

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

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

61
        return symbol_table
3✔
62

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

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

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

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

91
            if found_symbol is not None:
3✔
92
                symbol = found_symbol
3✔
93

94
        return GeneratorData(origin_node, symbol_id, symbol, result_type, index, origin_object_type, already_generated)
3✔
95

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

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

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

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

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

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

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

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

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

149
                    self.generator.convert_load_symbol(result.symbol_id, is_internal=is_internal, class_type=class_type if self.current_method is None else None)
3✔
150

151
                result.already_generated = True
3✔
152

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

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

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

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

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

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

185
        Fills module symbol table
186

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

200
        for stmt in function_stmts:
3✔
201
            self.visit(stmt)
3✔
202

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

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

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

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

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

238
                        class_non_static_stmts.append(cls_fun)
3✔
239

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

251
                self.visit(stmt)
3✔
252
                self.filename = cur_filename
3✔
253
                self._tree = cur_tree
3✔
254

255
            self._is_generating_initialize = False
3✔
256
            self.generator.end_initialize()
3✔
257

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

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

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

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

282
        return self.build_data(module)
3✔
283

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

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

299
        for stmt in node.body:
3✔
300
            self.visit(stmt)
3✔
301

302
        self.current_class = None
3✔
303
        return self.build_data(node)
3✔
304

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

309
        Generates the Neo VM code for the function
310

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

315
        if isinstance(method, Property):
3✔
316
            method = method.getter
3✔
317

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

325
            self._log_info(f"Compiling '{function_name}' function")
3✔
326

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

331
                    for stmt in function.body:
3✔
332
                        self.visit_to_map(stmt)
3✔
333

334
                    self.generator.convert_end_method(function.name)
3✔
335

336
            self.current_method = None
3✔
337

338
        return self.build_data(function, symbol=method, symbol_id=function.name)
3✔
339

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

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

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

358
        self.generator.insert_return()
3✔
359

360
        return self.build_data(ret)
3✔
361

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

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

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

384
                    self.generator.convert_set_item(value_address)
3✔
385

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

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

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

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

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

437
            var_id = target_data.symbol_id
3✔
438
            var_index = target_data.index
3✔
439

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

446
            vars_ids.append(VariableGenerationData(var_id, var_index, var_value_address))
3✔
447

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

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

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

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

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

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

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

488
        return self.visit_Subscript_Index(subscript)
3✔
489

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

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

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

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

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

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

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

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

531
        self.visit_to_generate(subscript.value)
3✔
532

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

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

539
        addresses = []
3✔
540

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

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

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

556
                self.generator.fix_index_out_of_range(has_another_index_in_stack=len(addresses) == 2)
3✔
557

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

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

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

577
        return self.build_data(subscript)
3✔
578

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

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

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

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

597
        return self.build_data(bin_op)
3✔
598

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

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

608
        return self.build_data(un_op)
3✔
609

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

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

638
        return self.build_data(compare, symbol=operation_symbol, result_type=operation_symbol.result)
3✔
639

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

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

653
        return self.build_data(bool_op)
3✔
654

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

735
        self.generator.convert_end_if(start_addr)
3✔
736
        return self.build_data(if_node)
3✔
737

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

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

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

755
        return value
3✔
756

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

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

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

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

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

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

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

782
        if assert_node.msg is not None:
3✔
783
            self.generator.duplicate_stack_top_item()
3✔
784
            self.generator.insert_not()
3✔
785

786
            # if assert is false, log the message
787
            start_addr: int = self.generator.convert_begin_if()
3✔
788

789
            self.visit_to_generate(assert_node.msg)
3✔
790
            self.generator.convert_builtin_method_call(Interop.Log)
3✔
791

792
            self.generator.convert_end_if(start_addr)
3✔
793

794
        self.generator.convert_assert()
3✔
795
        return self.build_data(assert_node)
3✔
796

797
    def visit_Call(self, call: ast.Call) -> GeneratorData:
3✔
798
        """
799
        Visitor of a function call node
800

801
        :param call: the python ast function call node
802
        :returns: The called function return type
803
        """
804
        # the parameters are included into the stack in the reversed order
805
        last_address = VMCodeMapping.instance().bytecode_size
3✔
806
        last_stack = self.generator.stack_size
3✔
807

808
        func_data = self.visit(call.func)
3✔
809
        function_id = func_data.symbol_id
3✔
810
        # if the symbol is not a method, check if it is a class method
811
        if (isinstance(func_data.type, ClassType) and func_data.symbol_id in func_data.type.symbols
3✔
812
                and isinstance(func_data.type.symbols[func_data.symbol_id], IExpression)):
813
            symbol = func_data.type.symbols[func_data.symbol_id]
3✔
814
        else:
815
            symbol = func_data.symbol
3✔
816

817
        if not isinstance(symbol, Method):
3✔
818
            is_internal = hasattr(call, 'is_internal_call') and call.is_internal_call
3✔
819
            _, symbol = self.generator.get_symbol(function_id, is_internal=is_internal)
3✔
820

821
        if self.is_implemented_class_type(symbol):
3✔
822
            self.generator.convert_init_user_class(symbol)
3✔
823
            symbol = symbol.constructor_method()
3✔
824
        args_addresses: List[int] = []
3✔
825

826
        has_cls_or_self_argument = isinstance(symbol, Method) and symbol.has_cls_or_self
3✔
827
        if not has_cls_or_self_argument:
3✔
828
            self._remove_inserted_opcodes_since(last_address, last_stack)
3✔
829

830
        if isinstance(symbol, Method):
3✔
831
            # self or cls is already generated
832
            call_args = (call.args[1:]
3✔
833
                         if has_cls_or_self_argument and len(call.args) == len(symbol.args)
834
                         else call.args)
835
            args_to_generate = [arg for index, arg in enumerate(call_args) if index in symbol.args_to_be_generated()]
3✔
836
            keywords_dict = {keyword.arg: keyword.value for keyword in call.keywords}
3✔
837
            keywords_with_index = {index: keywords_dict[arg_name]
3✔
838
                                   for index, arg_name in enumerate(symbol.args)
839
                                   if arg_name in keywords_dict}
840

841
            for index in keywords_with_index:
3✔
842
                if index < len(args_to_generate):
3✔
843
                    # override default values
844
                    args_to_generate[index] = keywords_with_index[index]
3✔
845
                else:
846
                    # put keywords
847
                    args_to_generate.append(keywords_with_index[index])
3✔
848

849
        else:
850
            args_to_generate = call.args
3✔
851

852
        if isinstance(symbol, IBuiltinMethod):
3✔
853
            reordered_args = []
3✔
854
            for index in symbol.generation_order:
3✔
855
                if 0 <= index < len(args_to_generate):
3✔
856
                    reordered_args.append(args_to_generate[index])
3✔
857

858
            args = reordered_args
3✔
859
        else:
860
            args = reversed(args_to_generate)
3✔
861

862
        args_begin_address = self.generator.last_code_start_address
3✔
863
        for arg in args:
3✔
864
            args_addresses.append(
3✔
865
                VMCodeMapping.instance().bytecode_size
866
            )
867
            self.visit_to_generate(arg)
3✔
868
        if has_cls_or_self_argument:
3✔
869
            num_args = len(args_addresses)
3✔
870
            if self.generator.stack_size > num_args:
3✔
871
                value = self.generator._stack_pop(-num_args - 1)
3✔
872
                self.generator._stack_append(value)
3✔
873
            end_address = VMCodeMapping.instance().move_to_end(last_address, args_begin_address)
3✔
874
            if not symbol.is_init:
3✔
875
                args_addresses.append(end_address)
3✔
876

877
        if self.is_exception_name(function_id):
3✔
878
            self.generator.convert_new_exception(len(call.args))
3✔
879
        elif isinstance(symbol, type(Builtin.Super)) and len(args_to_generate) == 0:
3✔
880
            self_or_cls_id = list(self.current_method.args)[0]
3✔
881
            self.generator.convert_load_symbol(self_or_cls_id)
3✔
882
        elif isinstance(symbol, IBuiltinMethod):
3✔
883
            self.generator.convert_builtin_method_call(symbol, args_addresses)
3✔
884
        else:
885
            self.generator.convert_load_symbol(function_id, args_addresses)
3✔
886

887
        return self.build_data(call, symbol=symbol, symbol_id=function_id,
3✔
888
                               result_type=symbol.type if isinstance(symbol, IExpression) else symbol,
889
                               already_generated=True)
890

891
    def visit_Raise(self, raise_node: ast.Raise) -> GeneratorData:
3✔
892
        """
893
        Visitor of the raise node
894

895
        :param raise_node: the python ast raise node
896
        """
897
        self.visit_to_map(raise_node.exc, generate=True)
3✔
898
        self.generator.convert_raise_exception()
3✔
899
        return self.build_data(raise_node)
3✔
900

901
    def visit_Try(self, try_node: ast.Try) -> GeneratorData:
3✔
902
        """
903
        Visitor of the try node
904

905
        :param try_node: the python ast try node
906
        """
907
        try_address: int = self.generator.convert_begin_try()
3✔
908
        try_end: Optional[int] = None
3✔
909
        for stmt in try_node.body:
3✔
910
            self.visit_to_map(stmt, generate=True)
3✔
911

912
        if len(try_node.handlers) == 1:
3✔
913
            handler = try_node.handlers[0]
3✔
914
            try_end = self.generator.convert_try_except(handler.name)
3✔
915
            for stmt in handler.body:
3✔
916
                self.visit_to_map(stmt, generate=True)
3✔
917

918
        else_address = None
3✔
919
        if len(try_node.orelse) > 0:
3✔
920
            else_start_address = self.generator.convert_begin_else(try_end)
3✔
921
            else_address = self.generator.bytecode_size
3✔
922
            for stmt in try_node.orelse:
3✔
923
                self.visit_to_map(stmt, generate=True)
3✔
924
            self.generator.convert_end_if(else_start_address)
3✔
925

926
        except_end = self.generator.convert_end_try(try_address, try_end, else_address)
3✔
927
        for stmt in try_node.finalbody:
3✔
928
            self.visit_to_map(stmt, generate=True)
3✔
929
        self.generator.convert_end_try_finally(except_end, try_address, len(try_node.finalbody) > 0)
3✔
930

931
        return self.build_data(try_node)
3✔
932

933
    def visit_Name(self, name_node: ast.Name) -> GeneratorData:
3✔
934
        """
935
        Visitor of a name node
936

937
        :param name_node: the python ast name identifier node
938
        :return: the identifier of the name
939
        """
940
        name = name_node.id
3✔
941
        if name not in self._symbols:
3✔
942
            hashed_name = self._get_unique_name(name, self._tree)
3✔
943
            if hashed_name not in self._symbols and hasattr(name_node, 'origin'):
3✔
944
                hashed_name = self._get_unique_name(name, name_node.origin)
3✔
945

946
            if hashed_name in self._symbols:
3✔
947
                name = hashed_name
3✔
948
        return self.build_data(name_node, name)
3✔
949

950
    def visit_Starred(self, starred: ast.Starred) -> GeneratorData:
3✔
951
        value_data = self.visit_to_generate(starred.value)
3✔
952
        self.generator.convert_starred_variable()
3✔
953

954
        starred_data = value_data.copy(starred)
3✔
955
        starred_data.already_generated = True
3✔
956
        return starred_data
3✔
957

958
    def visit_Attribute(self, attribute: ast.Attribute) -> GeneratorData:
3✔
959
        """
960
        Visitor of a attribute node
961

962
        :param attribute: the python ast attribute node
963
        :return: the identifier of the attribute
964
        """
965
        last_address = VMCodeMapping.instance().bytecode_size
3✔
966
        last_stack = self.generator.stack_size
3✔
967

968
        _attr, attr = self.generator.get_symbol(attribute.attr)
3✔
969
        value = attribute.value
3✔
970
        value_symbol = None
3✔
971
        value_type = None
3✔
972
        value_data = self.visit(value)
3✔
973
        need_to_visit_again = True
3✔
974

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

983
            if hasattr(value_type, 'symbols') and attribute.attr in value_type.symbols:
3✔
984
                attr = value_type.symbols[attribute.attr]
3✔
985
            elif isinstance(value_type, Package) and attribute.attr in value_type.inner_packages:
3✔
986
                attr = value_type.inner_packages[attribute.attr]
3✔
987

988
            if isinstance(value_symbol, UserClass):
3✔
989
                if isinstance(attr, Method) and attr.has_cls_or_self:
3✔
990
                    self.generator.convert_load_symbol(value_id)
3✔
991
                    need_to_visit_again = False
3✔
992

993
                if isinstance(attr, Variable):
3✔
994
                    cur_bytesize = self.generator.bytecode_size
3✔
995
                    self.visit_to_generate(attribute.value)
3✔
996
                    self.generator.convert_load_symbol(attribute.attr, class_type=value_symbol)
3✔
997

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

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

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

1023
        if isinstance(value, ast.Attribute) and need_to_visit_again:
3✔
1024
            value_data = self.visit(value)
3✔
1025
        elif hasattr(attribute, 'generate_value') and attribute.generate_value:
3✔
1026
            current_bytecode_size = self.generator.bytecode_size
3✔
1027
            if need_to_visit_again:
3✔
1028
                value_data = self.visit_to_generate(attribute.value)
3✔
1029

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

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

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

1068
                return self.build_data(attribute,
3✔
1069
                                       symbol_id=symbol_id, symbol=symbol,
1070
                                       result_type=result_type, index=symbol_index,
1071
                                       already_generated=generated)
1072

1073
        if value_data is not None and value_symbol is None:
3✔
1074
            value_symbol = value_data.symbol_id
×
1075

1076
        if value_data is not None and value_data.symbol_id is not None:
3✔
1077
            value_id = f'{value_data.symbol_id}{constants.ATTRIBUTE_NAME_SEPARATOR}{attribute.attr}'
3✔
1078
        else:
1079
            value_id = attribute.attr
×
1080

1081
        return self.build_data(attribute, symbol_id=value_id, symbol=value_symbol)
3✔
1082

1083
    def visit_Continue(self, continue_node: ast.Continue) -> GeneratorData:
3✔
1084
        """
1085
        :param continue_node: the python ast continue statement node
1086
        """
1087
        self.generator.convert_loop_continue()
3✔
1088
        return self.build_data(continue_node)
3✔
1089

1090
    def visit_Break(self, break_node: ast.Break) -> GeneratorData:
3✔
1091
        """
1092
        :param break_node: the python ast break statement node
1093
        """
1094
        self.generator.convert_loop_break()
3✔
1095
        return self.build_data(break_node)
3✔
1096

1097
    def visit_Constant(self, constant: ast.Constant) -> GeneratorData:
3✔
1098
        """
1099
        Visitor of constant values node
1100

1101
        :param constant: the python ast constant value node
1102
        """
1103
        index = self.generator.convert_literal(constant.value)
3✔
1104
        result_type = self.get_type(constant.value)
3✔
1105
        return self.build_data(constant, result_type=result_type, index=index, already_generated=True)
3✔
1106

1107
    def visit_NameConstant(self, constant: ast.NameConstant) -> GeneratorData:
3✔
1108
        """
1109
        Visitor of constant names node
1110

1111
        :param constant: the python ast name constant node
1112
        """
1113
        index = self.generator.convert_literal(constant.value)
×
1114
        result_type = self.get_type(constant.value)
×
1115
        return self.build_data(constant, result_type=result_type, index=index, already_generated=True)
×
1116

1117
    def visit_Num(self, num: ast.Num) -> GeneratorData:
3✔
1118
        """
1119
        Visitor of literal number node
1120

1121
        :param num: the python ast number node
1122
        """
1123
        index = self.generator.convert_literal(num.n)
×
1124
        result_type = self.get_type(num.n)
×
1125
        return self.build_data(num, result_type=result_type, index=index, already_generated=True)
×
1126

1127
    def visit_Str(self, string: ast.Str) -> GeneratorData:
3✔
1128
        """
1129
        Visitor of literal string node
1130

1131
        :param string: the python ast string node
1132
        """
1133
        index = self.generator.convert_literal(string.s)
×
1134
        result_type = self.get_type(string.s)
×
1135
        return self.build_data(string, result_type=result_type, index=index, already_generated=True)
×
1136

1137
    def visit_Bytes(self, bts: ast.Bytes) -> GeneratorData:
3✔
1138
        """
1139
        Visitor of literal bytes node
1140

1141
        :param bts: the python ast bytes node
1142
        """
1143
        index = self.generator.convert_literal(bts.s)
×
1144
        result_type = self.get_type(bts.s)
×
1145
        return self.build_data(bts, result_type=result_type, index=index, already_generated=True)
×
1146

1147
    def visit_Tuple(self, tup_node: ast.Tuple) -> GeneratorData:
3✔
1148
        """
1149
        Visitor of literal tuple node
1150

1151
        :param tup_node: the python ast tuple node
1152
        """
1153
        result_type = Type.tuple
3✔
1154
        self._create_array(tup_node.elts, result_type)
3✔
1155
        return self.build_data(tup_node, result_type=result_type, already_generated=True)
3✔
1156

1157
    def visit_List(self, list_node: ast.List) -> GeneratorData:
3✔
1158
        """
1159
        Visitor of literal list node
1160

1161
        :param list_node: the python ast list node
1162
        """
1163
        result_type = Type.list
3✔
1164
        self._create_array(list_node.elts, result_type)
3✔
1165
        return self.build_data(list_node, result_type=result_type, already_generated=True)
3✔
1166

1167
    def visit_Dict(self, dict_node: ast.Dict) -> GeneratorData:
3✔
1168
        """
1169
        Visitor of literal dict node
1170

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

1182
        return self.build_data(dict_node, result_type=result_type, already_generated=True)
3✔
1183

1184
    def visit_Pass(self, pass_node: ast.Pass) -> GeneratorData:
3✔
1185
        """
1186
        Visitor of pass node
1187

1188
        :param pass_node: the python ast dict node
1189
        """
1190
        # only generates if the scope is a function
1191
        result_type = None
3✔
1192
        generated = False
3✔
1193

1194
        if isinstance(self.current_method, Method):
3✔
1195
            self.generator.insert_nop()
3✔
1196
            generated = True
3✔
1197

1198
        return self.build_data(pass_node, result_type=result_type, already_generated=generated)
3✔
1199

1200
    def _create_array(self, values: List[ast.AST], array_type: IType):
3✔
1201
        """
1202
        Creates a new array from a literal sequence
1203

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

© 2025 Coveralls, Inc