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

polserver / polserver / 21108840797

18 Jan 2026 08:35AM UTC coverage: 60.508% (+0.02%) from 60.492%
21108840797

push

github

web-flow
ClangTidy readability-else-after-return (#857)

* trigger tidy

* Automated clang-tidy change: readability-else-after-return

* compile test

* rerun

* Automated clang-tidy change: readability-else-after-return

* trigger..

* Automated clang-tidy change: readability-else-after-return

* manually removed a few

* Automated clang-tidy change: readability-else-after-return

* removed duplicate code

* fix remaining warnings

* fixed scope

---------

Co-authored-by: Clang Tidy <clang-tidy@users.noreply.github.com>

837 of 1874 new or added lines in 151 files covered. (44.66%)

46 existing lines in 25 files now uncovered.

44448 of 73458 relevant lines covered (60.51%)

525066.38 hits per line

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

84.8
/pol-core/bscript/executor.cpp
1
/** @file
2
 *
3
 * @par History
4
 * - 2005/09/16 Shinigami: added scripts_thread_script* to support better debugging
5
 * - 2006/01/27 Shinigami: added missing TOK_BS* to Executor::GetInstrFunc
6
 * - 2006/06/10 Shinigami: getParamImp/2 - better Error Message added
7
 * - 2006/10/07 Shinigami: FreeBSD fix - changed __linux__ to __unix__
8
 * - 2007/07/07 Shinigami: added code to analyze memoryleaks in initForFnCall() (needs defined
9
 * MEMORYLEAK)
10
 * - 2009/07/19 MuadDib: Executor::ins_member() Removed, due to no longer used since case
11
 * optimization code added.
12
 * - 2009/09/05 Turley: Added struct .? and .- as shortcut for .exists() and .erase()
13
 */
14

15

16
#include "executor.h"
17
#include "bobject.h"
18
#include "executor.inl.h"
19

20
#include "../clib/clib.h"
21
#include "../clib/logfacility.h"
22
#include "../clib/passert.h"
23
#include "../clib/stlutil.h"
24
#include "../clib/strutil.h"
25
#include "bclassinstance.h"
26
#include "berror.h"
27
#include "config.h"
28
#include "continueimp.h"
29
#include "contiter.h"
30
#include "dict.h"
31
#include "eprog.h"
32
#include "escriptv.h"
33
#include "execmodl.h"
34
#include "fmodule.h"
35
#include "impstr.h"
36
#include "objmethods.h"
37
#include "regexp.h"
38
#include "str.h"
39
#include "token.h"
40
#include "tokens.h"
41
#include <iterator>
42
#include <limits>
43
#ifdef MEMORYLEAK
44
#include "../clib/mlog.h"
45
#endif
46

47
#include <boost/multi_index/ordered_index.hpp>
48
#include <boost/multi_index/sequenced_index.hpp>
49
#include <boost/multi_index_container.hpp>
50

51
#include <cstdlib>
52
#include <cstring>
53
#include <exception>
54
#include <numeric>
55

56

57
namespace Pol::Bscript
58
{
59
std::set<Executor*> executor_instances;
60

61
void display_executor_instances()
3✔
62
{
63
  for ( const auto& ex : executor_instances )
3✔
64
  {
65
    // Fix for crashes due to orphaned script instances.
66
    if ( !ex->empty_scriptname() )
×
67
      INFO_PRINTLN( ex->scriptname() );
×
68
  }
69
}
3✔
70

71
ExecutorDebugEnvironment::ExecutorDebugEnvironment( std::weak_ptr<ExecutorDebugListener> listener,
2✔
72
                                                    bool set_attaching )
2✔
73
    : debug_state( set_attaching ? ExecutorDebugState::ATTACHING : ExecutorDebugState::RUN ),
2✔
74
      breakpoints(),
2✔
75
      break_on_linechange_from{ ~0u, ~0u },
2✔
76
      bp_skip( ~0u ),
2✔
77
      listener( listener )
2✔
78
{
79
}
2✔
80

81
size_t ExecutorDebugEnvironment::sizeEstimate() const
×
82
{
83
  size_t size = sizeof( *this ) + Clib::memsize( breakpoints ) + Clib::memsize( tmpbreakpoints );
×
84
  return size;
×
85
}
86

87
bool ExecutorDebugEnvironment::on_instruction( Executor& ex )
305✔
88
{
89
  switch ( debug_state )
305✔
90
  {
91
  case ( ExecutorDebugState::ATTACHING ):
2✔
92
  {
93
    debug_state = ExecutorDebugState::ATTACHED;
2✔
94
    ex.sethalt( true );
2✔
95
    return false;
2✔
96
  }
97
  case ( ExecutorDebugState::INS_TRACE ):
×
98
  {
99
    // let this instruction through.
100
    debug_state = ExecutorDebugState::ATTACHED;
×
101
    ex.sethalt( true );
×
102
    // but let this instruction execute.
103
    break;
×
104
  }
105
  case ( ExecutorDebugState::STEP_INTO ):
1✔
106
  {
107
    debug_state = ExecutorDebugState::STEPPING_INTO;
1✔
108
    // let this instruction execute.
109
    break;
1✔
110
  }
111
  case ( ExecutorDebugState::STEPPING_INTO ):
6✔
112
  {
113
    if ( ex.prog()->dbg_ins_statementbegin.size() > ex.PC &&
12✔
114
         ex.prog()->dbg_ins_statementbegin[ex.PC] )
6✔
115
    {
116
      tmpbreakpoints.insert( ex.PC );
1✔
117
      // and let breakpoint processing catch it below.
118
    }
119
    break;
6✔
120
  }
121
  case ( ExecutorDebugState::STEP_OVER ):
2✔
122
  {
123
    break_on_linechange_from = { ex.prog()->dbg_linenum[ex.PC], ex.ControlStack.size() };
2✔
124
    debug_state = ExecutorDebugState::STEPPING_OVER;
2✔
125
    break;
2✔
126
  }
127
  case ( ExecutorDebugState::STEPPING_OVER ):
4✔
128
  {
129
    if ( ex.ControlStack.size() < break_on_linechange_from.control ||
8✔
130
         ( ex.ControlStack.size() == break_on_linechange_from.control &&
4✔
131
           ex.prog()->dbg_linenum[ex.PC] != break_on_linechange_from.line ) )
4✔
132
    {
133
      debug_state = ExecutorDebugState::ATTACHED;
2✔
134
      break_on_linechange_from = { ~0u, ~0u };
2✔
135
      ex.sethalt( true );
2✔
136
      return false;
2✔
137
    }
138
    break;
2✔
139
  }
140
  case ( ExecutorDebugState::STEP_OUT ):
1✔
141
  {
142
    if ( !ex.ControlStack.empty() )
1✔
143
    {
144
      tmpbreakpoints.insert( ex.ControlStack.back().PC );
1✔
145
    }
146
    debug_state = ExecutorDebugState::RUN;
1✔
147
    break;
1✔
148
  }
149
  case ( ExecutorDebugState::RUN ):
288✔
150
  {
151
    // do nothing
152
    break;
288✔
153
  }
154
  case ( ExecutorDebugState::ATTACHED ):
×
155
  {
156
    return false;
×
157
  }
158
  case ( ExecutorDebugState::BREAK_INTO ):
1✔
159
  {
160
    debug_state = ExecutorDebugState::ATTACHED;
1✔
161
    ex.sethalt( true );
1✔
162
    return false;
1✔
163
    break;
164
  }
165
  }
166

167
  // check for breakpoints on this instruction
168
  if ( ( breakpoints.count( ex.PC ) || tmpbreakpoints.count( ex.PC ) ) && bp_skip != ex.PC &&
304✔
169
       !ex.halt() )
4✔
170
  {
171
    tmpbreakpoints.erase( ex.PC );
4✔
172
    bp_skip = ex.PC;
4✔
173
    debug_state = ExecutorDebugState::ATTACHED;
4✔
174
    ex.sethalt( true );
4✔
175
    return false;
4✔
176
  }
177
  bp_skip = ~0u;
296✔
178

179
  return true;
296✔
180
}
181

182
extern int executor_count;
183
Clib::SpinLock Executor::_executor_lock;
184
Executor::Executor()
2,266✔
185
    : done( 0 ),
2,266✔
186
      error_( false ),
2,266✔
187
      halt_( false ),
2,266✔
188
      run_ok_( false ),
2,266✔
189
      debug_level( NONE ),
2,266✔
190
      PC( 0 ),
2,266✔
191
      Globals2( std::make_shared<BObjectRefVec>() ),
2,266✔
192
      Locals2( new BObjectRefVec ),
2,266✔
193
      nLines( 0 ),
2,266✔
194
      current_module_function( nullptr ),
2,266✔
195
      prog_ok_( false ),
2,266✔
196
      viewmode_( false ),
2,266✔
197
      runs_to_completion_( false ),
2,266✔
198
      dbg_env_( nullptr ),
2,266✔
199
      func_result_( nullptr )
6,798✔
200
{
201
  Clib::SpinLockGuard lock( _executor_lock );
2,266✔
202
  ++executor_count;
2,266✔
203
  executor_instances.insert( this );
2,266✔
204

205
  if ( !UninitObject::SharedInstance )
2,266✔
206
  {
207
    UninitObject::SharedInstance = new UninitObject;
1,988✔
208
    UninitObject::SharedInstanceOwner.set( UninitObject::SharedInstance );
1,988✔
209
  }
210
}
2,266✔
211

212
Executor::~Executor()
2,266✔
213
{
214
  {
215
    Clib::SpinLockGuard lock( _executor_lock );
2,266✔
216
    --executor_count;
2,266✔
217
    executor_instances.erase( this );
2,266✔
218
  }
2,266✔
219
  cleanup();
2,266✔
220
}
2,266✔
221
void Executor::cleanup()
2,547✔
222
{
223
  if ( dbg_env_ )
2,547✔
224
  {
225
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
8✔
226
      listener->on_destroy();
4✔
227
  }
228

229
  delete Locals2;
2,547✔
230
  Locals2 = nullptr;
2,547✔
231

232
  while ( !upperLocals2.empty() )
2,547✔
233
  {
234
    delete upperLocals2.back();
×
235
    upperLocals2.pop_back();
×
236
  }
237

238
  execmodules.clear();
2,547✔
239
  Clib::delete_all( availmodules );
2,547✔
240
}
2,547✔
241

242
bool Executor::AttachFunctionalityModules()
2,381✔
243
{
244
  for ( auto& fm : prog_->modules )
5,837✔
245
  {
246
    // if no function in the module is actually called, don't go searching for it.
247
    if ( fm->functions.empty() )
3,456✔
248
    {
249
      execmodules.push_back( nullptr );
×
250
      continue;
×
251
    }
252

253
    ExecutorModule* em = findModule( fm->modulename );
3,456✔
254
    execmodules.push_back( em );
3,456✔
255
    if ( em == nullptr )
3,456✔
256
    {
257
      ERROR_PRINTLN( "WARNING: {}: Unable to find module {}", scriptname(), fm->modulename.get() );
×
258
      return false;
×
259
    }
260

261
    if ( !fm->have_indexes )
3,456✔
262
    {
263
      /*
264
          FIXE: Possible optimization: store these function indexes in the
265
          EScriptProgram object, since those are cached.  Then, we only
266
          have to find the module index.
267
          */
268
      for ( unsigned fidx = 0; fidx < fm->functions.size(); fidx++ )
6,240✔
269
      {
270
        ModuleFunction* func = fm->functions[fidx];
3,562✔
271
        // FIXME: should check number of params, blah.
272
        if ( !func->name.get().empty() )
3,562✔
273
        {
274
          func->funcidx = em->functionIndex( func->name.get() );
3,562✔
275
          if ( func->funcidx == -1 )
3,562✔
276
          {
277
            ERROR_PRINTLN( "Unable to find {}::{}", fm->modulename.get(), func->name.get() );
×
278
            return false;
×
279
          }
280
        }
281
      }
282
      fm->have_indexes = true;
2,678✔
283
    }
284
  }
285
  return true;
2,381✔
286
}
287

288
int Executor::getParams( unsigned howMany )
37,030,390✔
289
{
290
  if ( howMany )
37,030,390✔
291
  {
292
    fparams.resize( howMany );
37,017,875✔
293
    for ( int i = howMany - 1; i >= 0; --i )
74,106,872✔
294
    {
295
      if ( ValueStack.empty() )
37,088,997✔
296
      {
297
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
298
        seterror( true );
×
299
        return -1;
×
300
      }
301
      fparams[i] = ValueStack.back();
37,088,997✔
302
      ValueStack.pop_back();
37,088,997✔
303
    }
304
  }
305
  expandParams();
37,030,390✔
306
  return 0;
37,030,390✔
307
}
308

309
void Executor::expandParams()
37,030,390✔
310
{
311
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
74,120,614✔
312
  {
313
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
37,090,224✔
314
    {
315
      // defer destruction
316
      BObjectRef obj( spread );
628✔
317

318
      // Remove the spread
319
      fparams.erase( fparams.begin() + i );
628✔
320

321
      BObjectRef refIter( UninitObject::create() );
628✔
322

323
      auto pIter = std::unique_ptr<ContIterator>(
324
          spread->object->impptr()->createIterator( refIter.get() ) );
628✔
325

326
      BObject* next = pIter->step();
628✔
327

328
      int added = 0;
628✔
329
      while ( next != nullptr )
1,855✔
330
      {
331
        fparams.insert( fparams.begin() + i + added, BObjectRef( next ) );
1,227✔
332
        next = pIter->step();
1,227✔
333
        added++;
1,227✔
334
      }
335
      i += added;
628✔
336
    }
628✔
337
  }
338
}
37,030,390✔
339

340
void Executor::cleanParams()
37,028,060✔
341
{
342
  fparams.clear();
37,028,060✔
343
}
37,028,060✔
344

345
int Executor::makeString( unsigned param )
8,236✔
346
{
347
  BObject* obj = getParam( param );
8,236✔
348
  if ( !obj )
8,236✔
349
    return -1;
×
350
  if ( obj->isa( BObjectImp::OTString ) )
8,236✔
351
    return 0;
8,236✔
352

353
  fparams[param].set( new String( obj->impref() ) );
×
354

355
  return 0;
×
356
}
357

358
const char* Executor::paramAsString( unsigned param )
4,164✔
359
{
360
  makeString( param );
4,164✔
361
  BObjectImp* objimp = fparams[param]->impptr();
4,164✔
362

363
  String* str = (String*)objimp;
4,164✔
364
  return str ? str->data() : "";
4,164✔
365
}
366

367
int Executor::makeDouble( unsigned param )
×
368
{
369
  BObject* obj = getParam( param );
×
370
  if ( !obj )
×
371
    return -1;
×
372
  if ( obj->isa( BObjectImp::OTDouble ) )
×
373
    return 0;
×
374
  if ( auto* v = obj->impptr_if<BLong>() )
×
375
    fparams[param].set( new Double( v->value() ) );
×
376
  else
377
    fparams[param].set( new Double( 0.0 ) );
×
378

379
  return 0;
×
380
}
381

382
double Executor::paramAsDouble( unsigned param )
×
383
{
384
  makeDouble( param );
×
385
  if ( auto* v = getParam( param )->impptr_if<Double>() )
×
386
    return v->value();
×
387
  return 0.0;
×
388
}
389

390
int Executor::paramAsLong( unsigned param )
36,939,092✔
391
{
392
  BObjectImp* objimp = getParam( param )->impptr();
36,939,092✔
393
  if ( auto* l = impptrIf<BLong>( objimp ) )
36,939,092✔
394
    return l->value();
36,939,092✔
NEW
395
  if ( auto* d = impptrIf<Double>( objimp ) )
×
396
    return static_cast<int>( d->value() );
×
397
  return 0;
×
398
}
399
BObject* Executor::getParam( unsigned param )
36,947,330✔
400
{
401
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
36,947,330✔
402
                                         ": Less Parameter than expected. " +
403
                                         "You should use *.em-files shipped with this Core and "
404
                                         "recompile ALL of your Scripts _now_! RTFM" );
405

406
  return fparams[param].get();
36,947,330✔
407
}
408

409
BObjectImp* Executor::getParamImp( unsigned param )
71,334✔
410
{
411
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
71,334✔
412
                                         ": Less Parameter than expected. " +
413
                                         "You should use *.em-files shipped with this Core and "
414
                                         "recompile ALL of your Scripts _now_! RTFM" );
415

416
  return fparams[param].get()->impptr();
71,334✔
417
}
418

419
BObject* Executor::getParamObj( unsigned param )
925✔
420
{
421
  if ( fparams.size() > param )
925✔
422
    return fparams[param].get();
925✔
423
  return nullptr;
×
424
}
425

426
BObjectImp* Executor::getParamImp( unsigned param, BObjectImp::BObjectType type )
40,981✔
427
{
428
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
40,981✔
429
                                         ": Less Parameter than expected. " +
430
                                         "You should use *.em-files shipped with this Core and "
431
                                         "recompile ALL of your Scripts _now_! RTFM" );
432

433
  BObjectImp* imp = fparams[param].get()->impptr();
40,981✔
434

435
  passert( imp != nullptr );
40,981✔
436

437
  if ( imp->isa( type ) )
40,981✔
438
    return imp;
40,939✔
439
  if ( !IS_DEBUGLOG_DISABLED )
42✔
440
  {
441
    std::string tmp = fmt::format( "Script Error in '{}' PC={}:\n", scriptname(), PC );
42✔
442
    if ( current_module_function )
42✔
443
      fmt::format_to( std::back_inserter( tmp ), "\tCall to function {}:\n",
52✔
444
                      current_module_function->name.get() );
26✔
445
    else
446
      tmp += "\tCall to an object method.\n";
16✔
447
    fmt::format_to( std::back_inserter( tmp ),
×
448
                    "\tParameter {}: Expected datatype {}, got datatype {}", param,
449
                    BObjectImp::typestr( type ), BObjectImp::typestr( imp->type() ) );
42✔
450
    DEBUGLOGLN( tmp );
42✔
451
  }
42✔
452
  return nullptr;
42✔
453
}
454

455
BObjectImp* Executor::getParamImp2( unsigned param, BObjectImp::BObjectType type )
26,479✔
456
{
457
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
26,479✔
458
                                         ": Less Parameter than expected. " +
459
                                         "You should use *.em-files shipped with this Core and "
460
                                         "recompile ALL of your Scripts _now_! RTFM" );
461

462
  BObjectImp* imp = fparams[param].get()->impptr();
26,479✔
463

464
  passert( imp != nullptr );
26,479✔
465

466
  if ( imp->isa( type ) )
26,479✔
467
    return imp;
26,479✔
468
  std::string report = "Invalid parameter type.  Expected param " + Clib::tostring( param ) +
×
469
                       " as " + BObjectImp::typestr( type ) + ", got " +
×
470
                       BObjectImp::typestr( imp->type() );
×
471
  func_result_ = new BError( report );
×
472
  return nullptr;
×
473
}
×
474

475

476
const String* Executor::getStringParam( unsigned param )
32,648✔
477
{
478
  return Clib::explicit_cast<String*, BObjectImp*>( getParamImp( param, BObjectImp::OTString ) );
32,648✔
479
}
480

481
const BLong* Executor::getLongParam( unsigned param )
×
482
{
483
  return Clib::explicit_cast<BLong*, BObjectImp*>( getParamImp( param, BObjectImp::OTLong ) );
×
484
}
485

486
bool Executor::getStringParam( unsigned param, const String*& pstr )
32,626✔
487
{
488
  pstr = getStringParam( param );
32,626✔
489
  return ( pstr != nullptr );
32,626✔
490
}
491

492
bool Executor::getParam( unsigned param, int& value )
6,718✔
493
{
494
  BLong* plong =
495
      Clib::explicit_cast<BLong*, BObjectImp*>( getParamImp( param, BObjectImp::OTLong ) );
6,718✔
496
  if ( plong == nullptr )
6,718✔
497
    return false;
11✔
498

499
  value = plong->value();
6,707✔
500
  return true;
6,707✔
501
}
502

503
void Executor::setFunctionResult( BObjectImp* imp )
9✔
504
{
505
  func_result_ = imp;
9✔
506
}
9✔
507

508
void Executor::printStack( const std::string& message = "" )
71,780✔
509
{
510
  if ( debug_level < INSTRUCTIONS )
71,780✔
511
    return;
71,780✔
512

513
  if ( !message.empty() )
×
514
  {
515
    INFO_PRINTLN( message );
×
516
  }
517

518
  size_t i = 0;
×
519
  for ( auto riter = fparams.rbegin(); riter != fparams.rend(); ++riter )
×
520
  {
521
    auto* ptr = riter->get()->impptr();
×
522
    INFO_PRINTLN( "fparam[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
523
    i++;
×
524
  }
525

526
  i = 0;
×
527
  for ( auto riter = ValueStack.rbegin(); riter != ValueStack.rend(); ++riter )
×
528
  {
529
    auto* ptr = riter->get()->impptr();
×
530
    INFO_PRINTLN( "vstack[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
531
    i++;
×
532
  }
533

534
  INFO_PRINTLN( "---" );
×
535
}
536

537
bool Executor::getParam( unsigned param, int& value, int maxval )
9✔
538
{
539
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
9✔
540
  if ( !imp )
9✔
541
    return false;
×
542
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
9✔
543

544
  value = plong->value();
9✔
545
  if ( value >= 0 && value <= maxval )
9✔
546
    return true;
9✔
547
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
548
                                          param, value, maxval ) );
×
549
  return false;
×
550
}
551

552
bool Executor::getParam( unsigned param, int& value, int minval, int maxval )
597✔
553
{
554
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
597✔
555
  if ( !imp )
597✔
556
    return false;
×
557
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
597✔
558

559
  value = plong->value();
597✔
560
  if ( value >= minval && value <= maxval )
597✔
561
    return true;
582✔
562
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
15✔
563
                                          param, value, minval, maxval ) );
30✔
564
  return false;
15✔
565
}
566

567
bool Executor::getRealParam( unsigned param, double& value )
159✔
568
{
569
  BObjectImp* imp = getParamImp( param );
159✔
570
  if ( auto* d = impptrIf<Double>( imp ) )
159✔
571
  {
572
    value = d->value();
39✔
573
    return true;
39✔
574
  }
575
  if ( auto* l = impptrIf<BLong>( imp ) )
120✔
576
  {
577
    value = l->value();
69✔
578
    return true;
69✔
579
  }
580
  DEBUGLOGLN(
51✔
581
      "Script Error in '{}' PC={}: \n"
582
      "\tCall to function {}:\n"
583
      "\tParameter {}: Expected Integer or Real, got datatype {}",
584
      scriptname(), PC, current_module_function->name.get(), param,
51✔
585
      BObjectImp::typestr( imp->type() ) );
102✔
586

587
  return false;
51✔
588
}
589

590
bool Executor::getObjArrayParam( unsigned param, ObjArray*& pobjarr )
420✔
591
{
592
  pobjarr =
420✔
593
      Clib::explicit_cast<ObjArray*, BObjectImp*>( getParamImp( param, BObjectImp::OTArray ) );
420✔
594
  return ( pobjarr != nullptr );
420✔
595
}
596

597
void* Executor::getApplicPtrParam( unsigned param, const BApplicObjType* pointer_type )
×
598
{
599
  auto ap = static_cast<BApplicPtr*>( getParamImp( param, BObjectImp::OTApplicPtr ) );
×
600
  if ( ap == nullptr )
×
601
    return nullptr;
×
602

603
  if ( ap->pointer_type() == pointer_type )
×
604
    return ap->ptr();
×
605
  DEBUGLOGLN(
×
606
      "Script Error in '{}' PC={}: \n"
607
      "\tCall to function {}:\n"
608
      "\tParameter {}: Expected datatype, got datatype {}",
609
      scriptname(), PC, current_module_function->name.get(), param,
×
610
      BObjectImp::typestr( ap->type() ) );
×
611

612
  return nullptr;
×
613
}
614

615
BApplicObjBase* Executor::getApplicObjParam( unsigned param, const BApplicObjType* object_type )
206✔
616
{
617
  auto aob = static_cast<BApplicObjBase*>( getParamImp( param, BObjectImp::OTApplicObj ) );
206✔
618
  if ( aob == nullptr )
206✔
619
    return nullptr;
×
620

621
  if ( aob->object_type() == object_type )
206✔
622
    return aob;
206✔
623
  DEBUGLOGLN(
×
624
      "Script Error in '{}' PC={}: \n"
625
      "\tCall to function {}:\n"
626
      "\tParameter {}: Expected datatype, got datatype {}",
627
      scriptname(), PC, current_module_function->name.get(), param, aob->getStringRep() );
×
628

629
  return nullptr;
×
630
}
631

632
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short maxval )
×
633
{
634
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
635
  if ( !imp )
×
636
    return false;
×
637
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
638

639
  int longvalue = plong->value();
×
640
  if ( longvalue >= 0 && longvalue <= maxval )
×
641
  {
642
    value = static_cast<unsigned short>( longvalue );
×
643
    return true;
×
644
  }
645
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
646
                                          param, longvalue, maxval ) );
×
647
  return false;
×
648
}
649

650
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short minval,
6,138✔
651
                         unsigned short maxval )
652
{
653
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,138✔
654
  if ( !imp )
6,138✔
655
    return false;
×
656
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
6,138✔
657

658
  int longvalue = plong->value();
6,138✔
659
  if ( longvalue >= minval && longvalue <= maxval )
6,138✔
660
  {
661
    value = static_cast<unsigned short>( longvalue );
6,138✔
662
    return true;
6,138✔
663
  }
664
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
665
                                          param, longvalue, minval, maxval ) );
×
666
  return false;
×
667
}
668
bool Executor::getParam( unsigned param, unsigned short& value )
13,046✔
669
{
670
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
13,046✔
671
  if ( !imp )
13,046✔
672
    return false;
×
673
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
13,046✔
674

675
  int longvalue = plong->value();
13,046✔
676
  if ( longvalue >= 0 && longvalue <= USHRT_MAX )
13,046✔
677
  {
678
    value = static_cast<unsigned short>( longvalue );
13,046✔
679
    return true;
13,046✔
680
  }
681
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
682
                                          param, longvalue, USHRT_MAX ) );
×
683
  return false;
×
684
}
685
bool Executor::getParam( unsigned param, unsigned& value )
15✔
686
{
687
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
15✔
688
  if ( !imp )
15✔
689
    return false;
×
690
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
15✔
691

692
  int longvalue = plong->value();
15✔
693
  if ( longvalue >= 0 )  // && longvalue <= (int)INT_MAX )
15✔
694
  {
695
    value = static_cast<unsigned>( longvalue );
15✔
696
    return true;
15✔
697
  }
698
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
699
                                          param, longvalue, INT_MAX ) );
×
700
  return false;
×
701
}
702

703
bool Executor::getParam( unsigned param, short& value )
289✔
704
{
705
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
289✔
706
  if ( !imp )
289✔
707
    return false;
×
708
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
289✔
709

710
  int longvalue = plong->value();
289✔
711
  if ( longvalue >= (int)SHRT_MIN && longvalue <= (int)SHRT_MAX )
289✔
712
  {
713
    value = static_cast<short>( longvalue );
289✔
714
    return true;
289✔
715
  }
716
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
717
                                          param, longvalue, SHRT_MIN, SHRT_MAX ) );
×
718
  return false;
×
719
}
720

721
bool Executor::getParam( unsigned param, short& value, short maxval )
×
722
{
723
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
724
  if ( !imp )
×
725
    return false;
×
726
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
727

728
  int longvalue = plong->value();
×
729
  if ( longvalue >= (int)SHRT_MIN && longvalue <= maxval )
×
730
  {
731
    value = static_cast<short>( longvalue );
×
732
    return true;
×
733
  }
734
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
735
                                          param, longvalue, SHRT_MIN, maxval ) );
×
736
  return false;
×
737
}
738

739
bool Executor::getParam( unsigned param, short& value, short minval, short maxval )
×
740
{
741
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
742
  if ( !imp )
×
743
    return false;
×
744
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
745

746
  int longvalue = plong->value();
×
747
  if ( longvalue >= minval && longvalue <= maxval )
×
748
  {
749
    value = static_cast<short>( longvalue );
×
750
    return true;
×
751
  }
752
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
753
                                          param, longvalue, minval, maxval ) );
×
754
  return false;
×
755
}
756

757
bool Executor::getParam( unsigned param, signed char& value )
6,385✔
758
{
759
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,385✔
760
  if ( !imp )
6,385✔
761
    return false;
×
762

763
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
6,385✔
764

765
  int longvalue = plong->value();
6,385✔
766
  if ( longvalue >= std::numeric_limits<s8>::min() && longvalue <= std::numeric_limits<s8>::max() )
6,385✔
767
  {
768
    value = static_cast<signed char>( longvalue );
6,385✔
769
    return true;
6,385✔
770
  }
771
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
772
                                          param, longvalue, std::numeric_limits<s8>::min(),
×
773
                                          std::numeric_limits<s8>::max() ) );
×
774
  return false;
×
775
}
776

777
bool Executor::getParam( unsigned param, bool& value )
15✔
778
{
779
  BObjectImp* imp = getParamImp( param );
15✔
780
  if ( auto* b = impptrIf<BBoolean>( imp ) )
15✔
781
  {
782
    value = b->value();
1✔
783
    return true;
1✔
784
  }
785
  if ( auto* l = impptrIf<BLong>( imp ) )
14✔
786
  {
787
    value = l->isTrue();
14✔
788
    return true;
14✔
789
  }
790
  DEBUGLOGLN(
×
791
      "Script Error in '{}' PC={}: \n"
792
      "\tCall to function {}:\n"
793
      "\tParameter {}: Expected Boolean or Integer, got datatype {}",
794
      scriptname(), PC, current_module_function->name.get(), param,
×
795
      BObjectImp::typestr( imp->type() ) );
×
796

797
  return false;
×
798
}
799

800
bool Executor::getUnicodeStringParam( unsigned param, const String*& pstr )
2✔
801
{
802
  BObject* obj = getParam( param );
2✔
803
  if ( !obj )
2✔
804
    return false;
×
805
  if ( auto* s = obj->impptr_if<String>() )
2✔
806
  {
807
    pstr = s;
2✔
808
    return true;
2✔
809
  }
NEW
810
  if ( auto* a = obj->impptr_if<ObjArray>() )
×
811
  {
812
    String* str = String::fromUCArray( a );
×
813
    fparams[param].set( str );  // store raw pointer
×
814
    pstr = str;
×
815
    return true;
×
816
  }
817
  func_result_ = new BError( fmt::format(
×
818
      "Invalid parameter type.  Expected param {} as {} or {}, got {}", param,
819
      BObjectImp::typestr( BObjectImp::OTString ), BObjectImp::typestr( BObjectImp::OTArray ),
×
820
      BObjectImp::typestr( obj->impptr()->type() ) ) );
×
821
  return false;
×
822
}
823

824
BObjectRef& Executor::LocalVar( unsigned int varnum )
×
825
{
826
  passert( Locals2 );
×
827
  passert( varnum < Locals2->size() );
×
828

829
  return ( *Locals2 )[varnum];
×
830
}
831

832
BObjectRef& Executor::GlobalVar( unsigned int varnum )
×
833
{
834
  passert( varnum < Globals2->size() );
×
835
  return ( *Globals2 )[varnum];
×
836
}
837

838
int Executor::getToken( Token& token, unsigned position )
×
839
{
840
  if ( position >= nLines )
×
841
    return -1;
×
842
  token = prog_->instr[position].token;
×
843
  return 0;
×
844
}
845

846

847
bool Executor::setProgram( EScriptProgram* i_prog )
2,266✔
848
{
849
  prog_.set( i_prog );
2,266✔
850
  prog_ok_ = false;
2,266✔
851
  seterror( true );
2,266✔
852
  if ( !viewmode_ )
2,266✔
853
  {
854
    if ( !AttachFunctionalityModules() )
2,266✔
855
      return false;
×
856
  }
857

858
  nLines = static_cast<unsigned int>( prog_->instr.size() );
2,266✔
859

860
  Globals2->clear();
2,266✔
861
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
4,550✔
862
  {
863
    Globals2->emplace_back( UninitObject::create() );
2,284✔
864
  }
865

866
  prog_ok_ = true;
2,266✔
867
  seterror( false );
2,266✔
868
  ++prog_->invocations;
2,266✔
869
  return true;
2,266✔
870
}
871

872
BObjectRef Executor::getObjRef()
62,252✔
873
{
874
  if ( ValueStack.empty() )
62,252✔
875
  {
876
    POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
877
    seterror( true );
×
878
    return BObjectRef( UninitObject::create() );
×
879
  }
880

881
  BObjectRef ref = ValueStack.back();
62,252✔
882
  ValueStack.pop_back();
62,252✔
883
  return ref;
62,252✔
884
}
62,252✔
885

886

887
void Executor::execFunc( const Token& token )
36,983,022✔
888
{
889
  FunctionalityModule* fm = prog_->modules[token.module];
36,983,022✔
890
  ModuleFunction* modfunc = fm->functions[token.lval];
36,983,022✔
891
  current_module_function = modfunc;
36,983,022✔
892
  if ( modfunc->funcidx == -1 )
36,983,022✔
893
  {
894
    DEBUGLOGLN(
×
895
        "Error in script '{}':\n"
896
        "\tModule Function {} was not found.",
897
        prog_->name.get(), modfunc->name.get() );
×
898

899
    throw std::runtime_error( "No implementation for function found." );
×
900
  }
901

902
  ExecutorModule* em = execmodules[token.module];
36,983,022✔
903

904
  func_result_ = nullptr;
36,983,022✔
905
  BObjectImp* resimp;
906
  {
907
    ESCRIPT_PROFILER( em, modfunc, fparams );
908
    resimp = em->execFunc( modfunc->funcidx );
36,983,022✔
909
  }
910
  if ( func_result_ )
36,983,022✔
911
  {
912
    if ( resimp )
8✔
913
    {
914
      BObject obj( resimp );
8✔
915
    }
8✔
916
    ValueStack.emplace_back( func_result_ );
8✔
917
    func_result_ = nullptr;
8✔
918
  }
919
  else if ( resimp )
36,983,014✔
920
  {
921
    ValueStack.emplace_back( resimp );
36,983,013✔
922
  }
923
  else
924
  {
925
    ValueStack.emplace_back( UninitObject::create() );
1✔
926
  }
927

928
  current_module_function = nullptr;
36,983,022✔
929
}
36,983,022✔
930

931
// RSV_LOCAL
932
void Executor::ins_makeLocal( const Instruction& /*ins*/ )
23,806✔
933
{
934
  passert( Locals2 != nullptr );
23,806✔
935

936
  Locals2->emplace_back( UninitObject::create() );
23,806✔
937

938
  ValueStack.emplace_back( Locals2->back().get() );
23,806✔
939
}
23,806✔
940

941
// RSV_DECLARE_ARRAY
942
void Executor::ins_declareArray( const Instruction& /*ins*/ )
69✔
943
{
944
  BObjectRef objref = getObjRef();
69✔
945

946
  if ( !objref->isa( BObjectImp::OTUninit ) )
69✔
947
  {
948
    // FIXME: weak error message
949
    ERROR_PRINTLN( "variable is already initialized.." );
×
950
    seterror( true );
×
951
    return;
×
952
  }
953
  objref->setimp( new ObjArray );
69✔
954

955
  ValueStack.emplace_back( objref );
69✔
956
}
69✔
957

958
void Executor::popParam( const Token& /*token*/ )
48,768✔
959
{
960
  BObjectRef objref = getObjRef();
48,768✔
961

962
  Locals2->emplace_back( objref->impptr()->copy() );
48,768✔
963
}
48,768✔
964

965
void Executor::popParamByRef( const Token& /*token*/ )
7,938✔
966
{
967
  BObjectRef objref = getObjRef();
7,938✔
968

969
  Locals2->emplace_back( objref );
7,938✔
970
}
7,938✔
971

972
void Executor::getArg( const Token& /*token*/ )
433✔
973
{
974
  if ( ValueStack.empty() )
433✔
975
  {
976
    Locals2->emplace_back( UninitObject::create() );
47✔
977
  }
978
  else
979
  {
980
    BObjectRef objref = getObjRef();
386✔
981
    Locals2->emplace_back( objref->impptr()->copy() );
386✔
982
  }
386✔
983
}
433✔
984

985

986
BObjectRef Executor::addmember( BObject& left, const BObject& right )
168✔
987
{
988
  if ( !right.isa( BObjectImp::OTString ) )
168✔
989
  {
990
    return BObjectRef( left.clone() );
×
991
  }
992

993
  const String& varname = right.impref<const String>();
168✔
994

995
  return left.impref().operDotPlus( varname.data() );
168✔
996
}
997

998
BObjectRef Executor::removemember( BObject& left, const BObject& right )
78✔
999
{
1000
  if ( !right.isa( BObjectImp::OTString ) )
78✔
1001
  {
1002
    return BObjectRef( left.clone() );
×
1003
  }
1004

1005
  const String& varname = right.impref<const String>();
78✔
1006

1007
  return left.impref().operDotMinus( varname.data() );
78✔
1008
}
1009

1010
BObjectRef Executor::checkmember( BObject& left, const BObject& right )
1,613✔
1011
{
1012
  if ( !right.isa( BObjectImp::OTString ) )
1,613✔
1013
  {
1014
    return BObjectRef( left.clone() );
×
1015
  }
1016

1017
  const String& varname = right.impref<const String>();
1,613✔
1018

1019
  return left.impref().operDotQMark( varname.data() );
1,613✔
1020
}
1021

1022

1023
ContIterator::ContIterator() : BObjectImp( BObjectImp::OTUnknown ) {}
2,953✔
1024
BObject* ContIterator::step()
18✔
1025
{
1026
  return nullptr;
18✔
1027
}
1028
BObjectImp* ContIterator::copy() const
×
1029
{
1030
  return nullptr;
×
1031
}
1032
size_t ContIterator::sizeEstimate() const
12✔
1033
{
1034
  return sizeof( ContIterator );
12✔
1035
}
1036
std::string ContIterator::getStringRep() const
×
1037
{
1038
  return "<iterator>";
×
1039
}
1040

1041
class ArrayIterator final : public ContIterator
1042
{
1043
public:
1044
  ArrayIterator( ObjArray* pArr, BObject* pIterVal );
1045
  BObject* step() override;
1046

1047
private:
1048
  size_t m_Index;
1049
  BObject m_Array;
1050
  ObjArray* m_pArray;
1051
  BObjectRef m_IterVal;
1052
  BLong* m_pIterVal;
1053
};
1054
ArrayIterator::ArrayIterator( ObjArray* pArr, BObject* pIterVal )
2,736✔
1055
    : ContIterator(),
1056
      m_Index( 0 ),
2,736✔
1057
      m_Array( pArr ),
2,736✔
1058
      m_pArray( pArr ),
2,736✔
1059
      m_IterVal( pIterVal ),
2,736✔
1060
      m_pIterVal( new BLong( 0 ) )
5,472✔
1061
{
1062
  m_IterVal.get()->setimp( m_pIterVal );
2,736✔
1063
}
2,736✔
1064
BObject* ArrayIterator::step()
24,516✔
1065
{
1066
  m_pIterVal->increment();
24,516✔
1067
  if ( ++m_Index > m_pArray->ref_arr.size() )
24,516✔
1068
    return nullptr;
2,633✔
1069

1070
  BObjectRef& objref = m_pArray->ref_arr[m_Index - 1];
21,883✔
1071
  BObject* elem = objref.get();
21,883✔
1072
  if ( elem == nullptr )
21,883✔
1073
  {
1074
    elem = new BObject( UninitObject::create() );
18✔
1075
    objref.set( elem );
18✔
1076
  }
1077
  return elem;
21,883✔
1078
}
1079

1080
ContIterator* BObjectImp::createIterator( BObject* /*pIterVal*/ )
24✔
1081
{
1082
  return new ContIterator();
24✔
1083
}
1084
ContIterator* ObjArray::createIterator( BObject* pIterVal )
2,736✔
1085
{
1086
  auto pItr = new ArrayIterator( this, pIterVal );
2,736✔
1087
  return pItr;
2,736✔
1088
}
1089

1090
/* Coming into initforeach, the expr to be iterated through is on the value stack.
1091
   Initforeach must create three local variables:
1092
   0. the iterator
1093
   1. the expression
1094
   2. the counter
1095
   and remove the expression from the value stack.
1096
   It then jumps to the STEPFOREACH instruction.
1097
   */
1098
void Executor::ins_initforeach( const Instruction& ins )
685✔
1099
{
1100
  Locals2->emplace_back( UninitObject::create() );  // the iterator
685✔
1101

1102
  auto pIterVal = new BObject( UninitObject::create() );
685✔
1103

1104
  // this is almost like popParam, only we don't want a copy.
1105
  BObjectRef objref = getObjRef();
685✔
1106
  ContIterator* pIter = objref->impptr()->createIterator( pIterVal );
685✔
1107
  Locals2->emplace_back( pIter );
685✔
1108

1109
  Locals2->emplace_back( pIterVal );
685✔
1110

1111
  // Jump to to the corresponding `stepforeach` instruction, advancing the iterator.
1112
  PC = ins.token.lval;
685✔
1113
}
685✔
1114

1115
void Executor::ins_stepforeach( const Instruction& ins )
18,437✔
1116
{
1117
  size_t locsize = Locals2->size();
18,437✔
1118
  ContIterator* pIter = ( *Locals2 )[locsize - 2]->impptr<ContIterator>();
18,437✔
1119

1120
  BObject* next = pIter->step();
18,437✔
1121
  // If iterator has a value, set it on the locals stack and jump to the
1122
  // corresponding instruction after `initforeach`.
1123
  if ( next != nullptr )
18,437✔
1124
  {
1125
    ( *Locals2 )[locsize - 3].set( next );
17,828✔
1126
    PC = ins.token.lval;
17,828✔
1127
  }
1128
}
18,437✔
1129

1130
/*
1131
    Coming into the INITFOR, there will be two values on the value stack:
1132
    START VALUE
1133
    END VALUE
1134

1135
    If START VALUE > END VALUE, we skip the whole for loop.
1136
    (the INITFOR's lval has the instr to jump to)
1137
    */
1138
void Executor::ins_initfor( const Instruction& ins )
62✔
1139
{
1140
  BObjectRef endref = getObjRef();
62✔
1141
  BObjectRef startref = getObjRef();
62✔
1142
  if ( *startref.get() > *endref.get() )
62✔
1143
  {
1144
    PC = ins.token.lval;
×
1145
    return;
×
1146
  }
1147

1148
  Locals2->emplace_back( startref->clone() );  // the iterator
62✔
1149
  Locals2->emplace_back( endref->clone() );
62✔
1150
}
62✔
1151

1152
void Executor::ins_nextfor( const Instruction& ins )
6,234✔
1153
{
1154
  size_t locsize = Locals2->size();
6,234✔
1155
  BObjectImp* itr = ( *Locals2 )[locsize - 2]->impptr();
6,234✔
1156
  BObjectImp* end = ( *Locals2 )[locsize - 1]->impptr();
6,234✔
1157

1158
  if ( auto* l = impptrIf<BLong>( itr ) )
6,234✔
1159
    l->increment();
6,234✔
1160
  else if ( auto* d = impptrIf<Double>( itr ) )
×
1161
    d->increment();
×
1162

1163
  if ( *end >= *itr )
6,234✔
1164
  {
1165
    PC = ins.token.lval;
6,182✔
1166
  }
1167
}
6,234✔
1168

1169
int Executor::ins_casejmp_findlong( const Token& token, BLong* blong )
148✔
1170
{
1171
  const unsigned char* dataptr = token.dataptr;
148✔
1172
  for ( ;; )
1173
  {
1174
    unsigned short offset;
1175
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
329✔
1176
    dataptr += 2;
329✔
1177
    unsigned char type = *dataptr;
329✔
1178
    dataptr += 1;
329✔
1179
    if ( type == CASE_TYPE_LONG )
329✔
1180
    {
1181
      int v = blong->value();
248✔
1182
      if ( std::memcmp( &v, dataptr, sizeof( int ) ) == 0 )
248✔
1183
        return offset;
115✔
1184
      dataptr += 4;
133✔
1185
    }
1186
    else if ( type == CASE_TYPE_DEFAULT )
81✔
1187
    {
1188
      return offset;
33✔
1189
    }
1190
    else if ( type == CASE_TYPE_UNINIT )
48✔
1191
    {
1192
      /* nothing */
1193
    }
1194
    else if ( type == CASE_TYPE_BOOL )
48✔
1195
    {
1196
      dataptr += 1;
×
1197
    }
1198
    else if ( type == CASE_TYPE_STRING )
48✔
1199
    {
1200
      unsigned char len = *dataptr;
48✔
1201
      dataptr += 1 + len;
48✔
1202
    }
1203
  }
181✔
1204
}
1205

1206
int Executor::ins_casejmp_findbool( const Token& token, BBoolean* bbool )
15✔
1207
{
1208
  const unsigned char* dataptr = token.dataptr;
15✔
1209
  for ( ;; )
1210
  {
1211
    unsigned short offset;
1212
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
45✔
1213
    dataptr += 2;
45✔
1214
    unsigned char type = *dataptr;
45✔
1215
    dataptr += 1;
45✔
1216
    if ( type == CASE_TYPE_LONG )
45✔
1217
    {
1218
      dataptr += 4;
24✔
1219
    }
1220
    else if ( type == CASE_TYPE_DEFAULT )
21✔
1221
    {
1222
      return offset;
15✔
1223
    }
1224
    else if ( type == CASE_TYPE_UNINIT )
21✔
1225
    {
1226
      /* nothing */
1227
    }
1228
    else if ( type == CASE_TYPE_BOOL )
21✔
1229
    {
1230
      bool value = static_cast<bool>( *dataptr );
21✔
1231
      dataptr += 1;
21✔
1232
      if ( value == bbool->value() )
21✔
1233
      {
1234
        return offset;
15✔
1235
      }
1236
    }
1237
    else if ( type == CASE_TYPE_STRING )
×
1238
    {
1239
      unsigned char len = *dataptr;
×
1240
      dataptr += 1 + len;
×
1241
    }
1242
  }
30✔
1243
}
1244

1245
int Executor::ins_casejmp_finduninit( const Token& token )
6✔
1246
{
1247
  const unsigned char* dataptr = token.dataptr;
6✔
1248
  for ( ;; )
1249
  {
1250
    unsigned short offset;
1251
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
30✔
1252
    dataptr += 2;
30✔
1253
    unsigned char type = *dataptr;
30✔
1254
    dataptr += 1;
30✔
1255
    if ( type == CASE_TYPE_LONG )
30✔
1256
    {
1257
      dataptr += 4;
12✔
1258
    }
1259
    else if ( type == CASE_TYPE_DEFAULT )
18✔
1260
    {
1261
      return offset;
6✔
1262
    }
1263
    else if ( type == CASE_TYPE_UNINIT )
18✔
1264
    {
1265
      return offset;
6✔
1266
    }
1267
    else if ( type == CASE_TYPE_BOOL )
12✔
1268
    {
1269
      dataptr += 1;
12✔
1270
    }
1271
    else if ( type == CASE_TYPE_STRING )
×
1272
    {
1273
      unsigned char len = *dataptr;
×
1274
      dataptr += 1 + len;
×
1275
    }
1276
  }
24✔
1277
}
1278

1279
int Executor::ins_casejmp_findstring( const Token& token, String* bstringimp )
141✔
1280
{
1281
  const std::string& bstring = bstringimp->value();
141✔
1282
  const unsigned char* dataptr = token.dataptr;
141✔
1283
  for ( ;; )
1284
  {
1285
    unsigned short offset;
1286
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
333✔
1287
    dataptr += 2;
333✔
1288
    unsigned char type = *dataptr;
333✔
1289
    dataptr += 1;
333✔
1290
    if ( type == CASE_TYPE_LONG )
333✔
1291
    {
1292
      dataptr += 4;
36✔
1293
    }
1294
    else if ( type == CASE_TYPE_DEFAULT )
297✔
1295
    {
1296
      return offset;
141✔
1297
    }
1298
    else if ( type == CASE_TYPE_BOOL )
275✔
1299
    {
1300
      dataptr += 1;
12✔
1301
    }
1302
    else if ( type == CASE_TYPE_UNINIT )
263✔
1303
    {
1304
      /* nothing */
1305
    }
1306
    else if ( type == CASE_TYPE_STRING )
257✔
1307
    {
1308
      unsigned char len = *dataptr;
257✔
1309
      dataptr += 1;
257✔
1310
      if ( bstring.size() == len && memcmp( bstring.data(), dataptr, len ) == 0 )
257✔
1311
      {
1312
        return offset;
119✔
1313
      }
1314
      dataptr += len;
138✔
1315
    }
1316
  }
192✔
1317
}
1318

1319
int Executor::ins_casejmp_finddefault( const Token& token )
11✔
1320
{
1321
  const unsigned char* dataptr = token.dataptr;
11✔
1322
  for ( ;; )
1323
  {
1324
    unsigned short offset;
1325
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
52✔
1326
    dataptr += 2;
52✔
1327
    unsigned char type = *dataptr;
52✔
1328
    dataptr += 1;
52✔
1329
    if ( type == CASE_TYPE_LONG )
52✔
1330
    {
1331
      dataptr += 4;
17✔
1332
    }
1333
    else if ( type == CASE_TYPE_DEFAULT )
35✔
1334
    {
1335
      return offset;
11✔
1336
    }
1337
    else if ( type == CASE_TYPE_UNINIT )
24✔
1338
    {
1339
      /* nothing */
1340
    }
1341
    else if ( type == CASE_TYPE_BOOL )
18✔
1342
    {
1343
      dataptr += 1;
12✔
1344
    }
1345
    else if ( type == CASE_TYPE_STRING )
6✔
1346
    {
1347
      unsigned char len = *dataptr;
6✔
1348
      dataptr += 1 + len;
6✔
1349
    }
1350
  }
41✔
1351
}
1352

1353
void Executor::ins_casejmp( const Instruction& ins )
321✔
1354
{
1355
  BObjectRef& objref = ValueStack.back();
321✔
1356
  BObjectImp* objimp = objref->impptr();
321✔
1357
  if ( auto* l = impptrIf<BLong>( objimp ) )
321✔
1358
    PC = ins_casejmp_findlong( ins.token, l );
148✔
1359
  else if ( auto* s = impptrIf<String>( objimp ) )
173✔
1360
    PC = ins_casejmp_findstring( ins.token, s );
141✔
1361
  else if ( auto* b = impptrIf<BBoolean>( objimp ) )
32✔
1362
    PC = ins_casejmp_findbool( ins.token, b );
15✔
1363
  else if ( impptrIf<UninitObject>( objimp ) )
17✔
1364
    PC = ins_casejmp_finduninit( ins.token );
6✔
1365
  else
1366
    PC = ins_casejmp_finddefault( ins.token );
11✔
1367
  ValueStack.pop_back();
321✔
1368
}
321✔
1369

1370

1371
void Executor::ins_jmpiftrue( const Instruction& ins )
3,114✔
1372
{
1373
  BObjectRef& objref = ValueStack.back();
3,114✔
1374

1375
  if ( objref->impptr()->isTrue() )
3,114✔
1376
    PC = (unsigned)ins.token.lval;
2,976✔
1377

1378
  ValueStack.pop_back();
3,114✔
1379
}
3,114✔
1380

1381
void Executor::ins_jmpiffalse( const Instruction& ins )
37,548✔
1382
{
1383
  BObjectRef& objref = ValueStack.back();
37,548✔
1384

1385
  if ( !objref->impptr()->isTrue() )
37,548✔
1386
    PC = (unsigned)ins.token.lval;
27,895✔
1387

1388
  ValueStack.pop_back();
37,548✔
1389
}
37,548✔
1390

1391
void Executor::ins_interpolate_string( const Instruction& ins )
10,394✔
1392
{
1393
  auto count = ins.token.lval;
10,394✔
1394
  if ( count == 0 )
10,394✔
1395
  {
1396
    ValueStack.emplace_back( new String( "" ) );
3✔
1397
  }
1398
  else
1399
  {
1400
    size_t length = 0;
10,391✔
1401

1402
    std::vector<std::string> contents;
10,391✔
1403
    contents.reserve( count );
10,391✔
1404

1405
    while ( count-- )
37,602✔
1406
    {
1407
      BObjectRef rightref = ValueStack.back();
27,211✔
1408
      ValueStack.pop_back();
27,211✔
1409
      auto str = rightref->impptr()->getStringRep();
27,211✔
1410
      length += str.length();
27,211✔
1411
      contents.push_back( std::move( str ) );
27,211✔
1412
    }
27,211✔
1413

1414
    std::string joined;
10,391✔
1415
    joined.reserve( length );
10,391✔
1416

1417
    while ( !contents.empty() )
37,602✔
1418
    {
1419
      joined += contents.back();
27,211✔
1420
      contents.pop_back();
27,211✔
1421
    }
1422

1423
    ValueStack.emplace_back( new String( joined ) );
10,391✔
1424
  }
10,391✔
1425
}
10,394✔
1426

1427
void Executor::ins_format_expression( const Instruction& )
196✔
1428
{
1429
  BObjectRef formatref = ValueStack.back();
196✔
1430
  ValueStack.pop_back();
196✔
1431
  BObjectRef& exprref = ValueStack.back();
196✔
1432
  BObject& expr = *exprref;
196✔
1433

1434
  auto format = formatref->impptr()->getFormattedStringRep();
196✔
1435
  auto formatted = Bscript::get_formatted( expr.impptr(), format );
196✔
1436

1437
  exprref.set( new String( formatted ) );
196✔
1438
}
196✔
1439

1440
void Executor::ins_skipiftrue_else_consume( const Instruction& ins )
1,862✔
1441
{
1442
  // This is for short-circuit evaluation of the elvis operator [expr_a] ?: [expr_b]
1443
  //
1444
  // Program instructions look like this:
1445
  //   [expr_a instructions] INS_SKIPIFTRUE_ELSE_CONSUME [expr_b instructions]
1446
  //
1447
  // The result value of expr_a is on the top of the value stack when this instruction executes.
1448
  //
1449
  // If [expr_a] evaluated to true, leave its result and skip over the expr_b instructions
1450
  // otherwise, consume the false value and continue so that expr_b can replace it.
1451
  //
1452
  BObjectRef& objref = ValueStack.back();
1,862✔
1453

1454
  if ( objref->impptr()->isTrue() )
1,862✔
1455
  {
1456
    PC = PC + (unsigned)( ins.token.lval );
850✔
1457
  }
1458
  else
1459
  {
1460
    ValueStack.pop_back();
1,012✔
1461
  }
1462
}
1,862✔
1463

1464

1465
// case TOK_LOCALVAR:
1466
void Executor::ins_localvar( const Instruction& ins )
185,120✔
1467
{
1468
  ValueStack.push_back( ( *Locals2 )[ins.token.lval] );
185,120✔
1469
}
185,120✔
1470

1471
// case RSV_GLOBAL:
1472
// case TOK_GLOBALVAR:
1473
void Executor::ins_globalvar( const Instruction& ins )
35,873✔
1474
{
1475
  ValueStack.push_back( ( *Globals2 )[ins.token.lval] );
35,873✔
1476
}
35,873✔
1477

1478
// case TOK_LONG:
1479
void Executor::ins_long( const Instruction& ins )
37,048,687✔
1480
{
1481
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
37,048,687✔
1482
}
37,048,687✔
1483

1484
// case TOK_BOOL:
1485
void Executor::ins_bool( const Instruction& ins )
349✔
1486
{
1487
  ValueStack.emplace_back( new BBoolean( ins.token.lval ) );
349✔
1488
}
349✔
1489

1490
// case TOK_CONSUMER:
1491
void Executor::ins_consume( const Instruction& /*ins*/ )
37,020,551✔
1492
{
1493
  ValueStack.pop_back();
37,020,551✔
1494
}
37,020,551✔
1495

1496
void Executor::ins_set_member( const Instruction& ins )
3✔
1497
{
1498
  BObjectRef rightref = ValueStack.back();
3✔
1499
  ValueStack.pop_back();
3✔
1500
  BObjectRef& leftref = ValueStack.back();
3✔
1501

1502
  BObject& right = *rightref;
3✔
1503
  BObject& left = *leftref;
3✔
1504

1505
  BObjectImp& rightimpref = right.impref();
3✔
1506
  left.impref().set_member( ins.token.tokval(), &rightimpref,
6✔
1507
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
3✔
1508
}
3✔
1509

1510
void Executor::ins_set_member_id( const Instruction& ins )
3✔
1511
{
1512
  BObjectRef rightref = ValueStack.back();
3✔
1513
  ValueStack.pop_back();
3✔
1514
  BObjectRef& leftref = ValueStack.back();
3✔
1515

1516
  BObject& right = *rightref;
3✔
1517
  BObject& left = *leftref;
3✔
1518

1519
  BObjectImp& rightimpref = right.impref();
3✔
1520
  left.impref().set_member_id( ins.token.lval, &rightimpref,
6✔
1521
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
3✔
1522
}
3✔
1523

1524
void Executor::ins_set_member_consume( const Instruction& ins )
454✔
1525
{
1526
  BObjectRef rightref = ValueStack.back();
454✔
1527
  ValueStack.pop_back();
454✔
1528
  BObjectRef& leftref = ValueStack.back();
454✔
1529

1530
  BObject& right = *rightref;
454✔
1531
  BObject& left = *leftref;
454✔
1532

1533
  BObjectImp& rightimpref = right.impref();
454✔
1534
  left.impref().set_member( ins.token.tokval(), &rightimpref,
908✔
1535
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
454✔
1536
  ValueStack.pop_back();
454✔
1537
}
454✔
1538

1539
void Executor::ins_set_member_id_consume( const Instruction& ins )
225✔
1540
{
1541
  BObjectRef rightref = ValueStack.back();
225✔
1542
  ValueStack.pop_back();
225✔
1543
  BObjectRef& leftref = ValueStack.back();
225✔
1544

1545
  BObject& right = *rightref;
225✔
1546
  BObject& left = *leftref;
225✔
1547

1548
  BObjectImp& rightimpref = right.impref();
225✔
1549

1550
  left.impref().set_member_id( ins.token.lval, &rightimpref,
450✔
1551
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
225✔
1552
  ValueStack.pop_back();
225✔
1553
}
225✔
1554

1555
void Executor::ins_set_member_id_consume_plusequal( const Instruction& ins )
15✔
1556
{
1557
  BObjectRef rightref = ValueStack.back();
15✔
1558
  ValueStack.pop_back();
15✔
1559
  BObjectRef& leftref = ValueStack.back();
15✔
1560

1561
  BObject& right = *rightref;
15✔
1562
  BObject& left = *leftref;
15✔
1563

1564
  BObjectImp& leftimpref = left.impref();
15✔
1565

1566
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15✔
1567

1568
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
21✔
1569
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1570
  {
1571
    tmp->impref().operPlusEqual( *tmp, right.impref() );
6✔
1572
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1573
  }
1574
  ValueStack.pop_back();
15✔
1575
}
15✔
1576

1577
void Executor::ins_set_member_id_consume_minusequal( const Instruction& ins )
6✔
1578
{
1579
  BObjectRef rightref = ValueStack.back();
6✔
1580
  ValueStack.pop_back();
6✔
1581
  BObjectRef& leftref = ValueStack.back();
6✔
1582

1583
  BObject& right = *rightref;
6✔
1584
  BObject& left = *leftref;
6✔
1585

1586
  BObjectImp& leftimpref = left.impref();
6✔
1587

1588
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1589

1590
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1591
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1592
  {
1593
    tmp->impref().operMinusEqual( *tmp, right.impref() );
6✔
1594
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1595
  }
1596
  ValueStack.pop_back();
6✔
1597
}
6✔
1598

1599
void Executor::ins_set_member_id_consume_timesequal( const Instruction& ins )
9✔
1600
{
1601
  BObjectRef rightref = ValueStack.back();
9✔
1602
  ValueStack.pop_back();
9✔
1603
  BObjectRef& leftref = ValueStack.back();
9✔
1604

1605
  BObject& right = *rightref;
9✔
1606
  BObject& left = *leftref;
9✔
1607

1608
  BObjectImp& leftimpref = left.impref();
9✔
1609

1610
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
9✔
1611

1612
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
18✔
1613
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
9✔
1614
  {
1615
    tmp->impref().operTimesEqual( *tmp, right.impref() );
9✔
1616
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
9✔
1617
  }
1618
  ValueStack.pop_back();
9✔
1619
}
9✔
1620

1621
void Executor::ins_set_member_id_consume_divideequal( const Instruction& ins )
6✔
1622
{
1623
  BObjectRef rightref = ValueStack.back();
6✔
1624
  ValueStack.pop_back();
6✔
1625
  BObjectRef& leftref = ValueStack.back();
6✔
1626

1627
  BObject& right = *rightref;
6✔
1628
  BObject& left = *leftref;
6✔
1629

1630
  BObjectImp& leftimpref = left.impref();
6✔
1631

1632
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1633

1634
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1635
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1636
  {
1637
    tmp->impref().operDivideEqual( *tmp, right.impref() );
6✔
1638
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1639
  }
1640
  ValueStack.pop_back();
6✔
1641
}
6✔
1642

1643
void Executor::ins_set_member_id_consume_modulusequal( const Instruction& ins )
6✔
1644
{
1645
  BObjectRef rightref = ValueStack.back();
6✔
1646
  ValueStack.pop_back();
6✔
1647
  BObjectRef& leftref = ValueStack.back();
6✔
1648

1649
  BObject& right = *rightref;
6✔
1650
  BObject& left = *leftref;
6✔
1651

1652
  BObjectImp& leftimpref = left.impref();
6✔
1653

1654
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1655

1656
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1657
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1658
  {
1659
    tmp->impref().operModulusEqual( *tmp, right.impref() );
6✔
1660
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1661
  }
1662
  ValueStack.pop_back();
6✔
1663
}
6✔
1664

1665
void Executor::ins_get_member( const Instruction& ins )
4,371✔
1666
{
1667
  BObjectRef& leftref = ValueStack.back();
4,371✔
1668
  BObject& left = *leftref;
4,371✔
1669
  {
1670
    ESCRIPT_PROFILER( ins, leftref, fparams );
1671
    leftref = left->get_member( ins.token.tokval() );
4,371✔
1672
  }
1673
}
4,371✔
1674

1675
void Executor::ins_get_member_id( const Instruction& ins )
15,256✔
1676
{
1677
  BObjectRef& leftref = ValueStack.back();
15,256✔
1678
  BObject& left = *leftref;
15,256✔
1679
  {
1680
    ESCRIPT_PROFILER( ins, leftref, fparams );
1681
    leftref = left->get_member_id( ins.token.lval );
15,256✔
1682
  }
1683
}
15,256✔
1684

1685
void Executor::ins_assign_localvar( const Instruction& ins )
6,283✔
1686
{
1687
  BObjectRef& lvar = ( *Locals2 )[ins.token.lval];
6,283✔
1688

1689
  BObjectRef& rightref = ValueStack.back();
6,283✔
1690

1691
  BObject& right = *rightref;
6,283✔
1692

1693
  BObjectImp& rightimpref = right.impref();
6,283✔
1694

1695
  if ( right.count() == 1 && rightimpref.count() == 1 )
6,283✔
1696
  {
1697
    lvar->setimp( &rightimpref );
5,531✔
1698
  }
1699
  else
1700
  {
1701
    lvar->setimp( rightimpref.copy() );
752✔
1702
  }
1703
  ValueStack.pop_back();
6,283✔
1704
}
6,283✔
1705
void Executor::ins_assign_globalvar( const Instruction& ins )
1,102✔
1706
{
1707
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
1,102✔
1708

1709
  BObjectRef& rightref = ValueStack.back();
1,102✔
1710

1711
  BObject& right = *rightref;
1,102✔
1712

1713
  BObjectImp& rightimpref = right.impref();
1,102✔
1714

1715
  if ( right.count() == 1 && rightimpref.count() == 1 )
1,102✔
1716
  {
1717
    gvar->setimp( &rightimpref );
1,025✔
1718
  }
1719
  else
1720
  {
1721
    gvar->setimp( rightimpref.copy() );
77✔
1722
  }
1723
  ValueStack.pop_back();
1,102✔
1724
}
1,102✔
1725

1726
// case INS_ASSIGN_CONSUME:
1727
void Executor::ins_assign_consume( const Instruction& /*ins*/ )
66✔
1728
{
1729
  BObjectRef rightref = ValueStack.back();
66✔
1730
  ValueStack.pop_back();
66✔
1731
  BObjectRef& leftref = ValueStack.back();
66✔
1732

1733
  BObject& right = *rightref;
66✔
1734
  BObject& left = *leftref;
66✔
1735

1736
  BObjectImp& rightimpref = right.impref();
66✔
1737

1738
  if ( right.count() == 1 && rightimpref.count() == 1 )
66✔
1739
  {
1740
    left.setimp( &rightimpref );
63✔
1741
  }
1742
  else
1743
  {
1744
    left.setimp( rightimpref.copy() );
3✔
1745
  }
1746
  ValueStack.pop_back();
66✔
1747
}
66✔
1748

1749
void Executor::ins_assign( const Instruction& /*ins*/ )
28,592✔
1750
{
1751
  /*
1752
      These each take two operands, and replace them with one.
1753
      We'll leave the second one on the value stack, and
1754
      just replace its object with the result
1755
      */
1756
  BObjectRef rightref = ValueStack.back();
28,592✔
1757
  ValueStack.pop_back();
28,592✔
1758
  BObjectRef& leftref = ValueStack.back();
28,592✔
1759

1760
  BObject& right = *rightref;
28,592✔
1761
  BObject& left = *leftref;
28,592✔
1762

1763
  BObjectImp& rightimpref = right.impref();
28,592✔
1764

1765
  if ( right.count() == 1 && rightimpref.count() == 1 )
28,592✔
1766
  {
1767
    left.setimp( &rightimpref );
27,015✔
1768
  }
1769
  else
1770
  {
1771
    left.setimp( rightimpref.copy() );
1,577✔
1772
  }
1773
}
28,592✔
1774

1775
void Executor::ins_array_assign( const Instruction& /*ins*/ )
9✔
1776
{
1777
  /*
1778
      on the value stack:
1779
      x[i] := y;
1780
      (top)
1781
      y
1782
      i
1783
      x
1784
      upon exit:
1785
      (x[i])
1786
      */
1787
  BObjectRef y_ref = ValueStack.back();
9✔
1788
  ValueStack.pop_back();
9✔
1789
  BObjectRef i_ref = ValueStack.back();
9✔
1790
  ValueStack.pop_back();
9✔
1791
  BObjectRef& x_ref = ValueStack.back();
9✔
1792

1793
  BObject& y = *y_ref;
9✔
1794
  BObject& i = *i_ref;
9✔
1795
  BObject& x = *x_ref;
9✔
1796

1797
  BObjectImp* result;
1798
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
9✔
1799

1800
  x_ref.set( result );
9✔
1801
}
9✔
1802
void Executor::ins_array_assign_consume( const Instruction& /*ins*/ )
669✔
1803
{
1804
  /*
1805
      on the value stack:
1806
      x[i] := y;
1807
      (top)
1808
      y
1809
      i
1810
      x
1811
      upon exit:
1812
      (x[i])
1813
      */
1814
  BObjectRef y_ref = ValueStack.back();
669✔
1815
  ValueStack.pop_back();
669✔
1816
  BObjectRef i_ref = ValueStack.back();
669✔
1817
  ValueStack.pop_back();
669✔
1818
  BObjectRef& x_ref = ValueStack.back();
669✔
1819

1820
  BObject& y = *y_ref;
669✔
1821
  BObject& i = *i_ref;
669✔
1822
  BObject& x = *x_ref;
669✔
1823

1824
  BObjectImp* result;
1825
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
669✔
1826

1827
  BObject obj( result );
669✔
1828
  ValueStack.pop_back();
669✔
1829
}
669✔
1830

1831
// TOK_ADD:
1832
void Executor::ins_add( const Instruction& /*ins*/ )
22,478✔
1833
{
1834
  /*
1835
      These each take two operands, and replace them with one.
1836
      We'll leave the second one on the value stack, and
1837
      just replace its object with the result
1838
      */
1839
  BObjectRef rightref = ValueStack.back();
22,478✔
1840
  ValueStack.pop_back();
22,478✔
1841
  BObjectRef& leftref = ValueStack.back();
22,478✔
1842

1843
  BObject& right = *rightref;
22,478✔
1844
  BObject& left = *leftref;
22,478✔
1845

1846
  leftref.set( right.impref().selfPlusObjImp( left.impref() ) );
22,478✔
1847
}
22,478✔
1848

1849
// TOK_SUBTRACT
1850
void Executor::ins_subtract( const Instruction& /*ins*/ )
3,467✔
1851
{
1852
  /*
1853
      These each take two operands, and replace them with one.
1854
      We'll leave the second one on the value stack, and
1855
      just replace its object with the result
1856
      */
1857
  BObjectRef rightref = ValueStack.back();
3,467✔
1858
  ValueStack.pop_back();
3,467✔
1859
  BObjectRef& leftref = ValueStack.back();
3,467✔
1860

1861
  BObject& right = *rightref;
3,467✔
1862
  BObject& left = *leftref;
3,467✔
1863

1864
  leftref.set( right.impref().selfMinusObjImp( left.impref() ) );
3,467✔
1865
}
3,467✔
1866

1867
// TOK_MULT:
1868
void Executor::ins_mult( const Instruction& /*ins*/ )
1,526✔
1869
{
1870
  /*
1871
      These each take two operands, and replace them with one.
1872
      We'll leave the second one on the value stack, and
1873
      just replace its object with the result
1874
      */
1875
  BObjectRef rightref = ValueStack.back();
1,526✔
1876
  ValueStack.pop_back();
1,526✔
1877
  BObjectRef& leftref = ValueStack.back();
1,526✔
1878

1879
  BObject& right = *rightref;
1,526✔
1880
  BObject& left = *leftref;
1,526✔
1881

1882
  leftref.set( right.impref().selfTimesObjImp( left.impref() ) );
1,526✔
1883
}
1,526✔
1884
// TOK_DIV:
1885
void Executor::ins_div( const Instruction& /*ins*/ )
332✔
1886
{
1887
  /*
1888
      These each take two operands, and replace them with one.
1889
      We'll leave the second one on the value stack, and
1890
      just replace its object with the result
1891
      */
1892
  BObjectRef rightref = ValueStack.back();
332✔
1893
  ValueStack.pop_back();
332✔
1894
  BObjectRef& leftref = ValueStack.back();
332✔
1895

1896
  BObject& right = *rightref;
332✔
1897
  BObject& left = *leftref;
332✔
1898

1899
  leftref.set( right.impref().selfDividedByObjImp( left.impref() ) );
332✔
1900
}
332✔
1901
// TOK_MODULUS:
1902
void Executor::ins_modulus( const Instruction& /*ins*/ )
495✔
1903
{
1904
  /*
1905
      These each take two operands, and replace them with one.
1906
      We'll leave the second one on the value stack, and
1907
      just replace its object with the result
1908
      */
1909
  BObjectRef rightref = ValueStack.back();
495✔
1910
  ValueStack.pop_back();
495✔
1911
  BObjectRef& leftref = ValueStack.back();
495✔
1912

1913
  BObject& right = *rightref;
495✔
1914
  BObject& left = *leftref;
495✔
1915

1916
  leftref.set( right.impref().selfModulusObjImp( left.impref() ) );
495✔
1917
}
495✔
1918

1919
// TOK_IS:
1920
void Executor::ins_is( const Instruction& /*ins*/ )
100✔
1921
{
1922
  BObjectRef rightref = ValueStack.back();
100✔
1923
  ValueStack.pop_back();
100✔
1924
  BObjectRef& leftref = ValueStack.back();
100✔
1925

1926
  BObject& right = *rightref;
100✔
1927
  BObject& left = *leftref;
100✔
1928

1929
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100✔
1930
}
100✔
1931

1932
// TOK_BSRIGHT:
1933
void Executor::ins_bitshift_right( const Instruction& /*ins*/ )
297✔
1934
{
1935
  /*
1936
      These each take two operands, and replace them with one.
1937
      We'll leave the second one on the value stack, and
1938
      just replace its object with the result
1939
      */
1940
  BObjectRef rightref = ValueStack.back();
297✔
1941
  ValueStack.pop_back();
297✔
1942
  BObjectRef& leftref = ValueStack.back();
297✔
1943

1944
  BObject& right = *rightref;
297✔
1945
  BObject& left = *leftref;
297✔
1946

1947
  leftref.set( right.impref().selfBitShiftRightObjImp( left.impref() ) );
297✔
1948
}
297✔
1949
// TOK_BSLEFT:
1950
void Executor::ins_bitshift_left( const Instruction& /*ins*/ )
297✔
1951
{
1952
  /*
1953
      These each take two operands, and replace them with one.
1954
      We'll leave the second one on the value stack, and
1955
      just replace its object with the result
1956
      */
1957
  BObjectRef rightref = ValueStack.back();
297✔
1958
  ValueStack.pop_back();
297✔
1959
  BObjectRef& leftref = ValueStack.back();
297✔
1960

1961
  BObject& right = *rightref;
297✔
1962
  BObject& left = *leftref;
297✔
1963

1964
  leftref.set( right.impref().selfBitShiftLeftObjImp( left.impref() ) );
297✔
1965
}
297✔
1966
// TOK_BITAND:
1967
void Executor::ins_bitwise_and( const Instruction& /*ins*/ )
300✔
1968
{
1969
  /*
1970
      These each take two operands, and replace them with one.
1971
      We'll leave the second one on the value stack, and
1972
      just replace its object with the result
1973
      */
1974
  BObjectRef rightref = ValueStack.back();
300✔
1975
  ValueStack.pop_back();
300✔
1976
  BObjectRef& leftref = ValueStack.back();
300✔
1977

1978
  BObject& right = *rightref;
300✔
1979
  BObject& left = *leftref;
300✔
1980

1981
  leftref.set( right.impref().selfBitAndObjImp( left.impref() ) );
300✔
1982
}
300✔
1983
// TOK_BITXOR:
1984
void Executor::ins_bitwise_xor( const Instruction& /*ins*/ )
297✔
1985
{
1986
  /*
1987
      These each take two operands, and replace them with one.
1988
      We'll leave the second one on the value stack, and
1989
      just replace its object with the result
1990
      */
1991
  BObjectRef rightref = ValueStack.back();
297✔
1992
  ValueStack.pop_back();
297✔
1993
  BObjectRef& leftref = ValueStack.back();
297✔
1994

1995
  BObject& right = *rightref;
297✔
1996
  BObject& left = *leftref;
297✔
1997

1998
  leftref.set( right.impref().selfBitXorObjImp( left.impref() ) );
297✔
1999
}
297✔
2000
// TOK_BITOR:
2001
void Executor::ins_bitwise_or( const Instruction& /*ins*/ )
297✔
2002
{
2003
  /*
2004
      These each take two operands, and replace them with one.
2005
      We'll leave the second one on the value stack, and
2006
      just replace its object with the result
2007
      */
2008
  BObjectRef rightref = ValueStack.back();
297✔
2009
  ValueStack.pop_back();
297✔
2010
  BObjectRef& leftref = ValueStack.back();
297✔
2011

2012
  BObject& right = *rightref;
297✔
2013
  BObject& left = *leftref;
297✔
2014

2015
  leftref.set( right.impref().selfBitOrObjImp( left.impref() ) );
297✔
2016
}
297✔
2017

2018
void Executor::ins_logical_and( const Instruction& /*ins*/ )
5,949✔
2019
{
2020
  /*
2021
      These each take two operands, and replace them with one.
2022
      We'll leave the second one on the value stack, and
2023
      just replace its object with the result
2024
      */
2025
  BObjectRef rightref = ValueStack.back();
5,949✔
2026
  ValueStack.pop_back();
5,949✔
2027
  BObjectRef& leftref = ValueStack.back();
5,949✔
2028

2029
  BObject& right = *rightref;
5,949✔
2030
  BObject& left = *leftref;
5,949✔
2031

2032
  int _true = ( left.isTrue() && right.isTrue() );
5,949✔
2033
  leftref.set( new BLong( _true ) );
5,949✔
2034
}
5,949✔
2035
void Executor::ins_logical_or( const Instruction& /*ins*/ )
1,670✔
2036
{
2037
  /*
2038
      These each take two operands, and replace them with one.
2039
      We'll leave the second one on the value stack, and
2040
      just replace its object with the result
2041
      */
2042
  BObjectRef rightref = ValueStack.back();
1,670✔
2043
  ValueStack.pop_back();
1,670✔
2044
  BObjectRef& leftref = ValueStack.back();
1,670✔
2045

2046
  BObject& right = *rightref;
1,670✔
2047
  BObject& left = *leftref;
1,670✔
2048

2049
  int _true = ( left.isTrue() || right.isTrue() );
1,670✔
2050
  leftref.set( new BLong( _true ) );
1,670✔
2051
}
1,670✔
2052

2053
void Executor::ins_notequal( const Instruction& /*ins*/ )
27,291✔
2054
{
2055
  /*
2056
      These each take two operands, and replace them with one.
2057
      We'll leave the second one on the value stack, and
2058
      just replace its object with the result
2059
      */
2060
  BObjectRef rightref = ValueStack.back();
27,291✔
2061
  ValueStack.pop_back();
27,291✔
2062
  BObjectRef& leftref = ValueStack.back();
27,291✔
2063

2064
  BObject& right = *rightref;
27,291✔
2065
  BObject& left = *leftref;
27,291✔
2066

2067
  int _true = ( left != right );
27,291✔
2068
  leftref.set( new BLong( _true ) );
27,291✔
2069
}
27,291✔
2070

2071
void Executor::ins_equal( const Instruction& /*ins*/ )
8,075✔
2072
{
2073
  /*
2074
      These each take two operands, and replace them with one.
2075
      We'll leave the second one on the value stack, and
2076
      just replace its object with the result
2077
      */
2078
  BObjectRef rightref = ValueStack.back();
8,075✔
2079
  ValueStack.pop_back();
8,075✔
2080
  BObjectRef& leftref = ValueStack.back();
8,075✔
2081

2082
  BObject& right = *rightref;
8,075✔
2083
  BObject& left = *leftref;
8,075✔
2084

2085
  int _true = ( left == right );
8,075✔
2086
  leftref.set( new BLong( _true ) );
8,075✔
2087
}
8,075✔
2088

2089
void Executor::ins_lessthan( const Instruction& /*ins*/ )
4,214✔
2090
{
2091
  /*
2092
      These each take two operands, and replace them with one.
2093
      We'll leave the second one on the value stack, and
2094
      just replace its object with the result
2095
      */
2096
  BObjectRef rightref = ValueStack.back();
4,214✔
2097
  ValueStack.pop_back();
4,214✔
2098
  BObjectRef& leftref = ValueStack.back();
4,214✔
2099

2100
  BObject& right = *rightref;
4,214✔
2101
  BObject& left = *leftref;
4,214✔
2102

2103
  int _true = ( left < right );
4,214✔
2104
  leftref.set( new BLong( _true ) );
4,214✔
2105
}
4,214✔
2106

2107
void Executor::ins_lessequal( const Instruction& /*ins*/ )
803✔
2108
{
2109
  /*
2110
      These each take two operands, and replace them with one.
2111
      We'll leave the second one on the value stack, and
2112
      just replace its object with the result
2113
      */
2114
  BObjectRef rightref = ValueStack.back();
803✔
2115
  ValueStack.pop_back();
803✔
2116
  BObjectRef& leftref = ValueStack.back();
803✔
2117

2118
  BObject& right = *rightref;
803✔
2119
  BObject& left = *leftref;
803✔
2120
  int _true = ( left <= right );
803✔
2121
  leftref.set( new BLong( _true ) );
803✔
2122
}
803✔
2123
void Executor::ins_greaterthan( const Instruction& /*ins*/ )
5,957✔
2124
{
2125
  /*
2126
      These each take two operands, and replace them with one.
2127
      We'll leave the second one on the value stack, and
2128
      just replace its object with the result
2129
      */
2130
  BObjectRef rightref = ValueStack.back();
5,957✔
2131
  ValueStack.pop_back();
5,957✔
2132
  BObjectRef& leftref = ValueStack.back();
5,957✔
2133

2134
  BObject& right = *rightref;
5,957✔
2135
  BObject& left = *leftref;
5,957✔
2136

2137
  int _true = ( left > right );
5,957✔
2138
  leftref.set( new BLong( _true ) );
5,957✔
2139
}
5,957✔
2140
void Executor::ins_greaterequal( const Instruction& /*ins*/ )
416✔
2141
{
2142
  /*
2143
      These each take two operands, and replace them with one.
2144
      We'll leave the second one on the value stack, and
2145
      just replace its object with the result
2146
      */
2147
  BObjectRef rightref = ValueStack.back();
416✔
2148
  ValueStack.pop_back();
416✔
2149
  BObjectRef& leftref = ValueStack.back();
416✔
2150

2151
  BObject& right = *rightref;
416✔
2152
  BObject& left = *leftref;
416✔
2153

2154
  int _true = ( left >= right );
416✔
2155
  leftref.set( new BLong( _true ) );
416✔
2156
}
416✔
2157

2158
// case TOK_ARRAY_SUBSCRIPT:
2159
void Executor::ins_arraysubscript( const Instruction& /*ins*/ )
25,700✔
2160
{
2161
  /*
2162
      These each take two operands, and replace them with one.
2163
      We'll leave the second one on the value stack, and
2164
      just replace its object with the result
2165
      */
2166
  BObjectRef rightref = ValueStack.back();
25,700✔
2167
  ValueStack.pop_back();
25,700✔
2168
  BObjectRef& leftref = ValueStack.back();
25,700✔
2169

2170
  leftref = ( *leftref )->OperSubscript( *rightref );
25,700✔
2171
}
25,700✔
2172

2173
void Executor::ins_multisubscript( const Instruction& ins )
7,225✔
2174
{
2175
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2176
  std::stack<BObjectRef> indices;
7,225✔
2177
  for ( int i = 0; i < ins.token.lval; ++i )
21,675✔
2178
  {
2179
    indices.push( ValueStack.back() );
14,450✔
2180
    ValueStack.pop_back();
14,450✔
2181
  }
2182

2183
  BObjectRef& leftref = ValueStack.back();
7,225✔
2184
  leftref = ( *leftref )->OperMultiSubscript( indices );
7,225✔
2185
}
7,225✔
2186

2187
void Executor::ins_unpack_sequence( const Instruction& ins )
104✔
2188
{
2189
  bool rest = ins.token.lval >> 14;
104✔
2190
  auto count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
104✔
2191

2192
  BObjectRef refIter( new BObject( UninitObject::create() ) );
104✔
2193

2194
  BObjectRef rightref = ValueStack.back();
104✔
2195
  ValueStack.pop_back();
104✔
2196

2197
  // Reserve to keep the insert_at iterator valid
2198
  ValueStack.reserve( ValueStack.size() + count );
104✔
2199
  auto insert_at = ValueStack.begin() + ValueStack.size();
104✔
2200
  auto pIter = std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
104✔
2201

2202
  // The default iterator is used for non-iterable objects, so we can't unpack
2203
  // them. Simply add errors for each value to unpack.
2204
  if ( pIter->is_default() )
104✔
2205
  {
2206
    if ( rest )
6✔
2207
    {
2208
      auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
3✔
2209
      for ( u8 i = 0; i < count; ++i )
12✔
2210
        ValueStack.emplace( insert_at, new BError( i == rest_index ? "Invalid type for rest binding"
9✔
2211
                                                                   : "Index out of bounds" ) );
9✔
2212
    }
2213
    else
2214
    {
2215
      for ( u8 i = 0; i < count; ++i )
9✔
2216
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6✔
2217
    }
2218
    return;
6✔
2219
  }
2220

2221
  if ( rest )
98✔
2222
  {
2223
    auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
66✔
2224

2225
    // Add all elements up to the rest index
2226
    for ( u8 i = 0; i < rest_index; ++i )
147✔
2227
    {
2228
      if ( auto res = pIter->step() )
81✔
2229
        ValueStack.emplace( insert_at, res );
75✔
2230
      else
2231
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6✔
2232
    }
2233

2234
    // Add the rest array, and fill it with the remaining iterator elements
2235
    auto rest_array = ValueStack.emplace( insert_at, new ObjArray )->get()->impptr<ObjArray>();
66✔
2236

2237
    while ( auto res = pIter->step() )
306✔
2238
      rest_array->addElement( res->impptr() );
240✔
2239

2240
    // Take the remaining elements from the rest array to fill the rest of the bindings.
2241
    auto left = count - rest_index - 1;
66✔
2242

2243
    for ( u8 i = 0; i < left; ++i )
138✔
2244
    {
2245
      if ( rest_array->ref_arr.empty() )
72✔
2246
        ValueStack.emplace( insert_at + i, new BError( "Index out of bounds" ) );
12✔
2247
      else
2248
      {
2249
        ValueStack.insert( insert_at + i, rest_array->ref_arr.back() );
60✔
2250

2251
        rest_array->ref_arr.pop_back();
60✔
2252
      }
2253
    }
2254
  }
2255
  else
2256
  {
2257
    for ( u8 i = 0; i < count; ++i )
116✔
2258
    {
2259
      if ( auto res = pIter->step() )
84✔
2260
        ValueStack.emplace( insert_at, res );
84✔
2261
      else
2262
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
×
2263
    }
2264
  }
2265
}
116✔
2266

2267
void Executor::ins_unpack_indices( const Instruction& ins )
121✔
2268
{
2269
  bool rest = ins.token.lval >> 14;
121✔
2270
  auto binding_count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
121✔
2271
  // If there is a rest binding, there will be one less index than the binding
2272
  // count, as the rest binding has no corresponding element index access.
2273
  auto index_count = rest ? Clib::clamp_convert<u8>( binding_count - 1 ) : binding_count;
121✔
2274

2275
  // Ensure there is a ValueStack entry for each index, + 1 for the unpacking object ('rightref').
2276
  passert_r( static_cast<int>( ValueStack.size() ) >= index_count + 1,
121✔
2277
             "Not enough values to unpack" );
2278

2279
  if ( rest )
121✔
2280
  {
2281
    // Use a multi_index because we need to (1) iterate over the indexes in
2282
    // order of the bindings in the script, and (2) keep track of which indexes
2283
    // have been used.
2284
    using namespace boost::multi_index;
2285
    using OrderedSet =
2286
        multi_index_container<BObject,
2287
                              indexed_by<sequenced<>,  // Maintains insertion order
2288
                                         ordered_unique<identity<BObject>>  // Ensures uniqueness
2289
                                         >>;
2290

2291
    OrderedSet indexes;
39✔
2292

2293
    for ( u8 i = 0; i < index_count; ++i )
120✔
2294
    {
2295
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
81✔
2296
      ValueStack.pop_back();
81✔
2297
    }
2298

2299
    BObjectRef rightref = ValueStack.back();
39✔
2300
    ValueStack.pop_back();
39✔
2301

2302
    // Reserve to keep the insert_at iterator valid
2303
    ValueStack.reserve( ValueStack.size() + binding_count );
39✔
2304
    auto insert_at = ValueStack.end();
39✔
2305

2306
    for ( const auto& index : indexes )
120✔
2307
    {
2308
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
81✔
2309
    }
2310

2311
    // Rest object is always last element (validated by semantic analyzer), so
2312
    // no need to calculate `rest_index`.
2313
    std::unique_ptr<BObjectImp> rest_obj;
39✔
2314

2315
    BObjectRef refIter( UninitObject::create() );
39✔
2316
    auto pIter =
2317
        std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
39✔
2318

2319
    // The default iterator is used for non-iterable objects, so we can't unpack them.
2320
    if ( pIter->is_default() )
39✔
2321
    {
2322
      ValueStack.emplace( insert_at, new BError( "Invalid type for rest binding" ) );
15✔
2323
      return;
15✔
2324
    }
2325
    rest_obj = std::make_unique<BDictionary>();
24✔
2326

2327
    auto& unique_index = indexes.get<1>();
24✔
2328

2329
    while ( auto res = pIter->step() )
120✔
2330
    {
2331
      auto itr = unique_index.find( *refIter.get() );
96✔
2332

2333
      if ( itr == unique_index.end() )
96✔
2334
        rest_obj->array_assign( refIter->impptr(), res->impptr(), true );
45✔
2335
    }
96✔
2336
    ValueStack.emplace( insert_at, rest_obj.release() );
24✔
2337
  }
99✔
2338
  else
2339
  {
2340
    // If not using a rest binding, only keep track of binding order.
2341
    std::list<BObject> indexes;
82✔
2342

2343
    for ( u8 i = 0; i < index_count; ++i )
257✔
2344
    {
2345
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
175✔
2346
      ValueStack.pop_back();
175✔
2347
    }
2348

2349
    BObjectRef rightref = ValueStack.back();
82✔
2350
    ValueStack.pop_back();
82✔
2351

2352
    // Reserve to keep the insert_at iterator valid
2353
    ValueStack.reserve( ValueStack.size() + binding_count );
82✔
2354
    auto insert_at = ValueStack.end();
82✔
2355

2356
    for ( const auto& index : indexes )
257✔
2357
    {
2358
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
175✔
2359
    }
2360
  }
82✔
2361
}
2362

2363
void Executor::ins_take_local( const Instruction& )
388✔
2364
{
2365
  passert( Locals2 != nullptr );
388✔
2366
  passert( !ValueStack.empty() );
388✔
2367

2368
  // There is no entry in the locals vector, so create a new one.
2369
  Locals2->emplace_back( UninitObject::create() );
388✔
2370
  BObjectRef& lvar = ( *Locals2 ).back();
388✔
2371

2372
  BObjectRef& rightref = ValueStack.back();
388✔
2373

2374
  BObject& right = *rightref;
388✔
2375

2376
  BObjectImp& rightimpref = right.impref();
388✔
2377

2378
  if ( right.count() == 1 && rightimpref.count() == 1 )
388✔
2379
  {
2380
    lvar->setimp( &rightimpref );
244✔
2381
  }
2382
  else
2383
  {
2384
    lvar->setimp( rightimpref.copy() );
144✔
2385
  }
2386
  ValueStack.pop_back();
388✔
2387
}
388✔
2388

2389
void Executor::ins_take_global( const Instruction& ins )
183✔
2390
{
2391
  passert( !ValueStack.empty() );
183✔
2392

2393
  // Globals already have an entry in the globals vector, so just index into it.
2394
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
183✔
2395

2396
  BObjectRef& rightref = ValueStack.back();
183✔
2397

2398
  BObject& right = *rightref;
183✔
2399

2400
  BObjectImp& rightimpref = right.impref();
183✔
2401

2402
  if ( right.count() == 1 && rightimpref.count() == 1 )
183✔
2403
  {
2404
    gvar->setimp( &rightimpref );
150✔
2405
  }
2406
  else
2407
  {
2408
    gvar->setimp( rightimpref.copy() );
33✔
2409
  }
2410
  ValueStack.pop_back();
183✔
2411
}
183✔
2412

2413
void Executor::ins_multisubscript_assign( const Instruction& ins )
6✔
2414
{
2415
  BObjectRef target_ref = ValueStack.back();
6✔
2416
  ValueStack.pop_back();
6✔
2417
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2418
  std::stack<BObjectRef> indices;
6✔
2419
  for ( int i = 0; i < ins.token.lval; ++i )
18✔
2420
  {
2421
    indices.push( ValueStack.back() );
12✔
2422
    ValueStack.pop_back();
12✔
2423
  }
2424

2425
  BObjectRef& leftref = ValueStack.back();
6✔
2426
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
6✔
2427
}
6✔
2428

2429
void Executor::ins_addmember( const Instruction& /*ins*/ )
168✔
2430
{
2431
  /*
2432
      These each take two operands, and replace them with one.
2433
      We'll leave the second one on the value stack, and
2434
      just replace its object with the result
2435
      */
2436
  BObjectRef rightref = ValueStack.back();
168✔
2437
  ValueStack.pop_back();
168✔
2438
  BObjectRef& leftref = ValueStack.back();
168✔
2439

2440
  BObject& right = *rightref;
168✔
2441
  BObject& left = *leftref;
168✔
2442

2443
  leftref = addmember( left, right );
168✔
2444
}
168✔
2445

2446
void Executor::ins_removemember( const Instruction& /*ins*/ )
78✔
2447
{
2448
  /*
2449
      These each take two operands, and replace them with one.
2450
      We'll leave the second one on the value stack, and
2451
      just replace its object with the result
2452
      */
2453
  BObjectRef rightref = ValueStack.back();
78✔
2454
  ValueStack.pop_back();
78✔
2455
  BObjectRef& leftref = ValueStack.back();
78✔
2456

2457
  BObject& right = *rightref;
78✔
2458
  BObject& left = *leftref;
78✔
2459

2460
  leftref = removemember( left, right );
78✔
2461
}
78✔
2462

2463
void Executor::ins_checkmember( const Instruction& /*ins*/ )
1,613✔
2464
{
2465
  /*
2466
      These each take two operands, and replace them with one.
2467
      We'll leave the second one on the value stack, and
2468
      just replace its object with the result
2469
      */
2470
  BObjectRef rightref = ValueStack.back();
1,613✔
2471
  ValueStack.pop_back();
1,613✔
2472
  BObjectRef& leftref = ValueStack.back();
1,613✔
2473

2474
  BObject& right = *rightref;
1,613✔
2475
  BObject& left = *leftref;
1,613✔
2476

2477
  leftref = checkmember( left, right );
1,613✔
2478
}
1,613✔
2479

2480
void Executor::ins_addmember2( const Instruction& ins )
45✔
2481
{
2482
  BObjectRef& obref = ValueStack.back();
45✔
2483

2484
  BObject& ob = *obref;
45✔
2485

2486
  ob.impref().operDotPlus( ins.token.tokval() );
45✔
2487
}
45✔
2488

2489
void Executor::ins_addmember_assign( const Instruction& ins )
1,837✔
2490
{
2491
  BObjectRef valref = ValueStack.back();
1,837✔
2492
  BObject& valob = *valref;
1,837✔
2493
  BObjectImp* valimp = valref->impptr();
1,837✔
2494

2495
  ValueStack.pop_back();
1,837✔
2496

2497
  BObjectRef& obref = ValueStack.back();
1,837✔
2498
  BObject& ob = *obref;
1,837✔
2499

2500
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
1,837✔
2501
  BObject& mem = *memref;
1,837✔
2502

2503
  if ( valob.count() == 1 && valimp->count() == 1 )
1,837✔
2504
  {
2505
    mem.setimp( valimp );
1,330✔
2506
  }
2507
  else
2508
  {
2509
    mem.setimp( valimp->copy() );
507✔
2510
  }
2511
  // the struct is at the top of the stack
2512
}
1,837✔
2513

2514
void Executor::ins_dictionary_addmember( const Instruction& /*ins*/ )
138✔
2515
{
2516
  /*
2517
      ENTRANCE: value stack:
2518
      dictionary
2519
      key
2520
      value
2521
      EXIT: value stack:
2522
      dictionary
2523
      FUNCTION:
2524
      adds the (key, value) pair to the dictionary
2525
      */
2526

2527
  BObjectRef valref = ValueStack.back();
138✔
2528
  ValueStack.pop_back();
138✔
2529
  BObject& valob = *valref;
138✔
2530
  BObjectImp* valimp = valob.impptr();
138✔
2531

2532
  BObjectRef keyref = ValueStack.back();
138✔
2533
  ValueStack.pop_back();
138✔
2534
  BObject& keyob = *keyref;
138✔
2535
  BObjectImp* keyimp = keyob.impptr();
138✔
2536

2537
  BObjectRef& dictref = ValueStack.back();
138✔
2538
  BObject& dictob = *dictref;
138✔
2539
  BDictionary* dict = dictob.impptr<BDictionary>();
138✔
2540

2541
  if ( keyob.count() != 1 || keyimp->count() != 1 )
138✔
2542
  {
2543
    keyimp = keyimp->copy();
6✔
2544
  }
2545
  if ( valob.count() != 1 || valimp->count() != 1 )
138✔
2546
  {
2547
    valimp = valimp->copy();
18✔
2548
  }
2549

2550
  dict->addMember( keyimp, valimp );
138✔
2551
  // the dictionary remains at the top of the stack.
2552
}
138✔
2553

2554
void Executor::ins_in( const Instruction& /*ins*/ )
780✔
2555
{
2556
  /*
2557
      These each take two operands, and replace them with one.
2558
      We'll leave the second one on the value stack, and
2559
      just replace its object with the result
2560
      */
2561
  BObjectRef rightref = ValueStack.back();
780✔
2562
  ValueStack.pop_back();
780✔
2563
  BObjectRef& leftref = ValueStack.back();
780✔
2564

2565
  BObject& right = *rightref;
780✔
2566
  BObject& left = *leftref;
780✔
2567

2568
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
780✔
2569
}
780✔
2570

2571
void Executor::ins_insert_into( const Instruction& /*ins*/ )
7,687✔
2572
{
2573
  BObjectRef rightref = ValueStack.back();
7,687✔
2574
  ValueStack.pop_back();
7,687✔
2575
  BObjectRef& leftref = ValueStack.back();
7,687✔
2576

2577
  BObject& right = *rightref;
7,687✔
2578
  BObject& left = *leftref;
7,687✔
2579

2580
  if ( auto* spread = right.impptr_if<BSpread>() )
7,687✔
2581
  {
2582
    BObjectRef refIter( UninitObject::create() );
1,473✔
2583

2584
    auto pIter =
2585
        std::unique_ptr<ContIterator>( spread->object->impptr()->createIterator( refIter.get() ) );
1,473✔
2586

2587
    BObject* next = pIter->step();
1,473✔
2588
    while ( next != nullptr )
4,317✔
2589
    {
2590
      left.impref().operInsertInto( left, next->impref() );
2,844✔
2591
      next = pIter->step();
2,844✔
2592
    }
2593
  }
1,473✔
2594
  else
2595
  {
2596
    left.impref().operInsertInto( left, right.impref() );
6,214✔
2597
  }
2598
}
7,687✔
2599

2600
void Executor::ins_plusequal( const Instruction& /*ins*/ )
818✔
2601
{
2602
  /*
2603
      These each take two operands, and replace them with one.
2604
      We'll leave the second one on the value stack, and
2605
      just replace its object with the result
2606
      */
2607
  BObjectRef rightref = ValueStack.back();
818✔
2608
  ValueStack.pop_back();
818✔
2609
  BObjectRef& leftref = ValueStack.back();
818✔
2610

2611
  BObject& right = *rightref;
818✔
2612
  BObject& left = *leftref;
818✔
2613

2614
  left.impref().operPlusEqual( left, right.impref() );
818✔
2615
}
818✔
2616

2617
void Executor::ins_minusequal( const Instruction& /*ins*/ )
240✔
2618
{
2619
  /*
2620
      These each take two operands, and replace them with one.
2621
      We'll leave the second one on the value stack, and
2622
      just replace its object with the result
2623
      */
2624
  BObjectRef rightref = ValueStack.back();
240✔
2625
  ValueStack.pop_back();
240✔
2626
  BObjectRef& leftref = ValueStack.back();
240✔
2627

2628
  BObject& right = *rightref;
240✔
2629
  BObject& left = *leftref;
240✔
2630

2631
  left.impref().operMinusEqual( left, right.impref() );
240✔
2632
}
240✔
2633

2634
void Executor::ins_timesequal( const Instruction& /*ins*/ )
237✔
2635
{
2636
  /*
2637
      These each take two operands, and replace them with one.
2638
      We'll leave the second one on the value stack, and
2639
      just replace its object with the result
2640
      */
2641
  BObjectRef rightref = ValueStack.back();
237✔
2642
  ValueStack.pop_back();
237✔
2643
  BObjectRef& leftref = ValueStack.back();
237✔
2644

2645
  BObject& right = *rightref;
237✔
2646
  BObject& left = *leftref;
237✔
2647

2648
  left.impref().operTimesEqual( left, right.impref() );
237✔
2649
}
237✔
2650

2651
void Executor::ins_divideequal( const Instruction& /*ins*/ )
228✔
2652
{
2653
  /*
2654
      These each take two operands, and replace them with one.
2655
      We'll leave the second one on the value stack, and
2656
      just replace its object with the result
2657
      */
2658
  BObjectRef rightref = ValueStack.back();
228✔
2659
  ValueStack.pop_back();
228✔
2660
  BObjectRef& leftref = ValueStack.back();
228✔
2661

2662
  BObject& right = *rightref;
228✔
2663
  BObject& left = *leftref;
228✔
2664

2665
  left.impref().operDivideEqual( left, right.impref() );
228✔
2666
}
228✔
2667

2668
void Executor::ins_modulusequal( const Instruction& /*ins*/ )
375✔
2669
{
2670
  /*
2671
      These each take two operands, and replace them with one.
2672
      We'll leave the second one on the value stack, and
2673
      just replace its object with the result
2674
      */
2675
  BObjectRef rightref = ValueStack.back();
375✔
2676
  ValueStack.pop_back();
375✔
2677
  BObjectRef& leftref = ValueStack.back();
375✔
2678

2679
  BObject& right = *rightref;
375✔
2680
  BObject& left = *leftref;
375✔
2681

2682
  left.impref().operModulusEqual( left, right.impref() );
375✔
2683
}
375✔
2684

2685
// case RSV_GOTO:
2686
void Executor::ins_goto( const Instruction& ins )
36,946,902✔
2687
{
2688
  PC = (unsigned)ins.token.lval;
36,946,902✔
2689
}
36,946,902✔
2690

2691
// TOK_FUNC:
2692
void Executor::ins_func( const Instruction& ins )
36,983,022✔
2693
{
2694
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
36,983,022✔
2695
  getParams( nparams );
36,983,022✔
2696
  execFunc( ins.token );
36,983,022✔
2697
  cleanParams();
36,983,022✔
2698
  return;
36,983,022✔
2699
}
2700

2701
void Executor::ins_call_method_id( const Instruction& ins )
45,734✔
2702
{
2703
  BContinuation* continuation = nullptr;
45,734✔
2704
  unsigned nparams = ins.token.type;
45,734✔
2705

2706
  do
2707
  {
2708
    getParams( nparams );
46,524✔
2709
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
46,524✔
2710
    {
2711
      Instruction jmp;
2,435✔
2712
      bool add_new_classinst = ins.token.lval == MTH_NEW;
2,435✔
2713

2714
      if ( add_new_classinst )
2,435✔
2715
      {
2716
        if ( funcr->constructor() )
108✔
2717
        {
2718
          fparams.insert( fparams.begin(),
81✔
2719
                          BObjectRef( new BConstObject( new BClassInstanceRef(
162✔
2720
                              new BClassInstance( prog_, funcr->class_index(), Globals2 ) ) ) ) );
162✔
2721
        }
2722
      }
2723

2724
      if ( funcr->validCall( continuation ? MTH_CALL : ins.token.lval, *this, &jmp ) )
2,435✔
2725
      {
2726
        BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
2,330✔
2727
        call_function_reference( funcr, continuation, jmp );
2,330✔
2728
        return;
2,330✔
2729
      }
2,330✔
2730
    }
2,435✔
2731
    // If there _was_ a continuation to be handled (from previous loop
2732
    // iteration), there must have been a FuncRef on the stack. Otherwise,
2733
    // `continuation` may leak.
2734
    passert_always( continuation == nullptr );
44,194✔
2735

2736
    size_t stacksize = ValueStack.size();  // ValueStack can grow
44,194✔
2737
    BObjectImp* imp;
2738
    {
2739
      ESCRIPT_PROFILER( ins, ValueStack.back(), fparams );
2740
      imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
44,194✔
2741

2742
      if ( auto* cont = impptrIf<BContinuation>( imp ) )
44,194✔
2743
      {
2744
        continuation = cont;
790✔
2745
        // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2746
        // move.
2747
        nparams = static_cast<unsigned int>( continuation->args.size() );
790✔
2748

2749
        // Add function reference to stack
2750
        ValueStack.emplace_back( continuation->func() );
790✔
2751

2752
        // Move all arguments to the value stack
2753
        ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
790✔
2754
                           std::make_move_iterator( continuation->args.end() ) );
2755

2756
        continuation->args.clear();
790✔
2757

2758
        cleanParams();
790✔
2759

2760
        printStack( fmt::format(
1,580✔
2761
            "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2762
            "funcref.call()",
2763
            nparams ) );
2764

2765
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2766
        // handle it.
2767

2768
        // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2769
        // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2770
        // jumps.
2771

2772
        continue;
790✔
2773
      }
2774
    }
2775

2776
    if ( impptrIf<BSpecialUserFuncJump>( imp ) )
43,404✔
2777
    {
2778
      cleanParams();
3✔
2779
      return;
3✔
2780
    }
2781
    BObjectRef& objref = ValueStack[stacksize - 1];
43,401✔
2782
    if ( func_result_ )
43,401✔
2783
    {
2784
      if ( imp )
16✔
2785
      {
2786
        BObject obj( imp );
7✔
2787
      }
7✔
2788

2789
      objref.set( func_result_ );
16✔
2790
      func_result_ = nullptr;
16✔
2791
    }
2792
    else if ( imp )
43,385✔
2793
    {
2794
      objref.set( imp );
43,331✔
2795
    }
2796
    else
2797
    {
2798
      objref.set( UninitObject::create() );
54✔
2799
    }
2800

2801
    cleanParams();
43,401✔
2802
    return;
43,401✔
2803
  }
2804
  // This condition should only ever evaluate to `true` once. In the second loop
2805
  // iteration, handling the FuncRef will return out of this method.
2806
  while ( continuation != nullptr );
790✔
2807
}
2808

2809
void Executor::ins_call_method( const Instruction& ins )
844✔
2810
{
2811
  unsigned nparams = ins.token.lval;
844✔
2812
  auto method_name = ins.token.tokval();
844✔
2813

2814
  getParams( nparams );
844✔
2815
  BObjectImp* callee = ValueStack.back()->impptr();
844✔
2816

2817
  if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
844✔
2818
  {
2819
    Instruction jmp;
×
2820
    if ( funcr->validCall( method_name, *this, &jmp ) )
×
2821
    {
2822
      BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
×
2823
      call_function_reference( funcr, nullptr, jmp );
×
2824
      return;
×
2825
    }
×
2826
  }
×
2827

2828
  size_t stacksize = ValueStack.size();  // ValueStack can grow
844✔
2829
  BObjectImp* imp;
2830
  {
2831
    ESCRIPT_PROFILER( ins, callee, method_name, fparams );
2832
#ifdef BOBJECTIMP_DEBUG
2833
    if ( strcmp( method_name, "impptr" ) == 0 )
844✔
2834
      imp = new String( fmt::format( "{}", static_cast<void*>( callee ) ) );
×
2835
    else
2836
      imp = callee->call_method( method_name, *this );
844✔
2837
#else
2838
    imp = callee->call_method( method_name, *this );
2839
#endif
2840
  }
2841

2842
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
844✔
2843
  {
2844
    cleanParams();
607✔
2845
    return;
607✔
2846
  }
2847

2848
  BObjectRef& objref = ValueStack[stacksize - 1];
237✔
2849
  if ( func_result_ )
237✔
2850
  {
2851
    if ( imp )
×
2852
    {
2853
      BObject obj( imp );
×
2854
    }
×
2855

2856
    objref.set( func_result_ );
×
2857
    func_result_ = nullptr;
×
2858
  }
2859
  else if ( imp )
237✔
2860
  {
2861
    objref.set( imp );
60✔
2862
  }
2863
  else
2864
  {
2865
    objref.set( UninitObject::create() );
177✔
2866
  }
2867
  cleanParams();
237✔
2868
}
2869

2870
// CTRL_STATEMENTBEGIN:
2871
void Executor::ins_statementbegin( const Instruction& ins )
×
2872
{
2873
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
2874
    INFO_PRINTLN( ins.token.tokval() );
×
2875
}
×
2876

2877
// case CTRL_PROGEND:
2878
void Executor::ins_progend( const Instruction& /*ins*/ )
3,057✔
2879
{
2880
  done = 1;
3,057✔
2881
  run_ok_ = false;
3,057✔
2882
  PC = 0;
3,057✔
2883
}
3,057✔
2884

2885

2886
// case CTRL_MAKELOCAL:
2887
void Executor::ins_makelocal( const Instruction& /*ins*/ )
44,268✔
2888
{
2889
  if ( Locals2 )
44,268✔
2890
    upperLocals2.push_back( Locals2 );
44,268✔
2891
  Locals2 = new BObjectRefVec;
44,268✔
2892
}
44,268✔
2893

2894
void Executor::ins_check_mro( const Instruction& ins )
280✔
2895
{
2896
  auto classinst_offset = ins.token.lval;
280✔
2897

2898
  if ( classinst_offset > static_cast<int>( ValueStack.size() ) || ValueStack.empty() )
280✔
2899
  {
2900
    POLLOG_ERRORLN( "Fatal error: Check MRO offset error! offset={}, ValueStack.size={} ({},PC={})",
×
2901
                    classinst_offset, ValueStack.size(), prog_->name, PC );
×
2902
    seterror( true );
×
2903
    return;
×
2904
  }
2905

2906
  const auto& classinst_ref = ValueStack.at( ValueStack.size() - classinst_offset - 1 );
280✔
2907

2908
  if ( nLines < PC + 1 )
280✔
2909
  {
2910
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction out of bounds! nLines={} ({},PC={})",
×
2911
                    nLines, prog_->name, PC );
×
2912
    seterror( true );
×
2913
    return;
×
2914
  }
2915

2916
  const Instruction& jsr_ins = prog_->instr.at( PC + 1 );
280✔
2917
  if ( jsr_ins.func != &Executor::ins_jsr_userfunc )
280✔
2918
  {
2919
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction not followed by JSR_USERFUNC! ({},PC={})",
×
2920
                    prog_->name, PC );
×
2921
    seterror( true );
×
2922
    return;
×
2923
  }
2924

2925
  auto ctor_addr = jsr_ins.token.lval;
280✔
2926

2927
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280✔
2928

2929
  if ( classinstref != nullptr && classinstref->instance()->constructors_called.find( ctor_addr ) ==
557✔
2930
                                      classinstref->instance()->constructors_called.end() )
557✔
2931
  {
2932
    classinstref->instance()->constructors_called.insert( ctor_addr );
224✔
2933
  }
2934
  else
2935
  {
2936
    // Constructor has been called, or `this` is not a class instance: clear
2937
    // arguments and skip jump instructions (makelocal, jsr_userfunc)
2938
    ValueStack.resize( ValueStack.size() - ins.token.lval );
56✔
2939
    PC += 2;
56✔
2940
  }
2941
}
2942

2943
// CTRL_JSR_USERFUNC:
2944
void Executor::ins_jsr_userfunc( const Instruction& ins )
6,228✔
2945
{
2946
  jump( ins.token.lval, nullptr, nullptr );
6,228✔
2947
}
6,228✔
2948

2949
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
44,268✔
2950
{
2951
  ReturnContext rc;
44,268✔
2952
  rc.PC = PC;
44,268✔
2953
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
44,268✔
2954
  if ( continuation )
44,268✔
2955
  {
2956
    rc.Continuation.set( continuation );
35,890✔
2957
  }
2958

2959
  // Only store our global context if the function is external to the current program.
2960
  if ( funcref != nullptr && funcref->prog() != prog_ )
44,268✔
2961
  {
2962
    // Store external context for the return path.
2963
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
115✔
2964

2965
    // Set the prog and globals to the external function's, updating nLines and
2966
    // execmodules.
2967
    prog_ = funcref->prog();
115✔
2968

2969
    Globals2 = funcref->globals;
115✔
2970

2971
    nLines = static_cast<unsigned int>( prog_->instr.size() );
115✔
2972

2973
    // Re-attach modules, as the external user function's module function call
2974
    // instructions refer to modules by index.
2975
    execmodules.clear();
115✔
2976

2977
    if ( !viewmode_ )
115✔
2978
    {
2979
      if ( !AttachFunctionalityModules() )
115✔
2980
      {
2981
        POLLOGLN( "Could not attach modules for external function call jump" );
×
2982
        seterror( true );
×
2983
      }
2984
    }
2985
  }
2986

2987
  ControlStack.push_back( rc );
44,268✔
2988

2989
  PC = target_PC;
44,268✔
2990
  if ( ControlStack.size() >= escript_config.max_call_depth )
44,268✔
2991
  {
2992
    std::string tmp = fmt::format(
2993
        "Script {} exceeded maximum call depth\n"
2994
        "Return path PCs: ",
2995
        scriptname() );
×
2996
    while ( !ControlStack.empty() )
×
2997
    {
2998
      rc = ControlStack.back();
×
2999
      ControlStack.pop_back();
×
3000
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
3001
    }
3002
    POLLOGLN( tmp );
×
3003
    seterror( true );
×
3004
  }
×
3005
}
44,268✔
3006

3007

3008
BObjectImp* Executor::get_stacktrace( bool as_array )
44✔
3009
{
3010
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
44✔
3011

3012
  auto with_dbginfo =
3013
      [&]( const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
42✔
3014
                                     unsigned int /*line*/, const std::string& /*functionName*/ )>&
3015
               handler )
3016
  {
3017
    walkCallStack(
42✔
3018
        [&]( unsigned int pc )
245✔
3019
        {
3020
          auto filename = prog()->dbg_filenames[prog()->dbg_filenum[pc]];
203✔
3021
          auto line = prog()->dbg_linenum[pc];
161✔
3022
          auto dbgFunction =
3023
              std::find_if( prog()->dbg_functions.begin(), prog()->dbg_functions.end(),
161✔
3024
                            [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
3,014✔
3025

3026
          std::string functionName =
3027
              dbgFunction != prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
161✔
3028

3029
          handler( pc, filename, line, functionName );
161✔
3030
        } );
161✔
3031
  };
42✔
3032

3033
  if ( as_array )
44✔
3034
  {
3035
    std::unique_ptr<ObjArray> result( new ObjArray );
4✔
3036

3037
    if ( has_symbols )
4✔
3038
    {
3039
      with_dbginfo(
2✔
3040
          [&]( unsigned int pc, const std::string& filename, unsigned int line,
4✔
3041
               const std::string& functionName )
3042
          {
3043
            std::unique_ptr<BStruct> entry( new BStruct );
8✔
3044
            entry->addMember( "file", new String( filename ) );
8✔
3045
            entry->addMember( "line", new BLong( line ) );
8✔
3046
            entry->addMember( "name", new String( functionName ) );
8✔
3047
            entry->addMember( "pc", new BLong( pc ) );
8✔
3048
            result->addElement( entry.release() );
8✔
3049
          } );
8✔
3050
    }
3051
    else
3052
    {
3053
      walkCallStack(
2✔
3054
          [&]( unsigned int pc )
2✔
3055
          {
3056
            std::unique_ptr<BStruct> entry( new BStruct );
3✔
3057
            entry->addMember( "file", new String( scriptname() ) );
3✔
3058
            entry->addMember( "pc", new BLong( pc ) );
3✔
3059
            result->addElement( entry.release() );
3✔
3060
          } );
3✔
3061
    }
3062

3063
    return result.release();
4✔
3064
  }
4✔
3065
  // as string
3066
  std::string result;
40✔
3067

3068
  if ( has_symbols )
40✔
3069
  {
3070
    with_dbginfo(
40✔
3071
        [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
80✔
3072
             const std::string& functionName )
3073
        {
3074
          result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
306✔
3075
                                      filename, line ) );
3076
        } );
153✔
3077
  }
3078
  else
3079
  {
NEW
3080
    walkCallStack(
×
NEW
3081
        [&]( unsigned int pc ) {
×
NEW
3082
          result.append(
×
NEW
3083
              fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
NEW
3084
        } );
×
3085
  }
3086

3087
  return new String( std::move( result ) );
40✔
3088
}
40✔
3089

3090
void Executor::ins_pop_param( const Instruction& ins )
48,768✔
3091
{
3092
  popParam( ins.token );
48,768✔
3093
}
48,768✔
3094

3095
void Executor::ins_pop_param_byref( const Instruction& ins )
7,938✔
3096
{
3097
  popParamByRef( ins.token );
7,938✔
3098
}
7,938✔
3099

3100
void Executor::ins_get_arg( const Instruction& ins )
433✔
3101
{
3102
  getArg( ins.token );
433✔
3103
}
433✔
3104

3105
// CTRL_LEAVE_BLOCK:
3106
void Executor::ins_leave_block( const Instruction& ins )
9,440✔
3107
{
3108
  if ( Locals2 )
9,440✔
3109
  {
3110
    for ( int i = 0; i < ins.token.lval; i++ )
25,164✔
3111
      Locals2->pop_back();
15,724✔
3112
  }
3113
  else  // at global level.  ick.
3114
  {
3115
    for ( int i = 0; i < ins.token.lval; i++ )
×
3116
      Globals2->pop_back();
×
3117
  }
3118
}
9,440✔
3119

3120
void Executor::ins_gosub( const Instruction& ins )
×
3121
{
3122
  ReturnContext rc;
×
3123
  rc.PC = PC;
×
3124
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3125
  ControlStack.push_back( rc );
×
3126
  if ( Locals2 )
×
3127
    upperLocals2.push_back( Locals2 );
×
3128
  Locals2 = new BObjectRefVec;
×
3129

3130
  PC = (unsigned)ins.token.lval;
×
3131
}
×
3132

3133
// case RSV_RETURN
3134
void Executor::ins_return( const Instruction& /*ins*/ )
44,268✔
3135
{
3136
  if ( ControlStack.empty() )
44,268✔
3137
  {
3138
    ERROR_PRINTLN( "Return without GOSUB! (PC={}, {})", PC, scriptname() );
×
3139

3140
    seterror( true );
×
3141
    return;
×
3142
  }
3143
  BObjectRef continuation;
44,268✔
3144
  ReturnContext& rc = ControlStack.back();
44,268✔
3145
  PC = rc.PC;
44,268✔
3146

3147
  if ( rc.Continuation.get() != nullptr )
44,268✔
3148
  {
3149
    continuation = rc.Continuation;
35,890✔
3150
  }
3151

3152
  if ( Locals2 )
44,268✔
3153
  {
3154
    delete Locals2;
44,268✔
3155
    Locals2 = nullptr;
44,268✔
3156
  }
3157
  if ( !upperLocals2.empty() )
44,268✔
3158
  {
3159
    Locals2 = upperLocals2.back();
44,268✔
3160
    upperLocals2.pop_back();
44,268✔
3161
  }
3162

3163
  if ( rc.ExternalContext.has_value() )
44,268✔
3164
  {
3165
    prog_ = std::move( rc.ExternalContext->Program );
115✔
3166
    nLines = static_cast<unsigned int>( prog_->instr.size() );
115✔
3167
    execmodules = std::move( rc.ExternalContext->Modules );
115✔
3168
    Globals2 = std::move( rc.ExternalContext->Globals );
115✔
3169
  }
3170

3171
  // FIXME do something with rc.ValueStackDepth
3172
  ControlStack.pop_back();
44,268✔
3173

3174
  if ( continuation != nullptr )
44,268✔
3175
  {
3176
    auto result = ValueStack.back();
35,890✔
3177
    ValueStack.pop_back();
35,890✔
3178

3179
    // Do not move the `result` object, as the continuation callback may return
3180
    // the result's BObjectImp*. If we move `result`, the BObjectImp* will be
3181
    // deleted when the callback ends.
3182
    auto* imp = continuation->impptr<BContinuation>()->continueWith( *this, result );
35,890✔
3183

3184
    // If the the continuation callback returned a continuation, handle the jump.
3185
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
35,890✔
3186
    {
3187
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3188
      // takes ownership.
3189

3190
      // Add function reference to stack
3191
      ValueStack.emplace_back( cont->func() );
35,100✔
3192

3193
      // Move all arguments to the fparams stack
3194
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
35,100✔
3195
                      std::make_move_iterator( cont->args.end() ) );
3196

3197
      cont->args.clear();
35,100✔
3198

3199
      printStack(
35,100✔
3200
          "continuation callback returned a continuation; continuation args added to fparams" );
3201

3202
      BObjectRef objref = ValueStack.back();
35,100✔
3203
      auto funcr = objref->impptr<BFunctionRef>();
35,100✔
3204
      Instruction jmp;
35,100✔
3205
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
35,100✔
3206
      {
3207
        call_function_reference( funcr, cont, jmp );
35,100✔
3208
      }
3209
      else
3210
      {
3211
        // Delete `imp` since a ReturnContext was not created.
3212
        BObject bobj( imp );
×
3213
      }
×
3214
    }
35,100✔
3215
    else
3216
    {
3217
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3218
      // `array{}.filter(...)`
3219
      ValueStack.pop_back();
790✔
3220

3221
      // Add the result to the stack.
3222
      ValueStack.emplace_back( imp );
790✔
3223
    }
3224
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
71,780✔
3225
  }
35,890✔
3226
}
44,268✔
3227

3228
void Executor::ins_exit( const Instruction& /*ins*/ )
3✔
3229
{
3230
  done = 1;
3✔
3231
  run_ok_ = false;
3✔
3232
}
3✔
3233

3234
void Executor::ins_double( const Instruction& ins )
427✔
3235
{
3236
  ValueStack.emplace_back( new Double( ins.token.dval ) );
427✔
3237
}
427✔
3238

3239
void Executor::ins_classinst( const Instruction& ins )
321✔
3240
{
3241
  ValueStack.emplace_back( new BConstObject(
321✔
3242
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2 ) ) ) );
642✔
3243
}
321✔
3244

3245
void Executor::ins_string( const Instruction& ins )
107,217✔
3246
{
3247
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
107,217✔
3248
}
107,217✔
3249

3250
void Executor::ins_regexp( const Instruction& )
150✔
3251
{
3252
  BObjectRef flags = ValueStack.back();
150✔
3253
  ValueStack.pop_back();
150✔
3254
  BObjectRef& pattern = ValueStack.back();
150✔
3255

3256
  pattern.set(
150✔
3257
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300✔
3258
}
150✔
3259

3260
void Executor::ins_error( const Instruction& /*ins*/ )
4,177✔
3261
{
3262
  ValueStack.emplace_back( new BError() );
4,177✔
3263
}
4,177✔
3264
void Executor::ins_struct( const Instruction& /*ins*/ )
810✔
3265
{
3266
  ValueStack.emplace_back( new BStruct );
810✔
3267
}
810✔
3268
void Executor::ins_spread( const Instruction& ins )
2,125✔
3269
{
3270
  bool spread_into = ins.token.lval;
2,125✔
3271

3272
  // Pops TOP, and spreads it into (new) TOP.
3273
  if ( spread_into )
2,125✔
3274
  {
3275
    BObjectRef rightref = ValueStack.back();
24✔
3276
    ValueStack.pop_back();
24✔
3277
    BObjectRef& leftref = ValueStack.back();
24✔
3278

3279
    BObject& right = *rightref;
24✔
3280
    BObject& left = *leftref;
24✔
3281

3282
    BObjectRef refIter( UninitObject::create() );
24✔
3283

3284
    auto pIter = std::unique_ptr<ContIterator>( right.impptr()->createIterator( refIter.get() ) );
24✔
3285

3286
    while ( auto next = pIter->step() )
72✔
3287
    {
3288
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48✔
3289
      BObject res_obj( result );
48✔
3290
    }
48✔
3291
  }
24✔
3292
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3293
  else
3294
  {
3295
    auto spread = new BSpread( ValueStack.back() );
2,101✔
3296
    ValueStack.pop_back();
2,101✔
3297
    ValueStack.emplace_back( spread );
2,101✔
3298
  }
3299
}
2,125✔
3300
void Executor::ins_array( const Instruction& /*ins*/ )
3,733✔
3301
{
3302
  ValueStack.emplace_back( new ObjArray );
3,733✔
3303
}
3,733✔
3304
void Executor::ins_dictionary( const Instruction& /*ins*/ )
161✔
3305
{
3306
  ValueStack.emplace_back( new BDictionary );
161✔
3307
}
161✔
3308
void Executor::ins_uninit( const Instruction& /*ins*/ )
113✔
3309
{
3310
  ValueStack.emplace_back( UninitObject::create() );
113✔
3311
}
113✔
3312
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3313
{
3314
  ValueStack.emplace_back( new BError( "Please recompile this script" ) );
×
3315
}
×
3316

3317
// case TOK_UNMINUS:
3318
void Executor::ins_unminus( const Instruction& /*ins*/ )
147✔
3319
{
3320
  BObjectRef ref = getObjRef();
147✔
3321
  BObjectImp* newobj;
3322
  newobj = ref->impref().inverse();
147✔
3323

3324
  ValueStack.emplace_back( newobj );
147✔
3325
}
147✔
3326

3327
// case TOK_UNPLUSPLUS:
3328
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
472✔
3329
{
3330
  BObjectRef& ref = ValueStack.back();
472✔
3331
  ref->impref().selfPlusPlus();
472✔
3332
}
472✔
3333

3334
// case TOK_UNMINUSMINUS:
3335
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
133✔
3336
{
3337
  BObjectRef& ref = ValueStack.back();
133✔
3338
  ref->impref().selfMinusMinus();
133✔
3339
}
133✔
3340

3341
// case TOK_UNPLUSPLUS_POST:
3342
void Executor::ins_unplusplus_post( const Instruction& /*ins*/ )
261✔
3343
{
3344
  BObjectRef& ref = ValueStack.back();
261✔
3345
  BObjectImp* imp = ref->impptr();
261✔
3346
  BObject* n = ref->clone();
261✔
3347
  imp->selfPlusPlus();
261✔
3348
  ref.set( n );
261✔
3349
}
261✔
3350

3351
// case TOK_UNMINUSMINUS_POST:
3352
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
85✔
3353
{
3354
  BObjectRef& ref = ValueStack.back();
85✔
3355
  BObjectImp* imp = ref->impptr();
85✔
3356
  BObject* n = ref->clone();
85✔
3357
  imp->selfMinusMinus();
85✔
3358
  ref.set( n );
85✔
3359
}
85✔
3360

3361
// case INS_SET_MEMBER_ID_UNPLUSPLUS:
3362
void Executor::ins_set_member_id_unplusplus( const Instruction& ins )
10✔
3363
{
3364
  BObjectRef& ref = ValueStack.back();
10✔
3365
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
10✔
3366
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
20✔
3367
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
10✔
3368
  {
3369
    tmp->impref().selfPlusPlus();
10✔
3370
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
10✔
3371
  }
3372
  ref.set( tmp.get() );
10✔
3373
}
10✔
3374

3375
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3376
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
3✔
3377
{
3378
  BObjectRef& ref = ValueStack.back();
3✔
3379
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3380
  BObject* res = tmp->clone();
3✔
3381
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3382
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3383
  {
3384
    tmp->impref().selfPlusPlus();
3✔
3385
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3386
  }
3387
  ref.set( res );
3✔
3388
}
3✔
3389

3390
// case INS_SET_MEMBER_ID_UNMINUSMINUS:
3391
void Executor::ins_set_member_id_unminusminus( const Instruction& ins )
13✔
3392
{
3393
  BObjectRef& ref = ValueStack.back();
13✔
3394
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
13✔
3395
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
26✔
3396
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
13✔
3397
  {
3398
    tmp->impref().selfMinusMinus();
13✔
3399
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
13✔
3400
  }
3401
  ref.set( tmp.get() );
13✔
3402
}
13✔
3403

3404
// case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3405
void Executor::ins_set_member_id_unminusminus_post( const Instruction& ins )
3✔
3406
{
3407
  BObjectRef& ref = ValueStack.back();
3✔
3408
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3409
  BObject* res = tmp->clone();
3✔
3410
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3411
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3412
  {
3413
    tmp->impref().selfMinusMinus();
3✔
3414
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3415
  }
3416
  ref.set( res );
3✔
3417
}
3✔
3418

3419
// case TOK_LOG_NOT:
3420
void Executor::ins_logical_not( const Instruction& /*ins*/ )
3,979✔
3421
{
3422
  BObjectRef ref = getObjRef();
3,979✔
3423
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
3,979✔
3424
}
3,979✔
3425

3426
// case TOK_BITWISE_NOT:
3427
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
156✔
3428
{
3429
  BObjectRef ref = getObjRef();
156✔
3430
  ValueStack.emplace_back( ref->impptr()->bitnot() );
156✔
3431
}
156✔
3432

3433
// case TOK_FUNCREF:
3434
void Executor::ins_funcref( const Instruction& ins )
1,485✔
3435
{
3436
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
1,485✔
3437
  {
3438
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3439
                    prog_->function_references.size() );
×
3440
    seterror( true );
×
3441
    return;
×
3442
  }
3443

3444
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
1,485✔
3445

3446
  ValueStack.emplace_back( new BFunctionRef( prog_, funcref_index, Globals2, {} /* captures */ ) );
1,485✔
3447
}
3448

3449
void Executor::ins_functor( const Instruction& ins )
722✔
3450
{
3451
  auto funcref_index = static_cast<int>( ins.token.type );
722✔
3452

3453
  const auto& ep_funcref = prog_->function_references[funcref_index];
722✔
3454

3455
  int capture_count = ep_funcref.capture_count;
722✔
3456

3457
  auto captures = ValueStackCont();
722✔
3458
  while ( capture_count > 0 )
1,354✔
3459
  {
3460
    captures.push_back( ValueStack.back() );
632✔
3461
    ValueStack.pop_back();
632✔
3462
    capture_count--;
632✔
3463
  }
3464

3465
  auto func = new BFunctionRef( prog_, funcref_index, Globals2, std::move( captures ) );
722✔
3466

3467
  ValueStack.emplace_back( func );
722✔
3468

3469
  PC += ins.token.lval;
722✔
3470
}
722✔
3471

3472
void Executor::ins_logical_jump( const Instruction& ins )
411✔
3473
{
3474
  BObjectRef& objref = ValueStack.back();
411✔
3475
  // jmp if true for ||, jmp if false for &&
3476
  const bool obj_true = objref->impptr()->isTrue();
411✔
3477
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
411✔
3478

3479
  if ( jmp )
411✔
3480
    PC = (unsigned)ins.token.lval;
84✔
3481
  // keep the obj on the stack if it should jump (ShortCircuit)
3482
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3483
  // BLong (original &&/|| convert to BLong bool)
3484
  if ( !jmp )
411✔
3485
    ValueStack.pop_back();
327✔
3486
  else
3487
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
84✔
3488
}
411✔
3489

3490
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
95✔
3491
{
3492
  BObjectRef& objref = ValueStack.back();
95✔
3493
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
95✔
3494
}
95✔
3495

3496
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3497

3498
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
160,356✔
3499
{
3500
  switch ( token.id )
160,356✔
3501
  {
3502
  case INS_INITFOREACH:
403✔
3503
    return &Executor::ins_initforeach;
403✔
3504
  case INS_STEPFOREACH:
403✔
3505
    return &Executor::ins_stepforeach;
403✔
3506
  case INS_INITFOR:
54✔
3507
    return &Executor::ins_initfor;
54✔
3508
  case INS_NEXTFOR:
54✔
3509
    return &Executor::ins_nextfor;
54✔
3510
  case INS_CASEJMP:
95✔
3511
    return &Executor::ins_casejmp;
95✔
3512
  case RSV_JMPIFTRUE:
1,008✔
3513
    return &Executor::ins_jmpiftrue;
1,008✔
3514
  case RSV_JMPIFFALSE:
1,909✔
3515
    return &Executor::ins_jmpiffalse;
1,909✔
3516
  case RSV_LOCAL:
2,593✔
3517
    return &Executor::ins_makeLocal;
2,593✔
3518
  case RSV_GLOBAL:
9,452✔
3519
  case TOK_GLOBALVAR:
3520
    return &Executor::ins_globalvar;
9,452✔
3521
  case TOK_LOCALVAR:
16,472✔
3522
    return &Executor::ins_localvar;
16,472✔
3523
  case TOK_LONG:
13,609✔
3524
    return &Executor::ins_long;
13,609✔
3525
  case TOK_DOUBLE:
424✔
3526
    return &Executor::ins_double;
424✔
3527
  case TOK_STRING:
24,529✔
3528
    return &Executor::ins_string;
24,529✔
3529
  case TOK_REGEXP:
150✔
3530
    return &Executor::ins_regexp;
150✔
3531
  case TOK_ERROR:
190✔
3532
    return &Executor::ins_error;
190✔
3533
  case TOK_STRUCT:
583✔
3534
    return &Executor::ins_struct;
583✔
3535
  case TOK_SPREAD:
670✔
3536
    return &Executor::ins_spread;
670✔
3537
  case TOK_CLASSINST:
242✔
3538
    return &Executor::ins_classinst;
242✔
3539
  case TOK_ARRAY:
2,287✔
3540
    return &Executor::ins_array;
2,287✔
3541
  case TOK_DICTIONARY:
156✔
3542
    return &Executor::ins_dictionary;
156✔
3543
  case TOK_FUNCREF:
734✔
3544
    return &Executor::ins_funcref;
734✔
3545
  case TOK_FUNCTOR:
661✔
3546
    return &Executor::ins_functor;
661✔
3547
  case INS_UNINIT:
106✔
3548
    return &Executor::ins_uninit;
106✔
3549
  case TOK_IDENT:
×
3550
    return &Executor::ins_ident;
×
3551
  case INS_ASSIGN_GLOBALVAR:
731✔
3552
    return &Executor::ins_assign_globalvar;
731✔
3553
  case INS_ASSIGN_LOCALVAR:
900✔
3554
    return &Executor::ins_assign_localvar;
900✔
3555
  case INS_ASSIGN_CONSUME:
63✔
3556
    return &Executor::ins_assign_consume;
63✔
3557
  case TOK_CONSUMER:
18,002✔
3558
    return &Executor::ins_consume;
18,002✔
3559
  case TOK_ASSIGN:
4,236✔
3560
    return &Executor::ins_assign;
4,236✔
3561
  case INS_SUBSCRIPT_ASSIGN:
9✔
3562
    return &Executor::ins_array_assign;
9✔
3563
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
422✔
3564
    return &Executor::ins_array_assign_consume;
422✔
3565
  case INS_MULTISUBSCRIPT:
237✔
3566
    return &Executor::ins_multisubscript;
237✔
3567
  case INS_MULTISUBSCRIPT_ASSIGN:
6✔
3568
    return &Executor::ins_multisubscript_assign;
6✔
3569
  case INS_GET_MEMBER:
973✔
3570
    return &Executor::ins_get_member;
973✔
3571
  case INS_SET_MEMBER:
3✔
3572
    return &Executor::ins_set_member;
3✔
3573
  case INS_SET_MEMBER_CONSUME:
225✔
3574
    return &Executor::ins_set_member_consume;
225✔
3575

3576
  case INS_UNPACK_SEQUENCE:
106✔
3577
    return &Executor::ins_unpack_sequence;
106✔
3578
  case INS_UNPACK_INDICES:
84✔
3579
    return &Executor::ins_unpack_indices;
84✔
3580
  case INS_TAKE_GLOBAL:
183✔
3581
    return &Executor::ins_take_global;
183✔
3582
  case INS_TAKE_LOCAL:
298✔
3583
    return &Executor::ins_take_local;
298✔
3584

3585
  case INS_GET_MEMBER_ID:
1,521✔
3586
    return &Executor::ins_get_member_id;  // test id
1,521✔
3587
  case INS_SET_MEMBER_ID:
3✔
3588
    return &Executor::ins_set_member_id;  // test id
3✔
3589
  case INS_SET_MEMBER_ID_CONSUME:
199✔
3590
    return &Executor::ins_set_member_id_consume;  // test id
199✔
3591

3592
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15✔
3593
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15✔
3594
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6✔
3595
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6✔
3596
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9✔
3597
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9✔
3598
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6✔
3599
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6✔
3600
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6✔
3601
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6✔
3602

3603
  case TOK_ADD:
2,426✔
3604
    return &Executor::ins_add;
2,426✔
3605
  case TOK_SUBTRACT:
423✔
3606
    return &Executor::ins_subtract;
423✔
3607
  case TOK_DIV:
59✔
3608
    return &Executor::ins_div;
59✔
3609
  case TOK_MULT:
228✔
3610
    return &Executor::ins_mult;
228✔
3611
  case TOK_MODULUS:
30✔
3612
    return &Executor::ins_modulus;
30✔
3613

3614
  case TOK_INSERTINTO:
5,090✔
3615
    return &Executor::ins_insert_into;
5,090✔
3616

3617
  case TOK_PLUSEQUAL:
80✔
3618
    return &Executor::ins_plusequal;
80✔
3619
  case TOK_MINUSEQUAL:
27✔
3620
    return &Executor::ins_minusequal;
27✔
3621
  case TOK_TIMESEQUAL:
24✔
3622
    return &Executor::ins_timesequal;
24✔
3623
  case TOK_DIVIDEEQUAL:
15✔
3624
    return &Executor::ins_divideequal;
15✔
3625
  case TOK_MODULUSEQUAL:
18✔
3626
    return &Executor::ins_modulusequal;
18✔
3627

3628
  case TOK_LESSTHAN:
210✔
3629
    return &Executor::ins_lessthan;
210✔
3630
  case TOK_LESSEQ:
74✔
3631
    return &Executor::ins_lessequal;
74✔
3632
  case RSV_GOTO:
1,759✔
3633
    return &Executor::ins_goto;
1,759✔
3634
  case TOK_ARRAY_SUBSCRIPT:
1,260✔
3635
    return &Executor::ins_arraysubscript;
1,260✔
3636
  case TOK_EQUAL:
943✔
3637
    return &Executor::ins_equal;
943✔
3638
  case TOK_FUNC:
12,041✔
3639
    return &Executor::ins_func;
12,041✔
3640
  case INS_CALL_METHOD:
364✔
3641
    return &Executor::ins_call_method;
364✔
3642
  case INS_CALL_METHOD_ID:
3,358✔
3643
    return &Executor::ins_call_method_id;
3,358✔
3644
  case CTRL_STATEMENTBEGIN:
×
3645
    return &Executor::ins_statementbegin;
×
3646
  case CTRL_MAKELOCAL:
3,828✔
3647
    return &Executor::ins_makelocal;
3,828✔
3648
  case INS_CHECK_MRO:
225✔
3649
    return &Executor::ins_check_mro;
225✔
3650
  case CTRL_JSR_USERFUNC:
3,828✔
3651
    return &Executor::ins_jsr_userfunc;
3,828✔
3652
  case INS_POP_PARAM:
2,898✔
3653
    return &Executor::ins_pop_param;
2,898✔
3654
  case INS_POP_PARAM_BYREF:
1,275✔
3655
    return &Executor::ins_pop_param_byref;
1,275✔
3656
  case INS_GET_ARG:
139✔
3657
    return &Executor::ins_get_arg;
139✔
3658
  case CTRL_LEAVE_BLOCK:
1,504✔
3659
    return &Executor::ins_leave_block;
1,504✔
3660
  case RSV_GOSUB:
×
3661
    return &Executor::ins_gosub;
×
3662
  case RSV_RETURN:
4,445✔
3663
    return &Executor::ins_return;
4,445✔
3664
  case RSV_EXIT:
18✔
3665
    return &Executor::ins_exit;
18✔
3666
  case INS_DECLARE_ARRAY:
69✔
3667
    return &Executor::ins_declareArray;
69✔
3668
  case TOK_UNMINUS:
3✔
3669
    return &Executor::ins_unminus;
3✔
3670
  case TOK_UNPLUS:
×
3671
    return &Executor::ins_nop;
×
3672
  case TOK_LOG_NOT:
190✔
3673
    return &Executor::ins_logical_not;
190✔
3674
  case TOK_BITWISE_NOT:
12✔
3675
    return &Executor::ins_bitwise_not;
12✔
3676
  case TOK_BSRIGHT:
15✔
3677
    return &Executor::ins_bitshift_right;
15✔
3678
  case TOK_BSLEFT:
15✔
3679
    return &Executor::ins_bitshift_left;
15✔
3680
  case TOK_BITAND:
17✔
3681
    return &Executor::ins_bitwise_and;
17✔
3682
  case TOK_BITXOR:
15✔
3683
    return &Executor::ins_bitwise_xor;
15✔
3684
  case TOK_BITOR:
15✔
3685
    return &Executor::ins_bitwise_or;
15✔
3686

3687
  case TOK_NEQ:
734✔
3688
    return &Executor::ins_notequal;
734✔
3689
  case TOK_GRTHAN:
152✔
3690
    return &Executor::ins_greaterthan;
152✔
3691
  case TOK_GREQ:
84✔
3692
    return &Executor::ins_greaterequal;
84✔
3693
  case TOK_AND:
66✔
3694
    return &Executor::ins_logical_and;
66✔
3695
  case TOK_OR:
296✔
3696
    return &Executor::ins_logical_or;
296✔
3697

3698
  case TOK_ADDMEMBER:
96✔
3699
    return &Executor::ins_addmember;
96✔
3700
  case TOK_DELMEMBER:
9✔
3701
    return &Executor::ins_removemember;
9✔
3702
  case TOK_CHKMEMBER:
78✔
3703
    return &Executor::ins_checkmember;
78✔
3704
  case INS_DICTIONARY_ADDMEMBER:
132✔
3705
    return &Executor::ins_dictionary_addmember;
132✔
3706
  case TOK_IN:
48✔
3707
    return &Executor::ins_in;
48✔
3708
  case TOK_IS:
56✔
3709
    return &Executor::ins_is;
56✔
3710
  case INS_ADDMEMBER2:
45✔
3711
    return &Executor::ins_addmember2;
45✔
3712
  case INS_ADDMEMBER_ASSIGN:
1,239✔
3713
    return &Executor::ins_addmember_assign;
1,239✔
3714
  case CTRL_PROGEND:
2,657✔
3715
    return &Executor::ins_progend;
2,657✔
3716
  case TOK_UNPLUSPLUS:
93✔
3717
    return &Executor::ins_unplusplus;
93✔
3718
  case TOK_UNMINUSMINUS:
57✔
3719
    return &Executor::ins_unminusminus;
57✔
3720
  case TOK_UNPLUSPLUS_POST:
100✔
3721
    return &Executor::ins_unplusplus_post;
100✔
3722
  case TOK_UNMINUSMINUS_POST:
49✔
3723
    return &Executor::ins_unminusminus_post;
49✔
3724
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10✔
3725
    return &Executor::ins_set_member_id_unplusplus;  // test id
10✔
3726
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13✔
3727
    return &Executor::ins_set_member_id_unminusminus;  // test id
13✔
3728
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3✔
3729
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3✔
3730
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3✔
3731
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3✔
3732
  case INS_SKIPIFTRUE_ELSE_CONSUME:
196✔
3733
    return &Executor::ins_skipiftrue_else_consume;
196✔
3734
  case TOK_INTERPOLATE_STRING:
1,757✔
3735
    return &Executor::ins_interpolate_string;
1,757✔
3736
  case TOK_FORMAT_EXPRESSION:
89✔
3737
    return &Executor::ins_format_expression;
89✔
3738
  case TOK_BOOL:
217✔
3739
    return &Executor::ins_bool;
217✔
3740
  case INS_LOGICAL_JUMP:
63✔
3741
    return &Executor::ins_logical_jump;
63✔
3742
  case INS_LOGICAL_CONVERT:
47✔
3743
    return &Executor::ins_logical_convert;
47✔
3744
  default:
×
3745
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3746
  }
3747
}
3748

3749
void Executor::sethalt( bool halt )
17✔
3750
{
3751
  halt_ = halt;
17✔
3752

3753
  if ( halt && dbg_env_ )
17✔
3754
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18✔
3755
      listener->on_halt();
9✔
3756

3757
  calcrunnable();
17✔
3758
}
17✔
3759

3760
void Executor::execInstr()
148,812,978✔
3761
{
3762
  unsigned onPC = PC;
148,812,978✔
3763
  try
3764
  {  // this is really more of a class invariant.
3765
    passert( run_ok_ );
148,812,978✔
3766
    passert( PC < nLines );
148,812,978✔
3767
    passert( !error_ );
148,812,978✔
3768
    passert( !done );
148,812,978✔
3769

3770
#ifdef NDEBUG
3771
    const Instruction& ins = prog_->instr[PC];
148,812,978✔
3772
#else
3773
    const Instruction& ins = prog_->instr.at( PC );
3774
#endif
3775
    if ( debug_level >= INSTRUCTIONS )
148,812,978✔
3776
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3777

3778
    // If `on_instruction` returns false, do not execute this instruction.
3779
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
148,812,978✔
3780
    {
3781
      return;
9✔
3782
    }
3783

3784
    ++ins.cycles;
148,812,969✔
3785
    ++prog_->instr_cycles;
148,812,969✔
3786
    ++escript_instr_cycles;
148,812,969✔
3787

3788
    ++PC;
148,812,969✔
3789

3790
    ( this->*( ins.func ) )( ins );
148,812,969✔
3791
  }
3792
  catch ( std::exception& ex )
×
3793
  {
3794
    std::string tmp =
3795
        fmt::format( "Exception in: {} PC={}: {}\n", prog_->name.get(), onPC, ex.what() );
×
3796
    if ( !run_ok_ )
×
3797
      tmp += "run_ok_ = false\n";
×
3798
    if ( PC < nLines )
×
3799
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
×
3800
    if ( error_ )
×
3801
      tmp += "error_ = true\n";
×
3802
    if ( done )
×
3803
      tmp += "done = true\n";
×
3804

3805
    seterror( true );
×
3806
    POLLOG_ERROR( tmp );
×
3807

3808
    show_context( onPC );
×
3809
  }
×
3810
#ifdef __unix__
3811
  catch ( ... )
×
3812
  {
3813
    seterror( true );
×
3814
    POLLOG_ERRORLN( "Exception in {}, PC={}: unclassified", prog_->name.get(), onPC );
×
3815

3816
    show_context( onPC );
×
3817
  }
×
3818
#endif
3819
}
3820

3821
std::string Executor::dbg_get_instruction( size_t atPC ) const
×
3822
{
3823
  std::string out;
×
3824
  dbg_get_instruction( atPC, out );
×
3825
  return out;
×
3826
}
×
3827

3828
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
264✔
3829
{
3830
  bool has_breakpoint =
3831
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
264✔
3832
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
264✔
3833
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
264✔
3834
}
264✔
3835

3836
void Executor::show_context( unsigned atPC )
×
3837
{
3838
  unsigned start, end;
3839
  if ( atPC >= 5 )
×
3840
    start = atPC - 5;
×
3841
  else
3842
    start = 0;
×
3843

3844
  end = atPC + 5;
×
3845

3846
  if ( end >= nLines )
×
3847
    end = nLines - 1;
×
3848

3849
  for ( unsigned i = start; i <= end; ++i )
×
3850
  {
3851
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
×
3852
  }
3853
}
×
3854
void Executor::show_context( std::string& os, unsigned atPC )
24✔
3855
{
3856
  unsigned start, end;
3857
  if ( atPC >= 5 )
24✔
3858
    start = atPC - 5;
24✔
3859
  else
3860
    start = 0;
×
3861

3862
  end = atPC + 5;
24✔
3863

3864
  if ( end >= nLines )
24✔
3865
    end = nLines - 1;
×
3866

3867
  for ( unsigned i = start; i <= end; ++i )
288✔
3868
  {
3869
    dbg_get_instruction( i, os );
264✔
3870
    os += '\n';
264✔
3871
  }
3872
}
24✔
3873

3874
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
38,040✔
3875
                                        const Instruction& jmp )
3876
{
3877
  // params need to be on the stack, without current objectref
3878
  ValueStack.pop_back();
38,040✔
3879

3880
  // Push captured parameters onto the stack prior to function parameters.
3881
  for ( auto& p : funcr->captures )
43,541✔
3882
    ValueStack.push_back( p );
5,501✔
3883

3884
  auto nparams = static_cast<int>( fparams.size() );
38,040✔
3885

3886
  // Handle variadic functions special. Construct an array{} corresponding to
3887
  // the rest parameter (the last parameter for the function). The logic for the
3888
  // condition:
3889
  // - if true, the last argument in the call may not be an array{}, so we need
3890
  //   to construct one (which is why the condition is `>=` and not `>`).
3891
  // - if false, then the address we're jumping to will be a "default argument
3892
  //   address" and _not_ the user function directly, which will create the
3893
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
3894
  if ( funcr->variadic() && nparams >= funcr->numParams() )
38,040✔
3895
  {
3896
    auto num_nonrest_args = funcr->numParams() - 1;
640✔
3897

3898
    auto rest_arg = std::make_unique<ObjArray>();
640✔
3899

3900
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,655✔
3901
    {
3902
      auto& p = fparams[i];
3,015✔
3903

3904
      if ( i < num_nonrest_args )
3,015✔
3905
      {
3906
        ValueStack.push_back( p );
615✔
3907
      }
3908
      else
3909
      {
3910
        rest_arg->ref_arr.push_back( p );
2,400✔
3911
      }
3912
    }
3913
    ValueStack.emplace_back( rest_arg.release() );
640✔
3914
  }
640✔
3915
  // The array{} will be created via the regular default-parameter handling by
3916
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
3917
  // prior to jumping to the user function.
3918
  else
3919
  {
3920
    for ( auto& p : fparams )
77,473✔
3921
      ValueStack.push_back( p );
40,073✔
3922
  }
3923

3924
  // jump to function
3925
  jump( jmp.token.lval, continuation, funcr );
38,040✔
3926
  fparams.clear();
38,040✔
3927
  // switch to new block
3928
  ins_makelocal( jmp );
38,040✔
3929
}
38,040✔
3930

3931
bool Executor::exec()
2,519✔
3932
{
3933
  passert( prog_ok_ );
2,519✔
3934
  passert( !error_ );
2,519✔
3935

3936
  Clib::scripts_thread_script = scriptname();
2,519✔
3937

3938
  set_running_to_completion( true );
2,519✔
3939
  while ( runnable() )
647,868✔
3940
  {
3941
    Clib::scripts_thread_scriptPC = PC;
645,349✔
3942
    execInstr();
645,349✔
3943
  }
3944

3945
  return !error_;
2,519✔
3946
}
3947

3948
void Executor::reinitExec()
×
3949
{
3950
  PC = 0;
×
3951
  done = 0;
×
3952
  seterror( false );
×
3953

3954
  ValueStack.clear();
×
3955
  delete Locals2;
×
3956
  Locals2 = new BObjectRefVec;
×
3957

3958
  if ( !prog_ok_ )
×
3959
  {
3960
    seterror( true );
×
3961
  }
3962
}
×
3963

3964
void Executor::initForFnCall( unsigned in_PC )
816✔
3965
{
3966
#ifdef MEMORYLEAK
3967
  bool data_shown = false;
3968
#endif
3969

3970
  PC = in_PC;
816✔
3971
  done = 0;
816✔
3972
  seterror( false );
816✔
3973

3974
#ifdef MEMORYLEAK
3975
  while ( !ValueStack.empty() )
3976
  {
3977
    if ( Clib::memoryleak_debug )
3978
    {
3979
      if ( !data_shown )
3980
      {
3981
        LEAKLOG( "ValueStack... " );
3982
        data_shown = true;
3983
      }
3984

3985
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
3986
               ValueStack.back()->impptr()->sizeEstimate() );
3987
    }
3988
    ValueStack.pop_back();
3989
  }
3990
  if ( Clib::memoryleak_debug )
3991
    if ( data_shown )
3992
      LEAKLOGLN( " ...deleted" );
3993
#endif
3994

3995
  ValueStack.clear();
816✔
3996
  Locals2->clear();
816✔
3997
}
816✔
3998

3999
void Executor::pushArg( BObjectImp* arg )
1,211✔
4000
{
4001
  passert_always( arg );
1,211✔
4002
  ValueStack.emplace_back( arg );
1,211✔
4003
}
1,211✔
4004

4005
void Executor::pushArg( const BObjectRef& arg )
×
4006
{
4007
  ValueStack.push_back( arg );
×
4008
}
×
4009

4010
void Executor::addModule( ExecutorModule* module )
20,792✔
4011
{
4012
  availmodules.push_back( module );
20,792✔
4013
}
20,792✔
4014

4015

4016
ExecutorModule* Executor::findModule( const std::string& name )
3,527✔
4017
{
4018
  unsigned idx;
4019
  for ( idx = 0; idx < availmodules.size(); idx++ )
19,047✔
4020
  {
4021
    ExecutorModule* module = availmodules[idx];
19,047✔
4022
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
19,047✔
4023
      return module;
3,527✔
4024
  }
4025
  return nullptr;
×
4026
}
4027

4028
bool Executor::attach_debugger( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching )
4✔
4029
{
4030
  // FIXME: a script can be in debugging state but have no debugger attached,
4031
  // eg. a script that called `os::Debugger()`. This needs to check if a
4032
  // debugger is attached. This works for `os::Debugger()` but not for poldbg cmd_attach.
4033
  if ( dbg_env_ )
4✔
4034
  {
4035
    if ( !listener.expired() )
2✔
4036
    {
4037
      auto& dbg_env_listener = dbg_env_->listener;
1✔
4038
      if ( !dbg_env_listener.expired() )
1✔
4039
      {
4040
        return false;
×
4041
      }
4042
      dbg_env_listener = listener;
1✔
4043
    }
4044
    if ( set_attaching )
2✔
4045
      dbg_env_->debug_state = ExecutorDebugState::ATTACHING;
2✔
4046
  }
4047
  else
4048
  {
4049
    dbg_env_ = std::make_unique<ExecutorDebugEnvironment>( listener, set_attaching );
2✔
4050
  }
4051

4052
  return true;
4✔
4053
}
4054

4055
void Executor::detach_debugger()
×
4056
{
4057
  dbg_env_.reset();
×
4058
  sethalt( false );
×
4059
}
×
4060

4061
void Executor::print_to_debugger( const std::string& message )
21,128✔
4062
{
4063
  if ( dbg_env_ )
21,128✔
4064
  {
4065
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2✔
4066
      listener->on_print( message );
1✔
4067
  }
4068
}
21,128✔
4069

4070
void Executor::dbg_ins_trace()
×
4071
{
4072
  if ( dbg_env_ )
×
4073
  {
4074
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4075
  }
4076
  sethalt( false );
×
4077
}
×
4078
void Executor::dbg_step_into()
1✔
4079
{
4080
  if ( dbg_env_ )
1✔
4081
  {
4082
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1✔
4083
  }
4084
  sethalt( false );
1✔
4085
}
1✔
4086
void Executor::dbg_step_over()
2✔
4087
{
4088
  if ( dbg_env_ )
2✔
4089
  {
4090
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2✔
4091
  }
4092
  sethalt( false );
2✔
4093
}
2✔
4094
void Executor::dbg_step_out()
1✔
4095
{
4096
  if ( dbg_env_ )
1✔
4097
  {
4098
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1✔
4099
  }
4100
  sethalt( false );
1✔
4101
}
1✔
4102
void Executor::dbg_run()
4✔
4103
{
4104
  if ( dbg_env_ )
4✔
4105
  {
4106
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4✔
4107
  }
4108
  sethalt( false );
4✔
4109
}
4✔
4110
void Executor::dbg_break()
1✔
4111
{
4112
  if ( dbg_env_ )
1✔
4113
  {
4114
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1✔
4115
  }
4116
}
1✔
4117

4118
void Executor::dbg_setbp( unsigned atPC )
2✔
4119
{
4120
  if ( dbg_env_ )
2✔
4121
  {
4122
    dbg_env_->breakpoints.insert( atPC );
2✔
4123
  }
4124
}
2✔
4125
void Executor::dbg_clrbp( unsigned atPC )
×
4126
{
4127
  if ( dbg_env_ )
×
4128
  {
4129
    dbg_env_->breakpoints.erase( atPC );
×
4130
  }
4131
}
×
4132

4133
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1✔
4134
{
4135
  if ( dbg_env_ )
1✔
4136
  {
4137
    std::set<unsigned> result;
1✔
4138
    auto& breakpoints = dbg_env_->breakpoints;
1✔
4139
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1✔
4140
                         std::inserter( result, result.end() ) );
4141
    breakpoints = result;
1✔
4142
  }
1✔
4143
}
1✔
4144

4145
void Executor::dbg_clrallbp()
×
4146
{
4147
  if ( dbg_env_ )
×
4148
  {
4149
    dbg_env_->breakpoints.clear();
×
4150
  }
4151
}
×
4152

4153
size_t Executor::sizeEstimate() const
1,999✔
4154
{
4155
  size_t size = sizeof( *this );
1,999✔
4156
  size += Clib::memsize( upperLocals2 );
1,999✔
4157
  for ( const auto& bobjectrefvec : upperLocals2 )
1,999✔
4158
  {
4159
    size += Clib::memsize( *bobjectrefvec );
×
4160
    for ( const auto& bojectref : *bobjectrefvec )
×
4161
    {
4162
      if ( bojectref != nullptr )
×
4163
        size += bojectref->sizeEstimate();
×
4164
    }
4165
  }
4166
  size += Clib::memsize( ControlStack );
1,999✔
4167

4168
  size += Clib::memsize( *Locals2 );
1,999✔
4169
  for ( const auto& bojectref : *Locals2 )
2,107✔
4170
  {
4171
    if ( bojectref != nullptr )
108✔
4172
      size += bojectref->sizeEstimate();
108✔
4173
  }
4174
  size += Clib::memsize( *Globals2 );
1,999✔
4175
  for ( const auto& bojectref : *Globals2 )
4,134✔
4176
  {
4177
    if ( bojectref != nullptr )
2,135✔
4178
      size += bojectref->sizeEstimate();
2,135✔
4179
  }
4180
  size += Clib::memsize( ValueStack );
1,999✔
4181
  for ( const auto& bojectref : ValueStack )
2,037✔
4182
  {
4183
    if ( bojectref != nullptr )
38✔
4184
      size += bojectref->sizeEstimate();
38✔
4185
  }
4186
  size += Clib::memsize( fparams );
1,999✔
4187
  for ( const auto& bojectref : fparams )
1,999✔
4188
  {
4189
    if ( bojectref != nullptr )
×
4190
      size += bojectref->sizeEstimate();
×
4191
  }
4192
  for ( const auto& module : availmodules )
16,160✔
4193
  {
4194
    if ( module != nullptr )
14,161✔
4195
      size += module->sizeEstimate();
14,161✔
4196
  }
4197
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
1,999✔
4198
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
1,999✔
4199
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
1,999✔
4200
  size += Clib::memsize( class_methods );
1,999✔
4201
  return size;
1,999✔
4202
}
4203

4204
bool Executor::builtinMethodForced( const char*& methodname )
7✔
4205
{
4206
  if ( methodname[0] == '_' )
7✔
4207
  {
4208
    ++methodname;
×
4209
    return true;
×
4210
  }
4211
  return false;
7✔
4212
}
4213

4214
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
35,100✔
4215
{
4216
  auto* func = continuation->func();
35,100✔
4217

4218
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4219
  // there are too many
4220
  while ( func->numParams() > static_cast<int>( args.size() ) )
35,106✔
4221
  {
4222
    args.emplace_back( UninitObject::create() );
6✔
4223
  }
4224

4225
  // Resize args only for non-varadic functions
4226
  if ( !func->variadic() )
35,100✔
4227
    args.resize( func->numParams() );
35,076✔
4228

4229
  continuation->args = std::move( args );
35,100✔
4230

4231
  return continuation;
35,100✔
4232
}
4233

4234
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
1,598✔
4235
{
4236
  // Compare the program pointers
4237
  if ( prog < other.prog )
1,598✔
4238
    return true;
×
4239
  if ( prog > other.prog )
1,598✔
4240
    return false;
×
4241

4242
  // Compare the indices
4243
  if ( index < other.index )
1,598✔
4244
    return true;
174✔
4245
  if ( index > other.index )
1,424✔
4246
    return false;
69✔
4247

4248
  // Perform a case-insensitive comparison for method_name using stricmp
4249
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
1,355✔
4250
}
4251

4252
#ifdef ESCRIPT_PROFILE
4253
std::map<std::string, EscriptProfiler::profile_instr> EscriptProfiler::escript_profile_map_{};
4254
EscriptProfiler::EscriptProfiler( ExecutorModule* em, const ModuleFunction* modfunc,
4255
                                  const std::vector<BObjectRef>& fparams )
4256
{
4257
  name_ = em->functionName( modfunc->funcidx );
4258
  if ( !fparams.empty() )
4259
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4260
}
4261
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectRef& leftref,
4262
                                  const std::vector<BObjectRef>& fparams )
4263
{
4264
  switch ( ins.token.id )
4265
  {
4266
  case INS_GET_MEMBER:
4267
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.tokval() );
4268
    break;
4269
  case INS_GET_MEMBER_ID:
4270
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4271
    break;
4272
  case INS_CALL_METHOD_ID:
4273
    name_ = fmt::format( "MTHID_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4274
  default:
4275
    break;
4276
  }
4277
  if ( !fparams.empty() )
4278
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4279
}
4280
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectImp* callee,
4281
                                  const char* method_name, const std::vector<BObjectRef>& fparams )
4282
{
4283
  switch ( ins.token.id )
4284
  {
4285
  case INS_CALL_METHOD:
4286
    name_ = fmt::format( "MTH_{} .{}", callee->typeOf(), method_name );
4287
    break;
4288
  default:
4289
    break;
4290
  }
4291
  if ( !fparams.empty() )
4292
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4293
}
4294

4295
EscriptProfiler::~EscriptProfiler()
4296
{
4297
  auto profile_end = timer_.ellapsed().count();
4298
  auto itr = escript_profile_map_.find( name_ );
4299
  if ( itr != escript_profile_map_.end() )
4300
  {
4301
    itr->second.count++;
4302
    itr->second.sum += profile_end;
4303
    if ( itr->second.max < profile_end )
4304
      itr->second.max = profile_end;
4305
    else if ( itr->second.min > profile_end )
4306
      itr->second.min = profile_end;
4307
  }
4308
  else
4309
  {
4310
    escript_profile_map_[name_] = {
4311
        .sum = profile_end, .max = profile_end, .min = profile_end, .count = 1 };
4312
  }
4313
}
4314
std::string EscriptProfiler::result()
4315
{
4316
  std::string buffer = "FuncName,Count,Min,Max,Sum,Avarage\n";
4317
  for ( const auto& [name, profile] : escript_profile_map_ )
4318
  {
4319
    fmt::format_to( std::back_inserter( buffer ), "{},{},{},{},{},{:.2f}\n", name, profile.count,
4320
                    profile.min, profile.max, profile.sum, profile.sum / ( 1.0 * profile.count ) );
4321
  }
4322
  return buffer;
4323
}
4324
#endif
4325
}  // namespace Pol::Bscript
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