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

polserver / polserver / 21008991693

14 Jan 2026 08:35PM UTC coverage: 60.508% (+0.001%) from 60.507%
21008991693

push

github

web-flow
ClangTidy "modernize-use-override" (#851)

* let clang-tidy do its thing

* Automated clang-tidy change: modernize-use-override

* compile test

---------

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

82 of 153 new or added lines in 40 files covered. (53.59%)

3 existing lines in 3 files now uncovered.

44461 of 73479 relevant lines covered (60.51%)

512457.07 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
58
{
59
namespace Bscript
60
{
61
std::set<Executor*> executor_instances;
62

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

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

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

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

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

181
  return true;
296✔
182
}
183

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

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

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

231
  delete Locals2;
2,547✔
232
  Locals2 = nullptr;
2,547✔
233

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

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

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

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

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

290
int Executor::getParams( unsigned howMany )
35,836,484✔
291
{
292
  if ( howMany )
35,836,484✔
293
  {
294
    fparams.resize( howMany );
35,823,958✔
295
    for ( int i = howMany - 1; i >= 0; --i )
71,719,031✔
296
    {
297
      if ( ValueStack.empty() )
35,895,073✔
298
      {
299
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
300
        seterror( true );
×
301
        return -1;
×
302
      }
303
      fparams[i] = ValueStack.back();
35,895,073✔
304
      ValueStack.pop_back();
35,895,073✔
305
    }
306
  }
307
  expandParams();
35,836,484✔
308
  return 0;
35,836,484✔
309
}
310

311
void Executor::expandParams()
35,836,484✔
312
{
313
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
71,732,784✔
314
  {
315
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
35,896,300✔
316
    {
317
      // defer destruction
318
      BObjectRef obj( spread );
628✔
319

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

323
      BObjectRef refIter( UninitObject::create() );
628✔
324

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

328
      BObject* next = pIter->step();
628✔
329

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

342
void Executor::cleanParams()
35,834,154✔
343
{
344
  fparams.clear();
35,834,154✔
345
}
35,834,154✔
346

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

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

357
  return 0;
×
358
}
359

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

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

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

381
  return 0;
×
382
}
383

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

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

408
  return fparams[param].get();
35,753,436✔
409
}
410

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

418
  return fparams[param].get()->impptr();
71,324✔
419
}
420

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

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

435
  BObjectImp* imp = fparams[param].get()->impptr();
40,971✔
436

437
  passert( imp != nullptr );
40,971✔
438

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

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

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

466
  passert( imp != nullptr );
26,479✔
467

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

477

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

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

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

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

501
  value = plong->value();
6,703✔
502
  return true;
6,703✔
503
}
504

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

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

515
  if ( !message.empty() )
×
516
  {
517
    INFO_PRINTLN( message );
×
518
  }
519

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

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

536
  INFO_PRINTLN( "---" );
×
537
}
538

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

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

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

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

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

589
  return false;
51✔
590
}
591

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

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

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

614
  return nullptr;
×
615
}
616

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

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

631
  return nullptr;
×
632
}
633

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

799
  return false;
×
800
}
801

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

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

831
  return ( *Locals2 )[varnum];
×
832
}
833

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

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

848

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

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

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

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

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

883
  BObjectRef ref = ValueStack.back();
62,233✔
884
  ValueStack.pop_back();
62,233✔
885
  return ref;
62,233✔
886
}
62,233✔
887

888

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

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

904
  ExecutorModule* em = execmodules[token.module];
35,789,108✔
905

906
  func_result_ = nullptr;
35,789,108✔
907
  BObjectImp* resimp;
908
  {
909
    ESCRIPT_PROFILER( em, modfunc, fparams );
910
    resimp = em->execFunc( modfunc->funcidx );
35,789,108✔
911
  }
912
  if ( func_result_ )
35,789,108✔
913
  {
914
    if ( resimp )
8✔
915
    {
916
      BObject obj( resimp );
8✔
917
    }
8✔
918
    ValueStack.emplace_back( func_result_ );
8✔
919
    func_result_ = nullptr;
8✔
920
  }
921
  else if ( resimp )
35,789,100✔
922
  {
923
    ValueStack.emplace_back( resimp );
35,789,099✔
924
  }
925
  else
926
  {
927
    ValueStack.emplace_back( UninitObject::create() );
1✔
928
  }
929

930
  current_module_function = nullptr;
35,789,108✔
931
}
35,789,108✔
932

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

938
  Locals2->emplace_back( UninitObject::create() );
23,794✔
939

940
  ValueStack.emplace_back( Locals2->back().get() );
23,794✔
941
}
23,794✔
942

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

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

957
  ValueStack.emplace_back( objref );
69✔
958
}
69✔
959

960
void Executor::popParam( const Token& /*token*/ )
48,760✔
961
{
962
  BObjectRef objref = getObjRef();
48,760✔
963

964
  Locals2->emplace_back( objref->impptr()->copy() );
48,760✔
965
}
48,760✔
966

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

971
  Locals2->emplace_back( objref );
7,938✔
972
}
7,938✔
973

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

987

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

995
  const String& varname = right.impref<const String>();
168✔
996

997
  return left.impref().operDotPlus( varname.data() );
168✔
998
}
999

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

1007
  const String& varname = right.impref<const String>();
78✔
1008

1009
  return left.impref().operDotMinus( varname.data() );
78✔
1010
}
1011

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

1019
  const String& varname = right.impref<const String>();
1,607✔
1020

1021
  return left.impref().operDotQMark( varname.data() );
1,607✔
1022
}
1023

1024

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

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

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

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

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

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

1104
  auto pIterVal = new BObject( UninitObject::create() );
685✔
1105

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

1111
  Locals2->emplace_back( pIterVal );
685✔
1112

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1372

1373
void Executor::ins_jmpiftrue( const Instruction& ins )
3,095✔
1374
{
1375
  BObjectRef& objref = ValueStack.back();
3,095✔
1376

1377
  if ( objref->impptr()->isTrue() )
3,095✔
1378
    PC = (unsigned)ins.token.lval;
2,953✔
1379

1380
  ValueStack.pop_back();
3,095✔
1381
}
3,095✔
1382

1383
void Executor::ins_jmpiffalse( const Instruction& ins )
37,543✔
1384
{
1385
  BObjectRef& objref = ValueStack.back();
37,543✔
1386

1387
  if ( !objref->impptr()->isTrue() )
37,543✔
1388
    PC = (unsigned)ins.token.lval;
27,902✔
1389

1390
  ValueStack.pop_back();
37,543✔
1391
}
37,543✔
1392

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

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

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

1416
    std::string joined;
10,391✔
1417
    joined.reserve( length );
10,391✔
1418

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

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

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

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

1439
  exprref.set( new String( formatted ) );
196✔
1440
}
196✔
1441

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

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

1466

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

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

1480
// case TOK_LONG:
1481
void Executor::ins_long( const Instruction& ins )
35,854,794✔
1482
{
1483
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
35,854,794✔
1484
}
35,854,794✔
1485

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

1492
// case TOK_CONSUMER:
1493
void Executor::ins_consume( const Instruction& /*ins*/ )
35,826,634✔
1494
{
1495
  ValueStack.pop_back();
35,826,634✔
1496
}
35,826,634✔
1497

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

1504
  BObject& right = *rightref;
3✔
1505
  BObject& left = *leftref;
3✔
1506

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

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

1518
  BObject& right = *rightref;
3✔
1519
  BObject& left = *leftref;
3✔
1520

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

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

1532
  BObject& right = *rightref;
454✔
1533
  BObject& left = *leftref;
454✔
1534

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

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

1547
  BObject& right = *rightref;
225✔
1548
  BObject& left = *leftref;
225✔
1549

1550
  BObjectImp& rightimpref = right.impref();
225✔
1551

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

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

1563
  BObject& right = *rightref;
15✔
1564
  BObject& left = *leftref;
15✔
1565

1566
  BObjectImp& leftimpref = left.impref();
15✔
1567

1568
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15✔
1569

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

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

1585
  BObject& right = *rightref;
6✔
1586
  BObject& left = *leftref;
6✔
1587

1588
  BObjectImp& leftimpref = left.impref();
6✔
1589

1590
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1591

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

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

1607
  BObject& right = *rightref;
9✔
1608
  BObject& left = *leftref;
9✔
1609

1610
  BObjectImp& leftimpref = left.impref();
9✔
1611

1612
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
9✔
1613

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

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

1629
  BObject& right = *rightref;
6✔
1630
  BObject& left = *leftref;
6✔
1631

1632
  BObjectImp& leftimpref = left.impref();
6✔
1633

1634
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1635

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

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

1651
  BObject& right = *rightref;
6✔
1652
  BObject& left = *leftref;
6✔
1653

1654
  BObjectImp& leftimpref = left.impref();
6✔
1655

1656
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1657

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

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

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

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

1691
  BObjectRef& rightref = ValueStack.back();
6,282✔
1692

1693
  BObject& right = *rightref;
6,282✔
1694

1695
  BObjectImp& rightimpref = right.impref();
6,282✔
1696

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

1711
  BObjectRef& rightref = ValueStack.back();
1,102✔
1712

1713
  BObject& right = *rightref;
1,102✔
1714

1715
  BObjectImp& rightimpref = right.impref();
1,102✔
1716

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

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

1735
  BObject& right = *rightref;
66✔
1736
  BObject& left = *leftref;
66✔
1737

1738
  BObjectImp& rightimpref = right.impref();
66✔
1739

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

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

1762
  BObject& right = *rightref;
28,583✔
1763
  BObject& left = *leftref;
28,583✔
1764

1765
  BObjectImp& rightimpref = right.impref();
28,583✔
1766

1767
  if ( right.count() == 1 && rightimpref.count() == 1 )
28,583✔
1768
  {
1769
    left.setimp( &rightimpref );
27,006✔
1770
  }
1771
  else
1772
  {
1773
    left.setimp( rightimpref.copy() );
1,577✔
1774
  }
1775
}
28,583✔
1776

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

1795
  BObject& y = *y_ref;
9✔
1796
  BObject& i = *i_ref;
9✔
1797
  BObject& x = *x_ref;
9✔
1798

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

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

1822
  BObject& y = *y_ref;
669✔
1823
  BObject& i = *i_ref;
669✔
1824
  BObject& x = *x_ref;
669✔
1825

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

1829
  BObject obj( result );
669✔
1830
  ValueStack.pop_back();
669✔
1831
}
669✔
1832

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

1845
  BObject& right = *rightref;
22,481✔
1846
  BObject& left = *leftref;
22,481✔
1847

1848
  leftref.set( right.impref().selfPlusObjImp( left.impref() ) );
22,481✔
1849
}
22,481✔
1850

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

1863
  BObject& right = *rightref;
3,467✔
1864
  BObject& left = *leftref;
3,467✔
1865

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

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

1881
  BObject& right = *rightref;
1,526✔
1882
  BObject& left = *leftref;
1,526✔
1883

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

1898
  BObject& right = *rightref;
332✔
1899
  BObject& left = *leftref;
332✔
1900

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

1915
  BObject& right = *rightref;
495✔
1916
  BObject& left = *leftref;
495✔
1917

1918
  leftref.set( right.impref().selfModulusObjImp( left.impref() ) );
495✔
1919
}
495✔
1920

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

1928
  BObject& right = *rightref;
100✔
1929
  BObject& left = *leftref;
100✔
1930

1931
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100✔
1932
}
100✔
1933

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

1946
  BObject& right = *rightref;
297✔
1947
  BObject& left = *leftref;
297✔
1948

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

1963
  BObject& right = *rightref;
297✔
1964
  BObject& left = *leftref;
297✔
1965

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

1980
  BObject& right = *rightref;
300✔
1981
  BObject& left = *leftref;
300✔
1982

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

1997
  BObject& right = *rightref;
297✔
1998
  BObject& left = *leftref;
297✔
1999

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

2014
  BObject& right = *rightref;
297✔
2015
  BObject& left = *leftref;
297✔
2016

2017
  leftref.set( right.impref().selfBitOrObjImp( left.impref() ) );
297✔
2018
}
297✔
2019

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

2031
  BObject& right = *rightref;
5,951✔
2032
  BObject& left = *leftref;
5,951✔
2033

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

2048
  BObject& right = *rightref;
1,660✔
2049
  BObject& left = *leftref;
1,660✔
2050

2051
  int _true = ( left.isTrue() || right.isTrue() );
1,660✔
2052
  leftref.set( new BLong( _true ) );
1,660✔
2053
}
1,660✔
2054

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

2066
  BObject& right = *rightref;
27,280✔
2067
  BObject& left = *leftref;
27,280✔
2068

2069
  int _true = ( left != right );
27,280✔
2070
  leftref.set( new BLong( _true ) );
27,280✔
2071
}
27,280✔
2072

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

2084
  BObject& right = *rightref;
8,081✔
2085
  BObject& left = *leftref;
8,081✔
2086

2087
  int _true = ( left == right );
8,081✔
2088
  leftref.set( new BLong( _true ) );
8,081✔
2089
}
8,081✔
2090

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

2102
  BObject& right = *rightref;
4,215✔
2103
  BObject& left = *leftref;
4,215✔
2104

2105
  int _true = ( left < right );
4,215✔
2106
  leftref.set( new BLong( _true ) );
4,215✔
2107
}
4,215✔
2108

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

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

2136
  BObject& right = *rightref;
5,957✔
2137
  BObject& left = *leftref;
5,957✔
2138

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

2153
  BObject& right = *rightref;
419✔
2154
  BObject& left = *leftref;
419✔
2155

2156
  int _true = ( left >= right );
419✔
2157
  leftref.set( new BLong( _true ) );
419✔
2158
}
419✔
2159

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

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

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

2185
  BObjectRef& leftref = ValueStack.back();
7,231✔
2186
  leftref = ( *leftref )->OperMultiSubscript( indices );
7,231✔
2187
}
7,231✔
2188

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

2194
  BObjectRef refIter( new BObject( UninitObject::create() ) );
104✔
2195

2196
  BObjectRef rightref = ValueStack.back();
104✔
2197
  ValueStack.pop_back();
104✔
2198

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

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

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

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

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

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

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

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

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

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

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

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

2293
    OrderedSet indexes;
39✔
2294

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

2301
    BObjectRef rightref = ValueStack.back();
39✔
2302
    ValueStack.pop_back();
39✔
2303

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

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

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

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

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

2330
    auto& unique_index = indexes.get<1>();
24✔
2331

2332
    while ( auto res = pIter->step() )
120✔
2333
    {
2334
      auto itr = unique_index.find( *refIter.get() );
96✔
2335

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

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

2352
    BObjectRef rightref = ValueStack.back();
82✔
2353
    ValueStack.pop_back();
82✔
2354

2355
    // Reserve to keep the insert_at iterator valid
2356
    ValueStack.reserve( ValueStack.size() + binding_count );
82✔
2357
    auto insert_at = ValueStack.end();
82✔
2358

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

2366
void Executor::ins_take_local( const Instruction& )
388✔
2367
{
2368
  passert( Locals2 != nullptr );
388✔
2369
  passert( !ValueStack.empty() );
388✔
2370

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

2375
  BObjectRef& rightref = ValueStack.back();
388✔
2376

2377
  BObject& right = *rightref;
388✔
2378

2379
  BObjectImp& rightimpref = right.impref();
388✔
2380

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

2392
void Executor::ins_take_global( const Instruction& ins )
183✔
2393
{
2394
  passert( !ValueStack.empty() );
183✔
2395

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

2399
  BObjectRef& rightref = ValueStack.back();
183✔
2400

2401
  BObject& right = *rightref;
183✔
2402

2403
  BObjectImp& rightimpref = right.impref();
183✔
2404

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

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

2428
  BObjectRef& leftref = ValueStack.back();
6✔
2429
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
6✔
2430
}
6✔
2431

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

2443
  BObject& right = *rightref;
168✔
2444
  BObject& left = *leftref;
168✔
2445

2446
  leftref = addmember( left, right );
168✔
2447
}
168✔
2448

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

2460
  BObject& right = *rightref;
78✔
2461
  BObject& left = *leftref;
78✔
2462

2463
  leftref = removemember( left, right );
78✔
2464
}
78✔
2465

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

2477
  BObject& right = *rightref;
1,607✔
2478
  BObject& left = *leftref;
1,607✔
2479

2480
  leftref = checkmember( left, right );
1,607✔
2481
}
1,607✔
2482

2483
void Executor::ins_addmember2( const Instruction& ins )
45✔
2484
{
2485
  BObjectRef& obref = ValueStack.back();
45✔
2486

2487
  BObject& ob = *obref;
45✔
2488

2489
  ob.impref().operDotPlus( ins.token.tokval() );
45✔
2490
}
45✔
2491

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

2498
  ValueStack.pop_back();
1,831✔
2499

2500
  BObjectRef& obref = ValueStack.back();
1,831✔
2501
  BObject& ob = *obref;
1,831✔
2502

2503
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
1,831✔
2504
  BObject& mem = *memref;
1,831✔
2505

2506
  if ( valob.count() == 1 && valimp->count() == 1 )
1,831✔
2507
  {
2508
    mem.setimp( valimp );
1,324✔
2509
  }
2510
  else
2511
  {
2512
    mem.setimp( valimp->copy() );
507✔
2513
  }
2514
  // the struct is at the top of the stack
2515
}
1,831✔
2516

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

2530
  BObjectRef valref = ValueStack.back();
138✔
2531
  ValueStack.pop_back();
138✔
2532
  BObject& valob = *valref;
138✔
2533
  BObjectImp* valimp = valob.impptr();
138✔
2534

2535
  BObjectRef keyref = ValueStack.back();
138✔
2536
  ValueStack.pop_back();
138✔
2537
  BObject& keyob = *keyref;
138✔
2538
  BObjectImp* keyimp = keyob.impptr();
138✔
2539

2540
  BObjectRef& dictref = ValueStack.back();
138✔
2541
  BObject& dictob = *dictref;
138✔
2542
  BDictionary* dict = dictob.impptr<BDictionary>();
138✔
2543

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

2553
  dict->addMember( keyimp, valimp );
138✔
2554
  // the dictionary remains at the top of the stack.
2555
}
138✔
2556

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

2568
  BObject& right = *rightref;
769✔
2569
  BObject& left = *leftref;
769✔
2570

2571
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
769✔
2572
}
769✔
2573

2574
void Executor::ins_insert_into( const Instruction& /*ins*/ )
7,681✔
2575
{
2576
  BObjectRef rightref = ValueStack.back();
7,681✔
2577
  ValueStack.pop_back();
7,681✔
2578
  BObjectRef& leftref = ValueStack.back();
7,681✔
2579

2580
  BObject& right = *rightref;
7,681✔
2581
  BObject& left = *leftref;
7,681✔
2582

2583
  if ( auto* spread = right.impptr_if<BSpread>() )
7,681✔
2584
  {
2585
    BObjectRef refIter( UninitObject::create() );
1,473✔
2586

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

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

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

2614
  BObject& right = *rightref;
820✔
2615
  BObject& left = *leftref;
820✔
2616

2617
  left.impref().operPlusEqual( left, right.impref() );
820✔
2618
}
820✔
2619

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

2631
  BObject& right = *rightref;
240✔
2632
  BObject& left = *leftref;
240✔
2633

2634
  left.impref().operMinusEqual( left, right.impref() );
240✔
2635
}
240✔
2636

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

2648
  BObject& right = *rightref;
237✔
2649
  BObject& left = *leftref;
237✔
2650

2651
  left.impref().operTimesEqual( left, right.impref() );
237✔
2652
}
237✔
2653

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

2665
  BObject& right = *rightref;
228✔
2666
  BObject& left = *leftref;
228✔
2667

2668
  left.impref().operDivideEqual( left, right.impref() );
228✔
2669
}
228✔
2670

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

2682
  BObject& right = *rightref;
375✔
2683
  BObject& left = *leftref;
375✔
2684

2685
  left.impref().operModulusEqual( left, right.impref() );
375✔
2686
}
375✔
2687

2688
// case RSV_GOTO:
2689
void Executor::ins_goto( const Instruction& ins )
35,752,990✔
2690
{
2691
  PC = (unsigned)ins.token.lval;
35,752,990✔
2692
}
35,752,990✔
2693

2694
// TOK_FUNC:
2695
void Executor::ins_func( const Instruction& ins )
35,789,108✔
2696
{
2697
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
35,789,108✔
2698
  getParams( nparams );
35,789,108✔
2699
  execFunc( ins.token );
35,789,108✔
2700
  cleanParams();
35,789,108✔
2701
  return;
35,789,108✔
2702
}
2703

2704
void Executor::ins_call_method_id( const Instruction& ins )
45,744✔
2705
{
2706
  BContinuation* continuation = nullptr;
45,744✔
2707
  unsigned nparams = ins.token.type;
45,744✔
2708

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

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

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

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

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

2752
        // Add function reference to stack
2753
        ValueStack.emplace_back( continuation->func() );
790✔
2754

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

2759
        continuation->args.clear();
790✔
2760

2761
        cleanParams();
790✔
2762

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

2768
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2769
        // handle it.
2770

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

2775
        continue;
790✔
2776
      }
2777
    }
2778

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

2792
      objref.set( func_result_ );
16✔
2793
      func_result_ = nullptr;
16✔
2794
    }
2795
    else if ( imp )
43,395✔
2796
    {
2797
      objref.set( imp );
43,341✔
2798
    }
2799
    else
2800
    {
2801
      objref.set( UninitObject::create() );
54✔
2802
    }
2803

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

2812
void Executor::ins_call_method( const Instruction& ins )
842✔
2813
{
2814
  unsigned nparams = ins.token.lval;
842✔
2815
  auto method_name = ins.token.tokval();
842✔
2816

2817
  getParams( nparams );
842✔
2818
  BObjectImp* callee = ValueStack.back()->impptr();
842✔
2819

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

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

2845
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
842✔
2846
  {
2847
    cleanParams();
607✔
2848
    return;
607✔
2849
  }
2850

2851
  BObjectRef& objref = ValueStack[stacksize - 1];
235✔
2852
  if ( func_result_ )
235✔
2853
  {
2854
    if ( imp )
×
2855
    {
2856
      BObject obj( imp );
×
2857
    }
×
2858

2859
    objref.set( func_result_ );
×
2860
    func_result_ = nullptr;
×
2861
  }
2862
  else if ( imp )
235✔
2863
  {
2864
    objref.set( imp );
60✔
2865
  }
2866
  else
2867
  {
2868
    objref.set( UninitObject::create() );
175✔
2869
  }
2870
  cleanParams();
235✔
2871
}
2872

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

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

2888

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

2897
void Executor::ins_check_mro( const Instruction& ins )
280✔
2898
{
2899
  auto classinst_offset = ins.token.lval;
280✔
2900

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

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

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

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

2928
  auto ctor_addr = jsr_ins.token.lval;
280✔
2929

2930
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280✔
2931

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

2946
// CTRL_JSR_USERFUNC:
2947
void Executor::ins_jsr_userfunc( const Instruction& ins )
6,224✔
2948
{
2949
  jump( ins.token.lval, nullptr, nullptr );
6,224✔
2950
}
6,224✔
2951

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

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

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

2972
    Globals2 = funcref->globals;
115✔
2973

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

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

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

2990
  ControlStack.push_back( rc );
44,264✔
2991

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

3010

3011
BObjectImp* Executor::get_stacktrace( bool as_array )
44✔
3012
{
3013
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
44✔
3014

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

3029
          std::string functionName =
3030
              dbgFunction != prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
161✔
3031

3032
          handler( pc, filename, line, functionName );
161✔
3033
        } );
161✔
3034
  };
42✔
3035

3036
  if ( as_array )
44✔
3037
  {
3038
    std::unique_ptr<ObjArray> result( new ObjArray );
4✔
3039

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

3066
    return result.release();
4✔
3067
  }
4✔
3068
  else  // as string
3069
  {
3070
    std::string result;
40✔
3071

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

3091
    return new String( std::move( result ) );
40✔
3092
  }
40✔
3093
}
3094

3095
void Executor::ins_pop_param( const Instruction& ins )
48,760✔
3096
{
3097
  popParam( ins.token );
48,760✔
3098
}
48,760✔
3099

3100
void Executor::ins_pop_param_byref( const Instruction& ins )
7,938✔
3101
{
3102
  popParamByRef( ins.token );
7,938✔
3103
}
7,938✔
3104

3105
void Executor::ins_get_arg( const Instruction& ins )
433✔
3106
{
3107
  getArg( ins.token );
433✔
3108
}
433✔
3109

3110
// CTRL_LEAVE_BLOCK:
3111
void Executor::ins_leave_block( const Instruction& ins )
9,424✔
3112
{
3113
  if ( Locals2 )
9,424✔
3114
  {
3115
    for ( int i = 0; i < ins.token.lval; i++ )
25,141✔
3116
      Locals2->pop_back();
15,717✔
3117
  }
3118
  else  // at global level.  ick.
3119
  {
3120
    for ( int i = 0; i < ins.token.lval; i++ )
×
3121
      Globals2->pop_back();
×
3122
  }
3123
}
9,424✔
3124

3125
void Executor::ins_gosub( const Instruction& ins )
×
3126
{
3127
  ReturnContext rc;
×
3128
  rc.PC = PC;
×
3129
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3130
  ControlStack.push_back( rc );
×
3131
  if ( Locals2 )
×
3132
    upperLocals2.push_back( Locals2 );
×
3133
  Locals2 = new BObjectRefVec;
×
3134

3135
  PC = (unsigned)ins.token.lval;
×
3136
}
×
3137

3138
// case RSV_RETURN
3139
void Executor::ins_return( const Instruction& /*ins*/ )
44,264✔
3140
{
3141
  if ( ControlStack.empty() )
44,264✔
3142
  {
3143
    ERROR_PRINTLN( "Return without GOSUB! (PC={}, {})", PC, scriptname() );
×
3144

3145
    seterror( true );
×
3146
    return;
×
3147
  }
3148
  BObjectRef continuation;
44,264✔
3149
  ReturnContext& rc = ControlStack.back();
44,264✔
3150
  PC = rc.PC;
44,264✔
3151

3152
  if ( rc.Continuation.get() != nullptr )
44,264✔
3153
  {
3154
    continuation = rc.Continuation;
35,890✔
3155
  }
3156

3157
  if ( Locals2 )
44,264✔
3158
  {
3159
    delete Locals2;
44,264✔
3160
    Locals2 = nullptr;
44,264✔
3161
  }
3162
  if ( !upperLocals2.empty() )
44,264✔
3163
  {
3164
    Locals2 = upperLocals2.back();
44,264✔
3165
    upperLocals2.pop_back();
44,264✔
3166
  }
3167

3168
  if ( rc.ExternalContext.has_value() )
44,264✔
3169
  {
3170
    prog_ = std::move( rc.ExternalContext->Program );
115✔
3171
    nLines = static_cast<unsigned int>( prog_->instr.size() );
115✔
3172
    execmodules = std::move( rc.ExternalContext->Modules );
115✔
3173
    Globals2 = std::move( rc.ExternalContext->Globals );
115✔
3174
  }
3175

3176
  // FIXME do something with rc.ValueStackDepth
3177
  ControlStack.pop_back();
44,264✔
3178

3179
  if ( continuation != nullptr )
44,264✔
3180
  {
3181
    auto result = ValueStack.back();
35,890✔
3182
    ValueStack.pop_back();
35,890✔
3183

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

3189
    // If the the continuation callback returned a continuation, handle the jump.
3190
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
35,890✔
3191
    {
3192
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3193
      // takes ownership.
3194

3195
      // Add function reference to stack
3196
      ValueStack.emplace_back( cont->func() );
35,100✔
3197

3198
      // Move all arguments to the fparams stack
3199
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
35,100✔
3200
                      std::make_move_iterator( cont->args.end() ) );
3201

3202
      cont->args.clear();
35,100✔
3203

3204
      printStack(
35,100✔
3205
          "continuation callback returned a continuation; continuation args added to fparams" );
3206

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

3226
      // Add the result to the stack.
3227
      ValueStack.emplace_back( imp );
790✔
3228
    }
3229
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
71,780✔
3230
  }
35,890✔
3231
}
44,264✔
3232

3233
void Executor::ins_exit( const Instruction& /*ins*/ )
3✔
3234
{
3235
  done = 1;
3✔
3236
  run_ok_ = false;
3✔
3237
}
3✔
3238

3239
void Executor::ins_double( const Instruction& ins )
427✔
3240
{
3241
  ValueStack.emplace_back( new Double( ins.token.dval ) );
427✔
3242
}
427✔
3243

3244
void Executor::ins_classinst( const Instruction& ins )
321✔
3245
{
3246
  ValueStack.emplace_back( new BConstObject(
321✔
3247
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2 ) ) ) );
642✔
3248
}
321✔
3249

3250
void Executor::ins_string( const Instruction& ins )
107,198✔
3251
{
3252
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
107,198✔
3253
}
107,198✔
3254

3255
void Executor::ins_regexp( const Instruction& )
150✔
3256
{
3257
  BObjectRef flags = ValueStack.back();
150✔
3258
  ValueStack.pop_back();
150✔
3259
  BObjectRef& pattern = ValueStack.back();
150✔
3260

3261
  pattern.set(
150✔
3262
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300✔
3263
}
150✔
3264

3265
void Executor::ins_error( const Instruction& /*ins*/ )
4,177✔
3266
{
3267
  ValueStack.emplace_back( new BError() );
4,177✔
3268
}
4,177✔
3269
void Executor::ins_struct( const Instruction& /*ins*/ )
808✔
3270
{
3271
  ValueStack.emplace_back( new BStruct );
808✔
3272
}
808✔
3273
void Executor::ins_spread( const Instruction& ins )
2,125✔
3274
{
3275
  bool spread_into = ins.token.lval;
2,125✔
3276

3277
  // Pops TOP, and spreads it into (new) TOP.
3278
  if ( spread_into )
2,125✔
3279
  {
3280
    BObjectRef rightref = ValueStack.back();
24✔
3281
    ValueStack.pop_back();
24✔
3282
    BObjectRef& leftref = ValueStack.back();
24✔
3283

3284
    BObject& right = *rightref;
24✔
3285
    BObject& left = *leftref;
24✔
3286

3287
    BObjectRef refIter( UninitObject::create() );
24✔
3288

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

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

3322
// case TOK_UNMINUS:
3323
void Executor::ins_unminus( const Instruction& /*ins*/ )
147✔
3324
{
3325
  BObjectRef ref = getObjRef();
147✔
3326
  BObjectImp* newobj;
3327
  newobj = ref->impref().inverse();
147✔
3328

3329
  ValueStack.emplace_back( newobj );
147✔
3330
}
147✔
3331

3332
// case TOK_UNPLUSPLUS:
3333
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
472✔
3334
{
3335
  BObjectRef& ref = ValueStack.back();
472✔
3336
  ref->impref().selfPlusPlus();
472✔
3337
}
472✔
3338

3339
// case TOK_UNMINUSMINUS:
3340
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
131✔
3341
{
3342
  BObjectRef& ref = ValueStack.back();
131✔
3343
  ref->impref().selfMinusMinus();
131✔
3344
}
131✔
3345

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

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

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

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

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

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

3424
// case TOK_LOG_NOT:
3425
void Executor::ins_logical_not( const Instruction& /*ins*/ )
3,968✔
3426
{
3427
  BObjectRef ref = getObjRef();
3,968✔
3428
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
3,968✔
3429
}
3,968✔
3430

3431
// case TOK_BITWISE_NOT:
3432
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
156✔
3433
{
3434
  BObjectRef ref = getObjRef();
156✔
3435
  ValueStack.emplace_back( ref->impptr()->bitnot() );
156✔
3436
}
156✔
3437

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

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

3451
  ValueStack.emplace_back( new BFunctionRef( prog_, funcref_index, Globals2, {} /* captures */ ) );
1,485✔
3452
}
3453

3454
void Executor::ins_functor( const Instruction& ins )
722✔
3455
{
3456
  auto funcref_index = static_cast<int>( ins.token.type );
722✔
3457

3458
  const auto& ep_funcref = prog_->function_references[funcref_index];
722✔
3459

3460
  int capture_count = ep_funcref.capture_count;
722✔
3461

3462
  auto captures = ValueStackCont();
722✔
3463
  while ( capture_count > 0 )
1,354✔
3464
  {
3465
    captures.push_back( ValueStack.back() );
632✔
3466
    ValueStack.pop_back();
632✔
3467
    capture_count--;
632✔
3468
  }
3469

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

3472
  ValueStack.emplace_back( func );
722✔
3473

3474
  PC += ins.token.lval;
722✔
3475
}
722✔
3476

3477
void Executor::ins_logical_jump( const Instruction& ins )
410✔
3478
{
3479
  BObjectRef& objref = ValueStack.back();
410✔
3480
  // jmp if true for ||, jmp if false for &&
3481
  const bool obj_true = objref->impptr()->isTrue();
410✔
3482
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
410✔
3483

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

3495
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
95✔
3496
{
3497
  BObjectRef& objref = ValueStack.back();
95✔
3498
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
95✔
3499
}
95✔
3500

3501
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3502

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

3581
  case INS_UNPACK_SEQUENCE:
106✔
3582
    return &Executor::ins_unpack_sequence;
106✔
3583
  case INS_UNPACK_INDICES:
84✔
3584
    return &Executor::ins_unpack_indices;
84✔
3585
  case INS_TAKE_GLOBAL:
183✔
3586
    return &Executor::ins_take_global;
183✔
3587
  case INS_TAKE_LOCAL:
298✔
3588
    return &Executor::ins_take_local;
298✔
3589

3590
  case INS_GET_MEMBER_ID:
1,521✔
3591
    return &Executor::ins_get_member_id;  // test id
1,521✔
3592
  case INS_SET_MEMBER_ID:
3✔
3593
    return &Executor::ins_set_member_id;  // test id
3✔
3594
  case INS_SET_MEMBER_ID_CONSUME:
199✔
3595
    return &Executor::ins_set_member_id_consume;  // test id
199✔
3596

3597
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15✔
3598
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15✔
3599
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6✔
3600
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6✔
3601
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9✔
3602
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9✔
3603
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6✔
3604
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6✔
3605
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6✔
3606
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6✔
3607

3608
  case TOK_ADD:
2,426✔
3609
    return &Executor::ins_add;
2,426✔
3610
  case TOK_SUBTRACT:
423✔
3611
    return &Executor::ins_subtract;
423✔
3612
  case TOK_DIV:
59✔
3613
    return &Executor::ins_div;
59✔
3614
  case TOK_MULT:
228✔
3615
    return &Executor::ins_mult;
228✔
3616
  case TOK_MODULUS:
30✔
3617
    return &Executor::ins_modulus;
30✔
3618

3619
  case TOK_INSERTINTO:
5,090✔
3620
    return &Executor::ins_insert_into;
5,090✔
3621

3622
  case TOK_PLUSEQUAL:
80✔
3623
    return &Executor::ins_plusequal;
80✔
3624
  case TOK_MINUSEQUAL:
27✔
3625
    return &Executor::ins_minusequal;
27✔
3626
  case TOK_TIMESEQUAL:
24✔
3627
    return &Executor::ins_timesequal;
24✔
3628
  case TOK_DIVIDEEQUAL:
15✔
3629
    return &Executor::ins_divideequal;
15✔
3630
  case TOK_MODULUSEQUAL:
18✔
3631
    return &Executor::ins_modulusequal;
18✔
3632

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

3692
  case TOK_NEQ:
734✔
3693
    return &Executor::ins_notequal;
734✔
3694
  case TOK_GRTHAN:
152✔
3695
    return &Executor::ins_greaterthan;
152✔
3696
  case TOK_GREQ:
84✔
3697
    return &Executor::ins_greaterequal;
84✔
3698
  case TOK_AND:
66✔
3699
    return &Executor::ins_logical_and;
66✔
3700
  case TOK_OR:
296✔
3701
    return &Executor::ins_logical_or;
296✔
3702

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

3754
void Executor::sethalt( bool halt )
17✔
3755
{
3756
  halt_ = halt;
17✔
3757

3758
  if ( halt && dbg_env_ )
17✔
3759
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18✔
3760
      listener->on_halt();
9✔
3761

3762
  calcrunnable();
17✔
3763
}
17✔
3764

3765
void Executor::execInstr()
144,037,109✔
3766
{
3767
  unsigned onPC = PC;
144,037,109✔
3768
  try
3769
  {  // this is really more of a class invariant.
3770
    passert( run_ok_ );
144,037,109✔
3771
    passert( PC < nLines );
144,037,109✔
3772
    passert( !error_ );
144,037,109✔
3773
    passert( !done );
144,037,109✔
3774

3775
#ifdef NDEBUG
3776
    const Instruction& ins = prog_->instr[PC];
144,037,109✔
3777
#else
3778
    const Instruction& ins = prog_->instr.at( PC );
3779
#endif
3780
    if ( debug_level >= INSTRUCTIONS )
144,037,109✔
3781
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3782

3783
    // If `on_instruction` returns false, do not execute this instruction.
3784
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
144,037,109✔
3785
    {
3786
      return;
9✔
3787
    }
3788

3789
    ++ins.cycles;
144,037,100✔
3790
    ++prog_->instr_cycles;
144,037,100✔
3791
    ++escript_instr_cycles;
144,037,100✔
3792

3793
    ++PC;
144,037,100✔
3794

3795
    ( this->*( ins.func ) )( ins );
144,037,100✔
3796
  }
3797
  catch ( std::exception& ex )
×
3798
  {
3799
    std::string tmp =
3800
        fmt::format( "Exception in: {} PC={}: {}\n", prog_->name.get(), onPC, ex.what() );
×
3801
    if ( !run_ok_ )
×
3802
      tmp += "run_ok_ = false\n";
×
3803
    if ( PC < nLines )
×
3804
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
×
3805
    if ( error_ )
×
3806
      tmp += "error_ = true\n";
×
3807
    if ( done )
×
3808
      tmp += "done = true\n";
×
3809

3810
    seterror( true );
×
3811
    POLLOG_ERROR( tmp );
×
3812

3813
    show_context( onPC );
×
3814
  }
×
3815
#ifdef __unix__
3816
  catch ( ... )
×
3817
  {
3818
    seterror( true );
×
3819
    POLLOG_ERRORLN( "Exception in {}, PC={}: unclassified", prog_->name.get(), onPC );
×
3820

3821
    show_context( onPC );
×
3822
  }
×
3823
#endif
3824
}
3825

3826
std::string Executor::dbg_get_instruction( size_t atPC ) const
×
3827
{
3828
  std::string out;
×
3829
  dbg_get_instruction( atPC, out );
×
3830
  return out;
×
3831
}
×
3832

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

3841
void Executor::show_context( unsigned atPC )
×
3842
{
3843
  unsigned start, end;
3844
  if ( atPC >= 5 )
×
3845
    start = atPC - 5;
×
3846
  else
3847
    start = 0;
×
3848

3849
  end = atPC + 5;
×
3850

3851
  if ( end >= nLines )
×
3852
    end = nLines - 1;
×
3853

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

3867
  end = atPC + 5;
24✔
3868

3869
  if ( end >= nLines )
24✔
3870
    end = nLines - 1;
×
3871

3872
  for ( unsigned i = start; i <= end; ++i )
288✔
3873
  {
3874
    dbg_get_instruction( i, os );
264✔
3875
    os += '\n';
264✔
3876
  }
3877
}
24✔
3878

3879
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
38,040✔
3880
                                        const Instruction& jmp )
3881
{
3882
  // params need to be on the stack, without current objectref
3883
  ValueStack.pop_back();
38,040✔
3884

3885
  // Push captured parameters onto the stack prior to function parameters.
3886
  for ( auto& p : funcr->captures )
43,541✔
3887
    ValueStack.push_back( p );
5,501✔
3888

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

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

3903
    auto rest_arg = std::make_unique<ObjArray>();
640✔
3904

3905
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,655✔
3906
    {
3907
      auto& p = fparams[i];
3,015✔
3908

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

3929
  // jump to function
3930
  jump( jmp.token.lval, continuation, funcr );
38,040✔
3931
  fparams.clear();
38,040✔
3932
  // switch to new block
3933
  ins_makelocal( jmp );
38,040✔
3934
}
38,040✔
3935

3936
bool Executor::exec()
2,519✔
3937
{
3938
  passert( prog_ok_ );
2,519✔
3939
  passert( !error_ );
2,519✔
3940

3941
  Clib::scripts_thread_script = scriptname();
2,519✔
3942

3943
  set_running_to_completion( true );
2,519✔
3944
  while ( runnable() )
647,864✔
3945
  {
3946
    Clib::scripts_thread_scriptPC = PC;
645,345✔
3947
    execInstr();
645,345✔
3948
  }
3949

3950
  return !error_;
2,519✔
3951
}
3952

3953
void Executor::reinitExec()
×
3954
{
3955
  PC = 0;
×
3956
  done = 0;
×
3957
  seterror( false );
×
3958

3959
  ValueStack.clear();
×
3960
  delete Locals2;
×
3961
  Locals2 = new BObjectRefVec;
×
3962

3963
  if ( !prog_ok_ )
×
3964
  {
3965
    seterror( true );
×
3966
  }
3967
}
×
3968

3969
void Executor::initForFnCall( unsigned in_PC )
816✔
3970
{
3971
#ifdef MEMORYLEAK
3972
  bool data_shown = false;
3973
#endif
3974

3975
  PC = in_PC;
816✔
3976
  done = 0;
816✔
3977
  seterror( false );
816✔
3978

3979
#ifdef MEMORYLEAK
3980
  while ( !ValueStack.empty() )
3981
  {
3982
    if ( Clib::memoryleak_debug )
3983
    {
3984
      if ( !data_shown )
3985
      {
3986
        LEAKLOG( "ValueStack... " );
3987
        data_shown = true;
3988
      }
3989

3990
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
3991
               ValueStack.back()->impptr()->sizeEstimate() );
3992
    }
3993
    ValueStack.pop_back();
3994
  }
3995
  if ( Clib::memoryleak_debug )
3996
    if ( data_shown )
3997
      LEAKLOGLN( " ...deleted" );
3998
#endif
3999

4000
  ValueStack.clear();
816✔
4001
  Locals2->clear();
816✔
4002
}
816✔
4003

4004
void Executor::pushArg( BObjectImp* arg )
1,211✔
4005
{
4006
  passert_always( arg );
1,211✔
4007
  ValueStack.emplace_back( arg );
1,211✔
4008
}
1,211✔
4009

4010
void Executor::pushArg( const BObjectRef& arg )
×
4011
{
4012
  ValueStack.push_back( arg );
×
4013
}
×
4014

4015
void Executor::addModule( ExecutorModule* module )
20,792✔
4016
{
4017
  availmodules.push_back( module );
20,792✔
4018
}
20,792✔
4019

4020

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

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

4057
  return true;
4✔
4058
}
4059

4060
void Executor::detach_debugger()
×
4061
{
4062
  dbg_env_.reset();
×
4063
  sethalt( false );
×
4064
}
×
4065

4066
void Executor::print_to_debugger( const std::string& message )
21,128✔
4067
{
4068
  if ( dbg_env_ )
21,128✔
4069
  {
4070
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2✔
4071
      listener->on_print( message );
1✔
4072
  }
4073
}
21,128✔
4074

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

4123
void Executor::dbg_setbp( unsigned atPC )
2✔
4124
{
4125
  if ( dbg_env_ )
2✔
4126
  {
4127
    dbg_env_->breakpoints.insert( atPC );
2✔
4128
  }
4129
}
2✔
4130
void Executor::dbg_clrbp( unsigned atPC )
×
4131
{
4132
  if ( dbg_env_ )
×
4133
  {
4134
    dbg_env_->breakpoints.erase( atPC );
×
4135
  }
4136
}
×
4137

4138
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1✔
4139
{
4140
  if ( dbg_env_ )
1✔
4141
  {
4142
    std::set<unsigned> result;
1✔
4143
    auto& breakpoints = dbg_env_->breakpoints;
1✔
4144
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1✔
4145
                         std::inserter( result, result.end() ) );
4146
    breakpoints = result;
1✔
4147
  }
1✔
4148
}
1✔
4149

4150
void Executor::dbg_clrallbp()
×
4151
{
4152
  if ( dbg_env_ )
×
4153
  {
4154
    dbg_env_->breakpoints.clear();
×
4155
  }
4156
}
×
4157

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

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

4209
bool Executor::builtinMethodForced( const char*& methodname )
7✔
4210
{
4211
  if ( methodname[0] == '_' )
7✔
4212
  {
4213
    ++methodname;
×
4214
    return true;
×
4215
  }
4216
  return false;
7✔
4217
}
4218

4219
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
35,100✔
4220
{
4221
  auto* func = continuation->func();
35,100✔
4222

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

4230
  // Resize args only for non-varadic functions
4231
  if ( !func->variadic() )
35,100✔
4232
    args.resize( func->numParams() );
35,076✔
4233

4234
  continuation->args = std::move( args );
35,100✔
4235

4236
  return continuation;
35,100✔
4237
}
4238

4239
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
1,598✔
4240
{
4241
  // Compare the program pointers
4242
  if ( prog < other.prog )
1,598✔
4243
    return true;
×
4244
  if ( prog > other.prog )
1,598✔
4245
    return false;
×
4246

4247
  // Compare the indices
4248
  if ( index < other.index )
1,598✔
4249
    return true;
174✔
4250
  if ( index > other.index )
1,424✔
4251
    return false;
69✔
4252

4253
  // Perform a case-insensitive comparison for method_name using stricmp
4254
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
1,355✔
4255
}
4256

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

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