• 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.94
/pol-core/bscript/compiler/codegen/InstructionEmitter.cpp
1
#include "InstructionEmitter.h"
2

3
#include <limits>
4
#include <list>
5
#include <set>
6

7
#include "StoredToken.h"
8
#include "bscript/compiler/Report.h"
9
#include "bscript/compiler/ast/ClassDeclaration.h"
10
#include "bscript/compiler/ast/ModuleFunctionDeclaration.h"
11
#include "bscript/compiler/ast/UserFunction.h"
12
#include "bscript/compiler/codegen/CaseJumpDataBlock.h"
13
#include "bscript/compiler/codegen/ClassDeclarationRegistrar.h"
14
#include "bscript/compiler/codegen/FunctionReferenceRegistrar.h"
15
#include "bscript/compiler/codegen/ModuleDeclarationRegistrar.h"
16
#include "bscript/compiler/model/ClassLink.h"
17
#include "bscript/compiler/model/FlowControlLabel.h"
18
#include "bscript/compiler/model/FunctionLink.h"
19
#include "bscript/compiler/model/LocalVariableScopeInfo.h"
20
#include "bscript/compiler/model/ScopableName.h"
21
#include "bscript/compiler/model/Variable.h"
22
#include "bscript/compiler/representation/ClassDescriptor.h"
23
#include "bscript/compiler/representation/CompiledScript.h"
24
#include "bscript/compiler/representation/ConstructorDescriptor.h"
25
#include "bscript/compiler/representation/ExportedFunction.h"
26
#include "escriptv.h"
27
#include "modules.h"
28
#include "token.h"
29
#include "tokens.h"
30

31
namespace Pol::Bscript::Compiler
32
{
33
InstructionEmitter::InstructionEmitter( CodeSection& code, DataSection& data, DebugStore& debug,
2,237✔
34
                                        ExportedFunctions& exported_functions,
35
                                        ModuleDeclarationRegistrar& module_declaration_registrar,
36
                                        FunctionReferenceRegistrar& function_reference_registrar,
37
                                        ClassDeclarationRegistrar& class_declaration_registrar,
38
                                        Report& report )
2,237✔
39
    : code_emitter( code ),
2,237✔
40
      data_emitter( data ),
2,237✔
41
      debug( debug ),
2,237✔
42
      exported_functions( exported_functions ),
2,237✔
43
      module_declaration_registrar( module_declaration_registrar ),
2,237✔
44
      function_reference_registrar( function_reference_registrar ),
2,237✔
45
      class_declaration_registrar( class_declaration_registrar ),
2,237✔
46
      report( report )
2,237✔
47
{
48
  initialize_data();
2,237✔
49
}
2,237✔
50

51
void InstructionEmitter::initialize_data()
2,237✔
52
{
53
  std::byte nul{};
2,237✔
54
  data_emitter.store( &nul, sizeof nul );
2,237✔
55
}
2,237✔
56

57
void InstructionEmitter::register_exported_function( FlowControlLabel& label,
607✔
58
                                                     const std::string& name, unsigned parameters )
59
{
60
  exported_functions.emplace_back( name, parameters, label.address() );
607✔
61
}
607✔
62

63
void InstructionEmitter::register_class_declaration(
519✔
64
    const ClassDeclaration& node, std::map<std::string, FlowControlLabel>& user_function_labels )
65
{
66
  std::set<std::string> visited;
519✔
67
  std::set<std::string, Clib::ci_cmp_pred> visited_methods;
519✔
68
  std::vector<ConstructorDescriptor> constructor_descriptors;
519✔
69
  std::set<std::string> method_names;
519✔
70
  std::vector<MethodDescriptor> method_descriptors;
519✔
71
  std::list<const ClassDeclaration*> to_link( { &node } );
519✔
72

73
  const auto& class_name = node.name;
519✔
74
  auto class_name_offset = this->emit_data( class_name );
519✔
75
  unsigned constructor_function_reference_index = std::numeric_limits<unsigned>::max();
519✔
76

77
  report.debug( node, "Registering class: {}", node.name );
519✔
78

79
  if ( node.constructor_link )
1,038✔
80
  {
81
    if ( auto uf = node.constructor_link->user_function() )
410✔
82
    {
83
      auto ctor_itr = user_function_labels.find( uf->scoped_name() );
410✔
84

85
      if ( ctor_itr == user_function_labels.end() )
410✔
86
      {
87
        uf->internal_error(
×
88
            fmt::format( "Constructor {} not found in user_function_labels", uf->scoped_name() ) );
×
89
      }
90

91
      function_reference_registrar.lookup_or_register_reference(
820✔
92
          *uf, ctor_itr->second, constructor_function_reference_index );
410✔
93
    }
94
  }
95
  if ( constructor_function_reference_index < std::numeric_limits<unsigned>::max() )
519✔
96
    report.debug( node, " - Constructor at FuncRef index {}",
410✔
97
                  constructor_function_reference_index );
98

99
  for ( auto itr = to_link.begin(); itr != to_link.end(); ++itr )
1,476✔
100
  {
101
    auto cd = *itr;
957✔
102
    if ( visited.find( cd->name ) != visited.end() )
957✔
103
      continue;
41✔
104

105
    visited.insert( cd->name );
916✔
106
    report.debug( *cd, "Class {} with {} methods", cd->name, cd->methods.size() );
916✔
107

108
    if ( cd->constructor_link && cd->constructor_link->user_function() )
1,832✔
109
    {
110
      auto type_tag_offset = emit_data( cd->type_tag() );
727✔
111
      constructor_descriptors.emplace_back( type_tag_offset );
727✔
112
    }
113

114
    for ( const auto& [method, uf_link] : cd->methods )
1,321✔
115
    {
116
      auto uf = uf_link->user_function();
405✔
117

118
      if ( !uf )
405✔
119
      {
120
        cd->internal_error( fmt::format( "method {}::{} no function linked", cd->name, method ) );
×
121
      }
122

123
      auto method_itr = user_function_labels.find( ScopableName( cd->name, method ).string() );
405✔
124
      if ( method_itr == user_function_labels.end() )
405✔
125
      {
126
        report.debug( *cd, " - Method: {} label=???", method );
×
127
        cd->internal_error( fmt::format( "Method {} not found in user_function_labels", method ) );
×
128
      }
129
      auto address = method_itr->second.address();
405✔
130
      if ( address == 0 )
405✔
131
      {
132
        report.debug( *cd, " - Method: {} PC=???", method );
×
133
        cd->internal_error( fmt::format( "Method {} has no PC for attached label", method ) );
×
134
      }
135

136
      bool use_method = method_names.find( method ) == method_names.end();
405✔
137

138
      if ( use_method )
405✔
139
      {
140
        unsigned funcref_index;
141
        function_reference_registrar.lookup_or_register_reference( *uf, method_itr->second,
339✔
142
                                                                   funcref_index );
143
        auto name_offset = this->emit_data( method );
339✔
144
        method_descriptors.emplace_back( name_offset, address, funcref_index );
339✔
UNCOV
145
        report.debug( *cd, " - Method: {} PC={} funcref_index={}", method,
×
146
                      method_itr->second.address(), funcref_index );
339✔
147
        method_names.insert( method );
339✔
148
      }
149
      else
150
      {
151
        report.debug( *cd, " - Method: {} PC={} [ignored]", method, method_itr->second.address() );
66✔
152
      }
153
    }
154

155
    for ( const auto& base_cd_link : cd->base_class_links )
1,354✔
156
    {
157
      if ( auto base_cd = base_cd_link->class_declaration() )
438✔
158
      {
159
        to_link.push_back( base_cd );
438✔
160
      }
161
    }
162
  }
163
  report.debug( node, "Class: {}", node.name );
519✔
164

165
  for ( const auto& constructor : constructor_descriptors )
1,246✔
166
  {
167
    report.debug( node, " - Constructor @ type_tag_offset={}", constructor.type_tag_offset );
727✔
168
  }
169

170
  for ( const auto& method_info : method_descriptors )
858✔
171
  {
172
    report.debug( node, " - Method @ PC={} name_offset={} funcref_index={} ", method_info.address,
339✔
173
                  method_info.name_offset, method_info.function_reference_index );
339✔
174
  }
175

176
  class_declaration_registrar.register_class( class_name_offset,
519✔
177
                                              constructor_function_reference_index,
178
                                              constructor_descriptors, method_descriptors );
179
}
519✔
180

181
unsigned InstructionEmitter::enter_debug_block(
10,734✔
182
    const LocalVariableScopeInfo& local_variable_scope_info )
183
{
184
  unsigned previous_debug_block_index = debug_instruction_info.block_index;
10,734✔
185

186
  if ( !local_variable_scope_info.variables.empty() )
10,734✔
187
  {
188
    debug_instruction_info.block_index =
4,727✔
189
        debug.add_block( debug_instruction_info.block_index, local_variable_scope_info );
4,727✔
190
  }
191

192
  return previous_debug_block_index;
10,734✔
193
}
194

195
void InstructionEmitter::set_debug_block( unsigned block_index )
10,734✔
196
{
197
  debug_instruction_info.block_index = block_index;
10,734✔
198
}
10,734✔
199

200
// - When visiting an identifier:
201
// - If type == Capture: set to Local and ValueStack offset will be current function's param count +
202
// this variable index
203
// - If type == Local: if offset >= function param count, add current function's capture count
204
void InstructionEmitter::access_variable( const Variable& v, VariableIndex function_params_count,
30,782✔
205
                                          VariableIndex function_capture_count )
206
{
207
  BTokenId token_id;
208
  unsigned offset;
209

210
  if ( v.scope == VariableScope::Capture )
30,782✔
211
  {
212
    token_id = TOK_LOCALVAR;
683✔
213

214
    offset = v.index + function_params_count;
683✔
215
  }
216
  else if ( v.scope == VariableScope::Local )
30,099✔
217
  {
218
    token_id = TOK_LOCALVAR;
21,436✔
219

220
    if ( v.index >= function_params_count )
21,436✔
221
    {
222
      offset = v.index + function_capture_count;
15,962✔
223
    }
224
    else
225
    {
226
      offset = v.index;
5,474✔
227
    }
228
  }
229
  else
230
  {
231
    token_id = TOK_GLOBALVAR;
8,663✔
232
    offset = v.index;
8,663✔
233
  }
234
  emit_token( token_id, TYP_OPERAND, offset );
30,782✔
235
}
30,782✔
236

237
void InstructionEmitter::array_append()
5,955✔
238
{
239
  emit_token( TOK_INSERTINTO, TYP_OPERATOR );
5,955✔
240
}
5,955✔
241

242
void InstructionEmitter::array_create()
2,779✔
243
{
244
  emit_token( TOK_ARRAY, TYP_OPERAND );
2,779✔
245
}
2,779✔
246

247
void InstructionEmitter::array_declare()
69✔
248
{
249
  emit_token( INS_DECLARE_ARRAY, TYP_RESERVED );
69✔
250
}
69✔
251

252
void InstructionEmitter::assign()
5,147✔
253
{
254
  emit_token( TOK_ASSIGN, TYP_OPERATOR );
5,147✔
255
}
5,147✔
256

257
void InstructionEmitter::assign_subscript_consume()
424✔
258
{
259
  unsigned indexes = 1;
424✔
260
  emit_token( INS_SUBSCRIPT_ASSIGN_CONSUME, TYP_UNARY_OPERATOR, indexes );
424✔
261
}
424✔
262

263
void InstructionEmitter::assign_subscript()
9✔
264
{
265
  unsigned indexes = 1;
9✔
266
  emit_token( INS_SUBSCRIPT_ASSIGN, TYP_UNARY_OPERATOR, indexes );
9✔
267
}
9✔
268

269
void InstructionEmitter::assign_multisubscript( unsigned indexes )
6✔
270
{
271
  emit_token( INS_MULTISUBSCRIPT_ASSIGN, TYP_UNARY_OPERATOR, indexes );
6✔
272
}
6✔
273

274
void InstructionEmitter::assign_variable( const Variable& v, VariableIndex function_params_count,
2,122✔
275
                                          VariableIndex function_capture_count )
276
{
277
  BTokenId token_id;
278
  unsigned offset;
279

280
  if ( v.scope == VariableScope::Capture )
2,122✔
281
  {
282
    token_id = INS_ASSIGN_LOCALVAR;
46✔
283

284
    offset = v.index + function_params_count;
46✔
285
  }
286
  else if ( v.scope == VariableScope::Local )
2,076✔
287
  {
288
    token_id = INS_ASSIGN_LOCALVAR;
1,288✔
289

290
    if ( v.index >= function_params_count )
1,288✔
291
    {
292
      offset = v.index + function_capture_count;
1,225✔
293
    }
294
    else
295
    {
296
      offset = v.index;
63✔
297
    }
298
  }
299
  else
300
  {
301
    token_id = INS_ASSIGN_GLOBALVAR;
788✔
302
    offset = v.index;
788✔
303
  }
304

305
  emit_token( token_id, TYP_UNARY_OPERATOR, offset );
2,122✔
306
}
2,122✔
307

308
void InstructionEmitter::basic_for_init( FlowControlLabel& label )
88✔
309
{
310
  register_with_label( label, emit_token( INS_INITFOR, TYP_RESERVED ) );
88✔
311
}
88✔
312

313
void InstructionEmitter::basic_for_next( FlowControlLabel& label )
88✔
314
{
315
  register_with_label( label, emit_token( INS_NEXTFOR, TYP_RESERVED ) );
88✔
316
}
88✔
317

318
void InstructionEmitter::binary_operator( BTokenId token_id )
8,184✔
319
{
320
  emit_token( token_id, TYP_OPERATOR );
8,184✔
321
}
8,184✔
322

323
void InstructionEmitter::call_method_id( MethodID method_id, unsigned argument_count )
3,992✔
324
{
325
  emit_token( INS_CALL_METHOD_ID, (BTokenType)argument_count, method_id );
3,992✔
326
}
3,992✔
327

328
void InstructionEmitter::call_method( const std::string& name, unsigned argument_count )
470✔
329
{
330
  unsigned offset = emit_data( name );
470✔
331
  emit_token( INS_CALL_METHOD, (BTokenType)argument_count, offset );
470✔
332
}
470✔
333

334
void InstructionEmitter::call_modulefunc(
13,564✔
335
    const ModuleFunctionDeclaration& module_function_declaration )
336
{
337
  unsigned module_id, function_index;
338
  module_declaration_registrar.lookup_or_register_module_function( module_function_declaration,
13,564✔
339
                                                                   module_id, function_index );
340
  unsigned sympos = include_debug ? emit_data( module_function_declaration.name ) : 0;
13,564✔
341
  StoredToken token(
342
      static_cast<unsigned char>( module_id ), TOK_FUNC,
343
      static_cast<BTokenType>(
13,564✔
344
          function_index ),  // function index, stored in Token.lval, saved in StoredToken.type
345
      sympos );
13,564✔
346
  append_token( token );
13,564✔
347
}
13,564✔
348

349
void InstructionEmitter::call_userfunc( FlowControlLabel& label )
5,601✔
350
{
351
  unsigned addr = emit_token( CTRL_JSR_USERFUNC, TYP_CONTROL );
5,601✔
352
  register_with_label( label, addr );
5,601✔
353
}
5,601✔
354

355
void InstructionEmitter::check_mro( unsigned offset )
237✔
356
{
357
  emit_token( INS_CHECK_MRO, TYP_CONTROL, offset );
237✔
358
}
237✔
359

360
void InstructionEmitter::classinst_create( unsigned index )
248✔
361
{
362
  emit_token( TOK_CLASSINST, TYP_OPERAND, index );
248✔
363
}
248✔
364

365
unsigned InstructionEmitter::casejmp()
104✔
366
{
367
  return emit_token( INS_CASEJMP, TYP_RESERVED );
104✔
368
}
369

370
unsigned InstructionEmitter::case_dispatch_table( const CaseJumpDataBlock& dispatch_table )
104✔
371
{
372
  auto& bytes = dispatch_table.get_data();
104✔
373
  return data_emitter.append( bytes.data(), bytes.size() );
104✔
374
}
375

376
void InstructionEmitter::consume()
20,518✔
377
{
378
  emit_token( TOK_CONSUMER, TYP_UNARY_OPERATOR );
20,518✔
379
}
20,518✔
380

UNCOV
381
void InstructionEmitter::ctrl_statementbegin( unsigned file_index, unsigned file_offset,
×
382
                                              const std::string& source_text )
383
{
UNCOV
384
  unsigned source_offset = emit_data( source_text );
×
385
  Pol::Bscript::DebugToken debug_token;
386
  debug_token.sourceFile = file_index + 1;
×
UNCOV
387
  debug_token.offset = file_offset;
×
388
  debug_token.strOffset = source_offset;
×
389
  unsigned offset =
390
      data_emitter.store( reinterpret_cast<std::byte*>( &debug_token ), sizeof debug_token );
×
UNCOV
391
  emit_token( CTRL_STATEMENTBEGIN, TYP_CONTROL, offset );
×
392
}
×
393

394
// - When declaring an identifier:
395
// - If type == Local: if offset >= function param count, add current function's capture count
396
void InstructionEmitter::declare_variable( const Variable& v, VariableIndex function_capture_count,
6,529✔
397
                                           bool take )
398
{
399
  int offset;
400

401
  if ( v.scope == VariableScope::Local && v.index >= function_capture_count )
6,529✔
402
  {
403
    offset = v.index + function_capture_count;
4,096✔
404
  }
405
  else
406
  {
407
    offset = v.index;
2,433✔
408
  }
409

410
  BTokenId token_id = v.scope == VariableScope::Global ? ( take ? INS_TAKE_GLOBAL : RSV_GLOBAL )
10,693✔
411
                                                       : ( take ? INS_TAKE_LOCAL : RSV_LOCAL );
4,164✔
412
  emit_token( token_id, TYP_RESERVED, offset );
6,529✔
413
}
6,529✔
414

415
void InstructionEmitter::dictionary_create()
172✔
416
{
417
  emit_token( TOK_DICTIONARY, TYP_OPERAND );
172✔
418
}
172✔
419

420
void InstructionEmitter::dictionary_add_member()
154✔
421
{
422
  emit_token( INS_DICTIONARY_ADDMEMBER, TYP_OPERATOR );
154✔
423
}
154✔
424

425
void InstructionEmitter::error_create()
294✔
426
{
427
  emit_token( TOK_ERROR, TYP_OPERAND );
294✔
428
}
294✔
429

430
void InstructionEmitter::exit()
18✔
431
{
432
  emit_token( RSV_EXIT, TYP_RESERVED );
18✔
433
}
18✔
434

435
void InstructionEmitter::foreach_init( FlowControlLabel& label )
484✔
436
{
437
  register_with_label( label, emit_token( INS_INITFOREACH, TYP_RESERVED ) );
484✔
438
}
484✔
439

440
void InstructionEmitter::foreach_step( FlowControlLabel& label )
484✔
441
{
442
  register_with_label( label, emit_token( INS_STEPFOREACH, TYP_RESERVED ) );
484✔
443
}
484✔
444

445
void InstructionEmitter::function_reference( const UserFunction& uf, FlowControlLabel& label )
759✔
446
{
447
  unsigned index;
448

449
  function_reference_registrar.lookup_or_register_reference( uf, label, index );
759✔
450

451
  emit_token( TOK_FUNCREF, TYP_OPERAND, index );
759✔
452
}
759✔
453

454
void InstructionEmitter::functor_create( const UserFunction& uf, FlowControlLabel& label )
671✔
455
{
456
  unsigned reference_index;
457
  function_reference_registrar.lookup_or_register_reference( uf, label, reference_index );
671✔
458
  StoredToken token( static_cast<unsigned char>( Mod_Basic ), TOK_FUNCTOR,
459
                     static_cast<BTokenType>(
671✔
460
                         reference_index ),  // index to the EScriptProgram's function_references,
461
                                             // stored in Token.lval, saved in StoredToken.type
462
                     0 );
671✔
463
  append_token( token );
671✔
464
}
671✔
465

466
void InstructionEmitter::get_arg( const std::string& name )
202✔
467
{
468
  unsigned offset = emit_data( name );
202✔
469
  emit_token( INS_GET_ARG, TYP_OPERATOR, offset );
202✔
470
}
202✔
471

472
void InstructionEmitter::get_member( const std::string& name )
1,722✔
473
{
474
  unsigned offset = emit_data( name );
1,722✔
475
  emit_token( INS_GET_MEMBER, TYP_UNARY_OPERATOR, offset );
1,722✔
476
}
1,722✔
477

478
void InstructionEmitter::get_member_id( MemberID member_id )
2,866✔
479
{
480
  emit_token( INS_GET_MEMBER_ID, TYP_UNARY_OPERATOR, member_id );
2,866✔
481
}
2,866✔
482

483
void InstructionEmitter::jmp_always( FlowControlLabel& label )
2,333✔
484
{
485
  register_with_label( label, emit_token( RSV_GOTO, TYP_RESERVED ) );
2,333✔
486
}
2,333✔
487

488
void InstructionEmitter::jmp_if_false( FlowControlLabel& label )
2,791✔
489
{
490
  register_with_label( label, emit_token( RSV_JMPIFFALSE, TYP_RESERVED ) );
2,791✔
491
}
2,791✔
492

493
void InstructionEmitter::jmp_if_true( FlowControlLabel& label )
1,792✔
494
{
495
  register_with_label( label, emit_token( RSV_JMPIFTRUE, TYP_RESERVED ) );
1,792✔
496
}
1,792✔
497

498
void InstructionEmitter::label( FlowControlLabel& label )
13,603✔
499
{
500
  label.assign_address( code_emitter.next_address() );
13,603✔
501

502
  for ( auto referencing_address : label.get_referencing_instruction_addresses() )
24,298✔
503
  {
504
    patch_offset( referencing_address, label.address() );
10,695✔
505
  }
506
}
13,603✔
507

508
void InstructionEmitter::leaveblock( unsigned local_vars_to_remove )
2,020✔
509
{
510
  emit_token( CTRL_LEAVE_BLOCK, TYP_CONTROL, local_vars_to_remove );
2,020✔
511
}
2,020✔
512

513
void InstructionEmitter::makelocal()
5,601✔
514
{
515
  emit_token( CTRL_MAKELOCAL, TYP_CONTROL );
5,601✔
516
}
5,601✔
517

518
void InstructionEmitter::pop_param( const std::string& name )
3,329✔
519
{
520
  unsigned offset = emit_data( name );
3,329✔
521
  emit_token( INS_POP_PARAM, TYP_OPERATOR, offset );
3,329✔
522
}
3,329✔
523

524
void InstructionEmitter::pop_param_byref( const std::string& name )
1,362✔
525
{
526
  unsigned offset = emit_data( name );
1,362✔
527
  emit_token( INS_POP_PARAM_BYREF, TYP_OPERATOR, offset );
1,362✔
528
}
1,362✔
529

530
void InstructionEmitter::progend()
3,156✔
531
{
532
  emit_token( CTRL_PROGEND, TYP_CONTROL );
3,156✔
533
}
3,156✔
534

535
void InstructionEmitter::return_from_user_function()
6,161✔
536
{
537
  emit_token( RSV_RETURN, TYP_RESERVED );
6,161✔
538
}
6,161✔
539

540
void InstructionEmitter::return_from_constructor_function( unsigned this_offset )
410✔
541
{
542
  // Emit `this`
543
  emit_token( TOK_LOCALVAR, TYP_OPERAND, this_offset );
410✔
544

545
  // Emit a return
546
  return_from_user_function();
410✔
547
}
410✔
548

549
void InstructionEmitter::set_member_id_consume( MemberID member_id )
306✔
550
{
551
  emit_token( INS_SET_MEMBER_ID_CONSUME, TYP_UNARY_OPERATOR, member_id );
306✔
552
}
306✔
553

554
void InstructionEmitter::set_member_id( MemberID member_id )
3✔
555
{
556
  emit_token( INS_SET_MEMBER_ID, TYP_UNARY_OPERATOR, member_id );
3✔
557
}
3✔
558

559
void InstructionEmitter::set_member_consume( const std::string& name )
254✔
560
{
561
  unsigned offset = emit_data( name );
254✔
562
  emit_token( INS_SET_MEMBER_CONSUME, TYP_UNARY_OPERATOR, offset );
254✔
563
}
254✔
564

565
void InstructionEmitter::set_member( const std::string& name )
3✔
566
{
567
  unsigned offset = emit_data( name );
3✔
568
  emit_token( INS_SET_MEMBER, TYP_UNARY_OPERATOR, offset );
3✔
569
}
3✔
570

571
void InstructionEmitter::set_member_by_operator( BTokenId token_id, MemberID member_id )
73✔
572
{
573
  emit_token( token_id, TYP_UNARY_OPERATOR, member_id );
73✔
574
}
73✔
575

576
void InstructionEmitter::spread( bool spread_into )
671✔
577
{
578
  emit_token( TOK_SPREAD, TYP_OPERAND, spread_into );
671✔
579
}
671✔
580

581
unsigned InstructionEmitter::skip_if_true_else_consume()
205✔
582
{
583
  return emit_token( INS_SKIPIFTRUE_ELSE_CONSUME, TYP_CONTROL );
205✔
584
}
585

586
void InstructionEmitter::struct_create()
894✔
587
{
588
  emit_token( TOK_STRUCT, TYP_OPERAND );
894✔
589
}
894✔
590

591
void InstructionEmitter::struct_add_member( const std::string& name )
2,051✔
592
{
593
  auto offset = emit_data( name );
2,051✔
594
  emit_token( INS_ADDMEMBER_ASSIGN, TYP_OPERAND, offset );
2,051✔
595
}
2,051✔
596

597
void InstructionEmitter::struct_add_uninit_member( const std::string& name )
45✔
598
{
599
  auto offset = emit_data( name );
45✔
600
  emit_token( INS_ADDMEMBER2, TYP_OPERAND, offset );
45✔
601
}
45✔
602

603
void InstructionEmitter::subscript_single()
1,759✔
604
{
605
  emit_token( TOK_ARRAY_SUBSCRIPT, TYP_OPERATOR, 1 );
1,759✔
606
}
1,759✔
607

608
void InstructionEmitter::subscript_multiple( unsigned indexes )
245✔
609
{
610
  emit_token( INS_MULTISUBSCRIPT, TYP_OPERATOR, indexes );
245✔
611
}
245✔
612

613
void InstructionEmitter::unary_operator( BTokenId token_id )
613✔
614
{
615
  emit_token( token_id, TYP_UNARY_OPERATOR );
613✔
616
}
613✔
617

618
void InstructionEmitter::uninit()
114✔
619
{
620
  emit_token( INS_UNINIT, TYP_OPERAND );
114✔
621
}
114✔
622

623
void InstructionEmitter::unpack_sequence( unsigned count, unsigned rest_at )
109✔
624
{
625
  // Two-byte offset encodes (1) if rest unpacking, (2) index of rest binding, (3) the number of
626
  // bindings: aa'bbbbbbb'ccccccc => a: is rest, b: rest index, c: number of bindings
627
  unsigned short offset = rest_at == 0xFF
109✔
628
                              ? ( count & 0x7F )
40✔
629
                              : ( 1 << 14 ) | ( ( rest_at & 0x7F ) << 7 ) | ( count & 0x7F );
69✔
630

631
  emit_token( INS_UNPACK_SEQUENCE, TYP_RESERVED, offset );
109✔
632
}
109✔
633

634
void InstructionEmitter::unpack_indices( unsigned count, unsigned rest_at )
88✔
635
{
636
  // Two-byte offset encodes (1) if rest unpacking, (2) index of rest binding, (3) the number of
637
  // bindings: aa'bbbbbbb'ccccccc => a: is rest, b: rest index, c: number of bindings
638
  unsigned short offset = rest_at == 0xFF
88✔
639
                              ? ( count & 0x7F )
61✔
640
                              : ( 1 << 14 ) | ( ( rest_at & 0x7F ) << 7 ) | ( count & 0x7F );
27✔
641

642
  emit_token( INS_UNPACK_INDICES, TYP_RESERVED, offset );
88✔
643
}
88✔
644

645
void InstructionEmitter::value( double v )
443✔
646
{
647
  unsigned offset = data_emitter.append( v );
443✔
648
  emit_token( TOK_DOUBLE, TYP_OPERAND, offset );
443✔
649
}
443✔
650

651
void InstructionEmitter::value( int v )
18,205✔
652
{
653
  unsigned offset = data_emitter.append( v );
18,205✔
654
  emit_token( TOK_LONG, TYP_OPERAND, offset );
18,205✔
655
}
18,205✔
656

657
void InstructionEmitter::value( bool v )
272✔
658
{
659
  emit_token( TOK_BOOL, TYP_OPERAND, v );
272✔
660
}
272✔
661

662
void InstructionEmitter::value( const std::string& v )
27,727✔
663
{
664
  unsigned data_offset = emit_data( v );
27,727✔
665
  emit_token( TOK_STRING, TYP_OPERAND, data_offset );
27,727✔
666
}
27,727✔
667

668
void InstructionEmitter::regular_expression_value( const std::string& pattern,
150✔
669
                                                   const std::string& flags )
670
{
671
  value( pattern );
150✔
672
  value( flags );
150✔
673
  emit_token( TOK_REGEXP, TYP_OPERAND );
150✔
674
}
150✔
675

676
void InstructionEmitter::interpolate_string( unsigned count )
2,456✔
677
{
678
  emit_token( TOK_INTERPOLATE_STRING, TYP_OPERAND, count );
2,456✔
679
}
2,456✔
680

681
void InstructionEmitter::format_expression()
110✔
682
{
683
  emit_token( TOK_FORMAT_EXPRESSION, TYP_OPERAND );
110✔
684
}
110✔
685

686
unsigned InstructionEmitter::emit_data( const std::string& s )
38,750✔
687
{
688
  return data_emitter.store( s );
38,750✔
689
}
690

691
unsigned InstructionEmitter::emit_token( BTokenId id, BTokenType type, unsigned offset )
186,368✔
692
{
693
  StoredToken token( Mod_Basic, id, type, offset );
186,368✔
694
  return append_token( token );
372,736✔
695
}
696

697
unsigned InstructionEmitter::append_token( StoredToken& token )
200,603✔
698
{
699
  debug.add_instruction( debug_instruction_info );
200,603✔
700
  debug_instruction_info.statement_begin = false;
200,603✔
701
  return code_emitter.append( token );
200,603✔
702
}
703

704
void InstructionEmitter::debug_file_line( unsigned file, unsigned line )
186,963✔
705
{
706
  // debug info always has file #0 = empty (keeping for parity, for now)
707
  debug_instruction_info.file_index = file + 1;
186,963✔
708
  debug_instruction_info.line_number = line;
186,963✔
709
}
186,963✔
710

711
void InstructionEmitter::debug_statementbegin()
41,178✔
712
{
713
  debug_instruction_info.statement_begin = true;
41,178✔
714
}
41,178✔
715

716
unsigned InstructionEmitter::next_instruction_address()
8,770✔
717
{
718
  return code_emitter.next_address();
8,770✔
719
}
720

721
void InstructionEmitter::debug_user_function( const std::string& name, unsigned first_pc,
3,403✔
722
                                              unsigned last_pc )
723
{
724
  DebugStore::UserFunctionInfo ufi{ name, first_pc, last_pc };
3,403✔
725
  debug.add_user_function( std::move( ufi ) );
3,403✔
726
}
3,403✔
727

728
void InstructionEmitter::patch_offset( unsigned index, unsigned offset )
14,704✔
729
{
730
  code_emitter.update_offset( index, offset );
14,704✔
731
}
14,704✔
732

733
bool InstructionEmitter::has_function_reference( const UserFunction& uf )
3,139✔
734
{
735
  unsigned index;
736
  return function_reference_registrar.lookup_reference( uf, index );
6,278✔
737
}
738

739
void InstructionEmitter::register_with_label( FlowControlLabel& label, unsigned offset )
13,724✔
740
{
741
  if ( label.has_address() )
13,724✔
742
  {
743
    patch_offset( offset, label.address() );
3,029✔
744
  }
745
  else
746
  {
747
    label.add_referencing_instruction_address( offset );
10,695✔
748
  }
749
}
13,724✔
750

751
void InstructionEmitter::logical_jmp( FlowControlLabel& label, bool if_true )
63✔
752
{
753
  register_with_label(
63✔
754
      label, emit_token( INS_LOGICAL_JUMP, if_true ? TYP_RESERVED : TYP_LOGICAL_JUMP_FALSE ) );
755
}
63✔
756

757
void InstructionEmitter::logical_convert()
47✔
758
{
759
  emit_token( INS_LOGICAL_CONVERT, TYP_OPERAND );
47✔
760
}
47✔
761

762
}  // 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