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

polserver / polserver / 26493844542

27 May 2026 06:02AM UTC coverage: 60.926% (-0.002%) from 60.928%
26493844542

push

github

web-flow
Fixed leak when a class/funcref is a global (#887)

* store globals as weakptr in class/funcs to prevent memleak when stored
as global WIP

* let it crash if globals are no longer valid?

* typo

* removed debug prints

* let only the current executor crash

* fixed logic

* added and corrected tests

* cleanup

* use emplace_back

* classes also need to switch to weakptr

* docs

42 of 58 new or added lines in 4 files covered. (72.41%)

1 existing line in 1 file now uncovered.

44642 of 73272 relevant lines covered (60.93%)

467929.9 hits per line

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

84.85
/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 "exectype.h"
19
#include "executor.inl.h"
20

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

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

52
#include <cstdlib>
53
#include <cstring>
54
#include <exception>
55
#include <numeric>
56
#include <ranges>
57

58

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

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

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

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

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

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

181
  return true;
296✔
182
}
183

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

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

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

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

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

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

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

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

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

290
int Executor::getParams( unsigned howMany )
33,160,679✔
291
{
292
  if ( howMany )
33,160,679✔
293
  {
294
    fparams.resize( howMany );
33,148,040✔
295
    for ( int i = howMany - 1; i >= 0; --i )
66,367,496✔
296
    {
297
      if ( ValueStack.empty() )
33,219,456✔
298
      {
299
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
300
        seterror( true );
×
301
        return -1;
×
302
      }
303
      fparams[i] = ValueStack.back();
33,219,456✔
304
      ValueStack.pop_back();
33,219,456✔
305
    }
306
  }
307
  expandParams();
33,160,679✔
308
  return 0;
33,160,679✔
309
}
310

311
void Executor::expandParams()
33,160,679✔
312
{
313
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
66,381,365✔
314
  {
315
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
33,220,686✔
316
    {
317
      // defer destruction
318
      BObjectRef obj( spread );
629✔
319

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

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

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

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

330
      int added = 0;
629✔
331
      while ( next != nullptr )
1,859✔
332
      {
333
        fparams.insert( fparams.begin() + i + added, BObjectRef( next ) );
1,230✔
334
        next = pIter->step();
1,230✔
335
        added++;
1,230✔
336
      }
337
      i += added;
629✔
338
    }
629✔
339
  }
340
}
33,160,679✔
341

342
void Executor::cleanParams()
33,158,334✔
343
{
344
  fparams.clear();
33,158,334✔
345
}
33,158,334✔
346

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

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

357
  return 0;
×
358
}
359

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

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

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

381
  return 0;
×
382
}
383

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

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

408
  return fparams[param].get();
33,077,119✔
409
}
410

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

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

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

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

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

437
  passert( imp != nullptr );
41,206✔
438

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

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

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

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

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

477

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

589
  return false;
51✔
590
}
591

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

599
BApplicObjBase* Executor::getApplicObjParam( unsigned param, const BApplicObjType* object_type )
206✔
600
{
601
  auto aob = static_cast<BApplicObjBase*>( getParamImp( param, BObjectImp::OTApplicObj ) );
206✔
602
  if ( aob == nullptr )
206✔
603
    return nullptr;
×
604

605
  if ( aob->object_type() == object_type )
206✔
606
    return aob;
206✔
607
  DEBUGLOGLN(
×
608
      "Script Error in '{}' PC={}: \n"
609
      "\tCall to function {}:\n"
610
      "\tParameter {}: Expected datatype, got datatype {}",
611
      scriptname(), PC, current_module_function->name.get(), param, aob->getStringRep() );
×
612

613
  return nullptr;
×
614
}
615

616
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short maxval )
×
617
{
618
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
619
  if ( !imp )
×
620
    return false;
×
621
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
622

623
  int longvalue = plong->value();
×
624
  if ( longvalue >= 0 && longvalue <= maxval )
×
625
  {
626
    value = static_cast<unsigned short>( longvalue );
×
627
    return true;
×
628
  }
629
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
630
                                          param, longvalue, maxval ) );
×
631
  return false;
×
632
}
633

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

642
  int longvalue = plong->value();
6,141✔
643
  if ( longvalue >= minval && longvalue <= maxval )
6,141✔
644
  {
645
    value = static_cast<unsigned short>( longvalue );
6,141✔
646
    return true;
6,141✔
647
  }
648
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
649
                                          param, longvalue, minval, maxval ) );
×
650
  return false;
×
651
}
652
bool Executor::getParam( unsigned param, unsigned short& value )
13,068✔
653
{
654
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
13,068✔
655
  if ( !imp )
13,068✔
656
    return false;
×
657
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
13,068✔
658

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

676
  int longvalue = plong->value();
20✔
677
  if ( longvalue >= 0 )  // && longvalue <= (int)INT_MAX )
20✔
678
  {
679
    value = static_cast<unsigned>( longvalue );
20✔
680
    return true;
20✔
681
  }
682
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
683
                                          param, longvalue, INT_MAX ) );
×
684
  return false;
×
685
}
686

687
bool Executor::getParam( unsigned param, short& value )
295✔
688
{
689
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
295✔
690
  if ( !imp )
295✔
691
    return false;
×
692
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
295✔
693

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

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

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

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

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

741
bool Executor::getParam( unsigned param, signed char& value )
6,393✔
742
{
743
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,393✔
744
  if ( !imp )
6,393✔
745
    return false;
×
746

747
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
6,393✔
748

749
  int longvalue = plong->value();
6,393✔
750
  if ( longvalue >= std::numeric_limits<s8>::min() && longvalue <= std::numeric_limits<s8>::max() )
6,393✔
751
  {
752
    value = static_cast<signed char>( longvalue );
6,393✔
753
    return true;
6,393✔
754
  }
755
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
756
                                          param, longvalue, std::numeric_limits<s8>::min(),
×
757
                                          std::numeric_limits<s8>::max() ) );
×
758
  return false;
×
759
}
760

761
bool Executor::getParam( unsigned param, bool& value )
15✔
762
{
763
  BObjectImp* imp = getParamImp( param );
15✔
764
  if ( auto* b = impptrIf<BBoolean>( imp ) )
15✔
765
  {
766
    value = b->value();
1✔
767
    return true;
1✔
768
  }
769
  if ( auto* l = impptrIf<BLong>( imp ) )
14✔
770
  {
771
    value = l->isTrue();
14✔
772
    return true;
14✔
773
  }
774
  DEBUGLOGLN(
×
775
      "Script Error in '{}' PC={}: \n"
776
      "\tCall to function {}:\n"
777
      "\tParameter {}: Expected Boolean or Integer, got datatype {}",
778
      scriptname(), PC, current_module_function->name.get(), param,
×
779
      BObjectImp::typestr( imp->type() ) );
×
780

781
  return false;
×
782
}
783

784
bool Executor::getUnicodeStringParam( unsigned param, const String*& pstr )
2✔
785
{
786
  BObject* obj = getParam( param );
2✔
787
  if ( !obj )
2✔
788
    return false;
×
789
  if ( auto* s = obj->impptr_if<String>() )
2✔
790
  {
791
    pstr = s;
2✔
792
    return true;
2✔
793
  }
794
  if ( auto* a = obj->impptr_if<ObjArray>() )
×
795
  {
796
    String* str = String::fromUCArray( a );
×
797
    fparams[param].set( str );  // store raw pointer
×
798
    pstr = str;
×
799
    return true;
×
800
  }
801
  func_result_ = new BError( fmt::format(
×
802
      "Invalid parameter type.  Expected param {} as {} or {}, got {}", param,
803
      BObjectImp::typestr( BObjectImp::OTString ), BObjectImp::typestr( BObjectImp::OTArray ),
×
804
      BObjectImp::typestr( obj->impptr()->type() ) ) );
×
805
  return false;
×
806
}
807

808
BObjectRef& Executor::LocalVar( unsigned int varnum )
×
809
{
810
  passert( Locals2 );
×
811
  passert( varnum < Locals2->size() );
×
812

813
  return ( *Locals2 )[varnum];
×
814
}
815

816
BObjectRef& Executor::GlobalVar( unsigned int varnum )
×
817
{
NEW
818
  if ( varnum >= Globals2->size() )
×
819
  {
NEW
820
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
×
NEW
821
    seterror( true );
×
NEW
822
    UninitObject::SharedInstanceRef.set( UninitObject::SharedInstance );
×
NEW
823
    return UninitObject::SharedInstanceRef;
×
824
  }
UNCOV
825
  return ( *Globals2 )[varnum];
×
826
}
827

828
int Executor::getToken( Token& token, unsigned position )
×
829
{
830
  if ( position >= nLines )
×
831
    return -1;
×
832
  token = prog_->instr[position].token;
×
833
  return 0;
×
834
}
835

836

837
bool Executor::setProgram( EScriptProgram* i_prog )
2,276✔
838
{
839
  prog_.set( i_prog );
2,276✔
840
  prog_ok_ = false;
2,276✔
841
  seterror( true );
2,276✔
842
  if ( !viewmode_ )
2,276✔
843
  {
844
    if ( !AttachFunctionalityModules() )
2,276✔
845
      return false;
×
846
  }
847

848
  nLines = static_cast<unsigned int>( prog_->instr.size() );
2,276✔
849

850
  Globals2->clear();
2,276✔
851
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
4,564✔
852
  {
853
    Globals2->emplace_back( UninitObject::create() );
2,288✔
854
  }
855

856
  prog_ok_ = true;
2,276✔
857
  seterror( false );
2,276✔
858
  ++prog_->invocations;
2,276✔
859
  return true;
2,276✔
860
}
861

862
BObjectRef Executor::getObjRef()
62,546✔
863
{
864
  if ( ValueStack.empty() )
62,546✔
865
  {
866
    POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
867
    seterror( true );
×
868
    return BObjectRef( UninitObject::create() );
×
869
  }
870

871
  BObjectRef ref = ValueStack.back();
62,546✔
872
  ValueStack.pop_back();
62,546✔
873
  return ref;
62,546✔
874
}
62,546✔
875

876

877
void Executor::execFunc( const Token& token )
33,113,026✔
878
{
879
  FunctionalityModule* fm = prog_->modules[token.module];
33,113,026✔
880
  ModuleFunction* modfunc = fm->functions[token.lval];
33,113,026✔
881
  current_module_function = modfunc;
33,113,026✔
882
  if ( modfunc->funcidx == -1 )
33,113,026✔
883
  {
884
    DEBUGLOGLN(
×
885
        "Error in script '{}':\n"
886
        "\tModule Function {} was not found.",
887
        prog_->name.get(), modfunc->name.get() );
×
888

889
    throw std::runtime_error( "No implementation for function found." );
×
890
  }
891

892
  ExecutorModule* em = execmodules[token.module];
33,113,026✔
893

894
  func_result_ = nullptr;
33,113,026✔
895
  BObjectImp* resimp;
896
  {
897
    ESCRIPT_PROFILER( em, modfunc, fparams );
898
    resimp = em->execFunc( modfunc->funcidx );
33,113,026✔
899
  }
900
  if ( func_result_ )
33,113,026✔
901
  {
902
    if ( resimp )
8✔
903
    {
904
      BObject obj( resimp );
8✔
905
    }
8✔
906
    ValueStack.emplace_back( func_result_ );
8✔
907
    func_result_ = nullptr;
8✔
908
  }
909
  else if ( resimp )
33,113,018✔
910
  {
911
    ValueStack.emplace_back( resimp );
33,113,017✔
912
  }
913
  else
914
  {
915
    ValueStack.emplace_back( UninitObject::create() );
1✔
916
  }
917

918
  current_module_function = nullptr;
33,113,026✔
919
}
33,113,026✔
920

921
// RSV_LOCAL
922
void Executor::ins_makeLocal( const Instruction& /*ins*/ )
24,110✔
923
{
924
  passert( Locals2 != nullptr );
24,110✔
925

926
  Locals2->emplace_back( UninitObject::create() );
24,110✔
927

928
  ValueStack.emplace_back( Locals2->back().get() );
24,110✔
929
}
24,110✔
930

931
// RSV_DECLARE_ARRAY
932
void Executor::ins_declareArray( const Instruction& /*ins*/ )
69✔
933
{
934
  BObjectRef objref = getObjRef();
69✔
935

936
  if ( !objref->isa( BObjectImp::OTUninit ) )
69✔
937
  {
938
    // FIXME: weak error message
939
    ERROR_PRINTLN( "variable is already initialized.." );
×
940
    seterror( true );
×
941
    return;
×
942
  }
943
  objref->setimp( new ObjArray );
69✔
944

945
  ValueStack.emplace_back( objref );
69✔
946
}
69✔
947

948
void Executor::popParam( const Token& /*token*/ )
48,959✔
949
{
950
  BObjectRef objref = getObjRef();
48,959✔
951

952
  Locals2->emplace_back( objref->impptr()->copy() );
48,959✔
953
}
48,959✔
954

955
void Executor::popParamByRef( const Token& /*token*/ )
7,995✔
956
{
957
  BObjectRef objref = getObjRef();
7,995✔
958

959
  Locals2->emplace_back( objref );
7,995✔
960
}
7,995✔
961

962
void Executor::getArg( const Token& /*token*/ )
449✔
963
{
964
  if ( ValueStack.empty() )
449✔
965
  {
966
    Locals2->emplace_back( UninitObject::create() );
47✔
967
  }
968
  else
969
  {
970
    BObjectRef objref = getObjRef();
402✔
971
    Locals2->emplace_back( objref->impptr()->copy() );
402✔
972
  }
402✔
973
}
449✔
974

975

976
BObjectRef Executor::addmember( BObject& left, const BObject& right )
168✔
977
{
978
  if ( !right.isa( BObjectImp::OTString ) )
168✔
979
  {
980
    return BObjectRef( left.clone() );
×
981
  }
982

983
  const String& varname = right.impref<const String>();
168✔
984

985
  return left.impref().operDotPlus( varname.data() );
168✔
986
}
987

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

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

997
  return left.impref().operDotMinus( varname.data() );
79✔
998
}
999

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

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

1009
  return left.impref().operDotQMark( varname.data() );
1,639✔
1010
}
1011

1012

1013
ContIterator::ContIterator() : BObjectImp( BObjectImp::OTUnknown ) {}
2,959✔
1014
BObject* ContIterator::step()
18✔
1015
{
1016
  return nullptr;
18✔
1017
}
1018
BObjectImp* ContIterator::copy() const
×
1019
{
1020
  return nullptr;
×
1021
}
1022
size_t ContIterator::sizeEstimate() const
12✔
1023
{
1024
  return sizeof( ContIterator );
12✔
1025
}
1026
std::string ContIterator::getStringRep() const
×
1027
{
1028
  return "<iterator>";
×
1029
}
1030

1031
class ArrayIterator final : public ContIterator
1032
{
1033
public:
1034
  ArrayIterator( ObjArray* pArr, BObject* pIterVal );
1035
  BObject* step() override;
1036

1037
private:
1038
  size_t m_Index;
1039
  BObject m_Array;
1040
  ObjArray* m_pArray;
1041
  BObjectRef m_IterVal;
1042
  BLong* m_pIterVal;
1043
};
1044
ArrayIterator::ArrayIterator( ObjArray* pArr, BObject* pIterVal )
2,742✔
1045
    : ContIterator(),
1046
      m_Index( 0 ),
2,742✔
1047
      m_Array( pArr ),
2,742✔
1048
      m_pArray( pArr ),
2,742✔
1049
      m_IterVal( pIterVal ),
2,742✔
1050
      m_pIterVal( new BLong( 0 ) )
5,484✔
1051
{
1052
  m_IterVal.get()->setimp( m_pIterVal );
2,742✔
1053
}
2,742✔
1054
BObject* ArrayIterator::step()
24,574✔
1055
{
1056
  m_pIterVal->increment();
24,574✔
1057
  if ( ++m_Index > m_pArray->ref_arr.size() )
24,574✔
1058
    return nullptr;
2,637✔
1059

1060
  BObjectRef& objref = m_pArray->ref_arr[m_Index - 1];
21,937✔
1061
  BObject* elem = objref.get();
21,937✔
1062
  if ( elem == nullptr )
21,937✔
1063
  {
1064
    elem = new BObject( UninitObject::create() );
18✔
1065
    objref.set( elem );
18✔
1066
  }
1067
  return elem;
21,937✔
1068
}
1069

1070
ContIterator* BObjectImp::createIterator( BObject* /*pIterVal*/ )
24✔
1071
{
1072
  return new ContIterator();
24✔
1073
}
1074
ContIterator* ObjArray::createIterator( BObject* pIterVal )
2,742✔
1075
{
1076
  auto pItr = new ArrayIterator( this, pIterVal );
2,742✔
1077
  return pItr;
2,742✔
1078
}
1079

1080
/* Coming into initforeach, the expr to be iterated through is on the value stack.
1081
   Initforeach must create three local variables:
1082
   0. the iterator
1083
   1. the expression
1084
   2. the counter
1085
   and remove the expression from the value stack.
1086
   It then jumps to the STEPFOREACH instruction.
1087
   */
1088
void Executor::ins_initforeach( const Instruction& ins )
690✔
1089
{
1090
  Locals2->emplace_back( UninitObject::create() );  // the iterator
690✔
1091

1092
  auto pIterVal = new BObject( UninitObject::create() );
690✔
1093

1094
  // this is almost like popParam, only we don't want a copy.
1095
  BObjectRef objref = getObjRef();
690✔
1096
  ContIterator* pIter = objref->impptr()->createIterator( pIterVal );
690✔
1097
  Locals2->emplace_back( pIter );
690✔
1098

1099
  Locals2->emplace_back( pIterVal );
690✔
1100

1101
  // Jump to to the corresponding `stepforeach` instruction, advancing the iterator.
1102
  PC = ins.token.lval;
690✔
1103
}
690✔
1104

1105
void Executor::ins_stepforeach( const Instruction& ins )
18,491✔
1106
{
1107
  size_t locsize = Locals2->size();
18,491✔
1108
  ContIterator* pIter = ( *Locals2 )[locsize - 2]->impptr<ContIterator>();
18,491✔
1109

1110
  BObject* next = pIter->step();
18,491✔
1111
  // If iterator has a value, set it on the locals stack and jump to the
1112
  // corresponding instruction after `initforeach`.
1113
  if ( next != nullptr )
18,491✔
1114
  {
1115
    ( *Locals2 )[locsize - 3].set( next );
17,879✔
1116
    PC = ins.token.lval;
17,879✔
1117
  }
1118
}
18,491✔
1119

1120
/*
1121
    Coming into the INITFOR, there will be two values on the value stack:
1122
    START VALUE
1123
    END VALUE
1124

1125
    If START VALUE > END VALUE, we skip the whole for loop.
1126
    (the INITFOR's lval has the instr to jump to)
1127
    */
1128
void Executor::ins_initfor( const Instruction& ins )
68✔
1129
{
1130
  BObjectRef endref = getObjRef();
68✔
1131
  BObjectRef startref = getObjRef();
68✔
1132
  if ( *startref.get() > *endref.get() )
68✔
1133
  {
1134
    PC = ins.token.lval;
×
1135
    return;
×
1136
  }
1137

1138
  Locals2->emplace_back( startref->clone() );  // the iterator
68✔
1139
  Locals2->emplace_back( endref->clone() );
68✔
1140
}
68✔
1141

1142
void Executor::ins_nextfor( const Instruction& ins )
6,248✔
1143
{
1144
  size_t locsize = Locals2->size();
6,248✔
1145
  BObjectImp* itr = ( *Locals2 )[locsize - 2]->impptr();
6,248✔
1146
  BObjectImp* end = ( *Locals2 )[locsize - 1]->impptr();
6,248✔
1147

1148
  if ( auto* l = impptrIf<BLong>( itr ) )
6,248✔
1149
    l->increment();
6,248✔
1150
  else if ( auto* d = impptrIf<Double>( itr ) )
×
1151
    d->increment();
×
1152

1153
  if ( *end >= *itr )
6,248✔
1154
  {
1155
    PC = ins.token.lval;
6,191✔
1156
  }
1157
}
6,248✔
1158

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

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

1235
int Executor::ins_casejmp_finduninit( const Token& token )
6✔
1236
{
1237
  const unsigned char* dataptr = token.dataptr;
6✔
1238
  for ( ;; )
1239
  {
1240
    unsigned short offset;
1241
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
30✔
1242
    dataptr += 2;
30✔
1243
    unsigned char type = *dataptr;
30✔
1244
    dataptr += 1;
30✔
1245
    if ( type == CASE_TYPE_LONG )
30✔
1246
    {
1247
      dataptr += 4;
12✔
1248
    }
1249
    else if ( type == CASE_TYPE_DEFAULT )
18✔
1250
    {
1251
      return offset;
6✔
1252
    }
1253
    else if ( type == CASE_TYPE_UNINIT )
18✔
1254
    {
1255
      return offset;
6✔
1256
    }
1257
    else if ( type == CASE_TYPE_BOOL )
12✔
1258
    {
1259
      dataptr += 1;
12✔
1260
    }
1261
    else if ( type == CASE_TYPE_STRING )
×
1262
    {
1263
      unsigned char len = *dataptr;
×
1264
      dataptr += 1 + len;
×
1265
    }
1266
  }
24✔
1267
}
1268

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

1309
int Executor::ins_casejmp_finddefault( const Token& token )
11✔
1310
{
1311
  const unsigned char* dataptr = token.dataptr;
11✔
1312
  for ( ;; )
1313
  {
1314
    unsigned short offset;
1315
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
52✔
1316
    dataptr += 2;
52✔
1317
    unsigned char type = *dataptr;
52✔
1318
    dataptr += 1;
52✔
1319
    if ( type == CASE_TYPE_LONG )
52✔
1320
    {
1321
      dataptr += 4;
17✔
1322
    }
1323
    else if ( type == CASE_TYPE_DEFAULT )
35✔
1324
    {
1325
      return offset;
11✔
1326
    }
1327
    else if ( type == CASE_TYPE_UNINIT )
24✔
1328
    {
1329
      /* nothing */
1330
    }
1331
    else if ( type == CASE_TYPE_BOOL )
18✔
1332
    {
1333
      dataptr += 1;
12✔
1334
    }
1335
    else if ( type == CASE_TYPE_STRING )
6✔
1336
    {
1337
      unsigned char len = *dataptr;
6✔
1338
      dataptr += 1 + len;
6✔
1339
    }
1340
  }
41✔
1341
}
1342

1343
void Executor::ins_casejmp( const Instruction& ins )
321✔
1344
{
1345
  BObjectRef& objref = ValueStack.back();
321✔
1346
  BObjectImp* objimp = objref->impptr();
321✔
1347
  if ( auto* l = impptrIf<BLong>( objimp ) )
321✔
1348
    PC = ins_casejmp_findlong( ins.token, l );
148✔
1349
  else if ( auto* s = impptrIf<String>( objimp ) )
173✔
1350
    PC = ins_casejmp_findstring( ins.token, s );
141✔
1351
  else if ( auto* b = impptrIf<BBoolean>( objimp ) )
32✔
1352
    PC = ins_casejmp_findbool( ins.token, b );
15✔
1353
  else if ( impptrIf<UninitObject>( objimp ) )
17✔
1354
    PC = ins_casejmp_finduninit( ins.token );
6✔
1355
  else
1356
    PC = ins_casejmp_finddefault( ins.token );
11✔
1357
  ValueStack.pop_back();
321✔
1358
}
321✔
1359

1360

1361
void Executor::ins_jmpiftrue( const Instruction& ins )
3,240✔
1362
{
1363
  BObjectRef& objref = ValueStack.back();
3,240✔
1364

1365
  if ( objref->impptr()->isTrue() )
3,240✔
1366
    PC = (unsigned)ins.token.lval;
3,097✔
1367

1368
  ValueStack.pop_back();
3,240✔
1369
}
3,240✔
1370

1371
void Executor::ins_jmpiffalse( const Instruction& ins )
37,785✔
1372
{
1373
  BObjectRef& objref = ValueStack.back();
37,785✔
1374

1375
  if ( !objref->impptr()->isTrue() )
37,785✔
1376
    PC = (unsigned)ins.token.lval;
28,093✔
1377

1378
  ValueStack.pop_back();
37,785✔
1379
}
37,785✔
1380

1381
void Executor::ins_interpolate_string( const Instruction& ins )
10,450✔
1382
{
1383
  auto count = ins.token.lval;
10,450✔
1384
  if ( count == 0 )
10,450✔
1385
  {
1386
    ValueStack.emplace_back( new String( "" ) );
3✔
1387
  }
1388
  else
1389
  {
1390
    size_t length = 0;
10,447✔
1391

1392
    std::vector<std::string> contents;
10,447✔
1393
    contents.reserve( count );
10,447✔
1394

1395
    while ( count-- )
37,817✔
1396
    {
1397
      BObjectRef rightref = ValueStack.back();
27,370✔
1398
      ValueStack.pop_back();
27,370✔
1399
      auto str = rightref->impptr()->getStringRep();
27,370✔
1400
      length += str.length();
27,370✔
1401
      contents.push_back( std::move( str ) );
27,370✔
1402
    }
27,370✔
1403

1404
    std::string joined;
10,447✔
1405
    joined.reserve( length );
10,447✔
1406

1407
    while ( !contents.empty() )
37,817✔
1408
    {
1409
      joined += contents.back();
27,370✔
1410
      contents.pop_back();
27,370✔
1411
    }
1412

1413
    ValueStack.emplace_back( new String( joined ) );
10,447✔
1414
  }
10,447✔
1415
}
10,450✔
1416

1417
void Executor::ins_format_expression( const Instruction& )
222✔
1418
{
1419
  BObjectRef formatref = ValueStack.back();
222✔
1420
  ValueStack.pop_back();
222✔
1421
  BObjectRef& exprref = ValueStack.back();
222✔
1422
  BObject& expr = *exprref;
222✔
1423

1424
  auto format = formatref->impptr()->getFormattedStringRep();
222✔
1425
  auto formatted = Bscript::get_formatted( expr.impptr(), format );
222✔
1426

1427
  exprref.set( new String( formatted ) );
222✔
1428
}
222✔
1429

1430
void Executor::ins_skipiftrue_else_consume( const Instruction& ins )
1,862✔
1431
{
1432
  // This is for short-circuit evaluation of the elvis operator [expr_a] ?: [expr_b]
1433
  //
1434
  // Program instructions look like this:
1435
  //   [expr_a instructions] INS_SKIPIFTRUE_ELSE_CONSUME [expr_b instructions]
1436
  //
1437
  // The result value of expr_a is on the top of the value stack when this instruction executes.
1438
  //
1439
  // If [expr_a] evaluated to true, leave its result and skip over the expr_b instructions
1440
  // otherwise, consume the false value and continue so that expr_b can replace it.
1441
  //
1442
  BObjectRef& objref = ValueStack.back();
1,862✔
1443

1444
  if ( objref->impptr()->isTrue() )
1,862✔
1445
  {
1446
    PC = PC + (unsigned)( ins.token.lval );
850✔
1447
  }
1448
  else
1449
  {
1450
    ValueStack.pop_back();
1,012✔
1451
  }
1452
}
1,862✔
1453

1454

1455
// case TOK_LOCALVAR:
1456
void Executor::ins_localvar( const Instruction& ins )
186,320✔
1457
{
1458
  ValueStack.push_back( ( *Locals2 )[ins.token.lval] );
186,320✔
1459
}
186,320✔
1460

1461
// case RSV_GLOBAL:
1462
// case TOK_GLOBALVAR:
1463
void Executor::ins_globalvar( const Instruction& ins )
35,945✔
1464
{
1465
  if ( (unsigned)ins.token.lval >= Globals2->size() )
35,945✔
1466
  {
1467
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
1✔
1468
    seterror( true );
1✔
1469
    ValueStack.emplace_back( UninitObject::create() );
1✔
1470
    return;
1✔
1471
  }
1472
  ValueStack.push_back( ( *Globals2 )[ins.token.lval] );
35,944✔
1473
}
1474

1475
// case TOK_LONG:
1476
void Executor::ins_long( const Instruction& ins )
33,179,054✔
1477
{
1478
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
33,179,054✔
1479
}
33,179,054✔
1480

1481
// case TOK_BOOL:
1482
void Executor::ins_bool( const Instruction& ins )
368✔
1483
{
1484
  ValueStack.emplace_back( new BBoolean( ins.token.lval ) );
368✔
1485
}
368✔
1486

1487
// case TOK_CONSUMER:
1488
void Executor::ins_consume( const Instruction& /*ins*/ )
33,150,828✔
1489
{
1490
  ValueStack.pop_back();
33,150,828✔
1491
}
33,150,828✔
1492

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

1499
  BObject& right = *rightref;
3✔
1500
  BObject& left = *leftref;
3✔
1501

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

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

1513
  BObject& right = *rightref;
3✔
1514
  BObject& left = *leftref;
3✔
1515

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

1521
void Executor::ins_set_member_consume( const Instruction& ins )
464✔
1522
{
1523
  BObjectRef rightref = ValueStack.back();
464✔
1524
  ValueStack.pop_back();
464✔
1525
  BObjectRef& leftref = ValueStack.back();
464✔
1526

1527
  BObject& right = *rightref;
464✔
1528
  BObject& left = *leftref;
464✔
1529

1530
  BObjectImp& rightimpref = right.impref();
464✔
1531
  left.impref().set_member( ins.token.tokval(), &rightimpref,
928✔
1532
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
464✔
1533
  ValueStack.pop_back();
464✔
1534
}
464✔
1535

1536
void Executor::ins_set_member_id_consume( const Instruction& ins )
226✔
1537
{
1538
  BObjectRef rightref = ValueStack.back();
226✔
1539
  ValueStack.pop_back();
226✔
1540
  BObjectRef& leftref = ValueStack.back();
226✔
1541

1542
  BObject& right = *rightref;
226✔
1543
  BObject& left = *leftref;
226✔
1544

1545
  BObjectImp& rightimpref = right.impref();
226✔
1546

1547
  left.impref().set_member_id( ins.token.lval, &rightimpref,
452✔
1548
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
226✔
1549
  ValueStack.pop_back();
226✔
1550
}
226✔
1551

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

1558
  BObject& right = *rightref;
15✔
1559
  BObject& left = *leftref;
15✔
1560

1561
  BObjectImp& leftimpref = left.impref();
15✔
1562

1563
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15✔
1564

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

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

1580
  BObject& right = *rightref;
6✔
1581
  BObject& left = *leftref;
6✔
1582

1583
  BObjectImp& leftimpref = left.impref();
6✔
1584

1585
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1586

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

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

1602
  BObject& right = *rightref;
9✔
1603
  BObject& left = *leftref;
9✔
1604

1605
  BObjectImp& leftimpref = left.impref();
9✔
1606

1607
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
9✔
1608

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

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

1624
  BObject& right = *rightref;
6✔
1625
  BObject& left = *leftref;
6✔
1626

1627
  BObjectImp& leftimpref = left.impref();
6✔
1628

1629
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1630

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

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

1646
  BObject& right = *rightref;
6✔
1647
  BObject& left = *leftref;
6✔
1648

1649
  BObjectImp& leftimpref = left.impref();
6✔
1650

1651
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1652

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

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

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

1682
void Executor::ins_assign_localvar( const Instruction& ins )
6,290✔
1683
{
1684
  BObjectRef& lvar = ( *Locals2 )[ins.token.lval];
6,290✔
1685

1686
  BObjectRef& rightref = ValueStack.back();
6,290✔
1687

1688
  BObject& right = *rightref;
6,290✔
1689

1690
  BObjectImp& rightimpref = right.impref();
6,290✔
1691

1692
  if ( right.count() == 1 && rightimpref.count() == 1 )
6,290✔
1693
  {
1694
    lvar->setimp( &rightimpref );
5,538✔
1695
  }
1696
  else
1697
  {
1698
    lvar->setimp( rightimpref.copy() );
752✔
1699
  }
1700
  ValueStack.pop_back();
6,290✔
1701
}
6,290✔
1702
void Executor::ins_assign_globalvar( const Instruction& ins )
1,102✔
1703
{
1704
  if ( (unsigned)ins.token.lval >= Globals2->size() )
1,102✔
1705
  {
NEW
1706
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
×
NEW
1707
    seterror( true );
×
NEW
1708
    ValueStack.pop_back();
×
NEW
1709
    return;
×
1710
  }
1711
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
1,102✔
1712

1713
  BObjectRef& rightref = ValueStack.back();
1,102✔
1714

1715
  BObject& right = *rightref;
1,102✔
1716

1717
  BObjectImp& rightimpref = right.impref();
1,102✔
1718

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

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

1737
  BObject& right = *rightref;
66✔
1738
  BObject& left = *leftref;
66✔
1739

1740
  BObjectImp& rightimpref = right.impref();
66✔
1741

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

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

1764
  BObject& right = *rightref;
28,899✔
1765
  BObject& left = *leftref;
28,899✔
1766

1767
  BObjectImp& rightimpref = right.impref();
28,899✔
1768

1769
  if ( right.count() == 1 && rightimpref.count() == 1 )
28,899✔
1770
  {
1771
    left.setimp( &rightimpref );
27,278✔
1772
  }
1773
  else
1774
  {
1775
    left.setimp( rightimpref.copy() );
1,621✔
1776
  }
1777
}
28,899✔
1778

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

1797
  BObject& y = *y_ref;
9✔
1798
  BObject& i = *i_ref;
9✔
1799
  BObject& x = *x_ref;
9✔
1800

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

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

1824
  BObject& y = *y_ref;
669✔
1825
  BObject& i = *i_ref;
669✔
1826
  BObject& x = *x_ref;
669✔
1827

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

1831
  BObject obj( result );
669✔
1832
  ValueStack.pop_back();
669✔
1833
}
669✔
1834

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

1847
  BObject& right = *rightref;
22,505✔
1848
  BObject& left = *leftref;
22,505✔
1849

1850
  leftref.set( right.impref().selfPlusObjImp( left.impref() ) );
22,505✔
1851
}
22,505✔
1852

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

1865
  BObject& right = *rightref;
3,495✔
1866
  BObject& left = *leftref;
3,495✔
1867

1868
  leftref.set( right.impref().selfMinusObjImp( left.impref() ) );
3,495✔
1869
}
3,495✔
1870

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

1883
  BObject& right = *rightref;
1,535✔
1884
  BObject& left = *leftref;
1,535✔
1885

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

1900
  BObject& right = *rightref;
332✔
1901
  BObject& left = *leftref;
332✔
1902

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

1917
  BObject& right = *rightref;
495✔
1918
  BObject& left = *leftref;
495✔
1919

1920
  leftref.set( right.impref().selfModulusObjImp( left.impref() ) );
495✔
1921
}
495✔
1922

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

1930
  BObject& right = *rightref;
100✔
1931
  BObject& left = *leftref;
100✔
1932

1933
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100✔
1934
}
100✔
1935

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

1948
  BObject& right = *rightref;
297✔
1949
  BObject& left = *leftref;
297✔
1950

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

1965
  BObject& right = *rightref;
297✔
1966
  BObject& left = *leftref;
297✔
1967

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

1982
  BObject& right = *rightref;
300✔
1983
  BObject& left = *leftref;
300✔
1984

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

1999
  BObject& right = *rightref;
297✔
2000
  BObject& left = *leftref;
297✔
2001

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

2016
  BObject& right = *rightref;
297✔
2017
  BObject& left = *leftref;
297✔
2018

2019
  leftref.set( right.impref().selfBitOrObjImp( left.impref() ) );
297✔
2020
}
297✔
2021

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

2033
  BObject& right = *rightref;
5,966✔
2034
  BObject& left = *leftref;
5,966✔
2035

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

2050
  BObject& right = *rightref;
1,682✔
2051
  BObject& left = *leftref;
1,682✔
2052

2053
  int _true = ( left.isTrue() || right.isTrue() );
1,682✔
2054
  leftref.set( new BLong( _true ) );
1,682✔
2055
}
1,682✔
2056

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

2068
  BObject& right = *rightref;
27,375✔
2069
  BObject& left = *leftref;
27,375✔
2070

2071
  int _true = ( left != right );
27,375✔
2072
  leftref.set( new BLong( _true ) );
27,375✔
2073
}
27,375✔
2074

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

2086
  BObject& right = *rightref;
8,132✔
2087
  BObject& left = *leftref;
8,132✔
2088

2089
  int _true = ( left == right );
8,132✔
2090
  leftref.set( new BLong( _true ) );
8,132✔
2091
}
8,132✔
2092

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

2104
  BObject& right = *rightref;
4,214✔
2105
  BObject& left = *leftref;
4,214✔
2106

2107
  int _true = ( left < right );
4,214✔
2108
  leftref.set( new BLong( _true ) );
4,214✔
2109
}
4,214✔
2110

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

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

2138
  BObject& right = *rightref;
5,963✔
2139
  BObject& left = *leftref;
5,963✔
2140

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

2155
  BObject& right = *rightref;
421✔
2156
  BObject& left = *leftref;
421✔
2157

2158
  int _true = ( left >= right );
421✔
2159
  leftref.set( new BLong( _true ) );
421✔
2160
}
421✔
2161

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

2174
  leftref = ( *leftref )->OperSubscript( *rightref );
25,850✔
2175
}
25,850✔
2176

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

2187
  BObjectRef& leftref = ValueStack.back();
7,235✔
2188
  leftref = ( *leftref )->OperMultiSubscript( indices );
7,235✔
2189
}
7,235✔
2190

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

2196
  BObjectRef refIter( new BObject( UninitObject::create() ) );
104✔
2197

2198
  BObjectRef rightref = ValueStack.back();
104✔
2199
  ValueStack.pop_back();
104✔
2200

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

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

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

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

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

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

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

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

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

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

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

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

2295
    OrderedSet indexes;
39✔
2296

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

2303
    BObjectRef rightref = ValueStack.back();
39✔
2304
    ValueStack.pop_back();
39✔
2305

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2378
  BObject& right = *rightref;
388✔
2379

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

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

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

2397
  if ( (unsigned)ins.token.lval >= Globals2->size() )
183✔
2398
  {
NEW
2399
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
×
NEW
2400
    seterror( true );
×
NEW
2401
    ValueStack.pop_back();
×
NEW
2402
    return;
×
2403
  }
2404
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
183✔
2405

2406
  BObjectRef& rightref = ValueStack.back();
183✔
2407

2408
  BObject& right = *rightref;
183✔
2409

2410
  BObjectImp& rightimpref = right.impref();
183✔
2411

2412
  if ( right.count() == 1 && rightimpref.count() == 1 )
183✔
2413
  {
2414
    gvar->setimp( &rightimpref );
150✔
2415
  }
2416
  else
2417
  {
2418
    gvar->setimp( rightimpref.copy() );
33✔
2419
  }
2420
  ValueStack.pop_back();
183✔
2421
}
2422

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

2435
  BObjectRef& leftref = ValueStack.back();
6✔
2436
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
6✔
2437
}
6✔
2438

2439
void Executor::ins_addmember( const Instruction& /*ins*/ )
168✔
2440
{
2441
  /*
2442
      These each take two operands, and replace them with one.
2443
      We'll leave the second one on the value stack, and
2444
      just replace its object with the result
2445
      */
2446
  BObjectRef rightref = ValueStack.back();
168✔
2447
  ValueStack.pop_back();
168✔
2448
  BObjectRef& leftref = ValueStack.back();
168✔
2449

2450
  BObject& right = *rightref;
168✔
2451
  BObject& left = *leftref;
168✔
2452

2453
  leftref = addmember( left, right );
168✔
2454
}
168✔
2455

2456
void Executor::ins_removemember( const Instruction& /*ins*/ )
79✔
2457
{
2458
  /*
2459
      These each take two operands, and replace them with one.
2460
      We'll leave the second one on the value stack, and
2461
      just replace its object with the result
2462
      */
2463
  BObjectRef rightref = ValueStack.back();
79✔
2464
  ValueStack.pop_back();
79✔
2465
  BObjectRef& leftref = ValueStack.back();
79✔
2466

2467
  BObject& right = *rightref;
79✔
2468
  BObject& left = *leftref;
79✔
2469

2470
  leftref = removemember( left, right );
79✔
2471
}
79✔
2472

2473
void Executor::ins_checkmember( const Instruction& /*ins*/ )
1,639✔
2474
{
2475
  /*
2476
      These each take two operands, and replace them with one.
2477
      We'll leave the second one on the value stack, and
2478
      just replace its object with the result
2479
      */
2480
  BObjectRef rightref = ValueStack.back();
1,639✔
2481
  ValueStack.pop_back();
1,639✔
2482
  BObjectRef& leftref = ValueStack.back();
1,639✔
2483

2484
  BObject& right = *rightref;
1,639✔
2485
  BObject& left = *leftref;
1,639✔
2486

2487
  leftref = checkmember( left, right );
1,639✔
2488
}
1,639✔
2489

2490
void Executor::ins_addmember2( const Instruction& ins )
45✔
2491
{
2492
  BObjectRef& obref = ValueStack.back();
45✔
2493

2494
  BObject& ob = *obref;
45✔
2495

2496
  ob.impref().operDotPlus( ins.token.tokval() );
45✔
2497
}
45✔
2498

2499
void Executor::ins_addmember_assign( const Instruction& ins )
1,877✔
2500
{
2501
  BObjectRef valref = ValueStack.back();
1,877✔
2502
  BObject& valob = *valref;
1,877✔
2503
  BObjectImp* valimp = valref->impptr();
1,877✔
2504

2505
  ValueStack.pop_back();
1,877✔
2506

2507
  BObjectRef& obref = ValueStack.back();
1,877✔
2508
  BObject& ob = *obref;
1,877✔
2509

2510
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
1,877✔
2511
  BObject& mem = *memref;
1,877✔
2512

2513
  if ( valob.count() == 1 && valimp->count() == 1 )
1,877✔
2514
  {
2515
    mem.setimp( valimp );
1,369✔
2516
  }
2517
  else
2518
  {
2519
    mem.setimp( valimp->copy() );
508✔
2520
  }
2521
  // the struct is at the top of the stack
2522
}
1,877✔
2523

2524
void Executor::ins_dictionary_addmember( const Instruction& /*ins*/ )
146✔
2525
{
2526
  /*
2527
      ENTRANCE: value stack:
2528
      dictionary
2529
      key
2530
      value
2531
      EXIT: value stack:
2532
      dictionary
2533
      FUNCTION:
2534
      adds the (key, value) pair to the dictionary
2535
      */
2536

2537
  BObjectRef valref = ValueStack.back();
146✔
2538
  ValueStack.pop_back();
146✔
2539
  BObject& valob = *valref;
146✔
2540
  BObjectImp* valimp = valob.impptr();
146✔
2541

2542
  BObjectRef keyref = ValueStack.back();
146✔
2543
  ValueStack.pop_back();
146✔
2544
  BObject& keyob = *keyref;
146✔
2545
  BObjectImp* keyimp = keyob.impptr();
146✔
2546

2547
  BObjectRef& dictref = ValueStack.back();
146✔
2548
  BObject& dictob = *dictref;
146✔
2549
  BDictionary* dict = dictob.impptr<BDictionary>();
146✔
2550

2551
  if ( keyob.count() != 1 || keyimp->count() != 1 )
146✔
2552
  {
2553
    keyimp = keyimp->copy();
6✔
2554
  }
2555
  if ( valob.count() != 1 || valimp->count() != 1 )
146✔
2556
  {
2557
    valimp = valimp->copy();
18✔
2558
  }
2559

2560
  dict->addMember( keyimp, valimp );
146✔
2561
  // the dictionary remains at the top of the stack.
2562
}
146✔
2563

2564
void Executor::ins_in( const Instruction& /*ins*/ )
799✔
2565
{
2566
  /*
2567
      These each take two operands, and replace them with one.
2568
      We'll leave the second one on the value stack, and
2569
      just replace its object with the result
2570
      */
2571
  BObjectRef rightref = ValueStack.back();
799✔
2572
  ValueStack.pop_back();
799✔
2573
  BObjectRef& leftref = ValueStack.back();
799✔
2574

2575
  BObject& right = *rightref;
799✔
2576
  BObject& left = *leftref;
799✔
2577

2578
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
799✔
2579
}
799✔
2580

2581
void Executor::ins_insert_into( const Instruction& /*ins*/ )
7,892✔
2582
{
2583
  BObjectRef rightref = ValueStack.back();
7,892✔
2584
  ValueStack.pop_back();
7,892✔
2585
  BObjectRef& leftref = ValueStack.back();
7,892✔
2586

2587
  BObject& right = *rightref;
7,892✔
2588
  BObject& left = *leftref;
7,892✔
2589

2590
  if ( auto* spread = right.impptr_if<BSpread>() )
7,892✔
2591
  {
2592
    BObjectRef refIter( UninitObject::create() );
1,473✔
2593

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

2597
    BObject* next = pIter->step();
1,473✔
2598
    while ( next != nullptr )
4,317✔
2599
    {
2600
      left.impref().operInsertInto( left, next->impref() );
2,844✔
2601
      next = pIter->step();
2,844✔
2602
    }
2603
  }
1,473✔
2604
  else
2605
  {
2606
    left.impref().operInsertInto( left, right.impref() );
6,419✔
2607
  }
2608
}
7,892✔
2609

2610
void Executor::ins_plusequal( const Instruction& /*ins*/ )
820✔
2611
{
2612
  /*
2613
      These each take two operands, and replace them with one.
2614
      We'll leave the second one on the value stack, and
2615
      just replace its object with the result
2616
      */
2617
  BObjectRef rightref = ValueStack.back();
820✔
2618
  ValueStack.pop_back();
820✔
2619
  BObjectRef& leftref = ValueStack.back();
820✔
2620

2621
  BObject& right = *rightref;
820✔
2622
  BObject& left = *leftref;
820✔
2623

2624
  left.impref().operPlusEqual( left, right.impref() );
820✔
2625
}
820✔
2626

2627
void Executor::ins_minusequal( const Instruction& /*ins*/ )
240✔
2628
{
2629
  /*
2630
      These each take two operands, and replace them with one.
2631
      We'll leave the second one on the value stack, and
2632
      just replace its object with the result
2633
      */
2634
  BObjectRef rightref = ValueStack.back();
240✔
2635
  ValueStack.pop_back();
240✔
2636
  BObjectRef& leftref = ValueStack.back();
240✔
2637

2638
  BObject& right = *rightref;
240✔
2639
  BObject& left = *leftref;
240✔
2640

2641
  left.impref().operMinusEqual( left, right.impref() );
240✔
2642
}
240✔
2643

2644
void Executor::ins_timesequal( const Instruction& /*ins*/ )
237✔
2645
{
2646
  /*
2647
      These each take two operands, and replace them with one.
2648
      We'll leave the second one on the value stack, and
2649
      just replace its object with the result
2650
      */
2651
  BObjectRef rightref = ValueStack.back();
237✔
2652
  ValueStack.pop_back();
237✔
2653
  BObjectRef& leftref = ValueStack.back();
237✔
2654

2655
  BObject& right = *rightref;
237✔
2656
  BObject& left = *leftref;
237✔
2657

2658
  left.impref().operTimesEqual( left, right.impref() );
237✔
2659
}
237✔
2660

2661
void Executor::ins_divideequal( const Instruction& /*ins*/ )
228✔
2662
{
2663
  /*
2664
      These each take two operands, and replace them with one.
2665
      We'll leave the second one on the value stack, and
2666
      just replace its object with the result
2667
      */
2668
  BObjectRef rightref = ValueStack.back();
228✔
2669
  ValueStack.pop_back();
228✔
2670
  BObjectRef& leftref = ValueStack.back();
228✔
2671

2672
  BObject& right = *rightref;
228✔
2673
  BObject& left = *leftref;
228✔
2674

2675
  left.impref().operDivideEqual( left, right.impref() );
228✔
2676
}
228✔
2677

2678
void Executor::ins_modulusequal( const Instruction& /*ins*/ )
375✔
2679
{
2680
  /*
2681
      These each take two operands, and replace them with one.
2682
      We'll leave the second one on the value stack, and
2683
      just replace its object with the result
2684
      */
2685
  BObjectRef rightref = ValueStack.back();
375✔
2686
  ValueStack.pop_back();
375✔
2687
  BObjectRef& leftref = ValueStack.back();
375✔
2688

2689
  BObject& right = *rightref;
375✔
2690
  BObject& left = *leftref;
375✔
2691

2692
  left.impref().operModulusEqual( left, right.impref() );
375✔
2693
}
375✔
2694

2695
// case RSV_GOTO:
2696
void Executor::ins_goto( const Instruction& ins )
33,076,750✔
2697
{
2698
  PC = (unsigned)ins.token.lval;
33,076,750✔
2699
}
33,076,750✔
2700

2701
// TOK_FUNC:
2702
void Executor::ins_func( const Instruction& ins )
33,113,026✔
2703
{
2704
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
33,113,026✔
2705
  getParams( nparams );
33,113,026✔
2706
  execFunc( ins.token );
33,113,026✔
2707
  cleanParams();
33,113,026✔
2708
  return;
33,113,026✔
2709
}
2710

2711
void Executor::ins_call_method_id( const Instruction& ins )
45,986✔
2712
{
2713
  BContinuation* continuation = nullptr;
45,986✔
2714
  unsigned nparams = ins.token.type;
45,986✔
2715

2716
  do
2717
  {
2718
    getParams( nparams );
46,777✔
2719
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
46,777✔
2720
    {
2721
      Instruction jmp;
2,450✔
2722
      bool add_new_classinst = ins.token.lval == MTH_NEW;
2,450✔
2723

2724
      if ( add_new_classinst )
2,450✔
2725
      {
2726
        if ( funcr->constructor() )
108✔
2727
        {
2728
          fparams.insert( fparams.begin(),
81✔
2729
                          BObjectRef( new BConstObject( new BClassInstanceRef(
162✔
2730
                              new BClassInstance( prog_, funcr->class_index(), Globals2 ) ) ) ) );
162✔
2731
        }
2732
      }
2733

2734
      if ( funcr->validCall( continuation ? MTH_CALL : ins.token.lval, *this, &jmp ) )
2,450✔
2735
      {
2736
        BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
2,345✔
2737
        call_function_reference( funcr, continuation, jmp );
2,345✔
2738
        return;
2,345✔
2739
      }
2,345✔
2740
    }
2,450✔
2741
    // If there _was_ a continuation to be handled (from previous loop
2742
    // iteration), there must have been a FuncRef on the stack. Otherwise,
2743
    // `continuation` may leak.
2744
    passert_always( continuation == nullptr );
44,432✔
2745

2746
    size_t stacksize = ValueStack.size();  // ValueStack can grow
44,432✔
2747
    BObjectImp* imp;
2748
    {
2749
      ESCRIPT_PROFILER( ins, ValueStack.back(), fparams );
2750
      imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
44,432✔
2751

2752
      if ( auto* cont = impptrIf<BContinuation>( imp ) )
44,432✔
2753
      {
2754
        continuation = cont;
791✔
2755
        // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2756
        // move.
2757
        nparams = static_cast<unsigned int>( continuation->args.size() );
791✔
2758

2759
        // Add function reference to stack
2760
        ValueStack.emplace_back( continuation->func() );
791✔
2761

2762
        // Move all arguments to the value stack
2763
        ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
791✔
2764
                           std::make_move_iterator( continuation->args.end() ) );
2765

2766
        continuation->args.clear();
791✔
2767

2768
        cleanParams();
791✔
2769

2770
        printStack( fmt::format(
1,582✔
2771
            "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2772
            "funcref.call()",
2773
            nparams ) );
2774

2775
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2776
        // handle it.
2777

2778
        // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2779
        // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2780
        // jumps.
2781

2782
        continue;
791✔
2783
      }
2784
    }
2785

2786
    if ( impptrIf<BSpecialUserFuncJump>( imp ) )
43,641✔
2787
    {
2788
      cleanParams();
3✔
2789
      return;
3✔
2790
    }
2791
    BObjectRef& objref = ValueStack[stacksize - 1];
43,638✔
2792
    if ( func_result_ )
43,638✔
2793
    {
2794
      if ( imp )
16✔
2795
      {
2796
        BObject obj( imp );
7✔
2797
      }
7✔
2798

2799
      objref.set( func_result_ );
16✔
2800
      func_result_ = nullptr;
16✔
2801
    }
2802
    else if ( imp )
43,622✔
2803
    {
2804
      objref.set( imp );
43,568✔
2805
    }
2806
    else
2807
    {
2808
      objref.set( UninitObject::create() );
54✔
2809
    }
2810

2811
    cleanParams();
43,638✔
2812
    return;
43,638✔
2813
  }
2814
  // This condition should only ever evaluate to `true` once. In the second loop
2815
  // iteration, handling the FuncRef will return out of this method.
2816
  while ( continuation != nullptr );
791✔
2817
}
2818

2819
void Executor::ins_call_method( const Instruction& ins )
876✔
2820
{
2821
  unsigned nparams = ins.token.lval;
876✔
2822
  auto method_name = ins.token.tokval();
876✔
2823

2824
  getParams( nparams );
876✔
2825
  BObjectImp* callee = ValueStack.back()->impptr();
876✔
2826

2827
  if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
876✔
2828
  {
2829
    Instruction jmp;
×
2830
    if ( funcr->validCall( method_name, *this, &jmp ) )
×
2831
    {
2832
      BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
×
2833
      call_function_reference( funcr, nullptr, jmp );
×
2834
      return;
×
2835
    }
×
2836
  }
×
2837

2838
  size_t stacksize = ValueStack.size();  // ValueStack can grow
876✔
2839
  BObjectImp* imp;
2840
  {
2841
    ESCRIPT_PROFILER( ins, callee, method_name, fparams );
2842
#ifdef BOBJECTIMP_DEBUG
2843
    if ( strcmp( method_name, "impptr" ) == 0 )
876✔
2844
      imp = new String( fmt::format( "{}", static_cast<void*>( callee ) ) );
×
2845
    else
2846
      imp = callee->call_method( method_name, *this );
876✔
2847
#else
2848
    imp = callee->call_method( method_name, *this );
2849
#endif
2850
  }
2851

2852
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
876✔
2853
  {
2854
    cleanParams();
634✔
2855
    return;
634✔
2856
  }
2857

2858
  BObjectRef& objref = ValueStack[stacksize - 1];
242✔
2859
  if ( func_result_ )
242✔
2860
  {
2861
    if ( imp )
×
2862
    {
2863
      BObject obj( imp );
×
2864
    }
×
2865

2866
    objref.set( func_result_ );
×
2867
    func_result_ = nullptr;
×
2868
  }
2869
  else if ( imp )
242✔
2870
  {
2871
    objref.set( imp );
63✔
2872
  }
2873
  else
2874
  {
2875
    objref.set( UninitObject::create() );
179✔
2876
  }
2877
  cleanParams();
242✔
2878
}
2879

2880
// CTRL_STATEMENTBEGIN:
2881
void Executor::ins_statementbegin( const Instruction& ins )
×
2882
{
2883
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
2884
    INFO_PRINTLN( ins.token.tokval() );
×
2885
}
×
2886

2887
// case CTRL_PROGEND:
2888
void Executor::ins_progend( const Instruction& /*ins*/ )
3,092✔
2889
{
2890
  done = 1;
3,092✔
2891
  run_ok_ = false;
3,092✔
2892
  PC = 0;
3,092✔
2893
}
3,092✔
2894

2895

2896
// case CTRL_MAKELOCAL:
2897
void Executor::ins_makelocal( const Instruction& /*ins*/ )
44,428✔
2898
{
2899
  if ( Locals2 )
44,428✔
2900
    upperLocals2.push_back( Locals2 );
44,428✔
2901
  Locals2 = new BObjectRefVec;
44,428✔
2902
}
44,428✔
2903

2904
void Executor::ins_check_mro( const Instruction& ins )
280✔
2905
{
2906
  auto classinst_offset = ins.token.lval;
280✔
2907

2908
  if ( classinst_offset > static_cast<int>( ValueStack.size() ) || ValueStack.empty() )
280✔
2909
  {
2910
    POLLOG_ERRORLN( "Fatal error: Check MRO offset error! offset={}, ValueStack.size={} ({},PC={})",
×
2911
                    classinst_offset, ValueStack.size(), prog_->name, PC );
×
2912
    seterror( true );
×
2913
    return;
×
2914
  }
2915

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

2918
  if ( nLines < PC + 1 )
280✔
2919
  {
2920
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction out of bounds! nLines={} ({},PC={})",
×
2921
                    nLines, prog_->name, PC );
×
2922
    seterror( true );
×
2923
    return;
×
2924
  }
2925

2926
  const Instruction& jsr_ins = prog_->instr.at( PC + 1 );
280✔
2927
  if ( jsr_ins.func != &Executor::ins_jsr_userfunc )
280✔
2928
  {
2929
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction not followed by JSR_USERFUNC! ({},PC={})",
×
2930
                    prog_->name, PC );
×
2931
    seterror( true );
×
2932
    return;
×
2933
  }
2934

2935
  auto ctor_addr = jsr_ins.token.lval;
280✔
2936

2937
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280✔
2938

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

2953
// CTRL_JSR_USERFUNC:
2954
void Executor::ins_jsr_userfunc( const Instruction& ins )
6,344✔
2955
{
2956
  jump( ins.token.lval, nullptr, nullptr );
6,344✔
2957
}
6,344✔
2958

2959
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
44,428✔
2960
{
2961
  ReturnContext rc;
44,428✔
2962
  rc.PC = PC;
44,428✔
2963
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
44,428✔
2964
  if ( continuation )
44,428✔
2965
  {
2966
    rc.Continuation.set( continuation );
35,893✔
2967
  }
2968

2969
  // Only store our global context if the function is external to the current program.
2970
  if ( funcref != nullptr && funcref->prog() != prog_ )
44,428✔
2971
  {
2972
    // Store external context for the return path.
2973
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
132✔
2974

2975
    if ( auto shared = funcref->globals.lock() )
264✔
2976
      Globals2 = shared;
130✔
2977
    else
2978
      Globals2 =
2979
          std::make_shared<BObjectRefVec>();  // empty but valid, access would stop the executor
132✔
2980

2981
    // Set the prog and globals to the external function's, updating nLines and
2982
    // execmodules.
2983
    prog_ = funcref->prog();
132✔
2984

2985

2986
    nLines = static_cast<unsigned int>( prog_->instr.size() );
132✔
2987

2988
    // Re-attach modules, as the external user function's module function call
2989
    // instructions refer to modules by index.
2990
    execmodules.clear();
132✔
2991

2992
    if ( !viewmode_ )
132✔
2993
    {
2994
      if ( !AttachFunctionalityModules() )
132✔
2995
      {
2996
        POLLOGLN( "Could not attach modules for external function call jump" );
×
2997
        seterror( true );
×
2998
      }
2999
    }
3000
  }
3001

3002
  ControlStack.push_back( rc );
44,428✔
3003

3004
  PC = target_PC;
44,428✔
3005
  if ( ControlStack.size() >= escript_config.max_call_depth )
44,428✔
3006
  {
3007
    std::string tmp = fmt::format(
3008
        "Script {} exceeded maximum call depth\n"
3009
        "Return path PCs: ",
3010
        scriptname() );
×
3011
    while ( !ControlStack.empty() )
×
3012
    {
3013
      rc = ControlStack.back();
×
3014
      ControlStack.pop_back();
×
3015
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
3016
    }
3017
    POLLOGLN( tmp );
×
3018
    seterror( true );
×
3019
  }
×
3020
}
44,428✔
3021

3022

3023
BObjectImp* Executor::get_stacktrace( bool as_array )
44✔
3024
{
3025
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
44✔
3026

3027
  auto with_dbginfo =
3028
      [&]( const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
42✔
3029
                                     unsigned int /*line*/, const std::string& /*functionName*/ )>&
3030
               handler )
3031
  {
3032
    walkCallStack(
42✔
3033
        [&]( unsigned int pc )
245✔
3034
        {
3035
          auto filename = prog()->dbg_filenames[prog()->dbg_filenum[pc]];
203✔
3036
          auto line = prog()->dbg_linenum[pc];
161✔
3037
          auto dbgFunction =
3038
              std::find_if( prog()->dbg_functions.begin(), prog()->dbg_functions.end(),
161✔
3039
                            [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
3,014✔
3040

3041
          std::string functionName =
3042
              dbgFunction != prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
161✔
3043

3044
          handler( pc, filename, line, functionName );
161✔
3045
        } );
161✔
3046
  };
42✔
3047

3048
  if ( as_array )
44✔
3049
  {
3050
    std::unique_ptr<ObjArray> result( new ObjArray );
4✔
3051

3052
    if ( has_symbols )
4✔
3053
    {
3054
      with_dbginfo(
2✔
3055
          [&]( unsigned int pc, const std::string& filename, unsigned int line,
4✔
3056
               const std::string& functionName )
3057
          {
3058
            std::unique_ptr<BStruct> entry( new BStruct );
8✔
3059
            entry->addMember( "file", new String( filename ) );
8✔
3060
            entry->addMember( "line", new BLong( line ) );
8✔
3061
            entry->addMember( "name", new String( functionName ) );
8✔
3062
            entry->addMember( "pc", new BLong( pc ) );
8✔
3063
            result->addElement( entry.release() );
8✔
3064
          } );
8✔
3065
    }
3066
    else
3067
    {
3068
      walkCallStack(
2✔
3069
          [&]( unsigned int pc )
2✔
3070
          {
3071
            std::unique_ptr<BStruct> entry( new BStruct );
3✔
3072
            entry->addMember( "file", new String( scriptname() ) );
3✔
3073
            entry->addMember( "pc", new BLong( pc ) );
3✔
3074
            result->addElement( entry.release() );
3✔
3075
          } );
3✔
3076
    }
3077

3078
    return result.release();
4✔
3079
  }
4✔
3080
  // as string
3081
  std::string result;
40✔
3082

3083
  if ( has_symbols )
40✔
3084
  {
3085
    with_dbginfo(
40✔
3086
        [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
80✔
3087
             const std::string& functionName )
3088
        {
3089
          result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
306✔
3090
                                      filename, line ) );
3091
        } );
153✔
3092
  }
3093
  else
3094
  {
3095
    walkCallStack(
×
3096
        [&]( unsigned int pc )
×
3097
        {
3098
          result.append(
×
3099
              fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
3100
        } );
×
3101
  }
3102

3103
  return new String( std::move( result ) );
40✔
3104
}
40✔
3105

3106
void Executor::ins_pop_param( const Instruction& ins )
48,959✔
3107
{
3108
  popParam( ins.token );
48,959✔
3109
}
48,959✔
3110

3111
void Executor::ins_pop_param_byref( const Instruction& ins )
7,995✔
3112
{
3113
  popParamByRef( ins.token );
7,995✔
3114
}
7,995✔
3115

3116
void Executor::ins_get_arg( const Instruction& ins )
449✔
3117
{
3118
  getArg( ins.token );
449✔
3119
}
449✔
3120

3121
// CTRL_LEAVE_BLOCK:
3122
void Executor::ins_leave_block( const Instruction& ins )
9,553✔
3123
{
3124
  if ( Locals2 )
9,553✔
3125
  {
3126
    for ( int i = 0; i < ins.token.lval; i++ )
25,474✔
3127
      Locals2->pop_back();
15,921✔
3128
  }
3129
  else  // at global level.  ick.
3130
  {
3131
    for ( int i = 0; i < ins.token.lval; i++ )
×
3132
      Globals2->pop_back();
×
3133
  }
3134
}
9,553✔
3135

3136
void Executor::ins_gosub( const Instruction& ins )
×
3137
{
3138
  ReturnContext rc;
×
3139
  rc.PC = PC;
×
3140
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3141
  ControlStack.push_back( rc );
×
3142
  if ( Locals2 )
×
3143
    upperLocals2.push_back( Locals2 );
×
3144
  Locals2 = new BObjectRefVec;
×
3145

3146
  PC = (unsigned)ins.token.lval;
×
3147
}
×
3148

3149
// case RSV_RETURN
3150
void Executor::ins_return( const Instruction& /*ins*/ )
44,427✔
3151
{
3152
  if ( ControlStack.empty() )
44,427✔
3153
  {
3154
    ERROR_PRINTLN( "Return without GOSUB! (PC={}, {})", PC, scriptname() );
×
3155

3156
    seterror( true );
×
3157
    return;
×
3158
  }
3159
  BObjectRef continuation;
44,427✔
3160
  ReturnContext& rc = ControlStack.back();
44,427✔
3161
  PC = rc.PC;
44,427✔
3162

3163
  if ( rc.Continuation.get() != nullptr )
44,427✔
3164
  {
3165
    continuation = rc.Continuation;
35,893✔
3166
  }
3167

3168
  if ( Locals2 )
44,427✔
3169
  {
3170
    delete Locals2;
44,427✔
3171
    Locals2 = nullptr;
44,427✔
3172
  }
3173
  if ( !upperLocals2.empty() )
44,427✔
3174
  {
3175
    Locals2 = upperLocals2.back();
44,427✔
3176
    upperLocals2.pop_back();
44,427✔
3177
  }
3178

3179
  if ( rc.ExternalContext.has_value() )
44,427✔
3180
  {
3181
    prog_ = std::move( rc.ExternalContext->Program );
131✔
3182
    nLines = static_cast<unsigned int>( prog_->instr.size() );
131✔
3183
    execmodules = std::move( rc.ExternalContext->Modules );
131✔
3184
    Globals2 = std::move( rc.ExternalContext->Globals );
131✔
3185
  }
3186

3187
  // FIXME do something with rc.ValueStackDepth
3188
  ControlStack.pop_back();
44,427✔
3189

3190
  if ( continuation != nullptr )
44,427✔
3191
  {
3192
    auto result = ValueStack.back();
35,893✔
3193
    ValueStack.pop_back();
35,893✔
3194

3195
    // Do not move the `result` object, as the continuation callback may return
3196
    // the result's BObjectImp*. If we move `result`, the BObjectImp* will be
3197
    // deleted when the callback ends.
3198
    auto* imp = continuation->impptr<BContinuation>()->continueWith( *this, result );
35,893✔
3199

3200
    // If the the continuation callback returned a continuation, handle the jump.
3201
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
35,893✔
3202
    {
3203
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3204
      // takes ownership.
3205

3206
      // Add function reference to stack
3207
      ValueStack.emplace_back( cont->func() );
35,102✔
3208

3209
      // Move all arguments to the fparams stack
3210
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
35,102✔
3211
                      std::make_move_iterator( cont->args.end() ) );
3212

3213
      cont->args.clear();
35,102✔
3214

3215
      printStack(
35,102✔
3216
          "continuation callback returned a continuation; continuation args added to fparams" );
3217

3218
      BObjectRef objref = ValueStack.back();
35,102✔
3219
      auto funcr = objref->impptr<BFunctionRef>();
35,102✔
3220
      Instruction jmp;
35,102✔
3221
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
35,102✔
3222
      {
3223
        call_function_reference( funcr, cont, jmp );
35,102✔
3224
      }
3225
      else
3226
      {
3227
        // Delete `imp` since a ReturnContext was not created.
3228
        BObject bobj( imp );
×
3229
      }
×
3230
    }
35,102✔
3231
    else
3232
    {
3233
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3234
      // `array{}.filter(...)`
3235
      ValueStack.pop_back();
791✔
3236

3237
      // Add the result to the stack.
3238
      ValueStack.emplace_back( imp );
791✔
3239
    }
3240
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
71,786✔
3241
  }
35,893✔
3242
}
44,427✔
3243

3244
void Executor::ins_exit( const Instruction& /*ins*/ )
3✔
3245
{
3246
  done = 1;
3✔
3247
  run_ok_ = false;
3✔
3248
}
3✔
3249

3250
void Executor::ins_double( const Instruction& ins )
427✔
3251
{
3252
  ValueStack.emplace_back( new Double( ins.token.dval ) );
427✔
3253
}
427✔
3254

3255
void Executor::ins_classinst( const Instruction& ins )
321✔
3256
{
3257
  ValueStack.emplace_back( new BConstObject(
321✔
3258
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2 ) ) ) );
642✔
3259
}
321✔
3260

3261
void Executor::ins_string( const Instruction& ins )
107,599✔
3262
{
3263
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
107,599✔
3264
}
107,599✔
3265

3266
void Executor::ins_regexp( const Instruction& )
150✔
3267
{
3268
  BObjectRef flags = ValueStack.back();
150✔
3269
  ValueStack.pop_back();
150✔
3270
  BObjectRef& pattern = ValueStack.back();
150✔
3271

3272
  pattern.set(
150✔
3273
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300✔
3274
}
150✔
3275

3276
void Executor::ins_error( const Instruction& /*ins*/ )
4,177✔
3277
{
3278
  ValueStack.emplace_back( new BError() );
4,177✔
3279
}
4,177✔
3280
void Executor::ins_struct( const Instruction& /*ins*/ )
815✔
3281
{
3282
  ValueStack.emplace_back( new BStruct );
815✔
3283
}
815✔
3284
void Executor::ins_spread( const Instruction& ins )
2,126✔
3285
{
3286
  bool spread_into = ins.token.lval;
2,126✔
3287

3288
  // Pops TOP, and spreads it into (new) TOP.
3289
  if ( spread_into )
2,126✔
3290
  {
3291
    BObjectRef rightref = ValueStack.back();
24✔
3292
    ValueStack.pop_back();
24✔
3293
    BObjectRef& leftref = ValueStack.back();
24✔
3294

3295
    BObject& right = *rightref;
24✔
3296
    BObject& left = *leftref;
24✔
3297

3298
    BObjectRef refIter( UninitObject::create() );
24✔
3299

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

3302
    while ( auto next = pIter->step() )
72✔
3303
    {
3304
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48✔
3305
      BObject res_obj( result );
48✔
3306
    }
48✔
3307
  }
24✔
3308
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3309
  else
3310
  {
3311
    auto spread = new BSpread( ValueStack.back() );
2,102✔
3312
    ValueStack.pop_back();
2,102✔
3313
    ValueStack.emplace_back( spread );
2,102✔
3314
  }
3315
}
2,126✔
3316
void Executor::ins_array( const Instruction& /*ins*/ )
3,843✔
3317
{
3318
  ValueStack.emplace_back( new ObjArray );
3,843✔
3319
}
3,843✔
3320
void Executor::ins_dictionary( const Instruction& /*ins*/ )
166✔
3321
{
3322
  ValueStack.emplace_back( new BDictionary );
166✔
3323
}
166✔
3324
void Executor::ins_uninit( const Instruction& /*ins*/ )
113✔
3325
{
3326
  ValueStack.emplace_back( UninitObject::create() );
113✔
3327
}
113✔
3328
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3329
{
3330
  ValueStack.emplace_back( new BError( "Please recompile this script" ) );
×
3331
}
×
3332

3333
// case TOK_UNMINUS:
3334
void Executor::ins_unminus( const Instruction& /*ins*/ )
147✔
3335
{
3336
  BObjectRef ref = getObjRef();
147✔
3337
  BObjectImp* newobj;
3338
  newobj = ref->impref().inverse();
147✔
3339

3340
  ValueStack.emplace_back( newobj );
147✔
3341
}
147✔
3342

3343
// case TOK_UNPLUSPLUS:
3344
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
472✔
3345
{
3346
  BObjectRef& ref = ValueStack.back();
472✔
3347
  ref->impref().selfPlusPlus();
472✔
3348
}
472✔
3349

3350
// case TOK_UNMINUSMINUS:
3351
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
136✔
3352
{
3353
  BObjectRef& ref = ValueStack.back();
136✔
3354
  ref->impref().selfMinusMinus();
136✔
3355
}
136✔
3356

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

3367
// case TOK_UNMINUSMINUS_POST:
3368
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
85✔
3369
{
3370
  BObjectRef& ref = ValueStack.back();
85✔
3371
  BObjectImp* imp = ref->impptr();
85✔
3372
  BObject* n = ref->clone();
85✔
3373
  imp->selfMinusMinus();
85✔
3374
  ref.set( n );
85✔
3375
}
85✔
3376

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

3391
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3392
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
3✔
3393
{
3394
  BObjectRef& ref = ValueStack.back();
3✔
3395
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3396
  BObject* res = tmp->clone();
3✔
3397
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3398
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3399
  {
3400
    tmp->impref().selfPlusPlus();
3✔
3401
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3402
  }
3403
  ref.set( res );
3✔
3404
}
3✔
3405

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

3420
// case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3421
void Executor::ins_set_member_id_unminusminus_post( const Instruction& ins )
3✔
3422
{
3423
  BObjectRef& ref = ValueStack.back();
3✔
3424
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3425
  BObject* res = tmp->clone();
3✔
3426
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3427
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3428
  {
3429
    tmp->impref().selfMinusMinus();
3✔
3430
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3431
  }
3432
  ref.set( res );
3✔
3433
}
3✔
3434

3435
// case TOK_LOG_NOT:
3436
void Executor::ins_logical_not( const Instruction& /*ins*/ )
3,992✔
3437
{
3438
  BObjectRef ref = getObjRef();
3,992✔
3439
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
3,992✔
3440
}
3,992✔
3441

3442
// case TOK_BITWISE_NOT:
3443
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
156✔
3444
{
3445
  BObjectRef ref = getObjRef();
156✔
3446
  ValueStack.emplace_back( ref->impptr()->bitnot() );
156✔
3447
}
156✔
3448

3449
// case TOK_FUNCREF:
3450
void Executor::ins_funcref( const Instruction& ins )
1,499✔
3451
{
3452
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
1,499✔
3453
  {
3454
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3455
                    prog_->function_references.size() );
×
3456
    seterror( true );
×
3457
    return;
×
3458
  }
3459

3460
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
1,499✔
3461

3462
  ValueStack.emplace_back( new BFunctionRef( prog_, funcref_index, Globals2, {} /* captures */ ) );
1,499✔
3463
}
3464

3465
void Executor::ins_functor( const Instruction& ins )
723✔
3466
{
3467
  auto funcref_index = static_cast<int>( ins.token.type );
723✔
3468

3469
  const auto& ep_funcref = prog_->function_references[funcref_index];
723✔
3470

3471
  int capture_count = ep_funcref.capture_count;
723✔
3472

3473
  auto captures = ValueStackCont();
723✔
3474
  while ( capture_count > 0 )
1,355✔
3475
  {
3476
    captures.push_back( ValueStack.back() );
632✔
3477
    ValueStack.pop_back();
632✔
3478
    capture_count--;
632✔
3479
  }
3480

3481
  auto func = new BFunctionRef( prog_, funcref_index, Globals2, std::move( captures ) );
723✔
3482

3483
  ValueStack.emplace_back( func );
723✔
3484

3485
  PC += ins.token.lval;
723✔
3486
}
723✔
3487

3488
void Executor::ins_logical_jump( const Instruction& ins )
409✔
3489
{
3490
  BObjectRef& objref = ValueStack.back();
409✔
3491
  // jmp if true for ||, jmp if false for &&
3492
  const bool obj_true = objref->impptr()->isTrue();
409✔
3493
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
409✔
3494

3495
  if ( jmp )
409✔
3496
    PC = (unsigned)ins.token.lval;
84✔
3497
  // keep the obj on the stack if it should jump (ShortCircuit)
3498
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3499
  // BLong (original &&/|| convert to BLong bool)
3500
  if ( !jmp )
409✔
3501
    ValueStack.pop_back();
325✔
3502
  else
3503
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
84✔
3504
}
409✔
3505

3506
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
95✔
3507
{
3508
  BObjectRef& objref = ValueStack.back();
95✔
3509
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
95✔
3510
}
95✔
3511

3512
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3513

3514
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
162,348✔
3515
{
3516
  switch ( token.id )
162,348✔
3517
  {
3518
  case INS_INITFOREACH:
406✔
3519
    return &Executor::ins_initforeach;
406✔
3520
  case INS_STEPFOREACH:
406✔
3521
    return &Executor::ins_stepforeach;
406✔
3522
  case INS_INITFOR:
60✔
3523
    return &Executor::ins_initfor;
60✔
3524
  case INS_NEXTFOR:
60✔
3525
    return &Executor::ins_nextfor;
60✔
3526
  case INS_CASEJMP:
95✔
3527
    return &Executor::ins_casejmp;
95✔
3528
  case RSV_JMPIFTRUE:
1,040✔
3529
    return &Executor::ins_jmpiftrue;
1,040✔
3530
  case RSV_JMPIFFALSE:
1,959✔
3531
    return &Executor::ins_jmpiffalse;
1,959✔
3532
  case RSV_LOCAL:
2,659✔
3533
    return &Executor::ins_makeLocal;
2,659✔
3534
  case RSV_GLOBAL:
9,487✔
3535
  case TOK_GLOBALVAR:
3536
    return &Executor::ins_globalvar;
9,487✔
3537
  case TOK_LOCALVAR:
16,776✔
3538
    return &Executor::ins_localvar;
16,776✔
3539
  case TOK_LONG:
13,836✔
3540
    return &Executor::ins_long;
13,836✔
3541
  case TOK_DOUBLE:
424✔
3542
    return &Executor::ins_double;
424✔
3543
  case TOK_STRING:
24,680✔
3544
    return &Executor::ins_string;
24,680✔
3545
  case TOK_REGEXP:
150✔
3546
    return &Executor::ins_regexp;
150✔
3547
  case TOK_ERROR:
190✔
3548
    return &Executor::ins_error;
190✔
3549
  case TOK_STRUCT:
585✔
3550
    return &Executor::ins_struct;
585✔
3551
  case TOK_SPREAD:
670✔
3552
    return &Executor::ins_spread;
670✔
3553
  case TOK_CLASSINST:
242✔
3554
    return &Executor::ins_classinst;
242✔
3555
  case TOK_ARRAY:
2,353✔
3556
    return &Executor::ins_array;
2,353✔
3557
  case TOK_DICTIONARY:
161✔
3558
    return &Executor::ins_dictionary;
161✔
3559
  case TOK_FUNCREF:
734✔
3560
    return &Executor::ins_funcref;
734✔
3561
  case TOK_FUNCTOR:
662✔
3562
    return &Executor::ins_functor;
662✔
3563
  case INS_UNINIT:
106✔
3564
    return &Executor::ins_uninit;
106✔
3565
  case TOK_IDENT:
×
3566
    return &Executor::ins_ident;
×
3567
  case INS_ASSIGN_GLOBALVAR:
732✔
3568
    return &Executor::ins_assign_globalvar;
732✔
3569
  case INS_ASSIGN_LOCALVAR:
910✔
3570
    return &Executor::ins_assign_localvar;
910✔
3571
  case INS_ASSIGN_CONSUME:
63✔
3572
    return &Executor::ins_assign_consume;
63✔
3573
  case TOK_CONSUMER:
18,088✔
3574
    return &Executor::ins_consume;
18,088✔
3575
  case TOK_ASSIGN:
4,299✔
3576
    return &Executor::ins_assign;
4,299✔
3577
  case INS_SUBSCRIPT_ASSIGN:
9✔
3578
    return &Executor::ins_array_assign;
9✔
3579
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
422✔
3580
    return &Executor::ins_array_assign_consume;
422✔
3581
  case INS_MULTISUBSCRIPT:
237✔
3582
    return &Executor::ins_multisubscript;
237✔
3583
  case INS_MULTISUBSCRIPT_ASSIGN:
6✔
3584
    return &Executor::ins_multisubscript_assign;
6✔
3585
  case INS_GET_MEMBER:
1,059✔
3586
    return &Executor::ins_get_member;
1,059✔
3587
  case INS_SET_MEMBER:
3✔
3588
    return &Executor::ins_set_member;
3✔
3589
  case INS_SET_MEMBER_CONSUME:
235✔
3590
    return &Executor::ins_set_member_consume;
235✔
3591

3592
  case INS_UNPACK_SEQUENCE:
106✔
3593
    return &Executor::ins_unpack_sequence;
106✔
3594
  case INS_UNPACK_INDICES:
84✔
3595
    return &Executor::ins_unpack_indices;
84✔
3596
  case INS_TAKE_GLOBAL:
183✔
3597
    return &Executor::ins_take_global;
183✔
3598
  case INS_TAKE_LOCAL:
298✔
3599
    return &Executor::ins_take_local;
298✔
3600

3601
  case INS_GET_MEMBER_ID:
1,549✔
3602
    return &Executor::ins_get_member_id;  // test id
1,549✔
3603
  case INS_SET_MEMBER_ID:
3✔
3604
    return &Executor::ins_set_member_id;  // test id
3✔
3605
  case INS_SET_MEMBER_ID_CONSUME:
200✔
3606
    return &Executor::ins_set_member_id_consume;  // test id
200✔
3607

3608
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15✔
3609
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15✔
3610
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6✔
3611
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6✔
3612
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9✔
3613
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9✔
3614
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6✔
3615
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6✔
3616
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6✔
3617
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6✔
3618

3619
  case TOK_ADD:
2,430✔
3620
    return &Executor::ins_add;
2,430✔
3621
  case TOK_SUBTRACT:
424✔
3622
    return &Executor::ins_subtract;
424✔
3623
  case TOK_DIV:
59✔
3624
    return &Executor::ins_div;
59✔
3625
  case TOK_MULT:
229✔
3626
    return &Executor::ins_mult;
229✔
3627
  case TOK_MODULUS:
30✔
3628
    return &Executor::ins_modulus;
30✔
3629

3630
  case TOK_INSERTINTO:
5,221✔
3631
    return &Executor::ins_insert_into;
5,221✔
3632

3633
  case TOK_PLUSEQUAL:
80✔
3634
    return &Executor::ins_plusequal;
80✔
3635
  case TOK_MINUSEQUAL:
27✔
3636
    return &Executor::ins_minusequal;
27✔
3637
  case TOK_TIMESEQUAL:
24✔
3638
    return &Executor::ins_timesequal;
24✔
3639
  case TOK_DIVIDEEQUAL:
15✔
3640
    return &Executor::ins_divideequal;
15✔
3641
  case TOK_MODULUSEQUAL:
18✔
3642
    return &Executor::ins_modulusequal;
18✔
3643

3644
  case TOK_LESSTHAN:
210✔
3645
    return &Executor::ins_lessthan;
210✔
3646
  case TOK_LESSEQ:
74✔
3647
    return &Executor::ins_lessequal;
74✔
3648
  case RSV_GOTO:
1,769✔
3649
    return &Executor::ins_goto;
1,769✔
3650
  case TOK_ARRAY_SUBSCRIPT:
1,318✔
3651
    return &Executor::ins_arraysubscript;
1,318✔
3652
  case TOK_EQUAL:
951✔
3653
    return &Executor::ins_equal;
951✔
3654
  case TOK_FUNC:
12,083✔
3655
    return &Executor::ins_func;
12,083✔
3656
  case INS_CALL_METHOD:
380✔
3657
    return &Executor::ins_call_method;
380✔
3658
  case INS_CALL_METHOD_ID:
3,366✔
3659
    return &Executor::ins_call_method_id;
3,366✔
3660
  case CTRL_STATEMENTBEGIN:
×
3661
    return &Executor::ins_statementbegin;
×
3662
  case CTRL_MAKELOCAL:
3,914✔
3663
    return &Executor::ins_makelocal;
3,914✔
3664
  case INS_CHECK_MRO:
225✔
3665
    return &Executor::ins_check_mro;
225✔
3666
  case CTRL_JSR_USERFUNC:
3,914✔
3667
    return &Executor::ins_jsr_userfunc;
3,914✔
3668
  case INS_POP_PARAM:
2,909✔
3669
    return &Executor::ins_pop_param;
2,909✔
3670
  case INS_POP_PARAM_BYREF:
1,277✔
3671
    return &Executor::ins_pop_param_byref;
1,277✔
3672
  case INS_GET_ARG:
139✔
3673
    return &Executor::ins_get_arg;
139✔
3674
  case CTRL_LEAVE_BLOCK:
1,521✔
3675
    return &Executor::ins_leave_block;
1,521✔
3676
  case RSV_GOSUB:
×
3677
    return &Executor::ins_gosub;
×
3678
  case RSV_RETURN:
4,540✔
3679
    return &Executor::ins_return;
4,540✔
3680
  case RSV_EXIT:
18✔
3681
    return &Executor::ins_exit;
18✔
3682
  case INS_DECLARE_ARRAY:
69✔
3683
    return &Executor::ins_declareArray;
69✔
3684
  case TOK_UNMINUS:
3✔
3685
    return &Executor::ins_unminus;
3✔
3686
  case TOK_UNPLUS:
×
3687
    return &Executor::ins_nop;
×
3688
  case TOK_LOG_NOT:
190✔
3689
    return &Executor::ins_logical_not;
190✔
3690
  case TOK_BITWISE_NOT:
12✔
3691
    return &Executor::ins_bitwise_not;
12✔
3692
  case TOK_BSRIGHT:
15✔
3693
    return &Executor::ins_bitshift_right;
15✔
3694
  case TOK_BSLEFT:
15✔
3695
    return &Executor::ins_bitshift_left;
15✔
3696
  case TOK_BITAND:
17✔
3697
    return &Executor::ins_bitwise_and;
17✔
3698
  case TOK_BITXOR:
15✔
3699
    return &Executor::ins_bitwise_xor;
15✔
3700
  case TOK_BITOR:
15✔
3701
    return &Executor::ins_bitwise_or;
15✔
3702

3703
  case TOK_NEQ:
778✔
3704
    return &Executor::ins_notequal;
778✔
3705
  case TOK_GRTHAN:
153✔
3706
    return &Executor::ins_greaterthan;
153✔
3707
  case TOK_GREQ:
84✔
3708
    return &Executor::ins_greaterequal;
84✔
3709
  case TOK_AND:
66✔
3710
    return &Executor::ins_logical_and;
66✔
3711
  case TOK_OR:
306✔
3712
    return &Executor::ins_logical_or;
306✔
3713

3714
  case TOK_ADDMEMBER:
96✔
3715
    return &Executor::ins_addmember;
96✔
3716
  case TOK_DELMEMBER:
10✔
3717
    return &Executor::ins_removemember;
10✔
3718
  case TOK_CHKMEMBER:
78✔
3719
    return &Executor::ins_checkmember;
78✔
3720
  case INS_DICTIONARY_ADDMEMBER:
140✔
3721
    return &Executor::ins_dictionary_addmember;
140✔
3722
  case TOK_IN:
51✔
3723
    return &Executor::ins_in;
51✔
3724
  case TOK_IS:
56✔
3725
    return &Executor::ins_is;
56✔
3726
  case INS_ADDMEMBER2:
45✔
3727
    return &Executor::ins_addmember2;
45✔
3728
  case INS_ADDMEMBER_ASSIGN:
1,271✔
3729
    return &Executor::ins_addmember_assign;
1,271✔
3730
  case CTRL_PROGEND:
2,672✔
3731
    return &Executor::ins_progend;
2,672✔
3732
  case TOK_UNPLUSPLUS:
93✔
3733
    return &Executor::ins_unplusplus;
93✔
3734
  case TOK_UNMINUSMINUS:
57✔
3735
    return &Executor::ins_unminusminus;
57✔
3736
  case TOK_UNPLUSPLUS_POST:
100✔
3737
    return &Executor::ins_unplusplus_post;
100✔
3738
  case TOK_UNMINUSMINUS_POST:
49✔
3739
    return &Executor::ins_unminusminus_post;
49✔
3740
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10✔
3741
    return &Executor::ins_set_member_id_unplusplus;  // test id
10✔
3742
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13✔
3743
    return &Executor::ins_set_member_id_unminusminus;  // test id
13✔
3744
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3✔
3745
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3✔
3746
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3✔
3747
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3✔
3748
  case INS_SKIPIFTRUE_ELSE_CONSUME:
196✔
3749
    return &Executor::ins_skipiftrue_else_consume;
196✔
3750
  case TOK_INTERPOLATE_STRING:
1,812✔
3751
    return &Executor::ins_interpolate_string;
1,812✔
3752
  case TOK_FORMAT_EXPRESSION:
90✔
3753
    return &Executor::ins_format_expression;
90✔
3754
  case TOK_BOOL:
221✔
3755
    return &Executor::ins_bool;
221✔
3756
  case INS_LOGICAL_JUMP:
63✔
3757
    return &Executor::ins_logical_jump;
63✔
3758
  case INS_LOGICAL_CONVERT:
47✔
3759
    return &Executor::ins_logical_convert;
47✔
3760
  default:
×
3761
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3762
  }
3763
}
3764

3765
void Executor::sethalt( bool halt )
17✔
3766
{
3767
  halt_ = halt;
17✔
3768

3769
  if ( halt && dbg_env_ )
17✔
3770
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18✔
3771
      listener->on_halt();
9✔
3772

3773
  calcrunnable();
17✔
3774
}
17✔
3775

3776
void Executor::execInstr()
133,338,544✔
3777
{
3778
  unsigned onPC = PC;
133,338,544✔
3779
  try
3780
  {  // this is really more of a class invariant.
3781
    passert( run_ok_ );
133,338,544✔
3782
    passert( PC < nLines );
133,338,544✔
3783
    passert( !error_ );
133,338,544✔
3784
    passert( !done );
133,338,544✔
3785

3786
#ifdef NDEBUG
3787
    const Instruction& ins = prog_->instr[PC];
133,338,544✔
3788
#else
3789
    const Instruction& ins = prog_->instr.at( PC );
3790
#endif
3791
    if ( debug_level >= INSTRUCTIONS )
133,338,544✔
3792
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3793

3794
    // If `on_instruction` returns false, do not execute this instruction.
3795
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
133,338,544✔
3796
    {
3797
      return;
9✔
3798
    }
3799

3800
    ++ins.cycles;
133,338,535✔
3801
    ++prog_->instr_cycles;
133,338,535✔
3802
    ++escript_instr_cycles;
133,338,535✔
3803

3804
    ++PC;
133,338,535✔
3805

3806
    ( this->*( ins.func ) )( ins );
133,338,535✔
3807
  }
3808
  catch ( std::exception& ex )
×
3809
  {
3810
    std::string tmp =
3811
        fmt::format( "Exception in: {} PC={}: {}\n", prog_->name.get(), onPC, ex.what() );
×
3812
    if ( !run_ok_ )
×
3813
      tmp += "run_ok_ = false\n";
×
3814
    if ( PC < nLines )
×
3815
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
×
3816
    if ( error_ )
×
3817
      tmp += "error_ = true\n";
×
3818
    if ( done )
×
3819
      tmp += "done = true\n";
×
3820

3821
    seterror( true );
×
3822
    POLLOG_ERROR( tmp );
×
3823

3824
    show_context( onPC );
×
3825
  }
×
3826
#ifdef __unix__
3827
  catch ( ... )
×
3828
  {
3829
    seterror( true );
×
3830
    POLLOG_ERRORLN( "Exception in {}, PC={}: unclassified", prog_->name.get(), onPC );
×
3831

3832
    show_context( onPC );
×
3833
  }
×
3834
#endif
3835
}
3836

3837
std::string Executor::dbg_get_instruction( size_t atPC ) const
×
3838
{
3839
  std::string out;
×
3840
  dbg_get_instruction( atPC, out );
×
3841
  return out;
×
3842
}
×
3843

3844
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
264✔
3845
{
3846
  bool has_breakpoint =
3847
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
264✔
3848
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
264✔
3849
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
264✔
3850
}
264✔
3851

3852
void Executor::show_context( unsigned atPC )
×
3853
{
3854
  unsigned start, end;
3855
  if ( atPC >= 5 )
×
3856
    start = atPC - 5;
×
3857
  else
3858
    start = 0;
×
3859

3860
  end = atPC + 5;
×
3861

3862
  if ( end >= nLines )
×
3863
    end = nLines - 1;
×
3864

3865
  for ( unsigned i = start; i <= end; ++i )
×
3866
  {
3867
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
×
3868
  }
3869
}
×
3870
void Executor::show_context( std::string& os, unsigned atPC )
24✔
3871
{
3872
  unsigned start, end;
3873
  if ( atPC >= 5 )
24✔
3874
    start = atPC - 5;
24✔
3875
  else
3876
    start = 0;
×
3877

3878
  end = atPC + 5;
24✔
3879

3880
  if ( end >= nLines )
24✔
3881
    end = nLines - 1;
×
3882

3883
  for ( unsigned i = start; i <= end; ++i )
288✔
3884
  {
3885
    dbg_get_instruction( i, os );
264✔
3886
    os += '\n';
264✔
3887
  }
3888
}
24✔
3889

3890
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
38,084✔
3891
                                        const Instruction& jmp )
3892
{
3893
  // params need to be on the stack, without current objectref
3894
  ValueStack.pop_back();
38,084✔
3895
  // Push captured parameters onto the stack prior to function parameters.
3896
  for ( auto& p : funcr->captures )
43,585✔
3897
    ValueStack.push_back( p );
5,501✔
3898

3899
  auto nparams = static_cast<int>( fparams.size() );
38,084✔
3900

3901
  // Handle variadic functions special. Construct an array{} corresponding to
3902
  // the rest parameter (the last parameter for the function). The logic for the
3903
  // condition:
3904
  // - if true, the last argument in the call may not be an array{}, so we need
3905
  //   to construct one (which is why the condition is `>=` and not `>`).
3906
  // - if false, then the address we're jumping to will be a "default argument
3907
  //   address" and _not_ the user function directly, which will create the
3908
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
3909
  if ( funcr->variadic() && nparams >= funcr->numParams() )
38,084✔
3910
  {
3911
    auto num_nonrest_args = funcr->numParams() - 1;
641✔
3912

3913
    auto rest_arg = std::make_unique<ObjArray>();
641✔
3914

3915
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,661✔
3916
    {
3917
      auto& p = fparams[i];
3,020✔
3918

3919
      if ( i < num_nonrest_args )
3,020✔
3920
      {
3921
        ValueStack.push_back( p );
617✔
3922
      }
3923
      else
3924
      {
3925
        rest_arg->ref_arr.push_back( p );
2,403✔
3926
      }
3927
    }
3928
    ValueStack.emplace_back( rest_arg.release() );
641✔
3929
  }
641✔
3930
  // The array{} will be created via the regular default-parameter handling by
3931
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
3932
  // prior to jumping to the user function.
3933
  else
3934
  {
3935
    for ( auto& p : fparams )
77,610✔
3936
      ValueStack.push_back( p );
40,167✔
3937
  }
3938

3939
  // jump to function
3940
  jump( jmp.token.lval, continuation, funcr );
38,084✔
3941
  fparams.clear();
38,084✔
3942
  // switch to new block
3943
  ins_makelocal( jmp );
38,084✔
3944
}
38,084✔
3945

3946
bool Executor::exec()
2,531✔
3947
{
3948
  passert( prog_ok_ );
2,531✔
3949
  passert( !error_ );
2,531✔
3950

3951
  Clib::scripts_thread_script = scriptname();
2,531✔
3952

3953
  set_running_to_completion( true );
2,531✔
3954
  while ( runnable() )
647,974✔
3955
  {
3956
    Clib::scripts_thread_scriptPC = PC;
645,443✔
3957
    execInstr();
645,443✔
3958
  }
3959

3960
  return !error_;
2,531✔
3961
}
3962

3963
void Executor::reinitExec()
×
3964
{
3965
  PC = 0;
×
3966
  done = 0;
×
3967
  seterror( false );
×
3968

3969
  ValueStack.clear();
×
3970
  delete Locals2;
×
3971
  Locals2 = new BObjectRefVec;
×
3972

3973
  if ( !prog_ok_ )
×
3974
  {
3975
    seterror( true );
×
3976
  }
3977
}
×
3978

3979
void Executor::initForFnCall( unsigned in_PC )
842✔
3980
{
3981
#ifdef MEMORYLEAK
3982
  bool data_shown = false;
3983
#endif
3984

3985
  PC = in_PC;
842✔
3986
  done = 0;
842✔
3987
  seterror( false );
842✔
3988

3989
#ifdef MEMORYLEAK
3990
  while ( !ValueStack.empty() )
3991
  {
3992
    if ( Clib::memoryleak_debug )
3993
    {
3994
      if ( !data_shown )
3995
      {
3996
        LEAKLOG( "ValueStack... " );
3997
        data_shown = true;
3998
      }
3999

4000
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
4001
               ValueStack.back()->impptr()->sizeEstimate() );
4002
    }
4003
    ValueStack.pop_back();
4004
  }
4005
  if ( Clib::memoryleak_debug )
4006
    if ( data_shown )
4007
      LEAKLOGLN( " ...deleted" );
4008
#endif
4009

4010
  ValueStack.clear();
842✔
4011
  Locals2->clear();
842✔
4012
}
842✔
4013

4014
void Executor::pushArg( BObjectImp* arg )
1,253✔
4015
{
4016
  passert_always( arg );
1,253✔
4017
  ValueStack.emplace_back( arg );
1,253✔
4018
}
1,253✔
4019

4020
void Executor::pushArg( const BObjectRef& arg )
×
4021
{
4022
  ValueStack.push_back( arg );
×
4023
}
×
4024

4025
void Executor::addModule( ExecutorModule* module )
20,982✔
4026
{
4027
  availmodules.push_back( module );
20,982✔
4028
}
20,982✔
4029

4030

4031
ExecutorModule* Executor::findModule( const std::string& name )
3,615✔
4032
{
4033
  unsigned idx;
4034
  for ( idx = 0; idx < availmodules.size(); idx++ )
20,103✔
4035
  {
4036
    ExecutorModule* module = availmodules[idx];
20,103✔
4037
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
20,103✔
4038
      return module;
3,615✔
4039
  }
4040
  return nullptr;
×
4041
}
4042

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

4067
  return true;
4✔
4068
}
4069

4070
void Executor::detach_debugger()
×
4071
{
4072
  dbg_env_.reset();
×
4073
  sethalt( false );
×
4074
}
×
4075

4076
void Executor::print_to_debugger( const std::string& message )
21,128✔
4077
{
4078
  if ( dbg_env_ )
21,128✔
4079
  {
4080
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2✔
4081
      listener->on_print( message );
1✔
4082
  }
4083
}
21,128✔
4084

4085
void Executor::dbg_ins_trace()
×
4086
{
4087
  if ( dbg_env_ )
×
4088
  {
4089
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4090
  }
4091
  sethalt( false );
×
4092
}
×
4093
void Executor::dbg_step_into()
1✔
4094
{
4095
  if ( dbg_env_ )
1✔
4096
  {
4097
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1✔
4098
  }
4099
  sethalt( false );
1✔
4100
}
1✔
4101
void Executor::dbg_step_over()
2✔
4102
{
4103
  if ( dbg_env_ )
2✔
4104
  {
4105
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2✔
4106
  }
4107
  sethalt( false );
2✔
4108
}
2✔
4109
void Executor::dbg_step_out()
1✔
4110
{
4111
  if ( dbg_env_ )
1✔
4112
  {
4113
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1✔
4114
  }
4115
  sethalt( false );
1✔
4116
}
1✔
4117
void Executor::dbg_run()
4✔
4118
{
4119
  if ( dbg_env_ )
4✔
4120
  {
4121
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4✔
4122
  }
4123
  sethalt( false );
4✔
4124
}
4✔
4125
void Executor::dbg_break()
1✔
4126
{
4127
  if ( dbg_env_ )
1✔
4128
  {
4129
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1✔
4130
  }
4131
}
1✔
4132

4133
void Executor::dbg_setbp( unsigned atPC )
2✔
4134
{
4135
  if ( dbg_env_ )
2✔
4136
  {
4137
    dbg_env_->breakpoints.insert( atPC );
2✔
4138
  }
4139
}
2✔
4140
void Executor::dbg_clrbp( unsigned atPC )
×
4141
{
4142
  if ( dbg_env_ )
×
4143
  {
4144
    dbg_env_->breakpoints.erase( atPC );
×
4145
  }
4146
}
×
4147

4148
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1✔
4149
{
4150
  if ( dbg_env_ )
1✔
4151
  {
4152
    std::set<unsigned> result;
1✔
4153
    auto& breakpoints = dbg_env_->breakpoints;
1✔
4154
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1✔
4155
                         std::inserter( result, result.end() ) );
4156
    breakpoints = result;
1✔
4157
  }
1✔
4158
}
1✔
4159

4160
void Executor::dbg_clrallbp()
×
4161
{
4162
  if ( dbg_env_ )
×
4163
  {
4164
    dbg_env_->breakpoints.clear();
×
4165
  }
4166
}
×
4167

4168
size_t Executor::sizeEstimate() const
1,999✔
4169
{
4170
  size_t size = sizeof( *this );
1,999✔
4171
  size += Clib::memsize( upperLocals2 );
1,999✔
4172
  for ( const auto& bobjectrefvec : upperLocals2 )
1,999✔
4173
  {
4174
    size += Clib::memsize( *bobjectrefvec );
×
4175
    for ( const auto& bojectref : *bobjectrefvec )
×
4176
    {
4177
      if ( bojectref != nullptr )
×
4178
        size += bojectref->sizeEstimate();
×
4179
    }
4180
  }
4181
  size += Clib::memsize( ControlStack );
1,999✔
4182

4183
  size += Clib::memsize( *Locals2 );
1,999✔
4184
  for ( const auto& bojectref : *Locals2 )
2,107✔
4185
  {
4186
    if ( bojectref != nullptr )
108✔
4187
      size += bojectref->sizeEstimate();
108✔
4188
  }
4189
  size += Clib::memsize( *Globals2 );
1,999✔
4190
  for ( const auto& bojectref : *Globals2 )
4,134✔
4191
  {
4192
    if ( bojectref != nullptr )
2,135✔
4193
      size += bojectref->sizeEstimate();
2,135✔
4194
  }
4195
  size += Clib::memsize( ValueStack );
1,999✔
4196
  for ( const auto& bojectref : ValueStack )
2,037✔
4197
  {
4198
    if ( bojectref != nullptr )
38✔
4199
      size += bojectref->sizeEstimate();
38✔
4200
  }
4201
  size += Clib::memsize( fparams );
1,999✔
4202
  for ( const auto& bojectref : fparams )
1,999✔
4203
  {
4204
    if ( bojectref != nullptr )
×
4205
      size += bojectref->sizeEstimate();
×
4206
  }
4207
  for ( const auto& module : availmodules )
16,160✔
4208
  {
4209
    if ( module != nullptr )
14,161✔
4210
      size += module->sizeEstimate();
14,161✔
4211
  }
4212
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
1,999✔
4213
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
1,999✔
4214
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
1,999✔
4215
  size += Clib::memsize( class_methods );
1,999✔
4216
  return size;
1,999✔
4217
}
4218

4219
bool Executor::builtinMethodForced( const char*& methodname )
7✔
4220
{
4221
  if ( methodname[0] == '_' )
7✔
4222
  {
4223
    ++methodname;
×
4224
    return true;
×
4225
  }
4226
  return false;
7✔
4227
}
4228

4229
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
35,102✔
4230
{
4231
  auto* func = continuation->func();
35,102✔
4232

4233
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4234
  // there are too many
4235
  while ( func->numParams() > static_cast<int>( args.size() ) )
35,108✔
4236
  {
4237
    args.emplace_back( UninitObject::create() );
6✔
4238
  }
4239

4240
  // Resize args only for non-varadic functions
4241
  if ( !func->variadic() )
35,102✔
4242
    args.resize( func->numParams() );
35,078✔
4243

4244
  continuation->args = std::move( args );
35,102✔
4245

4246
  return continuation;
35,102✔
4247
}
4248

4249
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
1,658✔
4250
{
4251
  // Compare the program pointers
4252
  if ( prog < other.prog )
1,658✔
4253
    return true;
×
4254
  if ( prog > other.prog )
1,658✔
4255
    return false;
×
4256

4257
  // Compare the indices
4258
  if ( index < other.index )
1,658✔
4259
    return true;
174✔
4260
  if ( index > other.index )
1,484✔
4261
    return false;
69✔
4262

4263
  // Perform a case-insensitive comparison for method_name using stricmp
4264
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
1,415✔
4265
}
4266

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

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