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

polserver / polserver / 25918451630

15 May 2026 12:43PM UTC coverage: 60.929% (+2.1%) from 58.859%
25918451630

push

github

turleypol
added dynamic property which returns a pointer of the object instead of
a copy like the current imp.
needed to be able to eg store a vector

43 of 61 new or added lines in 2 files covered. (70.49%)

14455 existing lines in 345 files now uncovered.

44695 of 73356 relevant lines covered (60.93%)

449621.59 hits per line

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

95.93
/pol-core/bscript/compiler/codegen/InstructionGenerator.cpp
1
#include "InstructionGenerator.h"
2

3
#include <ranges>
4

5
#include "bscript/compiler/ast/ArrayInitializer.h"
6
#include "bscript/compiler/ast/BasicForLoop.h"
7
#include "bscript/compiler/ast/BinaryOperator.h"
8
#include "bscript/compiler/ast/BinaryOperatorShortCircuit.h"
9
#include "bscript/compiler/ast/Block.h"
10
#include "bscript/compiler/ast/BooleanValue.h"
11
#include "bscript/compiler/ast/BranchSelector.h"
12
#include "bscript/compiler/ast/CaseDispatchGroup.h"
13
#include "bscript/compiler/ast/CaseDispatchGroups.h"
14
#include "bscript/compiler/ast/CaseDispatchSelectors.h"
15
#include "bscript/compiler/ast/CaseStatement.h"
16
#include "bscript/compiler/ast/ClassDeclaration.h"
17
#include "bscript/compiler/ast/ClassInstance.h"
18
#include "bscript/compiler/ast/ConditionalOperator.h"
19
#include "bscript/compiler/ast/ConstDeclaration.h"
20
#include "bscript/compiler/ast/ConstantPredicateLoop.h"
21
#include "bscript/compiler/ast/CstyleForLoop.h"
22
#include "bscript/compiler/ast/DebugStatementMarker.h"
23
#include "bscript/compiler/ast/DictionaryEntry.h"
24
#include "bscript/compiler/ast/DictionaryInitializer.h"
25
#include "bscript/compiler/ast/DoWhileLoop.h"
26
#include "bscript/compiler/ast/ElementAccess.h"
27
#include "bscript/compiler/ast/ElementAssignment.h"
28
#include "bscript/compiler/ast/ElementIndexes.h"
29
#include "bscript/compiler/ast/ElvisOperator.h"
30
#include "bscript/compiler/ast/ErrorInitializer.h"
31
#include "bscript/compiler/ast/ExitStatement.h"
32
#include "bscript/compiler/ast/FloatValue.h"
33
#include "bscript/compiler/ast/ForeachLoop.h"
34
#include "bscript/compiler/ast/FormatExpression.h"
35
#include "bscript/compiler/ast/FunctionBody.h"
36
#include "bscript/compiler/ast/FunctionCall.h"
37
#include "bscript/compiler/ast/FunctionExpression.h"
38
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
39
#include "bscript/compiler/ast/FunctionParameterList.h"
40
#include "bscript/compiler/ast/FunctionReference.h"
41
#include "bscript/compiler/ast/GeneratedFunction.h"
42
#include "bscript/compiler/ast/Identifier.h"
43
#include "bscript/compiler/ast/IfThenElseStatement.h"
44
#include "bscript/compiler/ast/IndexBinding.h"
45
#include "bscript/compiler/ast/IntegerValue.h"
46
#include "bscript/compiler/ast/InterpolateString.h"
47
#include "bscript/compiler/ast/JumpStatement.h"
48
#include "bscript/compiler/ast/MemberAccess.h"
49
#include "bscript/compiler/ast/MemberAssignment.h"
50
#include "bscript/compiler/ast/MemberAssignmentByOperator.h"
51
#include "bscript/compiler/ast/MethodCall.h"
52
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
53
#include "bscript/compiler/ast/Program.h"
54
#include "bscript/compiler/ast/ProgramParameterDeclaration.h"
55
#include "bscript/compiler/ast/RegularExpressionValue.h"
56
#include "bscript/compiler/ast/RepeatUntilLoop.h"
57
#include "bscript/compiler/ast/ReturnStatement.h"
58
#include "bscript/compiler/ast/SequenceBinding.h"
59
#include "bscript/compiler/ast/SpreadElement.h"
60
#include "bscript/compiler/ast/StringValue.h"
61
#include "bscript/compiler/ast/StructInitializer.h"
62
#include "bscript/compiler/ast/StructMemberInitializer.h"
63
#include "bscript/compiler/ast/UnaryOperator.h"
64
#include "bscript/compiler/ast/UninitializedValue.h"
65
#include "bscript/compiler/ast/UserFunction.h"
66
#include "bscript/compiler/ast/ValueConsumer.h"
67
#include "bscript/compiler/ast/VarStatement.h"
68
#include "bscript/compiler/ast/VariableAssignmentStatement.h"
69
#include "bscript/compiler/ast/VariableBinding.h"
70
#include "bscript/compiler/ast/WhileLoop.h"
71
#include "bscript/compiler/codegen/CaseDispatchGroupVisitor.h"
72
#include "bscript/compiler/codegen/CaseJumpDataBlock.h"
73
#include "bscript/compiler/codegen/DebugBlockGuard.h"
74
#include "bscript/compiler/codegen/InstructionEmitter.h"
75
#include "bscript/compiler/file/SourceFileIdentifier.h"
76
#include "bscript/compiler/model/FlowControlLabel.h"
77
#include "bscript/compiler/model/FunctionLink.h"
78
#include "bscript/compiler/model/Variable.h"
79
#include "symcont.h"
80

81
namespace Pol::Bscript::Compiler
82
{
83
InstructionGenerator::InstructionGenerator(
2,237✔
84
    InstructionEmitter& emitter, std::map<std::string, FlowControlLabel>& user_function_labels,
85
    const std::map<std::string, size_t>& class_declaration_indexes )
2,237✔
86
    : emitter( emitter ),
2,237✔
87
      emit( emitter ),
2,237✔
88
      user_function_labels( user_function_labels ),
2,237✔
89
      class_declaration_indexes( class_declaration_indexes ),
2,237✔
90
      user_functions()
2,237✔
91
{
92
}
2,237✔
93

94
void InstructionGenerator::generate( Node& node )
14,661✔
95
{
96
  // alternative: two identical methods 'evaluate' and 'execute', for readability
97
  update_debug_location( node );
14,661✔
98
  node.accept( *this );
14,661✔
99
}
14,661✔
100

101
void InstructionGenerator::generate_default_parameters( const UserFunction& user_function )
3,403✔
102
{
103
  // Emit instructions for default parameters only for method functions or those that have a
104
  // function reference.
105
  if ( user_function.type == UserFunctionType::Method ||
6,542✔
106
       emitter.has_function_reference( user_function ) )
3,139✔
107
  {
108
    FlowControlLabel& label = user_function_labels[user_function.scoped_name()];
1,274✔
109

110
    if ( !label.has_address() )
1,274✔
111
    {
UNCOV
112
      user_function.internal_error( "function reference label not set" );
×
113
    }
114

115
    std::vector<std::reference_wrapper<FunctionParameterDeclaration>> params;
1,274✔
116
    user_function.child<FunctionParameterList>( 0 ).get_children( params );
1,274✔
117

118
    // Track if the function has any default parameters.
119
    bool any_default_params = false;
1,274✔
120

121
    // Emit the default arguments for parameters in declaration order.
122
    for ( auto& param_ref : params )
3,101✔
123
    {
124
      auto& param = param_ref.get();
1,827✔
125
      auto default_value = param.default_value();
1,827✔
126

127
      if ( default_value || param.rest )
1,827✔
128
      {
129
        auto label_name =
130
            FlowControlLabel::label_for_user_function_default_argument( user_function, param );
212✔
131

132
        unsigned param_address = emitter.next_instruction_address();
212✔
133

134
        user_function_labels[label_name].assign_address( param_address );
212✔
135

136
        if ( default_value )
212✔
137
        {
138
          default_value->accept( *this );
114✔
139
        }
140
        else  // a rest param
141
        {
142
          emit.array_create();
98✔
143
        }
144

145
        any_default_params = true;
212✔
146
      }
212✔
147
    }
148

149
    // Jump to the actual user function if there are default parameters. Once
150
    // hitting the jump, the user function's pop params will be called.
151
    if ( any_default_params )
1,274✔
152
    {
153
      emit.jmp_always( label );
155✔
154
    }
155
  }
1,274✔
156
}
3,403✔
157

158
void InstructionGenerator::update_debug_location( const Node& node )
183,328✔
159
{
160
  update_debug_location( node.source_location );
183,328✔
161
}
183,328✔
162

163
void InstructionGenerator::update_debug_location( const SourceLocation& loc )
184,726✔
164
{
165
  emit.debug_file_line( loc.source_file_identifier->index,
184,726✔
166
                        static_cast<unsigned>( loc.range.start.line_number ) );
184,726✔
167
}
184,726✔
168

169
void InstructionGenerator::visit_array_initializer( ArrayInitializer& node )
2,438✔
170
{
171
  update_debug_location( node );
2,438✔
172
  emit.array_create();
2,438✔
173
  for ( const auto& child : node.children )
7,886✔
174
  {
175
    child->accept( *this );
5,448✔
176
    emit.array_append();
5,448✔
177
  }
178
}
2,438✔
179

180
void InstructionGenerator::visit_basic_for_loop( BasicForLoop& loop )
88✔
181
{
182
  emit.debug_statementbegin();
88✔
183
  update_debug_location( loop );
88✔
184
  FlowControlLabel skip, next;
88✔
185

186
  generate( loop.first() );
88✔
187
  generate( loop.last() );
88✔
188

189
  DebugBlockGuard debug_block_guard( emitter, loop.local_variable_scope_info );
88✔
190

191
  emit.basic_for_init( skip );
88✔
192

193
  emit.label( next );
88✔
194
  generate( loop.block() );
88✔
195

196
  emit.label( *loop.continue_label );
88✔
197
  emit.basic_for_next( next );
88✔
198

199
  emit.label( *loop.break_label );
88✔
200
  emit.leaveblock( 2 );
88✔
201

202
  emit.label( skip );
88✔
203
}
88✔
204

205
void InstructionGenerator::visit_case_statement( CaseStatement& node )
104✔
206
{
207
  emit.debug_statementbegin();
104✔
208
  generate( node.expression() );
104✔
209
  update_debug_location( node );
104✔
210
  const unsigned casejmp = emit.casejmp();
104✔
211

212
  CaseJumpDataBlock data_block;
104✔
213

214
  FlowControlLabel default_label;
104✔
215

216
  auto& groups = node.dispatch_groups();
104✔
217
  for ( size_t i = 0, c = groups.children.size(); i < c; ++i )
440✔
218
  {
219
    CaseDispatchGroup& group = groups.dispatch_group( i );
336✔
220
    FlowControlLabel group_label;
336✔
221
    emit.label( group_label );
336✔
222
    generate( group.block() );
336✔
223

224
    emit.label( *group.break_label );
336✔
225
    bool last = i == c - 1;
336✔
226
    if ( !last )
336✔
227
      emit.jmp_always( *node.break_label );
232✔
228

229
    CaseDispatchGroupVisitor visitor( data_block, group_label, default_label );
336✔
230
    group.selectors().accept( visitor );
336✔
231
  }
336✔
232

233
  if ( !default_label.has_address() )
104✔
234
    emit.label( default_label );
46✔
235

236
  data_block.on_default_jump_to( static_cast<unsigned short>( default_label.address() ) );
104✔
237

238
  emit.label( *node.break_label );
104✔
239

240
  unsigned dispatch_table_data_offset = emit.case_dispatch_table( data_block );
104✔
241
  emitter.patch_offset( casejmp, dispatch_table_data_offset );
104✔
242
}
104✔
243

244
void InstructionGenerator::visit_cstyle_for_loop( CstyleForLoop& loop )
69✔
245
{
246
  emit.debug_statementbegin();
69✔
247
  update_debug_location( loop );
69✔
248
  generate( loop.initializer() );
69✔
249
  emit.consume();
69✔
250

251
  FlowControlLabel check_predicate;
69✔
252
  emit.label( check_predicate );
69✔
253
  generate( loop.predicate() );
69✔
254

255
  emit.jmp_if_false( *loop.break_label );
69✔
256

257
  generate( loop.block() );
69✔
258

259
  emit.label( *loop.continue_label );
69✔
260
  generate( loop.advancer() );
69✔
261
  emit.consume();
69✔
262

263
  emit.jmp_always( check_predicate );
69✔
264

265
  emit.label( *loop.break_label );
69✔
266
}
69✔
267

268
void InstructionGenerator::visit_binary_operator( BinaryOperator& node )
8,184✔
269
{
270
  visit_children( node );
8,184✔
271

272
  update_debug_location( node );
8,184✔
273
  emit.binary_operator( node.token_id );
8,184✔
274
}
8,184✔
275

276
void InstructionGenerator::visit_block( Block& node )
6,311✔
277
{
278
  DebugBlockGuard debug_block_guard( emitter, node.local_variable_scope_info );
6,311✔
279

280
  visit_children( node );
6,311✔
281

282
  if ( !node.local_variable_scope_info.variables.empty() )
6,311✔
283
  {
284
    emit.leaveblock( static_cast<unsigned>( node.local_variable_scope_info.variables.size() ) );
779✔
285
  }
286
}
6,311✔
287

288
void InstructionGenerator::visit_boolean_value( BooleanValue& node )
272✔
289
{
290
  update_debug_location( node );
272✔
291
  emit.value( node.value );
272✔
292
}
272✔
293

294
void InstructionGenerator::visit_branch_selector( BranchSelector& node )
4,141✔
295
{
296
  visit_children( node );
4,141✔
297

298
  switch ( node.branch_type )
4,141✔
299
  {
300
  case BranchSelector::IfTrue:
1,781✔
301
    emit.jmp_if_true( *node.flow_control_label );
1,781✔
302
    break;
1,781✔
303
  case BranchSelector::IfFalse:
2,360✔
304
    emit.jmp_if_false( *node.flow_control_label );
2,360✔
305
    break;
2,360✔
306
  case BranchSelector::Always:
×
307
    emit.jmp_always( *node.flow_control_label );
×
308
    break;
×
UNCOV
309
  case BranchSelector::Never:
×
UNCOV
310
    break;
×
311
  }
312
}
4,141✔
313

314
void InstructionGenerator::visit_class_instance( ClassInstance& node )
248✔
315
{
316
  update_debug_location( node );
248✔
317

318
  auto index =
319
      static_cast<unsigned>( class_declaration_indexes.at( node.class_declaration->name ) );
248✔
320

321
  emit.classinst_create( index );
248✔
322
}
248✔
323

324
void InstructionGenerator::visit_debug_statement_marker( DebugStatementMarker& marker )
×
325
{
UNCOV
326
  emit.debug_statementbegin();
×
327
  update_debug_location( marker );
×
328

UNCOV
329
  unsigned source_file = marker.source_location.source_file_identifier->index;
×
330
  emit.ctrl_statementbegin( source_file, marker.start_index, marker.text );
×
331

UNCOV
332
  visit_children( marker );
×
UNCOV
333
}
×
334

335
void InstructionGenerator::visit_dictionary_initializer( DictionaryInitializer& node )
172✔
336
{
337
  update_debug_location( node );
172✔
338
  emit.dictionary_create();
172✔
339
  visit_children( node );
172✔
340
}
172✔
341

342
void InstructionGenerator::visit_dictionary_entry( DictionaryEntry& entry )
154✔
343
{
344
  update_debug_location( entry );
154✔
345
  visit_children( entry );
154✔
346
  emit.dictionary_add_member();
154✔
347
}
154✔
348

349
void InstructionGenerator::visit_do_while_loop( DoWhileLoop& node )
11✔
350
{
351
  emit.debug_statementbegin();
11✔
352
  update_debug_location( node );
11✔
353
  FlowControlLabel next;
11✔
354
  emit.label( next );
11✔
355
  generate( node.block() );
11✔
356
  emit.label( *node.continue_label );
11✔
357

358
  generate( node.predicate() );
11✔
359
  emit.jmp_if_true( next );
11✔
360

361
  emit.label( *node.break_label );
11✔
362
}
11✔
363

364
void InstructionGenerator::visit_element_access( ElementAccess& acc )
2,004✔
365
{
366
  visit_children( acc );
2,004✔
367
  update_debug_location( acc );
2,004✔
368
  auto indexes = static_cast<unsigned>( acc.indexes().children.size() );
2,004✔
369
  if ( indexes == 1 )
2,004✔
370
    emit.subscript_single();
1,759✔
371
  else
372
    emit.subscript_multiple( indexes );
245✔
373
}
2,004✔
374

375
void InstructionGenerator::visit_element_assignment( ElementAssignment& node )
439✔
376
{
377
  visit_children( node );
439✔
378
  update_debug_location( node );
439✔
379
  auto num_indexes = static_cast<unsigned>( node.indexes().children.size() );
439✔
380
  if ( node.consume )
439✔
381
  {
382
    if ( num_indexes == 1 )
430✔
383
    {
384
      emit.assign_subscript_consume();
424✔
385
    }
386
    else
387
    {
388
      // there is no assign-multisubscript-consume instruction
389
      emit.assign_multisubscript( num_indexes );
6✔
390
      emit.consume();
6✔
391
    }
392
  }
393
  else
394
  {
395
    if ( num_indexes == 1 )
9✔
396
      emit.assign_subscript();
9✔
397
    else
UNCOV
398
      emit.assign_multisubscript( num_indexes );
×
399
  }
400
}
439✔
401

402
void InstructionGenerator::visit_elvis_operator( ElvisOperator& elvis )
205✔
403
{
404
  FlowControlLabel skip_instruction, after_rhs;
205✔
405

406
  update_debug_location( elvis );
205✔
407
  generate( elvis.lhs() );
205✔
408

409
  unsigned address = emit.skip_if_true_else_consume();
205✔
410

411
  unsigned skip_start_address = emitter.next_instruction_address();
205✔
412

413
  generate( elvis.rhs() );
205✔
414

415
  unsigned distance = emitter.next_instruction_address() - skip_start_address;
205✔
416

417
  emitter.patch_offset( address, distance );
205✔
418
}
205✔
419

420
void InstructionGenerator::visit_error_initializer( ErrorInitializer& node )
294✔
421
{
422
  update_debug_location( node );
294✔
423
  emit.error_create();
294✔
424
  int i = 0;
294✔
425
  for ( auto& child : node.children )
467✔
426
  {
427
    child->accept( *this );
173✔
428
    emit.struct_add_member( node.names[i++] );
173✔
429
  }
430
}
294✔
431

432
void InstructionGenerator::visit_exit_statement( ExitStatement& node )
18✔
433
{
434
  emit.debug_statementbegin();
18✔
435
  update_debug_location( node );
18✔
436
  emit.exit();
18✔
437
}
18✔
438

439
void InstructionGenerator::visit_float_value( FloatValue& node )
443✔
440
{
441
  update_debug_location( node );
443✔
442
  emit.value( node.value );
443✔
443
}
443✔
444

445
void InstructionGenerator::visit_foreach_loop( ForeachLoop& loop )
484✔
446
{
447
  emit.debug_statementbegin();
484✔
448
  update_debug_location( loop );
484✔
449

450
  generate( loop.expression() );
484✔
451

452
  DebugBlockGuard debug_block_guard( emitter, loop.local_variable_scope_info );
484✔
453

454
  emit.foreach_init( *loop.continue_label );
484✔
455

456
  FlowControlLabel next;
484✔
457
  emit.label( next );
484✔
458

459
  generate( loop.block() );
484✔
460

461
  emit.label( *loop.continue_label );
484✔
462
  emit.foreach_step( next );
484✔
463

464
  emit.label( *loop.break_label );
484✔
465
  emit.leaveblock( 3 );
484✔
466
}
484✔
467

468
void InstructionGenerator::visit_function_call( FunctionCall& call )
19,008✔
469
{
470
  // A function call will have no link if it is an expression call, eg. `(foo)(1,2)`.
471
  if ( !call.function_link->function() )
19,008✔
472
  {
473
    // Visiting the children emits the instructions for each arguments in the order necessary for a
474
    // `MTH_CALL`.
475
    visit_children( call );
450✔
476
    update_debug_location( call );
450✔
477
    // Subtract 1 because the first child is the callee.
478
    emit.call_method_id( MTH_CALL, static_cast<unsigned int>( call.children.size() - 1 ) );
450✔
479
    // Constructing array from rest arguments is done in executor for dynamic function calls.
480
  }
481
  else
482
  {
483
    auto emit_args = [&]( Function& function )
18,558✔
484
    {
485
      if ( function.is_variadic() )
18,558✔
486
      {
487
        auto num_nonrest_args = function.parameter_count() - 1;
243✔
488

489
        // Push real args, then create an array for the last rest arg.
490
        // All children for a non-expression-as-callee FunctionCalls are arguments.
491
        for ( unsigned i = 0; i < call.children.size(); ++i )
1,026✔
492
        {
493
          bool is_spread = dynamic_cast<SpreadElement*>( call.children[i].get() );
783✔
494

495
          if ( is_spread && i < num_nonrest_args )
783✔
496
          {
497
            // Should be caught by semantic analyzer
UNCOV
498
            call.internal_error( "spread operator used in location before rest arguments" );
×
499
            return;
500
          }
501

502
          // Create the array once we've reached the rest argument.
503
          if ( i == num_nonrest_args )
783✔
504
          {
505
            emit.array_create();
216✔
506
          }
507

508
          call.children[i]->accept( *this );
783✔
509

510
          if ( i >= num_nonrest_args )
783✔
511
          {
512
            // `ins_insert_into` for arrays will spread BSpread's at runtime.
513
            emit.array_append();
507✔
514
          }
515
        }
516

517
        // If there was no rest argument, create an empty array.
518
        if ( call.children.size() <= num_nonrest_args )
243✔
519
        {
520
          emit.array_create();
27✔
521
        }
522
      }
523
      else
524
      {
525
        // Cannot use spread operator in non-variadic function calls.
526
        visit_children( call );
18,315✔
527
      }
528

529
      // Update the debug location to the function call after emitting the
530
      // arguments.
531
      update_debug_location( call );
18,558✔
532
    };
18,558✔
533

534
    if ( auto mf = call.function_link->module_function_declaration() )
18,558✔
535
    {
536
      emit_args( *mf );
13,564✔
537
      emit.call_modulefunc( *mf );
13,564✔
538
    }
539
    else if ( auto uf = call.function_link->user_function() )
4,994✔
540
    {
541
      emit_args( *uf );
4,994✔
542
      FlowControlLabel& label = user_function_labels[uf->scoped_name()];
4,994✔
543
      // Emit the `check_mro` instruction if the current function is a generated
544
      // function (super or generated constructor).
545
      if ( !user_functions.empty() &&
8,336✔
546
           dynamic_cast<GeneratedFunction*>( user_functions.top() ) != nullptr )
3,342✔
547
      {
548
        if ( call.children.empty() )
237✔
549
        {
UNCOV
550
          call.internal_error( "super/ctor call missing 'this'" );
×
551
        }
552

553
        // Arg to cast can never be negative, since size is >= 1.
554
        auto classinst_offset = static_cast<unsigned>( call.children.size() - 1 );
237✔
555

556
        emit.check_mro( classinst_offset );
237✔
557
      }
558

559
      emit.makelocal();
4,994✔
560
      emit.call_userfunc( label );
4,994✔
561
    }
562
    else
563
    {
UNCOV
564
      call.internal_error( "neither a module function nor a user function?" );
×
565
    }
566
  }
567
}
19,008✔
568

569
void InstructionGenerator::visit_function_parameter_list( FunctionParameterList& node )
3,403✔
570
{
571
  for ( auto& child : node.children | std::views::reverse )
7,674✔
572
  {
573
    child->accept( *this );
4,271✔
574
  }
575
}
3,403✔
576

577
void InstructionGenerator::visit_function_parameter_declaration(
4,271✔
578
    FunctionParameterDeclaration& node )
579
{
580
  update_debug_location( node );
4,271✔
581
  if ( node.byref )
4,271✔
582
    emit.pop_param_byref( node.name.string() );
942✔
583
  else
584
    emit.pop_param( node.name.string() );
3,329✔
585
}
4,271✔
586

587
// The function expression generation emits the following instructions:
588
// - captured vars
589
// - captured vars count
590
// - parameter count
591
// - is variadic
592
// - create-functor <instructions count>
593
// - function instructions
594
//
595
// TODO maybe use a data area to encapsulate counts + variadic flag?
596
void InstructionGenerator::visit_function_expression( FunctionExpression& node )
671✔
597
{
598
  update_debug_location( node );
671✔
599
  if ( auto user_function = node.function_link->user_function() )
671✔
600
  {
601
    // Push the captured variables
602
    for ( const auto& variable : user_function->capture_variable_scope_info.variables )
1,091✔
603
    {
604
      emit_access_variable( *variable->capturing );
420✔
605
    }
606

607
    // Create a new FlowControlLabel via operator[] on user_function_labels. Its
608
    // address will be assigned when visiting the user function (below).
609
    auto& label = user_function_labels[user_function->scoped_name()];
671✔
610

611
    emit.functor_create( *user_function, label );
671✔
612
    auto index = emitter.next_instruction_address() - 1;
671✔
613

614
    user_function->accept( *this );
671✔
615

616
    auto instrs_count = emitter.next_instruction_address() - index - 1;
671✔
617

618
    // Ensure the capture count does not exceed maximum positive signed `int` size.
619
    if ( user_function->capture_count() >
671✔
620
         static_cast<unsigned int>( std::numeric_limits<int>::max() ) )
671✔
621
    {
UNCOV
622
      node.internal_error( "capture count too large" );
×
623
    }
624

625
    emit.patch_offset( index, instrs_count );
671✔
626
  }
627
  else
628
  {
UNCOV
629
    node.internal_error( "user function for function expression not found" );
×
630
  }
631
}
671✔
632

633
void InstructionGenerator::visit_function_reference( FunctionReference& function_reference )
759✔
634
{
635
  if ( auto uf = function_reference.function_link->user_function() )
759✔
636
  {
637
    update_debug_location( function_reference );
759✔
638
    FlowControlLabel& label = user_function_labels[uf->scoped_name()];
759✔
639
    emit.function_reference( *uf, label );
759✔
640
  }
641
  else
642
  {
UNCOV
643
    function_reference.internal_error( "user function not found" );
×
644
  }
645
}
759✔
646

647
void InstructionGenerator::visit_identifier( Identifier& node )
30,362✔
648
{
649
  update_debug_location( node );
30,362✔
650
  if ( auto var = node.variable )
60,724✔
651
  {
652
    emit_access_variable( *var );
30,362✔
653
  }
654
  else
655
  {
UNCOV
656
    node.internal_error( "variable is not set" );
×
657
  }
30,362✔
658
}
30,362✔
659

660
void InstructionGenerator::visit_if_then_else_statement( IfThenElseStatement& node )
4,141✔
661
{
662
  emit.debug_statementbegin();
4,141✔
663
  update_debug_location( node );
4,141✔
664

665
  auto branch_selector = &node.branch_selector();
4,141✔
666
  generate( *branch_selector );
4,141✔
667

668
  std::shared_ptr<FlowControlLabel> skip_consequent = branch_selector->flow_control_label;
4,141✔
669

670
  generate( node.consequent() );
4,141✔
671

672
  if ( auto alternative = node.alternative() )
4,141✔
673
  {
674
    FlowControlLabel skip_alternative;
688✔
675
    emit.jmp_always( skip_alternative );
688✔
676
    emit.label( *skip_consequent );
688✔
677
    generate( *alternative );
688✔
678
    emit.label( skip_alternative );
688✔
679
  }
688✔
680
  else
681
  {
682
    emit.label( *skip_consequent );
3,453✔
683
  }
684
}
4,141✔
685

686
void InstructionGenerator::visit_integer_value( IntegerValue& node )
17,214✔
687
{
688
  update_debug_location( node );
17,214✔
689
  emit.value( node.value );
17,214✔
690
}
17,214✔
691

692
void InstructionGenerator::visit_jump_statement( JumpStatement& jump )
655✔
693
{
694
  emit.debug_statementbegin();
655✔
695
  update_debug_location( jump );
655✔
696
  if ( jump.local_variables_to_remove )
655✔
697
    emit.leaveblock( jump.local_variables_to_remove );
374✔
698
  emit.jmp_always( *jump.flow_control_label );
655✔
699
}
655✔
700

701
void InstructionGenerator::visit_member_access( MemberAccess& member_access )
4,588✔
702
{
703
  visit_children( member_access );
4,588✔
704

705
  update_debug_location( member_access );
4,588✔
706
  if ( auto km = member_access.known_member )
4,588✔
707
    emit.get_member_id( km->id );
2,866✔
708
  else
709
    emit.get_member( member_access.name );
1,722✔
710
}
4,588✔
711

712
void InstructionGenerator::visit_member_assignment( MemberAssignment& node )
566✔
713
{
714
  visit_children( node );
566✔
715

716
  update_debug_location( node );
566✔
717
  if ( auto known_member = node.known_member )
566✔
718
  {
719
    if ( node.consume )
309✔
720
      emit.set_member_id_consume( known_member->id );
306✔
721
    else
722
      emit.set_member_id( known_member->id );
3✔
723
  }
724
  else
725
  {
726
    if ( node.consume )
257✔
727
      emit.set_member_consume( node.name );
254✔
728
    else
729
      emit.set_member( node.name );
3✔
730
  }
731
}
566✔
732

733
void InstructionGenerator::visit_member_assignment_by_operator( MemberAssignmentByOperator& node )
73✔
734
{
735
  visit_children( node );
73✔
736

737
  update_debug_location( node );
73✔
738
  emit.set_member_by_operator( node.token_id, node.known_member.id );
73✔
739
}
73✔
740

741
void InstructionGenerator::visit_index_binding( IndexBinding& node )
88✔
742
{
743
  // Emit indexes
744
  node.indexes().accept( *this );
88✔
745

746
  update_debug_location( node );
88✔
747
  emit.unpack_indices( node.binding_count(), node.rest_index );
88✔
748

749
  // Emit bindings
750
  for ( const auto& binding : node.bindings() )
300✔
751
  {
752
    binding.get().accept( *this );
212✔
753
  }
88✔
754
}
88✔
755

756
void InstructionGenerator::visit_method_call( MethodCall& method_call )
4,012✔
757
{
758
  visit_children( method_call );
4,012✔
759

760
  update_debug_location( method_call );
4,012✔
761
  auto argument_count = method_call.argument_count();
4,012✔
762
  if ( auto km = method_call.known_method )
4,012✔
763
  {
764
    emit.call_method_id( km->id, argument_count );
3,542✔
765
  }
766
  else
767
  {
768
    std::string method_name = method_call.methodname;
470✔
769
    Clib::mklowerASCII( method_name );
470✔
770
    emit.call_method( method_name, argument_count );
470✔
771
  }
470✔
772
}
4,012✔
773

774
void InstructionGenerator::visit_program( Program& program )
448✔
775
{
776
  DebugBlockGuard debug_block_guard( emitter, program.local_variable_scope_info );
448✔
777

778
  emit.debug_statementbegin();
448✔
779
  update_debug_location( program );
448✔
780

781
  visit_children( program );
448✔
782

783
  if ( !program.local_variable_scope_info.variables.empty() )
448✔
784
  {
785
    emit.leaveblock( static_cast<unsigned>( program.local_variable_scope_info.variables.size() ) );
295✔
786
  }
787
}
448✔
788

789
void InstructionGenerator::visit_program_parameter_declaration( ProgramParameterDeclaration& param )
202✔
790
{
791
  update_debug_location( param );
202✔
792
  emit.get_arg( param.name );
202✔
793
}
202✔
794

795
void InstructionGenerator::visit_regular_expression_value( RegularExpressionValue& lit )
150✔
796
{
797
  update_debug_location( lit );
150✔
798
  emit.regular_expression_value( lit.pattern, lit.flags );
150✔
799
}
150✔
800

801
void InstructionGenerator::visit_repeat_until_loop( RepeatUntilLoop& loop )
36✔
802
{
803
  emit.debug_statementbegin();
36✔
804
  update_debug_location( loop );
36✔
805

806
  FlowControlLabel top;
36✔
807

808
  emit.label( top );
36✔
809
  generate( loop.block() );
36✔
810
  emit.label( *loop.continue_label );
36✔
811

812
  generate( loop.expression() );
36✔
813
  emit.jmp_if_false( top );
36✔
814

815
  emit.label( *loop.break_label );
36✔
816
}
36✔
817

818
void InstructionGenerator::visit_return_statement( ReturnStatement& ret )
5,075✔
819
{
820
  auto user_function = user_functions.empty() ? nullptr : user_functions.top();
5,075✔
821

822
  if ( user_function && user_function->type == UserFunctionType::Constructor )
5,075✔
823
  {
824
    // Semantic analyzer will ensure a return statement in a constructor does not have any
825
    // children, but lets be sure.
826
    if ( !ret.children.empty() )
3✔
827
    {
UNCOV
828
      ret.internal_error( "return statement in constructor should not have children" );
×
829
    }
830

831
    // This emitter method also emits the `this` variable based off parameter offset.
832
    emit.return_from_constructor_function( user_function->parameter_count() - 1 );
3✔
833
  }
834
  else
835
  {
836
    emit.debug_statementbegin();
5,072✔
837

838
    visit_children( ret );
5,072✔
839

840
    update_debug_location( ret );
5,072✔
841
    if ( user_function )
5,072✔
842
    {
843
      emit.return_from_user_function();
4,760✔
844
    }
845
    else
846
    {
847
      emit.progend();
312✔
848
    }
849
  }
850
}
5,075✔
851

852
void InstructionGenerator::visit_spread_element( SpreadElement& node )
671✔
853
{
854
  visit_children( node );
671✔
855
  update_debug_location( node );
671✔
856
  emit.spread( node.spread_into );
671✔
857
}
671✔
858

859
void InstructionGenerator::visit_string_value( StringValue& lit )
27,427✔
860
{
861
  update_debug_location( lit );
27,427✔
862
  emit.value( lit.value );
27,427✔
863
}
27,427✔
864

865
void InstructionGenerator::visit_struct_initializer( StructInitializer& node )
894✔
866
{
867
  update_debug_location( node );
894✔
868
  emit.struct_create();
894✔
869
  visit_children( node );
894✔
870
}
894✔
871

872
void InstructionGenerator::visit_struct_member_initializer( StructMemberInitializer& node )
1,923✔
873
{
874
  visit_children( node );
1,923✔
875

876
  update_debug_location( node );
1,923✔
877
  if ( node.children.empty() )
1,923✔
878
    emit.struct_add_uninit_member( node.name );
45✔
879
  else
880
    emit.struct_add_member( node.name );
1,878✔
881
}
1,923✔
882

883
void InstructionGenerator::visit_unary_operator( UnaryOperator& unary_operator )
613✔
884
{
885
  visit_children( unary_operator );
613✔
886
  emit.unary_operator( unary_operator.token_id );
613✔
887
}
613✔
888

889
void InstructionGenerator::visit_uninitialized_value( UninitializedValue& node )
114✔
890
{
891
  update_debug_location( node );
114✔
892
  emit.uninit();
114✔
893
}
114✔
894

895
void InstructionGenerator::visit_user_function( UserFunction& user_function )
3,403✔
896
{
897
  user_functions.push( &user_function );
3,403✔
898
  unsigned first_instruction_address = emitter.next_instruction_address();
3,403✔
899
  DebugBlockGuard debug_block_guard( emitter, user_function.local_variable_scope_info );
3,403✔
900

901
  emit.debug_statementbegin();
3,403✔
902
  update_debug_location( user_function );
3,403✔
903
  if ( user_function.exported )
3,403✔
904
  {
905
    // emit the exported entry stub
906
    FlowControlLabel exported_entrypoint, internal_entrypoint;
607✔
907

908
    emit.label( exported_entrypoint );
607✔
909
    emit.makelocal();
607✔
910
    emit.call_userfunc( internal_entrypoint );
607✔
911
    emit.progend();
607✔
912

913
    emit.label( internal_entrypoint );
607✔
914
    emitter.register_exported_function( exported_entrypoint, user_function.name,
607✔
915
                                        user_function.parameter_count() );
916
  }
607✔
917

918
  FlowControlLabel& label = user_function_labels[user_function.scoped_name()];
3,403✔
919
  emit.label( label );
3,403✔
920

921
  // Pop function parameters
922
  user_function.child<FunctionParameterList>( 0 ).accept( *this );
3,403✔
923

924
  // Pop caputured variables
925
  for ( const auto& variable : user_function.capture_variable_scope_info.variables )
3,823✔
926
  {
927
    emit.pop_param_byref( variable->name );
420✔
928
  }
929

930
  user_function.body().accept( *this );
3,403✔
931

932
  if ( !dynamic_cast<ReturnStatement*>( user_function.body().last_statement() ) )
3,403✔
933
  {
934
    emit.debug_statementbegin();
1,398✔
935
    update_debug_location( user_function.endfunction_location );
1,398✔
936
    if ( user_function.type == UserFunctionType::Constructor )
1,398✔
937
    {
938
      emit.return_from_constructor_function( user_function.parameter_count() - 1 );
407✔
939
    }
940
    else
941
    {
942
      emit.value( 0 );
991✔
943
      emit.return_from_user_function();
991✔
944
    }
945
  }
946
  unsigned last_instruction_address = emitter.next_instruction_address() - 1;
3,403✔
947
  emitter.debug_user_function( user_function.name, first_instruction_address,
3,403✔
948
                               last_instruction_address );
949
  user_functions.pop();
3,403✔
950
}
3,403✔
951

952
void InstructionGenerator::visit_sequence_binding( SequenceBinding& node )
109✔
953
{
954
  update_debug_location( node );
109✔
955

956
  emit.unpack_sequence( node.binding_count(), node.rest_index );
109✔
957
  visit_children( node );
109✔
958
}
109✔
959

960
void InstructionGenerator::visit_value_consumer( ValueConsumer& node )
14,343✔
961
{
962
  emitter.debug_statementbegin();
14,343✔
963
  update_debug_location( node );
14,343✔
964
  visit_children( node );
14,343✔
965

966
  emit.consume();
14,343✔
967
}
14,343✔
968

969
void InstructionGenerator::visit_var_statement( VarStatement& node )
6,031✔
970
{
971
  emit.debug_statementbegin();
6,031✔
972
  update_debug_location( node );
6,031✔
973
  if ( !node.variable )
12,062✔
UNCOV
974
    node.internal_error( "variable is not defined" );
×
975

976
  int function_capture_count = 0;
6,031✔
977

978
  if ( !user_functions.empty() )
6,031✔
979
  {
980
    function_capture_count = user_functions.top()->capture_count();
3,061✔
981
  }
982

983
  emit.declare_variable( *node.variable, function_capture_count, false );
6,031✔
984

985
  if ( node.initialize_as_empty_array )
6,031✔
986
  {
987
    emit.array_declare();
69✔
988
  }
989
  else if ( !node.children.empty() )
5,962✔
990
  {
991
    visit_children( node );
5,147✔
992

993
    emit.assign();
5,147✔
994
  }
995

996
  emit.consume();
6,031✔
997
}
6,031✔
998

999
void InstructionGenerator::visit_variable_assignment_statement( VariableAssignmentStatement& node )
2,122✔
1000
{
1001
  emitter.debug_statementbegin();
2,122✔
1002
  generate( node.rhs() );
2,122✔
1003
  update_debug_location( node );
2,122✔
1004
  auto& identifier = node.identifier();
2,122✔
1005
  auto& variable = identifier.variable;
2,122✔
1006

1007
  int function_params_count = 0;
2,122✔
1008
  int function_capture_count = 0;
2,122✔
1009

1010
  if ( !user_functions.empty() )
2,122✔
1011
  {
1012
    function_params_count = user_functions.top()->parameter_count();
1,161✔
1013
    function_capture_count = user_functions.top()->capture_count();
1,161✔
1014
  }
1015

1016
  emit.assign_variable( *variable, function_params_count, function_capture_count );
2,122✔
1017
}
2,122✔
1018

1019
void InstructionGenerator::visit_while_loop( WhileLoop& loop )
221✔
1020
{
1021
  emit.debug_statementbegin();
221✔
1022
  update_debug_location( loop );
221✔
1023
  emit.label( *loop.continue_label );
221✔
1024

1025
  generate( loop.predicate() );
221✔
1026
  emit.jmp_if_false( *loop.break_label );
221✔
1027

1028
  generate( loop.block() );
221✔
1029
  emit.jmp_always( *loop.continue_label );
221✔
1030
  emit.label( *loop.break_label );
221✔
1031
}
221✔
1032

1033
void InstructionGenerator::visit_variable_binding( VariableBinding& node )
498✔
1034
{
1035
  update_debug_location( node );
498✔
1036

1037
  if ( !node.variable )
996✔
UNCOV
1038
    node.internal_error( "variable is not defined" );
×
1039

1040
  int function_capture_count = 0;
498✔
1041

1042
  if ( !user_functions.empty() )
498✔
1043
  {
1044
    function_capture_count = user_functions.top()->capture_count();
36✔
1045
  }
1046
  emit.declare_variable( *node.variable, function_capture_count, true );
498✔
1047
}
498✔
1048

1049
void InstructionGenerator::visit_interpolate_string( InterpolateString& node )
2,456✔
1050
{
1051
  visit_children( node );
2,456✔
1052

1053
  update_debug_location( node );
2,456✔
1054
  emit.interpolate_string( static_cast<unsigned>( node.children.size() ) );
2,456✔
1055
}
2,456✔
1056

1057
void InstructionGenerator::visit_format_expression( FormatExpression& node )
110✔
1058
{
1059
  visit_children( node );
110✔
1060

1061
  update_debug_location( node );
110✔
1062
  emit.format_expression();
110✔
1063
}
110✔
1064

1065
void InstructionGenerator::visit_conditional_operator( ConditionalOperator& node )
105✔
1066
{
1067
  update_debug_location( node );
105✔
1068

1069
  // generate conditional
1070
  generate( node.conditional() );
105✔
1071
  // consume+jump to end-of-consequent label if false
1072
  emit.jmp_if_false( *node.consequent_label );
105✔
1073
  // generate consequent
1074
  generate( node.consequent() );
105✔
1075
  // jump to end-of-alternate label
1076
  emit.jmp_always( *node.alternate_label );
105✔
1077
  // end-of-consequent label
1078
  emit.label( *node.consequent_label );
105✔
1079
  // generate alternate
1080
  generate( node.alternate() );
105✔
1081
  // end-of-alternate label
1082
  emit.label( *node.alternate_label );
105✔
1083
}
105✔
1084

1085
void InstructionGenerator::emit_access_variable( Variable& variable )
30,782✔
1086
{
1087
  int function_params_count = 0;
30,782✔
1088
  int function_capture_count = 0;
30,782✔
1089

1090
  if ( !user_functions.empty() )
30,782✔
1091
  {
1092
    function_params_count = user_functions.top()->parameter_count();
20,652✔
1093
    function_capture_count = user_functions.top()->capture_count();
20,652✔
1094
  }
1095

1096
  emit.access_variable( variable, function_params_count, function_capture_count );
30,782✔
1097
}
30,782✔
1098

1099
void InstructionGenerator::visit_constant_loop( ConstantPredicateLoop& loop )
234✔
1100
{
1101
  emit.debug_statementbegin();
234✔
1102
  update_debug_location( loop );
234✔
1103

1104
  emit.label( *loop.continue_label );
234✔
1105
  generate( loop.block() );
234✔
1106
  if ( loop.is_endless() )
234✔
1107
    emit.jmp_always( *loop.continue_label );
208✔
1108
  // else will fall through and does not loop
1109
  emit.label( *loop.break_label );
234✔
1110
}
234✔
1111

1112
void InstructionGenerator::visit_binary_operator_short_circuit( BinaryOperatorShortCircuit& op )
63✔
1113
{
1114
  emit.debug_statementbegin();
63✔
1115
  update_debug_location( op );
63✔
1116
  generate( op.lhs() );
63✔
1117

1118
  emit.logical_jmp( op.linked_jmp_label ? *op.linked_jmp_label->jmp_label : *op.end_label,
63✔
1119
                    op.oper == ShortCircuitOp::OR );
63✔
1120
  generate( op.rhs() );
63✔
1121
  // dont emit convert if the rhs oper is also a ShortCircuit which generated already a convert, or
1122
  // the parent
1123
  if ( op.generate_logical_convert )
63✔
1124
    emit.logical_convert();
47✔
1125
  emit.label( *op.end_label );
63✔
1126
}
63✔
1127

1128
}  // namespace Pol::Bscript::Compiler
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