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

polserver / polserver / 21541532363

31 Jan 2026 08:14AM UTC coverage: 60.532% (+0.03%) from 60.507%
21541532363

push

github

web-flow
Tidy modernize for loops (#862)

* trigger loop convert

* Automated clang-tidy change: modernize-loop-convert

* fixed refactor

* Automated clang-tidy change: modernize-loop-convert

* compile

* first look through

* fixes and start to use a few ranges

* revert autogenerated file

* compilation fix

* second pass

* renamed loop variable

---------

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

164 of 447 new or added lines in 61 files covered. (36.69%)

6 existing lines in 5 files now uncovered.

44377 of 73312 relevant lines covered (60.53%)

499857.83 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
#include <ranges>
56

57

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

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

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

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

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

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

180
  return true;
296✔
181
}
182

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

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

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

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

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

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

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

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

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

289
int Executor::getParams( unsigned howMany )
34,521,073✔
290
{
291
  if ( howMany )
34,521,073✔
292
  {
293
    fparams.resize( howMany );
34,508,546✔
294
    for ( int i = howMany - 1; i >= 0; --i )
69,088,217✔
295
    {
296
      if ( ValueStack.empty() )
34,579,671✔
297
      {
298
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
299
        seterror( true );
×
300
        return -1;
×
301
      }
302
      fparams[i] = ValueStack.back();
34,579,671✔
303
      ValueStack.pop_back();
34,579,671✔
304
    }
305
  }
306
  expandParams();
34,521,073✔
307
  return 0;
34,521,073✔
308
}
309

310
void Executor::expandParams()
34,521,073✔
311
{
312
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
69,101,971✔
313
  {
314
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
34,580,898✔
315
    {
316
      // defer destruction
317
      BObjectRef obj( spread );
628✔
318

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

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

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

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

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

341
void Executor::cleanParams()
34,518,743✔
342
{
343
  fparams.clear();
34,518,743✔
344
}
34,518,743✔
345

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

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

356
  return 0;
×
357
}
358

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

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

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

380
  return 0;
×
381
}
382

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

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

407
  return fparams[param].get();
34,437,994✔
408
}
409

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

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

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

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

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

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

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

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

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

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

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

476

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

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

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

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

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

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

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

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

519
  size_t i = 0;
×
NEW
520
  for ( auto& fparam : std::ranges::reverse_view( fparams ) )
×
521
  {
NEW
522
    auto* ptr = fparam.get()->impptr();
×
523
    INFO_PRINTLN( "fparam[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
524
    i++;
×
525
  }
526

527
  i = 0;
×
NEW
528
  for ( auto& riter : std::ranges::reverse_view( ValueStack ) )
×
529
  {
NEW
530
    auto* ptr = riter.get()->impptr();
×
531
    INFO_PRINTLN( "vstack[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
532
    i++;
×
533
  }
534

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

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

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

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

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

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

588
  return false;
51✔
589
}
590

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

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

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

613
  return nullptr;
×
614
}
615

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

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

630
  return nullptr;
×
631
}
632

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

798
  return false;
×
799
}
800

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

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

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

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

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

847

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

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

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

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

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

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

887

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

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

903
  ExecutorModule* em = execmodules[token.module];
34,473,690✔
904

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

929
  current_module_function = nullptr;
34,473,690✔
930
}
34,473,690✔
931

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

937
  Locals2->emplace_back( UninitObject::create() );
23,817✔
938

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

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

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

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

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

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

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

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

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

986

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

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

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

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

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

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

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

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

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

1023

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1371

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

1376
  if ( objref->impptr()->isTrue() )
3,117✔
1377
    PC = (unsigned)ins.token.lval;
2,977✔
1378

1379
  ValueStack.pop_back();
3,117✔
1380
}
3,117✔
1381

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

1386
  if ( !objref->impptr()->isTrue() )
37,571✔
1387
    PC = (unsigned)ins.token.lval;
27,916✔
1388

1389
  ValueStack.pop_back();
37,571✔
1390
}
37,571✔
1391

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

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

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

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

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

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

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

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

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

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

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

1465

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

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

1479
// case TOK_LONG:
1480
void Executor::ins_long( const Instruction& ins )
34,539,377✔
1481
{
1482
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
34,539,377✔
1483
}
34,539,377✔
1484

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

1491
// case TOK_CONSUMER:
1492
void Executor::ins_consume( const Instruction& /*ins*/ )
34,511,226✔
1493
{
1494
  ValueStack.pop_back();
34,511,226✔
1495
}
34,511,226✔
1496

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1761
  BObject& right = *rightref;
28,606✔
1762
  BObject& left = *leftref;
28,606✔
1763

1764
  BObjectImp& rightimpref = right.impref();
28,606✔
1765

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

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

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

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

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

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

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

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

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

1844
  BObject& right = *rightref;
22,484✔
1845
  BObject& left = *leftref;
22,484✔
1846

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2030
  BObject& right = *rightref;
5,955✔
2031
  BObject& left = *leftref;
5,955✔
2032

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

2047
  BObject& right = *rightref;
1,666✔
2048
  BObject& left = *leftref;
1,666✔
2049

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

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

2065
  BObject& right = *rightref;
27,287✔
2066
  BObject& left = *leftref;
27,287✔
2067

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

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

2083
  BObject& right = *rightref;
8,093✔
2084
  BObject& left = *leftref;
8,093✔
2085

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

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

2101
  BObject& right = *rightref;
4,210✔
2102
  BObject& left = *leftref;
4,210✔
2103

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

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

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

2135
  BObject& right = *rightref;
5,960✔
2136
  BObject& left = *leftref;
5,960✔
2137

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2292
    OrderedSet indexes;
39✔
2293

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2375
  BObject& right = *rightref;
388✔
2376

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

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

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

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

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

2399
  BObject& right = *rightref;
183✔
2400

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2485
  BObject& ob = *obref;
45✔
2486

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

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

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

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

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

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

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

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

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

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

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

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

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

2566
  BObject& right = *rightref;
777✔
2567
  BObject& left = *leftref;
777✔
2568

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

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

2578
  BObject& right = *rightref;
7,693✔
2579
  BObject& left = *leftref;
7,693✔
2580

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

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

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

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

2612
  BObject& right = *rightref;
821✔
2613
  BObject& left = *leftref;
821✔
2614

2615
  left.impref().operPlusEqual( left, right.impref() );
821✔
2616
}
821✔
2617

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

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

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

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

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

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

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

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

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

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

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

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

2686
// case RSV_GOTO:
2687
void Executor::ins_goto( const Instruction& ins )
34,437,566✔
2688
{
2689
  PC = (unsigned)ins.token.lval;
34,437,566✔
2690
}
34,437,566✔
2691

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

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

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

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

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

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

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

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

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

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

2759
        cleanParams();
790✔
2760

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

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

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

2773
        continue;
790✔
2774
      }
2775
    }
2776

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

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

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

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

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

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

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

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

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

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

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

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

2886

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

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

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

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

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

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

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

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

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

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

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

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

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

2970
    Globals2 = funcref->globals;
115✔
2971

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

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

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

2988
  ControlStack.push_back( rc );
44,270✔
2989

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

3008

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3456
  int capture_count = ep_funcref.capture_count;
722✔
3457

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

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

3468
  ValueStack.emplace_back( func );
722✔
3469

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3785
    ++ins.cycles;
138,775,818✔
3786
    ++prog_->instr_cycles;
138,775,818✔
3787
    ++escript_instr_cycles;
138,775,818✔
3788

3789
    ++PC;
138,775,818✔
3790

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

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

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

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

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

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

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

3845
  end = atPC + 5;
×
3846

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

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

3863
  end = atPC + 5;
24✔
3864

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4016

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

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

4053
  return true;
4✔
4054
}
4055

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

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

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

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

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

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

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

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

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

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

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

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

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

4232
  return continuation;
35,100✔
4233
}
4234

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

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

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

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

4296
EscriptProfiler::~EscriptProfiler()
4297
{
4298
  auto profile_end = timer_.ellapsed().count();
4299
  auto itr = escript_profile_map_.find( name_ );
4300
  if ( itr != escript_profile_map_.end() )
4301
  {
4302
    itr->second.count++;
4303
    itr->second.sum += profile_end;
4304
    if ( itr->second.max < profile_end )
4305
      itr->second.max = profile_end;
4306
    else if ( itr->second.min > profile_end )
4307
      itr->second.min = profile_end;
4308
  }
4309
  else
4310
  {
4311
    escript_profile_map_[name_] = {
4312
        .sum = profile_end, .max = profile_end, .min = profile_end, .count = 1 };
4313
  }
4314
}
4315
std::string EscriptProfiler::result()
4316
{
4317
  std::string buffer = "FuncName,Count,Min,Max,Sum,Avarage\n";
4318
  for ( const auto& [name, profile] : escript_profile_map_ )
4319
  {
4320
    fmt::format_to( std::back_inserter( buffer ), "{},{},{},{},{},{:.2f}\n", name, profile.count,
4321
                    profile.min, profile.max, profile.sum, profile.sum / ( 1.0 * profile.count ) );
4322
  }
4323
  return buffer;
4324
}
4325
#endif
4326
}  // namespace Pol::Bscript
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc