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

polserver / polserver / 13378462461

17 Feb 2025 08:59PM UTC coverage: 58.754% (+0.3%) from 58.475%
13378462461

push

github

web-flow
Add support for sequence and index bindings (#760)

* update grammar

* add ast nodes; ast building

* Rename to unpacking

* semantic analysis

* executor work part 1, unpacking indices

* renamings; implement index binding

* small cleanup

* use multi_index; more tests

* use multi_index only for rest, otherwise list

* initial formatting

* add missing token decoding

* address self-review comments

* formatting tweaks

* add test for var binding in classes

* add StringIterator

* fix spread tests

* add cfgfile iterator; add cfgelem opersubscript; tests

* add iterator for SQLResultSet and SQLRow; tests

* Copy value in take global/local

* Allow any iterable can index rest unpacking
Always use dictionary as rest object in index unpacking

* add docs, core-changes, doc tests

* formatting changes...

* address self-review comments

* update formatter, format all binding test srcs

* address review comments
- unset var scope

* add cfgfile/cfgelem docs

* reformat objref svg

501 of 550 new or added lines in 19 files covered. (91.09%)

14 existing lines in 3 files now uncovered.

42235 of 71885 relevant lines covered (58.75%)

377989.47 hits per line

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

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

3
#include <boost/range/adaptor/reversed.hpp>
4
#include <boost/range/adaptor/sliced.hpp>
5

6
#include "bscript/compiler/ast/ArrayInitializer.h"
7
#include "bscript/compiler/ast/BasicForLoop.h"
8
#include "bscript/compiler/ast/BinaryOperator.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/CstyleForLoop.h"
21
#include "bscript/compiler/ast/DebugStatementMarker.h"
22
#include "bscript/compiler/ast/DictionaryEntry.h"
23
#include "bscript/compiler/ast/DictionaryInitializer.h"
24
#include "bscript/compiler/ast/DoWhileLoop.h"
25
#include "bscript/compiler/ast/ElementAccess.h"
26
#include "bscript/compiler/ast/ElementAssignment.h"
27
#include "bscript/compiler/ast/ElementIndexes.h"
28
#include "bscript/compiler/ast/ElvisOperator.h"
29
#include "bscript/compiler/ast/ErrorInitializer.h"
30
#include "bscript/compiler/ast/ExitStatement.h"
31
#include "bscript/compiler/ast/FloatValue.h"
32
#include "bscript/compiler/ast/ForeachLoop.h"
33
#include "bscript/compiler/ast/FormatExpression.h"
34
#include "bscript/compiler/ast/FunctionBody.h"
35
#include "bscript/compiler/ast/FunctionCall.h"
36
#include "bscript/compiler/ast/FunctionExpression.h"
37
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
38
#include "bscript/compiler/ast/FunctionParameterList.h"
39
#include "bscript/compiler/ast/FunctionReference.h"
40
#include "bscript/compiler/ast/GeneratedFunction.h"
41
#include "bscript/compiler/ast/Identifier.h"
42
#include "bscript/compiler/ast/IfThenElseStatement.h"
43
#include "bscript/compiler/ast/IndexBinding.h"
44
#include "bscript/compiler/ast/IntegerValue.h"
45
#include "bscript/compiler/ast/InterpolateString.h"
46
#include "bscript/compiler/ast/JumpStatement.h"
47
#include "bscript/compiler/ast/MemberAccess.h"
48
#include "bscript/compiler/ast/MemberAssignment.h"
49
#include "bscript/compiler/ast/MemberAssignmentByOperator.h"
50
#include "bscript/compiler/ast/MethodCall.h"
51
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
52
#include "bscript/compiler/ast/Program.h"
53
#include "bscript/compiler/ast/ProgramParameterDeclaration.h"
54
#include "bscript/compiler/ast/RepeatUntilLoop.h"
55
#include "bscript/compiler/ast/ReturnStatement.h"
56
#include "bscript/compiler/ast/SequenceBinding.h"
57
#include "bscript/compiler/ast/SpreadElement.h"
58
#include "bscript/compiler/ast/StringValue.h"
59
#include "bscript/compiler/ast/StructInitializer.h"
60
#include "bscript/compiler/ast/StructMemberInitializer.h"
61
#include "bscript/compiler/ast/UnaryOperator.h"
62
#include "bscript/compiler/ast/UninitializedValue.h"
63
#include "bscript/compiler/ast/UserFunction.h"
64
#include "bscript/compiler/ast/ValueConsumer.h"
65
#include "bscript/compiler/ast/VarStatement.h"
66
#include "bscript/compiler/ast/VariableAssignmentStatement.h"
67
#include "bscript/compiler/ast/VariableBinding.h"
68
#include "bscript/compiler/ast/WhileLoop.h"
69
#include "bscript/compiler/codegen/CaseDispatchGroupVisitor.h"
70
#include "bscript/compiler/codegen/CaseJumpDataBlock.h"
71
#include "bscript/compiler/codegen/DebugBlockGuard.h"
72
#include "bscript/compiler/codegen/InstructionEmitter.h"
73
#include "bscript/compiler/file/SourceFileIdentifier.h"
74
#include "bscript/compiler/model/FlowControlLabel.h"
75
#include "bscript/compiler/model/FunctionLink.h"
76
#include "bscript/compiler/model/Variable.h"
77
#include "symcont.h"
78

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

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

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

108
    if ( !label.has_address() )
544✔
109
    {
110
      user_function.internal_error( "function reference label not set" );
×
111
    }
112

113
    std::vector<std::reference_wrapper<FunctionParameterDeclaration>> params;
544✔
114
    user_function.child<FunctionParameterList>( 0 ).get_children( params );
544✔
115

116
    // Track if the function has any default parameters.
117
    bool any_default_params = false;
544✔
118

119
    // Emit the default arguments for parameters in declaration order.
120
    for ( auto& param_ref : params )
1,322✔
121
    {
122
      auto& param = param_ref.get();
778✔
123
      auto default_value = param.default_value();
778✔
124

125
      if ( default_value || param.rest )
778✔
126
      {
127
        auto label_name =
128
            FlowControlLabel::label_for_user_function_default_argument( user_function, param );
114✔
129

130
        unsigned param_address = emitter.next_instruction_address();
114✔
131

132
        user_function_labels[label_name].assign_address( param_address );
114✔
133

134
        if ( default_value )
114✔
135
        {
136
          default_value->accept( *this );
52✔
137
        }
138
        else  // a rest param
139
        {
140
          emit.array_create();
62✔
141
        }
142

143
        any_default_params = true;
114✔
144
      }
114✔
145
    }
146

147
    // Jump to the actual user function if there are default parameters. Once
148
    // hitting the jump, the user function's pop params will be called.
149
    if ( any_default_params )
544✔
150
    {
151
      emit.jmp_always( label );
86✔
152
    }
153
  }
544✔
154
}
2,000✔
155

156
void InstructionGenerator::update_debug_location( const Node& node )
120,737✔
157
{
158
  update_debug_location( node.source_location );
120,737✔
159
}
120,737✔
160

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

167
void InstructionGenerator::visit_array_initializer( ArrayInitializer& node )
1,776✔
168
{
169
  update_debug_location( node );
1,776✔
170
  emit.array_create();
1,776✔
171
  for ( const auto& child : node.children )
5,888✔
172
  {
173
    child->accept( *this );
4,112✔
174
    emit.array_append();
4,112✔
175
  }
176
}
1,776✔
177

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

184
  generate( loop.first() );
52✔
185
  generate( loop.last() );
52✔
186

187
  DebugBlockGuard debug_block_guard( emitter, loop.local_variable_scope_info );
52✔
188

189
  emit.basic_for_init( skip );
52✔
190

191
  emit.label( next );
52✔
192
  generate( loop.block() );
52✔
193

194
  emit.label( *loop.continue_label );
52✔
195
  emit.basic_for_next( next );
52✔
196

197
  emit.label( *loop.break_label );
52✔
198
  emit.leaveblock( 2 );
52✔
199

200
  emit.label( skip );
52✔
201
}
52✔
202

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

210
  CaseJumpDataBlock data_block;
72✔
211

212
  FlowControlLabel default_label;
72✔
213

214
  auto& groups = node.dispatch_groups();
72✔
215
  for ( size_t i = 0, c = groups.children.size(); i < c; ++i )
300✔
216
  {
217
    CaseDispatchGroup& group = groups.dispatch_group( i );
228✔
218
    FlowControlLabel group_label;
228✔
219
    emit.label( group_label );
228✔
220
    generate( group.block() );
228✔
221

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

227
    CaseDispatchGroupVisitor visitor( data_block, group_label, default_label );
228✔
228
    group.selectors().accept( visitor );
228✔
229
  }
228✔
230

231
  if ( !default_label.has_address() )
72✔
232
    emit.label( default_label );
32✔
233

234
  data_block.on_default_jump_to( static_cast<unsigned short>( default_label.address() ) );
72✔
235

236
  emit.label( *node.break_label );
72✔
237

238
  unsigned dispatch_table_data_offset = emit.case_dispatch_table( data_block );
72✔
239
  emitter.patch_offset( casejmp, dispatch_table_data_offset );
72✔
240
}
72✔
241

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

249
  FlowControlLabel check_predicate;
44✔
250
  emit.label( check_predicate );
44✔
251
  generate( loop.predicate() );
44✔
252

253
  emit.jmp_if_false( *loop.break_label );
44✔
254

255
  generate( loop.block() );
44✔
256

257
  emit.label( *loop.continue_label );
44✔
258
  generate( loop.advancer() );
44✔
259
  emit.consume();
44✔
260

261
  emit.jmp_always( check_predicate );
44✔
262

263
  emit.label( *loop.break_label );
44✔
264
}
44✔
265

266
void InstructionGenerator::visit_binary_operator( BinaryOperator& node )
5,624✔
267
{
268
  visit_children( node );
5,624✔
269

270
  update_debug_location( node );
5,624✔
271
  emit.binary_operator( node.token_id );
5,624✔
272
}
5,624✔
273

274
void InstructionGenerator::visit_block( Block& node )
4,446✔
275
{
276
  DebugBlockGuard debug_block_guard( emitter, node.local_variable_scope_info );
4,446✔
277

278
  visit_children( node );
4,446✔
279

280
  if ( !node.local_variable_scope_info.variables.empty() )
4,446✔
281
  {
282
    emit.leaveblock( static_cast<unsigned>( node.local_variable_scope_info.variables.size() ) );
598✔
283
  }
284
}
4,446✔
285

286
void InstructionGenerator::visit_boolean_value( BooleanValue& node )
110✔
287
{
288
  update_debug_location( node );
110✔
289
  emit.value( node.value );
110✔
290
}
110✔
291

292
void InstructionGenerator::visit_branch_selector( BranchSelector& node )
2,986✔
293
{
294
  visit_children( node );
2,986✔
295

296
  switch ( node.branch_type )
2,986✔
297
  {
298
  case BranchSelector::IfTrue:
1,320✔
299
    emit.jmp_if_true( *node.flow_control_label );
1,320✔
300
    break;
1,320✔
301
  case BranchSelector::IfFalse:
1,666✔
302
    emit.jmp_if_false( *node.flow_control_label );
1,666✔
303
    break;
1,666✔
304
  case BranchSelector::Always:
×
305
    emit.jmp_always( *node.flow_control_label );
×
306
    break;
×
307
  case BranchSelector::Never:
×
308
    break;
×
309
  }
310
}
2,986✔
311

312
void InstructionGenerator::visit_class_instance( ClassInstance& node )
126✔
313
{
314
  update_debug_location( node );
126✔
315

316
  auto index =
317
      static_cast<unsigned>( class_declaration_indexes.at( node.class_declaration->name ) );
126✔
318

319
  emit.classinst_create( index );
126✔
320
}
126✔
321

322
void InstructionGenerator::visit_debug_statement_marker( DebugStatementMarker& marker )
×
323
{
324
  emit.debug_statementbegin();
×
325
  update_debug_location( marker );
×
326

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

330
  visit_children( marker );
×
331
}
×
332

333
void InstructionGenerator::visit_dictionary_initializer( DictionaryInitializer& node )
96✔
334
{
335
  update_debug_location( node );
96✔
336
  emit.dictionary_create();
96✔
337
  visit_children( node );
96✔
338
}
96✔
339

340
void InstructionGenerator::visit_dictionary_entry( DictionaryEntry& entry )
84✔
341
{
342
  update_debug_location( entry );
84✔
343
  visit_children( entry );
84✔
344
  emit.dictionary_add_member();
84✔
345
}
84✔
346

347
void InstructionGenerator::visit_do_while_loop( DoWhileLoop& node )
28✔
348
{
349
  emit.debug_statementbegin();
28✔
350
  update_debug_location( node );
28✔
351
  FlowControlLabel next;
28✔
352
  emit.label( next );
28✔
353
  generate( node.block() );
28✔
354
  emit.label( *node.continue_label );
28✔
355
  generate( node.predicate() );
28✔
356
  emit.jmp_if_true( next );
28✔
357
  emit.label( *node.break_label );
28✔
358
}
28✔
359

360
void InstructionGenerator::visit_element_access( ElementAccess& acc )
1,208✔
361
{
362
  visit_children( acc );
1,208✔
363
  update_debug_location( acc );
1,208✔
364
  auto indexes = static_cast<unsigned>( acc.indexes().children.size() );
1,208✔
365
  if ( indexes == 1 )
1,208✔
366
    emit.subscript_single();
1,120✔
367
  else
368
    emit.subscript_multiple( indexes );
88✔
369
}
1,208✔
370

371
void InstructionGenerator::visit_element_assignment( ElementAssignment& node )
288✔
372
{
373
  visit_children( node );
288✔
374
  update_debug_location( node );
288✔
375
  auto num_indexes = static_cast<unsigned>( node.indexes().children.size() );
288✔
376
  if ( node.consume )
288✔
377
  {
378
    if ( num_indexes == 1 )
288✔
379
    {
380
      emit.assign_subscript_consume();
284✔
381
    }
382
    else
383
    {
384
      // there is no assign-multisubscript-consume instruction
385
      emit.assign_multisubscript( num_indexes );
4✔
386
      emit.consume();
4✔
387
    }
388
  }
389
  else
390
  {
391
    if ( num_indexes == 1 )
×
392
      emit.assign_subscript();
×
393
    else
394
      emit.assign_multisubscript( num_indexes );
×
395
  }
396
}
288✔
397

398
void InstructionGenerator::visit_elvis_operator( ElvisOperator& elvis )
108✔
399
{
400
  FlowControlLabel skip_instruction, after_rhs;
108✔
401

402
  update_debug_location( elvis );
108✔
403
  generate( elvis.lhs() );
108✔
404

405
  unsigned address = emit.skip_if_true_else_consume();
108✔
406

407
  unsigned skip_start_address = emitter.next_instruction_address();
108✔
408

409
  generate( elvis.rhs() );
108✔
410

411
  unsigned distance = emitter.next_instruction_address() - skip_start_address;
108✔
412

413
  emitter.patch_offset( address, distance );
108✔
414
}
108✔
415

416
void InstructionGenerator::visit_error_initializer( ErrorInitializer& node )
244✔
417
{
418
  update_debug_location( node );
244✔
419
  emit.error_create();
244✔
420
  int i = 0;
244✔
421
  for ( auto& child : node.children )
400✔
422
  {
423
    child->accept( *this );
156✔
424
    emit.struct_add_member( node.names[i++] );
156✔
425
  }
426
}
244✔
427

428
void InstructionGenerator::visit_exit_statement( ExitStatement& node )
12✔
429
{
430
  emit.debug_statementbegin();
12✔
431
  update_debug_location( node );
12✔
432
  emit.exit();
12✔
433
}
12✔
434

435
void InstructionGenerator::visit_float_value( FloatValue& node )
248✔
436
{
437
  update_debug_location( node );
248✔
438
  emit.value( node.value );
248✔
439
}
248✔
440

441
void InstructionGenerator::visit_foreach_loop( ForeachLoop& loop )
350✔
442
{
443
  emit.debug_statementbegin();
350✔
444
  update_debug_location( loop );
350✔
445

446
  generate( loop.expression() );
350✔
447

448
  DebugBlockGuard debug_block_guard( emitter, loop.local_variable_scope_info );
350✔
449

450
  emit.foreach_init( *loop.continue_label );
350✔
451

452
  FlowControlLabel next;
350✔
453
  emit.label( next );
350✔
454

455
  generate( loop.block() );
350✔
456

457
  emit.label( *loop.continue_label );
350✔
458
  emit.foreach_step( next );
350✔
459

460
  emit.label( *loop.break_label );
350✔
461
  emit.leaveblock( 3 );
350✔
462
}
350✔
463

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

485
        // Push real args, then create an array for the last rest arg.
486
        // All children for a non-expression-as-callee FunctionCalls are arguments.
487
        for ( unsigned i = 0; i < call.children.size(); ++i )
684✔
488
        {
489
          bool is_spread = dynamic_cast<SpreadElement*>( call.children[i].get() );
522✔
490

491
          if ( is_spread && i < num_nonrest_args )
522✔
492
          {
493
            // Should be caught by semantic analyzer
494
            call.internal_error( "spread operator used in location before rest arguments" );
×
495
            return;
496
          }
497

498
          // Create the array once we've reached the rest argument.
499
          if ( i == num_nonrest_args )
522✔
500
          {
501
            emit.array_create();
144✔
502
          }
503

504
          call.children[i]->accept( *this );
522✔
505

506
          if ( i >= num_nonrest_args )
522✔
507
          {
508
            // `ins_insert_into` for arrays will spread BSpread's at runtime.
509
            emit.array_append();
338✔
510
          }
511
        }
512

513
        // If there was no rest argument, create an empty array.
514
        if ( call.children.size() <= num_nonrest_args )
162✔
515
        {
516
          emit.array_create();
18✔
517
        }
518
      }
519
      else
520
      {
521
        // Cannot use spread operator in non-variadic function calls.
522
        visit_children( call );
12,211✔
523
      }
524

525
      // Update the debug location to the function call after emitting the
526
      // arguments.
527
      update_debug_location( call );
12,373✔
528
    };
12,373✔
529

530
    if ( auto mf = call.function_link->module_function_declaration() )
12,373✔
531
    {
532
      emit_args( *mf );
8,757✔
533
      emit.call_modulefunc( *mf );
8,757✔
534
    }
535
    else if ( auto uf = call.function_link->user_function() )
3,616✔
536
    {
537
      emit_args( *uf );
3,616✔
538
      FlowControlLabel& label = user_function_labels[uf->scoped_name()];
3,616✔
539
      // Emit the `check_mro` instruction if the current function is a generated
540
      // function (super or generated constructor).
541
      if ( !user_functions.empty() &&
6,174✔
542
           dynamic_cast<GeneratedFunction*>( user_functions.top() ) != nullptr )
2,558✔
543
      {
544
        if ( call.children.empty() )
158✔
545
        {
546
          call.internal_error( "super/ctor call missing 'this'" );
×
547
        }
548

549
        // Arg to cast can never be negative, since size is >= 1.
550
        auto classinst_offset = static_cast<unsigned>( call.children.size() - 1 );
158✔
551

552
        emit.check_mro( classinst_offset );
158✔
553
      }
554

555
      emit.makelocal();
3,616✔
556
      emit.call_userfunc( label );
3,616✔
557
    }
558
    else
559
    {
560
      call.internal_error( "neither a module function nor a user function?" );
×
561
    }
562
  }
563
}
12,675✔
564

565
void InstructionGenerator::visit_function_parameter_list( FunctionParameterList& node )
2,000✔
566
{
567
  for ( auto& child : boost::adaptors::reverse( node.children ) )
6,692✔
568
  {
569
    child->accept( *this );
2,346✔
570
  }
571
}
2,000✔
572

573
void InstructionGenerator::visit_function_parameter_declaration(
2,346✔
574
    FunctionParameterDeclaration& node )
575
{
576
  update_debug_location( node );
2,346✔
577
  if ( node.byref )
2,346✔
578
    emit.pop_param_byref( node.name.string() );
540✔
579
  else
580
    emit.pop_param( node.name.string() );
1,806✔
581
}
2,346✔
582

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

603
    // Create a new FlowControlLabel via operator[] on user_function_labels. Its
604
    // address will be assigned when visiting the user function (below).
605
    auto& label = user_function_labels[user_function->scoped_name()];
218✔
606

607
    emit.functor_create( *user_function, label );
218✔
608
    auto index = emitter.next_instruction_address() - 1;
218✔
609

610
    user_function->accept( *this );
218✔
611

612
    auto instrs_count = emitter.next_instruction_address() - index - 1;
218✔
613

614
    // Ensure the capture count does not exceed maximum positive signed `int` size.
615
    if ( user_function->capture_count() >
218✔
616
         static_cast<unsigned int>( std::numeric_limits<int>::max() ) )
218✔
617
    {
618
      node.internal_error( "capture count too large" );
×
619
    }
620

621
    emit.patch_offset( index, instrs_count );
218✔
622
  }
623
  else
624
  {
625
    node.internal_error( "user function for function expression not found" );
×
626
  }
627
}
218✔
628

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

643
void InstructionGenerator::visit_identifier( Identifier& node )
20,008✔
644
{
645
  update_debug_location( node );
20,008✔
646
  if ( auto var = node.variable )
20,008✔
647
  {
648
    emit_access_variable( *var );
20,008✔
649
  }
650
  else
651
  {
652
    node.internal_error( "variable is not set" );
×
653
  }
20,008✔
654
}
20,008✔
655

656
void InstructionGenerator::visit_if_then_else_statement( IfThenElseStatement& node )
2,986✔
657
{
658
  emit.debug_statementbegin();
2,986✔
659
  update_debug_location( node );
2,986✔
660

661
  auto branch_selector = &node.branch_selector();
2,986✔
662
  generate( *branch_selector );
2,986✔
663

664
  std::shared_ptr<FlowControlLabel> skip_consequent = branch_selector->flow_control_label;
2,986✔
665

666
  generate( node.consequent() );
2,986✔
667

668
  if ( auto alternative = node.alternative() )
2,986✔
669
  {
670
    FlowControlLabel skip_alternative;
512✔
671
    emit.jmp_always( skip_alternative );
512✔
672
    emit.label( *skip_consequent );
512✔
673
    generate( *alternative );
512✔
674
    emit.label( skip_alternative );
512✔
675
  }
512✔
676
  else
677
  {
678
    emit.label( *skip_consequent );
2,474✔
679
  }
680
}
2,986✔
681

682
void InstructionGenerator::visit_integer_value( IntegerValue& node )
12,424✔
683
{
684
  update_debug_location( node );
12,424✔
685
  emit.value( node.value );
12,424✔
686
}
12,424✔
687

688
void InstructionGenerator::visit_jump_statement( JumpStatement& jump )
530✔
689
{
690
  emit.debug_statementbegin();
530✔
691
  update_debug_location( jump );
530✔
692
  if ( jump.local_variables_to_remove )
530✔
693
    emit.leaveblock( jump.local_variables_to_remove );
334✔
694
  emit.jmp_always( *jump.flow_control_label );
530✔
695
}
530✔
696

697
void InstructionGenerator::visit_member_access( MemberAccess& member_access )
3,518✔
698
{
699
  visit_children( member_access );
3,518✔
700

701
  update_debug_location( member_access );
3,518✔
702
  if ( auto km = member_access.known_member )
3,518✔
703
    emit.get_member_id( km->id );
2,316✔
704
  else
705
    emit.get_member( member_access.name );
1,202✔
706
}
3,518✔
707

708
void InstructionGenerator::visit_member_assignment( MemberAssignment& node )
438✔
709
{
710
  visit_children( node );
438✔
711

712
  update_debug_location( node );
438✔
713
  if ( auto known_member = node.known_member )
438✔
714
  {
715
    if ( node.consume )
270✔
716
      emit.set_member_id_consume( known_member->id );
268✔
717
    else
718
      emit.set_member_id( known_member->id );
2✔
719
  }
720
  else
721
  {
722
    if ( node.consume )
168✔
723
      emit.set_member_consume( node.name );
168✔
724
    else
725
      emit.set_member( node.name );
×
726
  }
727
}
438✔
728

729
void InstructionGenerator::visit_member_assignment_by_operator( MemberAssignmentByOperator& node )
44✔
730
{
731
  visit_children( node );
44✔
732

733
  update_debug_location( node );
44✔
734
  emit.set_member_by_operator( node.token_id, node.known_member.id );
44✔
735
}
44✔
736

737
void InstructionGenerator::visit_index_binding( IndexBinding& node )
50✔
738
{
739
  // Emit indexes
740
  node.indexes().accept( *this );
50✔
741

742
  update_debug_location( node );
50✔
743
  emit.unpack_indices( node.binding_count(), node.rest_index );
50✔
744

745
  // Emit bindings
746
  for ( const auto& binding : node.bindings() )
170✔
747
  {
748
    binding.get().accept( *this );
120✔
749
  }
50✔
750
}
50✔
751

752
void InstructionGenerator::visit_method_call( MethodCall& method_call )
2,128✔
753
{
754
  visit_children( method_call );
2,128✔
755

756
  update_debug_location( method_call );
2,128✔
757
  auto argument_count = method_call.argument_count();
2,128✔
758
  if ( auto km = method_call.known_method )
2,128✔
759
  {
760
    emit.call_method_id( km->id, argument_count );
1,936✔
761
  }
762
  else
763
  {
764
    std::string method_name = method_call.methodname;
192✔
765
    Clib::mklowerASCII( method_name );
192✔
766
    emit.call_method( method_name, argument_count );
192✔
767
  }
192✔
768
}
2,128✔
769

770
void InstructionGenerator::visit_program( Program& program )
347✔
771
{
772
  DebugBlockGuard debug_block_guard( emitter, program.local_variable_scope_info );
347✔
773

774
  emit.debug_statementbegin();
347✔
775
  update_debug_location( program );
347✔
776

777
  visit_children( program );
347✔
778

779
  if ( !program.local_variable_scope_info.variables.empty() )
347✔
780
  {
781
    emit.leaveblock( static_cast<unsigned>( program.local_variable_scope_info.variables.size() ) );
214✔
782
  }
783
}
347✔
784

785
void InstructionGenerator::visit_program_parameter_declaration( ProgramParameterDeclaration& param )
152✔
786
{
787
  update_debug_location( param );
152✔
788
  emit.get_arg( param.name );
152✔
789
}
152✔
790

791
void InstructionGenerator::visit_repeat_until_loop( RepeatUntilLoop& loop )
24✔
792
{
793
  emit.debug_statementbegin();
24✔
794
  update_debug_location( loop );
24✔
795

796
  FlowControlLabel top;
24✔
797

798
  emit.label( top );
24✔
799
  generate( loop.block() );
24✔
800
  emit.label( *loop.continue_label );
24✔
801
  generate( loop.expression() );
24✔
802
  emit.jmp_if_false( top );
24✔
803
  emit.label( *loop.break_label );
24✔
804
}
24✔
805

806
void InstructionGenerator::visit_return_statement( ReturnStatement& ret )
3,430✔
807
{
808
  auto user_function = user_functions.empty() ? nullptr : user_functions.top();
3,430✔
809

810
  if ( user_function && user_function->type == UserFunctionType::Constructor )
3,430✔
811
  {
812
    // Semantic analyzer will ensure a return statement in a constructor does not have any
813
    // children, but lets be sure.
814
    if ( !ret.children.empty() )
2✔
815
    {
816
      ret.internal_error( "return statement in constructor should not have children" );
×
817
    }
818

819
    // This emitter method also emits the `this` variable based off parameter offset.
820
    emit.return_from_constructor_function( user_function->parameter_count() - 1 );
2✔
821
  }
822
  else
823
  {
824
    emit.debug_statementbegin();
3,428✔
825

826
    visit_children( ret );
3,428✔
827

828
    update_debug_location( ret );
3,428✔
829
    if ( user_function )
3,428✔
830
    {
831
      emit.return_from_user_function();
3,156✔
832
    }
833
    else
834
    {
835
      emit.progend();
272✔
836
    }
837
  }
838
}
3,430✔
839

840
void InstructionGenerator::visit_spread_element( SpreadElement& node )
432✔
841
{
842
  visit_children( node );
432✔
843
  update_debug_location( node );
432✔
844
  emit.spread();
432✔
845
}
432✔
846

847
void InstructionGenerator::visit_string_value( StringValue& lit )
17,190✔
848
{
849
  update_debug_location( lit );
17,190✔
850
  emit.value( lit.value );
17,190✔
851
}
17,190✔
852

853
void InstructionGenerator::visit_struct_initializer( StructInitializer& node )
694✔
854
{
855
  update_debug_location( node );
694✔
856
  emit.struct_create();
694✔
857
  visit_children( node );
694✔
858
}
694✔
859

860
void InstructionGenerator::visit_struct_member_initializer( StructMemberInitializer& node )
1,474✔
861
{
862
  visit_children( node );
1,474✔
863

864
  update_debug_location( node );
1,474✔
865
  if ( node.children.empty() )
1,474✔
866
    emit.struct_add_uninit_member( node.name );
30✔
867
  else
868
    emit.struct_add_member( node.name );
1,444✔
869
}
1,474✔
870

871
void InstructionGenerator::visit_unary_operator( UnaryOperator& unary_operator )
378✔
872
{
873
  visit_children( unary_operator );
378✔
874
  emit.unary_operator( unary_operator.token_id );
378✔
875
}
378✔
876

877
void InstructionGenerator::visit_uninitialized_value( UninitializedValue& node )
80✔
878
{
879
  update_debug_location( node );
80✔
880
  emit.uninit();
80✔
881
}
80✔
882

883
void InstructionGenerator::visit_user_function( UserFunction& user_function )
2,000✔
884
{
885
  user_functions.push( &user_function );
2,000✔
886
  unsigned first_instruction_address = emitter.next_instruction_address();
2,000✔
887
  DebugBlockGuard debug_block_guard( emitter, user_function.local_variable_scope_info );
2,000✔
888

889
  emit.debug_statementbegin();
2,000✔
890
  update_debug_location( user_function );
2,000✔
891
  if ( user_function.exported )
2,000✔
892
  {
893
    // emit the exported entry stub
894
    FlowControlLabel exported_entrypoint, internal_entrypoint;
480✔
895

896
    emit.label( exported_entrypoint );
480✔
897
    emit.makelocal();
480✔
898
    emit.call_userfunc( internal_entrypoint );
480✔
899
    emit.progend();
480✔
900

901
    emit.label( internal_entrypoint );
480✔
902
    emitter.register_exported_function( exported_entrypoint, user_function.name,
480✔
903
                                        user_function.parameter_count() );
904
  }
480✔
905

906
  FlowControlLabel& label = user_function_labels[user_function.scoped_name()];
2,000✔
907
  emit.label( label );
2,000✔
908

909
  // Pop function parameters
910
  user_function.child<FunctionParameterList>( 0 ).accept( *this );
2,000✔
911

912
  // Pop caputured variables
913
  for ( const auto& variable : user_function.capture_variable_scope_info.variables )
2,244✔
914
  {
915
    emit.pop_param_byref( variable->name );
244✔
916
  }
917

918
  user_function.body().accept( *this );
2,000✔
919

920
  if ( !dynamic_cast<ReturnStatement*>( user_function.body().last_statement() ) )
2,000✔
921
  {
922
    emit.debug_statementbegin();
804✔
923
    update_debug_location( user_function.endfunction_location );
804✔
924
    if ( user_function.type == UserFunctionType::Constructor )
804✔
925
    {
926
      emit.return_from_constructor_function( user_function.parameter_count() - 1 );
230✔
927
    }
928
    else
929
    {
930
      emit.value( 0 );
574✔
931
      emit.return_from_user_function();
574✔
932
    }
933
  }
934
  unsigned last_instruction_address = emitter.next_instruction_address() - 1;
2,000✔
935
  emitter.debug_user_function( user_function.name, first_instruction_address,
2,000✔
936
                               last_instruction_address );
937
  user_functions.pop();
2,000✔
938
}
2,000✔
939

940
void InstructionGenerator::visit_sequence_binding( SequenceBinding& node )
68✔
941
{
942
  update_debug_location( node );
68✔
943

944
  emit.unpack_sequence( node.binding_count(), node.rest_index );
68✔
945
  visit_children( node );
68✔
946
}
68✔
947

948
void InstructionGenerator::visit_value_consumer( ValueConsumer& node )
8,561✔
949
{
950
  emitter.debug_statementbegin();
8,561✔
951
  update_debug_location( node );
8,561✔
952
  visit_children( node );
8,561✔
953

954
  emit.consume();
8,561✔
955
}
8,561✔
956

957
void InstructionGenerator::visit_var_statement( VarStatement& node )
4,044✔
958
{
959
  emit.debug_statementbegin();
4,044✔
960
  update_debug_location( node );
4,044✔
961
  if ( !node.variable )
4,044✔
962
    node.internal_error( "variable is not defined" );
×
963

964
  int function_capture_count = 0;
4,044✔
965

966
  if ( !user_functions.empty() )
4,044✔
967
  {
968
    function_capture_count = user_functions.top()->capture_count();
2,054✔
969
  }
970

971
  emit.declare_variable( *node.variable, function_capture_count, false );
4,044✔
972

973
  if ( node.initialize_as_empty_array )
4,044✔
974
  {
975
    emit.array_declare();
46✔
976
  }
977
  else if ( !node.children.empty() )
3,998✔
978
  {
979
    visit_children( node );
3,366✔
980

981
    emit.assign();
3,366✔
982
  }
983

984
  emit.consume();
4,044✔
985
}
4,044✔
986

987
void InstructionGenerator::visit_variable_assignment_statement( VariableAssignmentStatement& node )
1,378✔
988
{
989
  emitter.debug_statementbegin();
1,378✔
990
  generate( node.rhs() );
1,378✔
991
  update_debug_location( node );
1,378✔
992
  auto& identifier = node.identifier();
1,378✔
993
  auto& variable = identifier.variable;
1,378✔
994

995
  int function_params_count = 0;
1,378✔
996
  int function_capture_count = 0;
1,378✔
997

998
  if ( !user_functions.empty() )
1,378✔
999
  {
1000
    function_params_count = user_functions.top()->parameter_count();
734✔
1001
    function_capture_count = user_functions.top()->capture_count();
734✔
1002
  }
1003

1004
  emit.assign_variable( *variable, function_params_count, function_capture_count );
1,378✔
1005
}
1,378✔
1006

1007
void InstructionGenerator::visit_while_loop( WhileLoop& loop )
310✔
1008
{
1009
  emit.debug_statementbegin();
310✔
1010
  update_debug_location( loop );
310✔
1011
  emit.label( *loop.continue_label );
310✔
1012
  generate( loop.predicate() );
310✔
1013
  emit.jmp_if_false( *loop.break_label );
310✔
1014
  generate( loop.block() );
310✔
1015
  emit.jmp_always( *loop.continue_label );
310✔
1016
  emit.label( *loop.break_label );
310✔
1017
}
310✔
1018

1019
void InstructionGenerator::visit_variable_binding( VariableBinding& node )
300✔
1020
{
1021
  update_debug_location( node );
300✔
1022

1023
  if ( !node.variable )
300✔
NEW
1024
    node.internal_error( "variable is not defined" );
×
1025

1026
  int function_capture_count = 0;
300✔
1027

1028
  if ( !user_functions.empty() )
300✔
1029
  {
1030
    function_capture_count = user_functions.top()->capture_count();
4✔
1031
  }
1032
  emit.declare_variable( *node.variable, function_capture_count, true );
300✔
1033
}
300✔
1034

1035
void InstructionGenerator::visit_interpolate_string( InterpolateString& node )
1,502✔
1036
{
1037
  visit_children( node );
1,502✔
1038

1039
  update_debug_location( node );
1,502✔
1040
  emit.interpolate_string( static_cast<unsigned>( node.children.size() ) );
1,502✔
1041
}
1,502✔
1042

1043
void InstructionGenerator::visit_format_expression( FormatExpression& node )
92✔
1044
{
1045
  visit_children( node );
92✔
1046

1047
  update_debug_location( node );
92✔
1048
  emit.format_expression();
92✔
1049
}
92✔
1050

1051
void InstructionGenerator::visit_conditional_operator( ConditionalOperator& node )
54✔
1052
{
1053
  update_debug_location( node );
54✔
1054

1055
  // generate conditional
1056
  generate( node.conditional() );
54✔
1057
  // consume+jump to end-of-consequent label if false
1058
  emit.jmp_if_false( *node.consequent_label );
54✔
1059
  // generate consequent
1060
  generate( node.consequent() );
54✔
1061
  // jump to end-of-alternate label
1062
  emit.jmp_always( *node.alternate_label );
54✔
1063
  // end-of-consequent label
1064
  emit.label( *node.consequent_label );
54✔
1065
  // generate alternate
1066
  generate( node.alternate() );
54✔
1067
  // end-of-alternate label
1068
  emit.label( *node.alternate_label );
54✔
1069
}
54✔
1070

1071
void InstructionGenerator::emit_access_variable( Variable& variable )
20,252✔
1072
{
1073
  int function_params_count = 0;
20,252✔
1074
  int function_capture_count = 0;
20,252✔
1075

1076
  if ( !user_functions.empty() )
20,252✔
1077
  {
1078
    function_params_count = user_functions.top()->parameter_count();
13,636✔
1079
    function_capture_count = user_functions.top()->capture_count();
13,636✔
1080
  }
1081

1082
  emit.access_variable( variable, function_params_count, function_capture_count );
20,252✔
1083
}
20,252✔
1084
}  // 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