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

polserver / polserver / 23132103023

16 Mar 2026 07:11AM UTC coverage: 60.692% (+0.2%) from 60.541%
23132103023

push

github

web-flow
Non blocking gump cmd (#871)

* refactor gump pkt creation

* testclient returns buttonid 1 pressed when 'button' is contained in the
commands

* extended test

* cleanup destructor mess a bit

* store gumps as pair to decide if the executor needs to be revived
more refactoring

* disallow copy, if used creates nasty bugs

* helper method to convert imp to character

* gumpevent

* new non-blocking dialog function, sends event instead

* test closegump

* test if serialnumber gets correctly converted and invalid entries get
skipped

* removed unused BApplicPtr class
added BApplicObjBase to impptrIf convert function

* use impptrIf

* CloseGump accepts now also an array of characters
extended tests

* moved deleted copy constructor to the public part

* by default clang tidy will check .inc files, removed this filter

* the last tidy run with modernize-loop wasnt added to the PR check

* keep vector with chr and gumpid per uomod
to prevent growing dont add to the cache if same chr with same id
already exists.

* docs

* core-changes

213 of 345 new or added lines in 9 files covered. (61.74%)

19 existing lines in 6 files now uncovered.

44514 of 73344 relevant lines covered (60.69%)

464879.85 hits per line

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

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

15

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

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

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

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

57

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

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

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

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

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

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

180
  return true;
296✔
181
}
182

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

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

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

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

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

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

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

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

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

289
int Executor::getParams( unsigned howMany )
31,191,782✔
290
{
291
  if ( howMany )
31,191,782✔
292
  {
293
    fparams.resize( howMany );
31,179,205✔
294
    for ( int i = howMany - 1; i >= 0; --i )
62,429,691✔
295
    {
296
      if ( ValueStack.empty() )
31,250,486✔
297
      {
298
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
299
        seterror( true );
×
300
        return -1;
×
301
      }
302
      fparams[i] = ValueStack.back();
31,250,486✔
303
      ValueStack.pop_back();
31,250,486✔
304
    }
305
  }
306
  expandParams();
31,191,782✔
307
  return 0;
31,191,782✔
308
}
309

310
void Executor::expandParams()
31,191,782✔
311
{
312
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
62,443,495✔
313
  {
314
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
31,251,713✔
315
    {
316
      // defer destruction
317
      BObjectRef obj( spread );
628✔
318

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

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

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

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

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

341
void Executor::cleanParams()
31,189,448✔
342
{
343
  fparams.clear();
31,189,448✔
344
}
31,189,448✔
345

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

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

356
  return 0;
×
357
}
358

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

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

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

380
  return 0;
×
381
}
382

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

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

407
  return fparams[param].get();
31,108,480✔
408
}
409

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

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

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

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

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

436
  passert( imp != nullptr );
41,106✔
437

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

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

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

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

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

476

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

588
  return false;
51✔
589
}
590

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

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

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

612
  return nullptr;
×
613
}
614

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

780
  return false;
×
781
}
782

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

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

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

815
BObjectRef& Executor::GlobalVar( unsigned int varnum )
×
816
{
817
  passert( varnum < Globals2->size() );
×
818
  return ( *Globals2 )[varnum];
×
819
}
820

821
int Executor::getToken( Token& token, unsigned position )
×
822
{
823
  if ( position >= nLines )
×
824
    return -1;
×
825
  token = prog_->instr[position].token;
×
826
  return 0;
×
827
}
828

829

830
bool Executor::setProgram( EScriptProgram* i_prog )
2,272✔
831
{
832
  prog_.set( i_prog );
2,272✔
833
  prog_ok_ = false;
2,272✔
834
  seterror( true );
2,272✔
835
  if ( !viewmode_ )
2,272✔
836
  {
837
    if ( !AttachFunctionalityModules() )
2,272✔
838
      return false;
×
839
  }
840

841
  nLines = static_cast<unsigned int>( prog_->instr.size() );
2,272✔
842

843
  Globals2->clear();
2,272✔
844
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
4,557✔
845
  {
846
    Globals2->emplace_back( UninitObject::create() );
2,285✔
847
  }
848

849
  prog_ok_ = true;
2,272✔
850
  seterror( false );
2,272✔
851
  ++prog_->invocations;
2,272✔
852
  return true;
2,272✔
853
}
854

855
BObjectRef Executor::getObjRef()
62,397✔
856
{
857
  if ( ValueStack.empty() )
62,397✔
858
  {
859
    POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
860
    seterror( true );
×
861
    return BObjectRef( UninitObject::create() );
×
862
  }
863

864
  BObjectRef ref = ValueStack.back();
62,397✔
865
  ValueStack.pop_back();
62,397✔
866
  return ref;
62,397✔
867
}
62,397✔
868

869

870
void Executor::execFunc( const Token& token )
31,144,289✔
871
{
872
  FunctionalityModule* fm = prog_->modules[token.module];
31,144,289✔
873
  ModuleFunction* modfunc = fm->functions[token.lval];
31,144,289✔
874
  current_module_function = modfunc;
31,144,289✔
875
  if ( modfunc->funcidx == -1 )
31,144,289✔
876
  {
877
    DEBUGLOGLN(
×
878
        "Error in script '{}':\n"
879
        "\tModule Function {} was not found.",
880
        prog_->name.get(), modfunc->name.get() );
×
881

882
    throw std::runtime_error( "No implementation for function found." );
×
883
  }
884

885
  ExecutorModule* em = execmodules[token.module];
31,144,289✔
886

887
  func_result_ = nullptr;
31,144,289✔
888
  BObjectImp* resimp;
889
  {
890
    ESCRIPT_PROFILER( em, modfunc, fparams );
891
    resimp = em->execFunc( modfunc->funcidx );
31,144,289✔
892
  }
893
  if ( func_result_ )
31,144,289✔
894
  {
895
    if ( resimp )
8✔
896
    {
897
      BObject obj( resimp );
8✔
898
    }
8✔
899
    ValueStack.emplace_back( func_result_ );
8✔
900
    func_result_ = nullptr;
8✔
901
  }
902
  else if ( resimp )
31,144,281✔
903
  {
904
    ValueStack.emplace_back( resimp );
31,144,280✔
905
  }
906
  else
907
  {
908
    ValueStack.emplace_back( UninitObject::create() );
1✔
909
  }
910

911
  current_module_function = nullptr;
31,144,289✔
912
}
31,144,289✔
913

914
// RSV_LOCAL
915
void Executor::ins_makeLocal( const Instruction& /*ins*/ )
23,951✔
916
{
917
  passert( Locals2 != nullptr );
23,951✔
918

919
  Locals2->emplace_back( UninitObject::create() );
23,951✔
920

921
  ValueStack.emplace_back( Locals2->back().get() );
23,951✔
922
}
23,951✔
923

924
// RSV_DECLARE_ARRAY
925
void Executor::ins_declareArray( const Instruction& /*ins*/ )
69✔
926
{
927
  BObjectRef objref = getObjRef();
69✔
928

929
  if ( !objref->isa( BObjectImp::OTUninit ) )
69✔
930
  {
931
    // FIXME: weak error message
932
    ERROR_PRINTLN( "variable is already initialized.." );
×
933
    seterror( true );
×
934
    return;
×
935
  }
936
  objref->setimp( new ObjArray );
69✔
937

938
  ValueStack.emplace_back( objref );
69✔
939
}
69✔
940

941
void Executor::popParam( const Token& /*token*/ )
48,847✔
942
{
943
  BObjectRef objref = getObjRef();
48,847✔
944

945
  Locals2->emplace_back( objref->impptr()->copy() );
48,847✔
946
}
48,847✔
947

948
void Executor::popParamByRef( const Token& /*token*/ )
7,981✔
949
{
950
  BObjectRef objref = getObjRef();
7,981✔
951

952
  Locals2->emplace_back( objref );
7,981✔
953
}
7,981✔
954

955
void Executor::getArg( const Token& /*token*/ )
439✔
956
{
957
  if ( ValueStack.empty() )
439✔
958
  {
959
    Locals2->emplace_back( UninitObject::create() );
47✔
960
  }
961
  else
962
  {
963
    BObjectRef objref = getObjRef();
392✔
964
    Locals2->emplace_back( objref->impptr()->copy() );
392✔
965
  }
392✔
966
}
439✔
967

968

969
BObjectRef Executor::addmember( BObject& left, const BObject& right )
168✔
970
{
971
  if ( !right.isa( BObjectImp::OTString ) )
168✔
972
  {
973
    return BObjectRef( left.clone() );
×
974
  }
975

976
  const String& varname = right.impref<const String>();
168✔
977

978
  return left.impref().operDotPlus( varname.data() );
168✔
979
}
980

981
BObjectRef Executor::removemember( BObject& left, const BObject& right )
79✔
982
{
983
  if ( !right.isa( BObjectImp::OTString ) )
79✔
984
  {
985
    return BObjectRef( left.clone() );
×
986
  }
987

988
  const String& varname = right.impref<const String>();
79✔
989

990
  return left.impref().operDotMinus( varname.data() );
79✔
991
}
992

993
BObjectRef Executor::checkmember( BObject& left, const BObject& right )
1,627✔
994
{
995
  if ( !right.isa( BObjectImp::OTString ) )
1,627✔
996
  {
997
    return BObjectRef( left.clone() );
×
998
  }
999

1000
  const String& varname = right.impref<const String>();
1,627✔
1001

1002
  return left.impref().operDotQMark( varname.data() );
1,627✔
1003
}
1004

1005

1006
ContIterator::ContIterator() : BObjectImp( BObjectImp::OTUnknown ) {}
2,955✔
1007
BObject* ContIterator::step()
18✔
1008
{
1009
  return nullptr;
18✔
1010
}
1011
BObjectImp* ContIterator::copy() const
×
1012
{
1013
  return nullptr;
×
1014
}
1015
size_t ContIterator::sizeEstimate() const
12✔
1016
{
1017
  return sizeof( ContIterator );
12✔
1018
}
1019
std::string ContIterator::getStringRep() const
×
1020
{
1021
  return "<iterator>";
×
1022
}
1023

1024
class ArrayIterator final : public ContIterator
1025
{
1026
public:
1027
  ArrayIterator( ObjArray* pArr, BObject* pIterVal );
1028
  BObject* step() override;
1029

1030
private:
1031
  size_t m_Index;
1032
  BObject m_Array;
1033
  ObjArray* m_pArray;
1034
  BObjectRef m_IterVal;
1035
  BLong* m_pIterVal;
1036
};
1037
ArrayIterator::ArrayIterator( ObjArray* pArr, BObject* pIterVal )
2,738✔
1038
    : ContIterator(),
1039
      m_Index( 0 ),
2,738✔
1040
      m_Array( pArr ),
2,738✔
1041
      m_pArray( pArr ),
2,738✔
1042
      m_IterVal( pIterVal ),
2,738✔
1043
      m_pIterVal( new BLong( 0 ) )
5,476✔
1044
{
1045
  m_IterVal.get()->setimp( m_pIterVal );
2,738✔
1046
}
2,738✔
1047
BObject* ArrayIterator::step()
24,529✔
1048
{
1049
  m_pIterVal->increment();
24,529✔
1050
  if ( ++m_Index > m_pArray->ref_arr.size() )
24,529✔
1051
    return nullptr;
2,634✔
1052

1053
  BObjectRef& objref = m_pArray->ref_arr[m_Index - 1];
21,895✔
1054
  BObject* elem = objref.get();
21,895✔
1055
  if ( elem == nullptr )
21,895✔
1056
  {
1057
    elem = new BObject( UninitObject::create() );
18✔
1058
    objref.set( elem );
18✔
1059
  }
1060
  return elem;
21,895✔
1061
}
1062

1063
ContIterator* BObjectImp::createIterator( BObject* /*pIterVal*/ )
24✔
1064
{
1065
  return new ContIterator();
24✔
1066
}
1067
ContIterator* ObjArray::createIterator( BObject* pIterVal )
2,738✔
1068
{
1069
  auto pItr = new ArrayIterator( this, pIterVal );
2,738✔
1070
  return pItr;
2,738✔
1071
}
1072

1073
/* Coming into initforeach, the expr to be iterated through is on the value stack.
1074
   Initforeach must create three local variables:
1075
   0. the iterator
1076
   1. the expression
1077
   2. the counter
1078
   and remove the expression from the value stack.
1079
   It then jumps to the STEPFOREACH instruction.
1080
   */
1081
void Executor::ins_initforeach( const Instruction& ins )
687✔
1082
{
1083
  Locals2->emplace_back( UninitObject::create() );  // the iterator
687✔
1084

1085
  auto pIterVal = new BObject( UninitObject::create() );
687✔
1086

1087
  // this is almost like popParam, only we don't want a copy.
1088
  BObjectRef objref = getObjRef();
687✔
1089
  ContIterator* pIter = objref->impptr()->createIterator( pIterVal );
687✔
1090
  Locals2->emplace_back( pIter );
687✔
1091

1092
  Locals2->emplace_back( pIterVal );
687✔
1093

1094
  // Jump to to the corresponding `stepforeach` instruction, advancing the iterator.
1095
  PC = ins.token.lval;
687✔
1096
}
687✔
1097

1098
void Executor::ins_stepforeach( const Instruction& ins )
18,450✔
1099
{
1100
  size_t locsize = Locals2->size();
18,450✔
1101
  ContIterator* pIter = ( *Locals2 )[locsize - 2]->impptr<ContIterator>();
18,450✔
1102

1103
  BObject* next = pIter->step();
18,450✔
1104
  // If iterator has a value, set it on the locals stack and jump to the
1105
  // corresponding instruction after `initforeach`.
1106
  if ( next != nullptr )
18,450✔
1107
  {
1108
    ( *Locals2 )[locsize - 3].set( next );
17,840✔
1109
    PC = ins.token.lval;
17,840✔
1110
  }
1111
}
18,450✔
1112

1113
/*
1114
    Coming into the INITFOR, there will be two values on the value stack:
1115
    START VALUE
1116
    END VALUE
1117

1118
    If START VALUE > END VALUE, we skip the whole for loop.
1119
    (the INITFOR's lval has the instr to jump to)
1120
    */
1121
void Executor::ins_initfor( const Instruction& ins )
67✔
1122
{
1123
  BObjectRef endref = getObjRef();
67✔
1124
  BObjectRef startref = getObjRef();
67✔
1125
  if ( *startref.get() > *endref.get() )
67✔
1126
  {
1127
    PC = ins.token.lval;
×
1128
    return;
×
1129
  }
1130

1131
  Locals2->emplace_back( startref->clone() );  // the iterator
67✔
1132
  Locals2->emplace_back( endref->clone() );
67✔
1133
}
67✔
1134

1135
void Executor::ins_nextfor( const Instruction& ins )
6,248✔
1136
{
1137
  size_t locsize = Locals2->size();
6,248✔
1138
  BObjectImp* itr = ( *Locals2 )[locsize - 2]->impptr();
6,248✔
1139
  BObjectImp* end = ( *Locals2 )[locsize - 1]->impptr();
6,248✔
1140

1141
  if ( auto* l = impptrIf<BLong>( itr ) )
6,248✔
1142
    l->increment();
6,248✔
1143
  else if ( auto* d = impptrIf<Double>( itr ) )
×
1144
    d->increment();
×
1145

1146
  if ( *end >= *itr )
6,248✔
1147
  {
1148
    PC = ins.token.lval;
6,191✔
1149
  }
1150
}
6,248✔
1151

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

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

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

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

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

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

1353

1354
void Executor::ins_jmpiftrue( const Instruction& ins )
3,189✔
1355
{
1356
  BObjectRef& objref = ValueStack.back();
3,189✔
1357

1358
  if ( objref->impptr()->isTrue() )
3,189✔
1359
    PC = (unsigned)ins.token.lval;
3,046✔
1360

1361
  ValueStack.pop_back();
3,189✔
1362
}
3,189✔
1363

1364
void Executor::ins_jmpiffalse( const Instruction& ins )
37,662✔
1365
{
1366
  BObjectRef& objref = ValueStack.back();
37,662✔
1367

1368
  if ( !objref->impptr()->isTrue() )
37,662✔
1369
    PC = (unsigned)ins.token.lval;
28,002✔
1370

1371
  ValueStack.pop_back();
37,662✔
1372
}
37,662✔
1373

1374
void Executor::ins_interpolate_string( const Instruction& ins )
10,418✔
1375
{
1376
  auto count = ins.token.lval;
10,418✔
1377
  if ( count == 0 )
10,418✔
1378
  {
1379
    ValueStack.emplace_back( new String( "" ) );
3✔
1380
  }
1381
  else
1382
  {
1383
    size_t length = 0;
10,415✔
1384

1385
    std::vector<std::string> contents;
10,415✔
1386
    contents.reserve( count );
10,415✔
1387

1388
    while ( count-- )
37,694✔
1389
    {
1390
      BObjectRef rightref = ValueStack.back();
27,279✔
1391
      ValueStack.pop_back();
27,279✔
1392
      auto str = rightref->impptr()->getStringRep();
27,279✔
1393
      length += str.length();
27,279✔
1394
      contents.push_back( std::move( str ) );
27,279✔
1395
    }
27,279✔
1396

1397
    std::string joined;
10,415✔
1398
    joined.reserve( length );
10,415✔
1399

1400
    while ( !contents.empty() )
37,694✔
1401
    {
1402
      joined += contents.back();
27,279✔
1403
      contents.pop_back();
27,279✔
1404
    }
1405

1406
    ValueStack.emplace_back( new String( joined ) );
10,415✔
1407
  }
10,415✔
1408
}
10,418✔
1409

1410
void Executor::ins_format_expression( const Instruction& )
204✔
1411
{
1412
  BObjectRef formatref = ValueStack.back();
204✔
1413
  ValueStack.pop_back();
204✔
1414
  BObjectRef& exprref = ValueStack.back();
204✔
1415
  BObject& expr = *exprref;
204✔
1416

1417
  auto format = formatref->impptr()->getFormattedStringRep();
204✔
1418
  auto formatted = Bscript::get_formatted( expr.impptr(), format );
204✔
1419

1420
  exprref.set( new String( formatted ) );
204✔
1421
}
204✔
1422

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

1437
  if ( objref->impptr()->isTrue() )
1,862✔
1438
  {
1439
    PC = PC + (unsigned)( ins.token.lval );
850✔
1440
  }
1441
  else
1442
  {
1443
    ValueStack.pop_back();
1,012✔
1444
  }
1445
}
1,862✔
1446

1447

1448
// case TOK_LOCALVAR:
1449
void Executor::ins_localvar( const Instruction& ins )
185,657✔
1450
{
1451
  ValueStack.push_back( ( *Locals2 )[ins.token.lval] );
185,657✔
1452
}
185,657✔
1453

1454
// case RSV_GLOBAL:
1455
// case TOK_GLOBALVAR:
1456
void Executor::ins_globalvar( const Instruction& ins )
35,906✔
1457
{
1458
  ValueStack.push_back( ( *Globals2 )[ins.token.lval] );
35,906✔
1459
}
35,906✔
1460

1461
// case TOK_LONG:
1462
void Executor::ins_long( const Instruction& ins )
31,210,105✔
1463
{
1464
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
31,210,105✔
1465
}
31,210,105✔
1466

1467
// case TOK_BOOL:
1468
void Executor::ins_bool( const Instruction& ins )
364✔
1469
{
1470
  ValueStack.emplace_back( new BBoolean( ins.token.lval ) );
364✔
1471
}
364✔
1472

1473
// case TOK_CONSUMER:
1474
void Executor::ins_consume( const Instruction& /*ins*/ )
31,181,939✔
1475
{
1476
  ValueStack.pop_back();
31,181,939✔
1477
}
31,181,939✔
1478

1479
void Executor::ins_set_member( const Instruction& ins )
3✔
1480
{
1481
  BObjectRef rightref = ValueStack.back();
3✔
1482
  ValueStack.pop_back();
3✔
1483
  BObjectRef& leftref = ValueStack.back();
3✔
1484

1485
  BObject& right = *rightref;
3✔
1486
  BObject& left = *leftref;
3✔
1487

1488
  BObjectImp& rightimpref = right.impref();
3✔
1489
  left.impref().set_member( ins.token.tokval(), &rightimpref,
6✔
1490
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
3✔
1491
}
3✔
1492

1493
void Executor::ins_set_member_id( 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_id( ins.token.lval, &rightimpref,
6✔
1504
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
3✔
1505
}
3✔
1506

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

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

1516
  BObjectImp& rightimpref = right.impref();
456✔
1517
  left.impref().set_member( ins.token.tokval(), &rightimpref,
912✔
1518
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
456✔
1519
  ValueStack.pop_back();
456✔
1520
}
456✔
1521

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

1528
  BObject& right = *rightref;
225✔
1529
  BObject& left = *leftref;
225✔
1530

1531
  BObjectImp& rightimpref = right.impref();
225✔
1532

1533
  left.impref().set_member_id( ins.token.lval, &rightimpref,
450✔
1534
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
225✔
1535
  ValueStack.pop_back();
225✔
1536
}
225✔
1537

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

1544
  BObject& right = *rightref;
15✔
1545
  BObject& left = *leftref;
15✔
1546

1547
  BObjectImp& leftimpref = left.impref();
15✔
1548

1549
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15✔
1550

1551
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
21✔
1552
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1553
  {
1554
    tmp->impref().operPlusEqual( *tmp, right.impref() );
6✔
1555
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1556
  }
1557
  ValueStack.pop_back();
15✔
1558
}
15✔
1559

1560
void Executor::ins_set_member_id_consume_minusequal( const Instruction& ins )
6✔
1561
{
1562
  BObjectRef rightref = ValueStack.back();
6✔
1563
  ValueStack.pop_back();
6✔
1564
  BObjectRef& leftref = ValueStack.back();
6✔
1565

1566
  BObject& right = *rightref;
6✔
1567
  BObject& left = *leftref;
6✔
1568

1569
  BObjectImp& leftimpref = left.impref();
6✔
1570

1571
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1572

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

1582
void Executor::ins_set_member_id_consume_timesequal( const Instruction& ins )
9✔
1583
{
1584
  BObjectRef rightref = ValueStack.back();
9✔
1585
  ValueStack.pop_back();
9✔
1586
  BObjectRef& leftref = ValueStack.back();
9✔
1587

1588
  BObject& right = *rightref;
9✔
1589
  BObject& left = *leftref;
9✔
1590

1591
  BObjectImp& leftimpref = left.impref();
9✔
1592

1593
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
9✔
1594

1595
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
18✔
1596
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
9✔
1597
  {
1598
    tmp->impref().operTimesEqual( *tmp, right.impref() );
9✔
1599
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
9✔
1600
  }
1601
  ValueStack.pop_back();
9✔
1602
}
9✔
1603

1604
void Executor::ins_set_member_id_consume_divideequal( const Instruction& ins )
6✔
1605
{
1606
  BObjectRef rightref = ValueStack.back();
6✔
1607
  ValueStack.pop_back();
6✔
1608
  BObjectRef& leftref = ValueStack.back();
6✔
1609

1610
  BObject& right = *rightref;
6✔
1611
  BObject& left = *leftref;
6✔
1612

1613
  BObjectImp& leftimpref = left.impref();
6✔
1614

1615
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1616

1617
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1618
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1619
  {
1620
    tmp->impref().operDivideEqual( *tmp, right.impref() );
6✔
1621
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1622
  }
1623
  ValueStack.pop_back();
6✔
1624
}
6✔
1625

1626
void Executor::ins_set_member_id_consume_modulusequal( const Instruction& ins )
6✔
1627
{
1628
  BObjectRef rightref = ValueStack.back();
6✔
1629
  ValueStack.pop_back();
6✔
1630
  BObjectRef& leftref = ValueStack.back();
6✔
1631

1632
  BObject& right = *rightref;
6✔
1633
  BObject& left = *leftref;
6✔
1634

1635
  BObjectImp& leftimpref = left.impref();
6✔
1636

1637
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1638

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

1648
void Executor::ins_get_member( const Instruction& ins )
4,461✔
1649
{
1650
  BObjectRef& leftref = ValueStack.back();
4,461✔
1651
  BObject& left = *leftref;
4,461✔
1652
  {
1653
    ESCRIPT_PROFILER( ins, leftref, fparams );
1654
    leftref = left->get_member( ins.token.tokval() );
4,461✔
1655
  }
1656
}
4,461✔
1657

1658
void Executor::ins_get_member_id( const Instruction& ins )
15,298✔
1659
{
1660
  BObjectRef& leftref = ValueStack.back();
15,298✔
1661
  BObject& left = *leftref;
15,298✔
1662
  {
1663
    ESCRIPT_PROFILER( ins, leftref, fparams );
1664
    leftref = left->get_member_id( ins.token.lval );
15,298✔
1665
  }
1666
}
15,298✔
1667

1668
void Executor::ins_assign_localvar( const Instruction& ins )
6,288✔
1669
{
1670
  BObjectRef& lvar = ( *Locals2 )[ins.token.lval];
6,288✔
1671

1672
  BObjectRef& rightref = ValueStack.back();
6,288✔
1673

1674
  BObject& right = *rightref;
6,288✔
1675

1676
  BObjectImp& rightimpref = right.impref();
6,288✔
1677

1678
  if ( right.count() == 1 && rightimpref.count() == 1 )
6,288✔
1679
  {
1680
    lvar->setimp( &rightimpref );
5,536✔
1681
  }
1682
  else
1683
  {
1684
    lvar->setimp( rightimpref.copy() );
752✔
1685
  }
1686
  ValueStack.pop_back();
6,288✔
1687
}
6,288✔
1688
void Executor::ins_assign_globalvar( const Instruction& ins )
1,103✔
1689
{
1690
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
1,103✔
1691

1692
  BObjectRef& rightref = ValueStack.back();
1,103✔
1693

1694
  BObject& right = *rightref;
1,103✔
1695

1696
  BObjectImp& rightimpref = right.impref();
1,103✔
1697

1698
  if ( right.count() == 1 && rightimpref.count() == 1 )
1,103✔
1699
  {
1700
    gvar->setimp( &rightimpref );
1,026✔
1701
  }
1702
  else
1703
  {
1704
    gvar->setimp( rightimpref.copy() );
77✔
1705
  }
1706
  ValueStack.pop_back();
1,103✔
1707
}
1,103✔
1708

1709
// case INS_ASSIGN_CONSUME:
1710
void Executor::ins_assign_consume( const Instruction& /*ins*/ )
66✔
1711
{
1712
  BObjectRef rightref = ValueStack.back();
66✔
1713
  ValueStack.pop_back();
66✔
1714
  BObjectRef& leftref = ValueStack.back();
66✔
1715

1716
  BObject& right = *rightref;
66✔
1717
  BObject& left = *leftref;
66✔
1718

1719
  BObjectImp& rightimpref = right.impref();
66✔
1720

1721
  if ( right.count() == 1 && rightimpref.count() == 1 )
66✔
1722
  {
1723
    left.setimp( &rightimpref );
63✔
1724
  }
1725
  else
1726
  {
1727
    left.setimp( rightimpref.copy() );
3✔
1728
  }
1729
  ValueStack.pop_back();
66✔
1730
}
66✔
1731

1732
void Executor::ins_assign( const Instruction& /*ins*/ )
28,738✔
1733
{
1734
  /*
1735
      These each take two operands, and replace them with one.
1736
      We'll leave the second one on the value stack, and
1737
      just replace its object with the result
1738
      */
1739
  BObjectRef rightref = ValueStack.back();
28,738✔
1740
  ValueStack.pop_back();
28,738✔
1741
  BObjectRef& leftref = ValueStack.back();
28,738✔
1742

1743
  BObject& right = *rightref;
28,738✔
1744
  BObject& left = *leftref;
28,738✔
1745

1746
  BObjectImp& rightimpref = right.impref();
28,738✔
1747

1748
  if ( right.count() == 1 && rightimpref.count() == 1 )
28,738✔
1749
  {
1750
    left.setimp( &rightimpref );
27,147✔
1751
  }
1752
  else
1753
  {
1754
    left.setimp( rightimpref.copy() );
1,591✔
1755
  }
1756
}
28,738✔
1757

1758
void Executor::ins_array_assign( const Instruction& /*ins*/ )
9✔
1759
{
1760
  /*
1761
      on the value stack:
1762
      x[i] := y;
1763
      (top)
1764
      y
1765
      i
1766
      x
1767
      upon exit:
1768
      (x[i])
1769
      */
1770
  BObjectRef y_ref = ValueStack.back();
9✔
1771
  ValueStack.pop_back();
9✔
1772
  BObjectRef i_ref = ValueStack.back();
9✔
1773
  ValueStack.pop_back();
9✔
1774
  BObjectRef& x_ref = ValueStack.back();
9✔
1775

1776
  BObject& y = *y_ref;
9✔
1777
  BObject& i = *i_ref;
9✔
1778
  BObject& x = *x_ref;
9✔
1779

1780
  BObjectImp* result;
1781
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
9✔
1782

1783
  x_ref.set( result );
9✔
1784
}
9✔
1785
void Executor::ins_array_assign_consume( const Instruction& /*ins*/ )
669✔
1786
{
1787
  /*
1788
      on the value stack:
1789
      x[i] := y;
1790
      (top)
1791
      y
1792
      i
1793
      x
1794
      upon exit:
1795
      (x[i])
1796
      */
1797
  BObjectRef y_ref = ValueStack.back();
669✔
1798
  ValueStack.pop_back();
669✔
1799
  BObjectRef i_ref = ValueStack.back();
669✔
1800
  ValueStack.pop_back();
669✔
1801
  BObjectRef& x_ref = ValueStack.back();
669✔
1802

1803
  BObject& y = *y_ref;
669✔
1804
  BObject& i = *i_ref;
669✔
1805
  BObject& x = *x_ref;
669✔
1806

1807
  BObjectImp* result;
1808
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
669✔
1809

1810
  BObject obj( result );
669✔
1811
  ValueStack.pop_back();
669✔
1812
}
669✔
1813

1814
// TOK_ADD:
1815
void Executor::ins_add( const Instruction& /*ins*/ )
22,490✔
1816
{
1817
  /*
1818
      These each take two operands, and replace them with one.
1819
      We'll leave the second one on the value stack, and
1820
      just replace its object with the result
1821
      */
1822
  BObjectRef rightref = ValueStack.back();
22,490✔
1823
  ValueStack.pop_back();
22,490✔
1824
  BObjectRef& leftref = ValueStack.back();
22,490✔
1825

1826
  BObject& right = *rightref;
22,490✔
1827
  BObject& left = *leftref;
22,490✔
1828

1829
  leftref.set( right.impref().selfPlusObjImp( left.impref() ) );
22,490✔
1830
}
22,490✔
1831

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

1844
  BObject& right = *rightref;
3,480✔
1845
  BObject& left = *leftref;
3,480✔
1846

1847
  leftref.set( right.impref().selfMinusObjImp( left.impref() ) );
3,480✔
1848
}
3,480✔
1849

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

1862
  BObject& right = *rightref;
1,529✔
1863
  BObject& left = *leftref;
1,529✔
1864

1865
  leftref.set( right.impref().selfTimesObjImp( left.impref() ) );
1,529✔
1866
}
1,529✔
1867
// TOK_DIV:
1868
void Executor::ins_div( const Instruction& /*ins*/ )
332✔
1869
{
1870
  /*
1871
      These each take two operands, and replace them with one.
1872
      We'll leave the second one on the value stack, and
1873
      just replace its object with the result
1874
      */
1875
  BObjectRef rightref = ValueStack.back();
332✔
1876
  ValueStack.pop_back();
332✔
1877
  BObjectRef& leftref = ValueStack.back();
332✔
1878

1879
  BObject& right = *rightref;
332✔
1880
  BObject& left = *leftref;
332✔
1881

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

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

1899
  leftref.set( right.impref().selfModulusObjImp( left.impref() ) );
495✔
1900
}
495✔
1901

1902
// TOK_IS:
1903
void Executor::ins_is( const Instruction& /*ins*/ )
100✔
1904
{
1905
  BObjectRef rightref = ValueStack.back();
100✔
1906
  ValueStack.pop_back();
100✔
1907
  BObjectRef& leftref = ValueStack.back();
100✔
1908

1909
  BObject& right = *rightref;
100✔
1910
  BObject& left = *leftref;
100✔
1911

1912
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100✔
1913
}
100✔
1914

1915
// TOK_BSRIGHT:
1916
void Executor::ins_bitshift_right( const Instruction& /*ins*/ )
297✔
1917
{
1918
  /*
1919
      These each take two operands, and replace them with one.
1920
      We'll leave the second one on the value stack, and
1921
      just replace its object with the result
1922
      */
1923
  BObjectRef rightref = ValueStack.back();
297✔
1924
  ValueStack.pop_back();
297✔
1925
  BObjectRef& leftref = ValueStack.back();
297✔
1926

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

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

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

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

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

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

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

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

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

1998
  leftref.set( right.impref().selfBitOrObjImp( left.impref() ) );
297✔
1999
}
297✔
2000

2001
void Executor::ins_logical_and( const Instruction& /*ins*/ )
5,958✔
2002
{
2003
  /*
2004
      These each take two operands, and replace them with one.
2005
      We'll leave the second one on the value stack, and
2006
      just replace its object with the result
2007
      */
2008
  BObjectRef rightref = ValueStack.back();
5,958✔
2009
  ValueStack.pop_back();
5,958✔
2010
  BObjectRef& leftref = ValueStack.back();
5,958✔
2011

2012
  BObject& right = *rightref;
5,958✔
2013
  BObject& left = *leftref;
5,958✔
2014

2015
  int _true = ( left.isTrue() && right.isTrue() );
5,958✔
2016
  leftref.set( new BLong( _true ) );
5,958✔
2017
}
5,958✔
2018
void Executor::ins_logical_or( const Instruction& /*ins*/ )
1,677✔
2019
{
2020
  /*
2021
      These each take two operands, and replace them with one.
2022
      We'll leave the second one on the value stack, and
2023
      just replace its object with the result
2024
      */
2025
  BObjectRef rightref = ValueStack.back();
1,677✔
2026
  ValueStack.pop_back();
1,677✔
2027
  BObjectRef& leftref = ValueStack.back();
1,677✔
2028

2029
  BObject& right = *rightref;
1,677✔
2030
  BObject& left = *leftref;
1,677✔
2031

2032
  int _true = ( left.isTrue() || right.isTrue() );
1,677✔
2033
  leftref.set( new BLong( _true ) );
1,677✔
2034
}
1,677✔
2035

2036
void Executor::ins_notequal( const Instruction& /*ins*/ )
27,335✔
2037
{
2038
  /*
2039
      These each take two operands, and replace them with one.
2040
      We'll leave the second one on the value stack, and
2041
      just replace its object with the result
2042
      */
2043
  BObjectRef rightref = ValueStack.back();
27,335✔
2044
  ValueStack.pop_back();
27,335✔
2045
  BObjectRef& leftref = ValueStack.back();
27,335✔
2046

2047
  BObject& right = *rightref;
27,335✔
2048
  BObject& left = *leftref;
27,335✔
2049

2050
  int _true = ( left != right );
27,335✔
2051
  leftref.set( new BLong( _true ) );
27,335✔
2052
}
27,335✔
2053

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

2065
  BObject& right = *rightref;
8,097✔
2066
  BObject& left = *leftref;
8,097✔
2067

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

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

2083
  BObject& right = *rightref;
4,204✔
2084
  BObject& left = *leftref;
4,204✔
2085

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

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

2101
  BObject& right = *rightref;
803✔
2102
  BObject& left = *leftref;
803✔
2103
  int _true = ( left <= right );
803✔
2104
  leftref.set( new BLong( _true ) );
803✔
2105
}
803✔
2106
void Executor::ins_greaterthan( const Instruction& /*ins*/ )
5,955✔
2107
{
2108
  /*
2109
      These each take two operands, and replace them with one.
2110
      We'll leave the second one on the value stack, and
2111
      just replace its object with the result
2112
      */
2113
  BObjectRef rightref = ValueStack.back();
5,955✔
2114
  ValueStack.pop_back();
5,955✔
2115
  BObjectRef& leftref = ValueStack.back();
5,955✔
2116

2117
  BObject& right = *rightref;
5,955✔
2118
  BObject& left = *leftref;
5,955✔
2119

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

2134
  BObject& right = *rightref;
418✔
2135
  BObject& left = *leftref;
418✔
2136

2137
  int _true = ( left >= right );
418✔
2138
  leftref.set( new BLong( _true ) );
418✔
2139
}
418✔
2140

2141
// case TOK_ARRAY_SUBSCRIPT:
2142
void Executor::ins_arraysubscript( const Instruction& /*ins*/ )
25,762✔
2143
{
2144
  /*
2145
      These each take two operands, and replace them with one.
2146
      We'll leave the second one on the value stack, and
2147
      just replace its object with the result
2148
      */
2149
  BObjectRef rightref = ValueStack.back();
25,762✔
2150
  ValueStack.pop_back();
25,762✔
2151
  BObjectRef& leftref = ValueStack.back();
25,762✔
2152

2153
  leftref = ( *leftref )->OperSubscript( *rightref );
25,762✔
2154
}
25,762✔
2155

2156
void Executor::ins_multisubscript( const Instruction& ins )
7,229✔
2157
{
2158
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2159
  std::stack<BObjectRef> indices;
7,229✔
2160
  for ( int i = 0; i < ins.token.lval; ++i )
21,687✔
2161
  {
2162
    indices.push( ValueStack.back() );
14,458✔
2163
    ValueStack.pop_back();
14,458✔
2164
  }
2165

2166
  BObjectRef& leftref = ValueStack.back();
7,229✔
2167
  leftref = ( *leftref )->OperMultiSubscript( indices );
7,229✔
2168
}
7,229✔
2169

2170
void Executor::ins_unpack_sequence( const Instruction& ins )
104✔
2171
{
2172
  bool rest = ins.token.lval >> 14;
104✔
2173
  auto count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
104✔
2174

2175
  BObjectRef refIter( new BObject( UninitObject::create() ) );
104✔
2176

2177
  BObjectRef rightref = ValueStack.back();
104✔
2178
  ValueStack.pop_back();
104✔
2179

2180
  // Reserve to keep the insert_at iterator valid
2181
  ValueStack.reserve( ValueStack.size() + count );
104✔
2182
  auto insert_at = ValueStack.begin() + ValueStack.size();
104✔
2183
  auto pIter = std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
104✔
2184

2185
  // The default iterator is used for non-iterable objects, so we can't unpack
2186
  // them. Simply add errors for each value to unpack.
2187
  if ( pIter->is_default() )
104✔
2188
  {
2189
    if ( rest )
6✔
2190
    {
2191
      auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
3✔
2192
      for ( u8 i = 0; i < count; ++i )
12✔
2193
        ValueStack.emplace( insert_at, new BError( i == rest_index ? "Invalid type for rest binding"
9✔
2194
                                                                   : "Index out of bounds" ) );
9✔
2195
    }
2196
    else
2197
    {
2198
      for ( u8 i = 0; i < count; ++i )
9✔
2199
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6✔
2200
    }
2201
    return;
6✔
2202
  }
2203

2204
  if ( rest )
98✔
2205
  {
2206
    auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
66✔
2207

2208
    // Add all elements up to the rest index
2209
    for ( u8 i = 0; i < rest_index; ++i )
147✔
2210
    {
2211
      if ( auto res = pIter->step() )
81✔
2212
        ValueStack.emplace( insert_at, res );
75✔
2213
      else
2214
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6✔
2215
    }
2216

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

2220
    while ( auto res = pIter->step() )
306✔
2221
      rest_array->addElement( res->impptr() );
240✔
2222

2223
    // Take the remaining elements from the rest array to fill the rest of the bindings.
2224
    auto left = count - rest_index - 1;
66✔
2225

2226
    for ( u8 i = 0; i < left; ++i )
138✔
2227
    {
2228
      if ( rest_array->ref_arr.empty() )
72✔
2229
        ValueStack.emplace( insert_at + i, new BError( "Index out of bounds" ) );
12✔
2230
      else
2231
      {
2232
        ValueStack.insert( insert_at + i, rest_array->ref_arr.back() );
60✔
2233

2234
        rest_array->ref_arr.pop_back();
60✔
2235
      }
2236
    }
2237
  }
2238
  else
2239
  {
2240
    for ( u8 i = 0; i < count; ++i )
116✔
2241
    {
2242
      if ( auto res = pIter->step() )
84✔
2243
        ValueStack.emplace( insert_at, res );
84✔
2244
      else
2245
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
×
2246
    }
2247
  }
2248
}
116✔
2249

2250
void Executor::ins_unpack_indices( const Instruction& ins )
121✔
2251
{
2252
  bool rest = ins.token.lval >> 14;
121✔
2253
  auto binding_count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
121✔
2254
  // If there is a rest binding, there will be one less index than the binding
2255
  // count, as the rest binding has no corresponding element index access.
2256
  auto index_count = rest ? Clib::clamp_convert<u8>( binding_count - 1 ) : binding_count;
121✔
2257

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

2262
  if ( rest )
121✔
2263
  {
2264
    // Use a multi_index because we need to (1) iterate over the indexes in
2265
    // order of the bindings in the script, and (2) keep track of which indexes
2266
    // have been used.
2267
    using namespace boost::multi_index;
2268
    using OrderedSet =
2269
        multi_index_container<BObject,
2270
                              indexed_by<sequenced<>,  // Maintains insertion order
2271
                                         ordered_unique<identity<BObject>>  // Ensures uniqueness
2272
                                         >>;
2273

2274
    OrderedSet indexes;
39✔
2275

2276
    for ( u8 i = 0; i < index_count; ++i )
120✔
2277
    {
2278
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
81✔
2279
      ValueStack.pop_back();
81✔
2280
    }
2281

2282
    BObjectRef rightref = ValueStack.back();
39✔
2283
    ValueStack.pop_back();
39✔
2284

2285
    // Reserve to keep the insert_at iterator valid
2286
    ValueStack.reserve( ValueStack.size() + binding_count );
39✔
2287
    auto insert_at = ValueStack.end();
39✔
2288

2289
    for ( const auto& index : indexes )
120✔
2290
    {
2291
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
81✔
2292
    }
2293

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

2298
    BObjectRef refIter( UninitObject::create() );
39✔
2299
    auto pIter =
2300
        std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
39✔
2301

2302
    // The default iterator is used for non-iterable objects, so we can't unpack them.
2303
    if ( pIter->is_default() )
39✔
2304
    {
2305
      ValueStack.emplace( insert_at, new BError( "Invalid type for rest binding" ) );
15✔
2306
      return;
15✔
2307
    }
2308
    rest_obj = std::make_unique<BDictionary>();
24✔
2309

2310
    auto& unique_index = indexes.get<1>();
24✔
2311

2312
    while ( auto res = pIter->step() )
120✔
2313
    {
2314
      auto itr = unique_index.find( *refIter.get() );
96✔
2315

2316
      if ( itr == unique_index.end() )
96✔
2317
        rest_obj->array_assign( refIter->impptr(), res->impptr(), true );
45✔
2318
    }
96✔
2319
    ValueStack.emplace( insert_at, rest_obj.release() );
24✔
2320
  }
99✔
2321
  else
2322
  {
2323
    // If not using a rest binding, only keep track of binding order.
2324
    std::list<BObject> indexes;
82✔
2325

2326
    for ( u8 i = 0; i < index_count; ++i )
257✔
2327
    {
2328
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
175✔
2329
      ValueStack.pop_back();
175✔
2330
    }
2331

2332
    BObjectRef rightref = ValueStack.back();
82✔
2333
    ValueStack.pop_back();
82✔
2334

2335
    // Reserve to keep the insert_at iterator valid
2336
    ValueStack.reserve( ValueStack.size() + binding_count );
82✔
2337
    auto insert_at = ValueStack.end();
82✔
2338

2339
    for ( const auto& index : indexes )
257✔
2340
    {
2341
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
175✔
2342
    }
2343
  }
82✔
2344
}
2345

2346
void Executor::ins_take_local( const Instruction& )
388✔
2347
{
2348
  passert( Locals2 != nullptr );
388✔
2349
  passert( !ValueStack.empty() );
388✔
2350

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

2355
  BObjectRef& rightref = ValueStack.back();
388✔
2356

2357
  BObject& right = *rightref;
388✔
2358

2359
  BObjectImp& rightimpref = right.impref();
388✔
2360

2361
  if ( right.count() == 1 && rightimpref.count() == 1 )
388✔
2362
  {
2363
    lvar->setimp( &rightimpref );
244✔
2364
  }
2365
  else
2366
  {
2367
    lvar->setimp( rightimpref.copy() );
144✔
2368
  }
2369
  ValueStack.pop_back();
388✔
2370
}
388✔
2371

2372
void Executor::ins_take_global( const Instruction& ins )
183✔
2373
{
2374
  passert( !ValueStack.empty() );
183✔
2375

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

2379
  BObjectRef& rightref = ValueStack.back();
183✔
2380

2381
  BObject& right = *rightref;
183✔
2382

2383
  BObjectImp& rightimpref = right.impref();
183✔
2384

2385
  if ( right.count() == 1 && rightimpref.count() == 1 )
183✔
2386
  {
2387
    gvar->setimp( &rightimpref );
150✔
2388
  }
2389
  else
2390
  {
2391
    gvar->setimp( rightimpref.copy() );
33✔
2392
  }
2393
  ValueStack.pop_back();
183✔
2394
}
183✔
2395

2396
void Executor::ins_multisubscript_assign( const Instruction& ins )
6✔
2397
{
2398
  BObjectRef target_ref = ValueStack.back();
6✔
2399
  ValueStack.pop_back();
6✔
2400
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2401
  std::stack<BObjectRef> indices;
6✔
2402
  for ( int i = 0; i < ins.token.lval; ++i )
18✔
2403
  {
2404
    indices.push( ValueStack.back() );
12✔
2405
    ValueStack.pop_back();
12✔
2406
  }
2407

2408
  BObjectRef& leftref = ValueStack.back();
6✔
2409
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
6✔
2410
}
6✔
2411

2412
void Executor::ins_addmember( const Instruction& /*ins*/ )
168✔
2413
{
2414
  /*
2415
      These each take two operands, and replace them with one.
2416
      We'll leave the second one on the value stack, and
2417
      just replace its object with the result
2418
      */
2419
  BObjectRef rightref = ValueStack.back();
168✔
2420
  ValueStack.pop_back();
168✔
2421
  BObjectRef& leftref = ValueStack.back();
168✔
2422

2423
  BObject& right = *rightref;
168✔
2424
  BObject& left = *leftref;
168✔
2425

2426
  leftref = addmember( left, right );
168✔
2427
}
168✔
2428

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

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

2443
  leftref = removemember( left, right );
79✔
2444
}
79✔
2445

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

2457
  BObject& right = *rightref;
1,627✔
2458
  BObject& left = *leftref;
1,627✔
2459

2460
  leftref = checkmember( left, right );
1,627✔
2461
}
1,627✔
2462

2463
void Executor::ins_addmember2( const Instruction& ins )
45✔
2464
{
2465
  BObjectRef& obref = ValueStack.back();
45✔
2466

2467
  BObject& ob = *obref;
45✔
2468

2469
  ob.impref().operDotPlus( ins.token.tokval() );
45✔
2470
}
45✔
2471

2472
void Executor::ins_addmember_assign( const Instruction& ins )
1,831✔
2473
{
2474
  BObjectRef valref = ValueStack.back();
1,831✔
2475
  BObject& valob = *valref;
1,831✔
2476
  BObjectImp* valimp = valref->impptr();
1,831✔
2477

2478
  ValueStack.pop_back();
1,831✔
2479

2480
  BObjectRef& obref = ValueStack.back();
1,831✔
2481
  BObject& ob = *obref;
1,831✔
2482

2483
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
1,831✔
2484
  BObject& mem = *memref;
1,831✔
2485

2486
  if ( valob.count() == 1 && valimp->count() == 1 )
1,831✔
2487
  {
2488
    mem.setimp( valimp );
1,324✔
2489
  }
2490
  else
2491
  {
2492
    mem.setimp( valimp->copy() );
507✔
2493
  }
2494
  // the struct is at the top of the stack
2495
}
1,831✔
2496

2497
void Executor::ins_dictionary_addmember( const Instruction& /*ins*/ )
138✔
2498
{
2499
  /*
2500
      ENTRANCE: value stack:
2501
      dictionary
2502
      key
2503
      value
2504
      EXIT: value stack:
2505
      dictionary
2506
      FUNCTION:
2507
      adds the (key, value) pair to the dictionary
2508
      */
2509

2510
  BObjectRef valref = ValueStack.back();
138✔
2511
  ValueStack.pop_back();
138✔
2512
  BObject& valob = *valref;
138✔
2513
  BObjectImp* valimp = valob.impptr();
138✔
2514

2515
  BObjectRef keyref = ValueStack.back();
138✔
2516
  ValueStack.pop_back();
138✔
2517
  BObject& keyob = *keyref;
138✔
2518
  BObjectImp* keyimp = keyob.impptr();
138✔
2519

2520
  BObjectRef& dictref = ValueStack.back();
138✔
2521
  BObject& dictob = *dictref;
138✔
2522
  BDictionary* dict = dictob.impptr<BDictionary>();
138✔
2523

2524
  if ( keyob.count() != 1 || keyimp->count() != 1 )
138✔
2525
  {
2526
    keyimp = keyimp->copy();
6✔
2527
  }
2528
  if ( valob.count() != 1 || valimp->count() != 1 )
138✔
2529
  {
2530
    valimp = valimp->copy();
18✔
2531
  }
2532

2533
  dict->addMember( keyimp, valimp );
138✔
2534
  // the dictionary remains at the top of the stack.
2535
}
138✔
2536

2537
void Executor::ins_in( const Instruction& /*ins*/ )
794✔
2538
{
2539
  /*
2540
      These each take two operands, and replace them with one.
2541
      We'll leave the second one on the value stack, and
2542
      just replace its object with the result
2543
      */
2544
  BObjectRef rightref = ValueStack.back();
794✔
2545
  ValueStack.pop_back();
794✔
2546
  BObjectRef& leftref = ValueStack.back();
794✔
2547

2548
  BObject& right = *rightref;
794✔
2549
  BObject& left = *leftref;
794✔
2550

2551
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
794✔
2552
}
794✔
2553

2554
void Executor::ins_insert_into( const Instruction& /*ins*/ )
7,769✔
2555
{
2556
  BObjectRef rightref = ValueStack.back();
7,769✔
2557
  ValueStack.pop_back();
7,769✔
2558
  BObjectRef& leftref = ValueStack.back();
7,769✔
2559

2560
  BObject& right = *rightref;
7,769✔
2561
  BObject& left = *leftref;
7,769✔
2562

2563
  if ( auto* spread = right.impptr_if<BSpread>() )
7,769✔
2564
  {
2565
    BObjectRef refIter( UninitObject::create() );
1,473✔
2566

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

2570
    BObject* next = pIter->step();
1,473✔
2571
    while ( next != nullptr )
4,317✔
2572
    {
2573
      left.impref().operInsertInto( left, next->impref() );
2,844✔
2574
      next = pIter->step();
2,844✔
2575
    }
2576
  }
1,473✔
2577
  else
2578
  {
2579
    left.impref().operInsertInto( left, right.impref() );
6,296✔
2580
  }
2581
}
7,769✔
2582

2583
void Executor::ins_plusequal( const Instruction& /*ins*/ )
818✔
2584
{
2585
  /*
2586
      These each take two operands, and replace them with one.
2587
      We'll leave the second one on the value stack, and
2588
      just replace its object with the result
2589
      */
2590
  BObjectRef rightref = ValueStack.back();
818✔
2591
  ValueStack.pop_back();
818✔
2592
  BObjectRef& leftref = ValueStack.back();
818✔
2593

2594
  BObject& right = *rightref;
818✔
2595
  BObject& left = *leftref;
818✔
2596

2597
  left.impref().operPlusEqual( left, right.impref() );
818✔
2598
}
818✔
2599

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

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

2614
  left.impref().operMinusEqual( left, right.impref() );
240✔
2615
}
240✔
2616

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

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

2631
  left.impref().operTimesEqual( left, right.impref() );
237✔
2632
}
237✔
2633

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

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

2648
  left.impref().operDivideEqual( left, right.impref() );
228✔
2649
}
228✔
2650

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

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

2665
  left.impref().operModulusEqual( left, right.impref() );
375✔
2666
}
375✔
2667

2668
// case RSV_GOTO:
2669
void Executor::ins_goto( const Instruction& ins )
31,108,075✔
2670
{
2671
  PC = (unsigned)ins.token.lval;
31,108,075✔
2672
}
31,108,075✔
2673

2674
// TOK_FUNC:
2675
void Executor::ins_func( const Instruction& ins )
31,144,289✔
2676
{
2677
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
31,144,289✔
2678
  getParams( nparams );
31,144,289✔
2679
  execFunc( ins.token );
31,144,289✔
2680
  cleanParams();
31,144,289✔
2681
  return;
31,144,289✔
2682
}
2683

2684
void Executor::ins_call_method_id( const Instruction& ins )
45,848✔
2685
{
2686
  BContinuation* continuation = nullptr;
45,848✔
2687
  unsigned nparams = ins.token.type;
45,848✔
2688

2689
  do
2690
  {
2691
    getParams( nparams );
46,638✔
2692
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
46,638✔
2693
    {
2694
      Instruction jmp;
2,439✔
2695
      bool add_new_classinst = ins.token.lval == MTH_NEW;
2,439✔
2696

2697
      if ( add_new_classinst )
2,439✔
2698
      {
2699
        if ( funcr->constructor() )
108✔
2700
        {
2701
          fparams.insert( fparams.begin(),
81✔
2702
                          BObjectRef( new BConstObject( new BClassInstanceRef(
162✔
2703
                              new BClassInstance( prog_, funcr->class_index(), Globals2 ) ) ) ) );
162✔
2704
        }
2705
      }
2706

2707
      if ( funcr->validCall( continuation ? MTH_CALL : ins.token.lval, *this, &jmp ) )
2,439✔
2708
      {
2709
        BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
2,334✔
2710
        call_function_reference( funcr, continuation, jmp );
2,334✔
2711
        return;
2,334✔
2712
      }
2,334✔
2713
    }
2,439✔
2714
    // If there _was_ a continuation to be handled (from previous loop
2715
    // iteration), there must have been a FuncRef on the stack. Otherwise,
2716
    // `continuation` may leak.
2717
    passert_always( continuation == nullptr );
44,304✔
2718

2719
    size_t stacksize = ValueStack.size();  // ValueStack can grow
44,304✔
2720
    BObjectImp* imp;
2721
    {
2722
      ESCRIPT_PROFILER( ins, ValueStack.back(), fparams );
2723
      imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
44,304✔
2724

2725
      if ( auto* cont = impptrIf<BContinuation>( imp ) )
44,304✔
2726
      {
2727
        continuation = cont;
790✔
2728
        // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2729
        // move.
2730
        nparams = static_cast<unsigned int>( continuation->args.size() );
790✔
2731

2732
        // Add function reference to stack
2733
        ValueStack.emplace_back( continuation->func() );
790✔
2734

2735
        // Move all arguments to the value stack
2736
        ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
790✔
2737
                           std::make_move_iterator( continuation->args.end() ) );
2738

2739
        continuation->args.clear();
790✔
2740

2741
        cleanParams();
790✔
2742

2743
        printStack( fmt::format(
1,580✔
2744
            "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2745
            "funcref.call()",
2746
            nparams ) );
2747

2748
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2749
        // handle it.
2750

2751
        // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2752
        // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2753
        // jumps.
2754

2755
        continue;
790✔
2756
      }
2757
    }
2758

2759
    if ( impptrIf<BSpecialUserFuncJump>( imp ) )
43,514✔
2760
    {
2761
      cleanParams();
3✔
2762
      return;
3✔
2763
    }
2764
    BObjectRef& objref = ValueStack[stacksize - 1];
43,511✔
2765
    if ( func_result_ )
43,511✔
2766
    {
2767
      if ( imp )
16✔
2768
      {
2769
        BObject obj( imp );
7✔
2770
      }
7✔
2771

2772
      objref.set( func_result_ );
16✔
2773
      func_result_ = nullptr;
16✔
2774
    }
2775
    else if ( imp )
43,495✔
2776
    {
2777
      objref.set( imp );
43,441✔
2778
    }
2779
    else
2780
    {
2781
      objref.set( UninitObject::create() );
54✔
2782
    }
2783

2784
    cleanParams();
43,511✔
2785
    return;
43,511✔
2786
  }
2787
  // This condition should only ever evaluate to `true` once. In the second loop
2788
  // iteration, handling the FuncRef will return out of this method.
2789
  while ( continuation != nullptr );
790✔
2790
}
2791

2792
void Executor::ins_call_method( const Instruction& ins )
855✔
2793
{
2794
  unsigned nparams = ins.token.lval;
855✔
2795
  auto method_name = ins.token.tokval();
855✔
2796

2797
  getParams( nparams );
855✔
2798
  BObjectImp* callee = ValueStack.back()->impptr();
855✔
2799

2800
  if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
855✔
2801
  {
2802
    Instruction jmp;
×
2803
    if ( funcr->validCall( method_name, *this, &jmp ) )
×
2804
    {
2805
      BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
×
2806
      call_function_reference( funcr, nullptr, jmp );
×
2807
      return;
×
2808
    }
×
2809
  }
×
2810

2811
  size_t stacksize = ValueStack.size();  // ValueStack can grow
855✔
2812
  BObjectImp* imp;
2813
  {
2814
    ESCRIPT_PROFILER( ins, callee, method_name, fparams );
2815
#ifdef BOBJECTIMP_DEBUG
2816
    if ( strcmp( method_name, "impptr" ) == 0 )
855✔
2817
      imp = new String( fmt::format( "{}", static_cast<void*>( callee ) ) );
×
2818
    else
2819
      imp = callee->call_method( method_name, *this );
855✔
2820
#else
2821
    imp = callee->call_method( method_name, *this );
2822
#endif
2823
  }
2824

2825
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
855✔
2826
  {
2827
    cleanParams();
620✔
2828
    return;
620✔
2829
  }
2830

2831
  BObjectRef& objref = ValueStack[stacksize - 1];
235✔
2832
  if ( func_result_ )
235✔
2833
  {
2834
    if ( imp )
×
2835
    {
2836
      BObject obj( imp );
×
2837
    }
×
2838

2839
    objref.set( func_result_ );
×
2840
    func_result_ = nullptr;
×
2841
  }
2842
  else if ( imp )
235✔
2843
  {
2844
    objref.set( imp );
60✔
2845
  }
2846
  else
2847
  {
2848
    objref.set( UninitObject::create() );
175✔
2849
  }
2850
  cleanParams();
235✔
2851
}
2852

2853
// CTRL_STATEMENTBEGIN:
2854
void Executor::ins_statementbegin( const Instruction& ins )
×
2855
{
2856
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
2857
    INFO_PRINTLN( ins.token.tokval() );
×
2858
}
×
2859

2860
// case CTRL_PROGEND:
2861
void Executor::ins_progend( const Instruction& /*ins*/ )
3,078✔
2862
{
2863
  done = 1;
3,078✔
2864
  run_ok_ = false;
3,078✔
2865
  PC = 0;
3,078✔
2866
}
3,078✔
2867

2868

2869
// case CTRL_MAKELOCAL:
2870
void Executor::ins_makelocal( const Instruction& /*ins*/ )
44,353✔
2871
{
2872
  if ( Locals2 )
44,353✔
2873
    upperLocals2.push_back( Locals2 );
44,353✔
2874
  Locals2 = new BObjectRefVec;
44,353✔
2875
}
44,353✔
2876

2877
void Executor::ins_check_mro( const Instruction& ins )
280✔
2878
{
2879
  auto classinst_offset = ins.token.lval;
280✔
2880

2881
  if ( classinst_offset > static_cast<int>( ValueStack.size() ) || ValueStack.empty() )
280✔
2882
  {
2883
    POLLOG_ERRORLN( "Fatal error: Check MRO offset error! offset={}, ValueStack.size={} ({},PC={})",
×
2884
                    classinst_offset, ValueStack.size(), prog_->name, PC );
×
2885
    seterror( true );
×
2886
    return;
×
2887
  }
2888

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

2891
  if ( nLines < PC + 1 )
280✔
2892
  {
2893
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction out of bounds! nLines={} ({},PC={})",
×
2894
                    nLines, prog_->name, PC );
×
2895
    seterror( true );
×
2896
    return;
×
2897
  }
2898

2899
  const Instruction& jsr_ins = prog_->instr.at( PC + 1 );
280✔
2900
  if ( jsr_ins.func != &Executor::ins_jsr_userfunc )
280✔
2901
  {
2902
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction not followed by JSR_USERFUNC! ({},PC={})",
×
2903
                    prog_->name, PC );
×
2904
    seterror( true );
×
2905
    return;
×
2906
  }
2907

2908
  auto ctor_addr = jsr_ins.token.lval;
280✔
2909

2910
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280✔
2911

2912
  if ( classinstref != nullptr && classinstref->instance()->constructors_called.find( ctor_addr ) ==
557✔
2913
                                      classinstref->instance()->constructors_called.end() )
557✔
2914
  {
2915
    classinstref->instance()->constructors_called.insert( ctor_addr );
224✔
2916
  }
2917
  else
2918
  {
2919
    // Constructor has been called, or `this` is not a class instance: clear
2920
    // arguments and skip jump instructions (makelocal, jsr_userfunc)
2921
    ValueStack.resize( ValueStack.size() - ins.token.lval );
56✔
2922
    PC += 2;
56✔
2923
  }
2924
}
2925

2926
// CTRL_JSR_USERFUNC:
2927
void Executor::ins_jsr_userfunc( const Instruction& ins )
6,296✔
2928
{
2929
  jump( ins.token.lval, nullptr, nullptr );
6,296✔
2930
}
6,296✔
2931

2932
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
44,353✔
2933
{
2934
  ReturnContext rc;
44,353✔
2935
  rc.PC = PC;
44,353✔
2936
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
44,353✔
2937
  if ( continuation )
44,353✔
2938
  {
2939
    rc.Continuation.set( continuation );
35,890✔
2940
  }
2941

2942
  // Only store our global context if the function is external to the current program.
2943
  if ( funcref != nullptr && funcref->prog() != prog_ )
44,353✔
2944
  {
2945
    // Store external context for the return path.
2946
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
119✔
2947

2948
    // Set the prog and globals to the external function's, updating nLines and
2949
    // execmodules.
2950
    prog_ = funcref->prog();
119✔
2951

2952
    Globals2 = funcref->globals;
119✔
2953

2954
    nLines = static_cast<unsigned int>( prog_->instr.size() );
119✔
2955

2956
    // Re-attach modules, as the external user function's module function call
2957
    // instructions refer to modules by index.
2958
    execmodules.clear();
119✔
2959

2960
    if ( !viewmode_ )
119✔
2961
    {
2962
      if ( !AttachFunctionalityModules() )
119✔
2963
      {
2964
        POLLOGLN( "Could not attach modules for external function call jump" );
×
2965
        seterror( true );
×
2966
      }
2967
    }
2968
  }
2969

2970
  ControlStack.push_back( rc );
44,353✔
2971

2972
  PC = target_PC;
44,353✔
2973
  if ( ControlStack.size() >= escript_config.max_call_depth )
44,353✔
2974
  {
2975
    std::string tmp = fmt::format(
2976
        "Script {} exceeded maximum call depth\n"
2977
        "Return path PCs: ",
2978
        scriptname() );
×
2979
    while ( !ControlStack.empty() )
×
2980
    {
2981
      rc = ControlStack.back();
×
2982
      ControlStack.pop_back();
×
2983
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
2984
    }
2985
    POLLOGLN( tmp );
×
2986
    seterror( true );
×
2987
  }
×
2988
}
44,353✔
2989

2990

2991
BObjectImp* Executor::get_stacktrace( bool as_array )
44✔
2992
{
2993
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
44✔
2994

2995
  auto with_dbginfo =
2996
      [&]( const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
42✔
2997
                                     unsigned int /*line*/, const std::string& /*functionName*/ )>&
2998
               handler )
2999
  {
3000
    walkCallStack(
42✔
3001
        [&]( unsigned int pc )
245✔
3002
        {
3003
          auto filename = prog()->dbg_filenames[prog()->dbg_filenum[pc]];
203✔
3004
          auto line = prog()->dbg_linenum[pc];
161✔
3005
          auto dbgFunction =
3006
              std::find_if( prog()->dbg_functions.begin(), prog()->dbg_functions.end(),
161✔
3007
                            [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
3,014✔
3008

3009
          std::string functionName =
3010
              dbgFunction != prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
161✔
3011

3012
          handler( pc, filename, line, functionName );
161✔
3013
        } );
161✔
3014
  };
42✔
3015

3016
  if ( as_array )
44✔
3017
  {
3018
    std::unique_ptr<ObjArray> result( new ObjArray );
4✔
3019

3020
    if ( has_symbols )
4✔
3021
    {
3022
      with_dbginfo(
2✔
3023
          [&]( unsigned int pc, const std::string& filename, unsigned int line,
4✔
3024
               const std::string& functionName )
3025
          {
3026
            std::unique_ptr<BStruct> entry( new BStruct );
8✔
3027
            entry->addMember( "file", new String( filename ) );
8✔
3028
            entry->addMember( "line", new BLong( line ) );
8✔
3029
            entry->addMember( "name", new String( functionName ) );
8✔
3030
            entry->addMember( "pc", new BLong( pc ) );
8✔
3031
            result->addElement( entry.release() );
8✔
3032
          } );
8✔
3033
    }
3034
    else
3035
    {
3036
      walkCallStack(
2✔
3037
          [&]( unsigned int pc )
2✔
3038
          {
3039
            std::unique_ptr<BStruct> entry( new BStruct );
3✔
3040
            entry->addMember( "file", new String( scriptname() ) );
3✔
3041
            entry->addMember( "pc", new BLong( pc ) );
3✔
3042
            result->addElement( entry.release() );
3✔
3043
          } );
3✔
3044
    }
3045

3046
    return result.release();
4✔
3047
  }
4✔
3048
  // as string
3049
  std::string result;
40✔
3050

3051
  if ( has_symbols )
40✔
3052
  {
3053
    with_dbginfo(
40✔
3054
        [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
80✔
3055
             const std::string& functionName )
3056
        {
3057
          result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
306✔
3058
                                      filename, line ) );
3059
        } );
153✔
3060
  }
3061
  else
3062
  {
3063
    walkCallStack(
×
NEW
3064
        [&]( unsigned int pc )
×
3065
        {
3066
          result.append(
×
3067
              fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
3068
        } );
×
3069
  }
3070

3071
  return new String( std::move( result ) );
40✔
3072
}
40✔
3073

3074
void Executor::ins_pop_param( const Instruction& ins )
48,847✔
3075
{
3076
  popParam( ins.token );
48,847✔
3077
}
48,847✔
3078

3079
void Executor::ins_pop_param_byref( const Instruction& ins )
7,981✔
3080
{
3081
  popParamByRef( ins.token );
7,981✔
3082
}
7,981✔
3083

3084
void Executor::ins_get_arg( const Instruction& ins )
439✔
3085
{
3086
  getArg( ins.token );
439✔
3087
}
439✔
3088

3089
// CTRL_LEAVE_BLOCK:
3090
void Executor::ins_leave_block( const Instruction& ins )
9,486✔
3091
{
3092
  if ( Locals2 )
9,486✔
3093
  {
3094
    for ( int i = 0; i < ins.token.lval; i++ )
25,293✔
3095
      Locals2->pop_back();
15,807✔
3096
  }
3097
  else  // at global level.  ick.
3098
  {
3099
    for ( int i = 0; i < ins.token.lval; i++ )
×
3100
      Globals2->pop_back();
×
3101
  }
3102
}
9,486✔
3103

3104
void Executor::ins_gosub( const Instruction& ins )
×
3105
{
3106
  ReturnContext rc;
×
3107
  rc.PC = PC;
×
3108
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3109
  ControlStack.push_back( rc );
×
3110
  if ( Locals2 )
×
3111
    upperLocals2.push_back( Locals2 );
×
3112
  Locals2 = new BObjectRefVec;
×
3113

3114
  PC = (unsigned)ins.token.lval;
×
3115
}
×
3116

3117
// case RSV_RETURN
3118
void Executor::ins_return( const Instruction& /*ins*/ )
44,353✔
3119
{
3120
  if ( ControlStack.empty() )
44,353✔
3121
  {
3122
    ERROR_PRINTLN( "Return without GOSUB! (PC={}, {})", PC, scriptname() );
×
3123

3124
    seterror( true );
×
3125
    return;
×
3126
  }
3127
  BObjectRef continuation;
44,353✔
3128
  ReturnContext& rc = ControlStack.back();
44,353✔
3129
  PC = rc.PC;
44,353✔
3130

3131
  if ( rc.Continuation.get() != nullptr )
44,353✔
3132
  {
3133
    continuation = rc.Continuation;
35,890✔
3134
  }
3135

3136
  if ( Locals2 )
44,353✔
3137
  {
3138
    delete Locals2;
44,353✔
3139
    Locals2 = nullptr;
44,353✔
3140
  }
3141
  if ( !upperLocals2.empty() )
44,353✔
3142
  {
3143
    Locals2 = upperLocals2.back();
44,353✔
3144
    upperLocals2.pop_back();
44,353✔
3145
  }
3146

3147
  if ( rc.ExternalContext.has_value() )
44,353✔
3148
  {
3149
    prog_ = std::move( rc.ExternalContext->Program );
119✔
3150
    nLines = static_cast<unsigned int>( prog_->instr.size() );
119✔
3151
    execmodules = std::move( rc.ExternalContext->Modules );
119✔
3152
    Globals2 = std::move( rc.ExternalContext->Globals );
119✔
3153
  }
3154

3155
  // FIXME do something with rc.ValueStackDepth
3156
  ControlStack.pop_back();
44,353✔
3157

3158
  if ( continuation != nullptr )
44,353✔
3159
  {
3160
    auto result = ValueStack.back();
35,890✔
3161
    ValueStack.pop_back();
35,890✔
3162

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

3168
    // If the the continuation callback returned a continuation, handle the jump.
3169
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
35,890✔
3170
    {
3171
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3172
      // takes ownership.
3173

3174
      // Add function reference to stack
3175
      ValueStack.emplace_back( cont->func() );
35,100✔
3176

3177
      // Move all arguments to the fparams stack
3178
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
35,100✔
3179
                      std::make_move_iterator( cont->args.end() ) );
3180

3181
      cont->args.clear();
35,100✔
3182

3183
      printStack(
35,100✔
3184
          "continuation callback returned a continuation; continuation args added to fparams" );
3185

3186
      BObjectRef objref = ValueStack.back();
35,100✔
3187
      auto funcr = objref->impptr<BFunctionRef>();
35,100✔
3188
      Instruction jmp;
35,100✔
3189
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
35,100✔
3190
      {
3191
        call_function_reference( funcr, cont, jmp );
35,100✔
3192
      }
3193
      else
3194
      {
3195
        // Delete `imp` since a ReturnContext was not created.
3196
        BObject bobj( imp );
×
3197
      }
×
3198
    }
35,100✔
3199
    else
3200
    {
3201
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3202
      // `array{}.filter(...)`
3203
      ValueStack.pop_back();
790✔
3204

3205
      // Add the result to the stack.
3206
      ValueStack.emplace_back( imp );
790✔
3207
    }
3208
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
71,780✔
3209
  }
35,890✔
3210
}
44,353✔
3211

3212
void Executor::ins_exit( const Instruction& /*ins*/ )
3✔
3213
{
3214
  done = 1;
3✔
3215
  run_ok_ = false;
3✔
3216
}
3✔
3217

3218
void Executor::ins_double( const Instruction& ins )
427✔
3219
{
3220
  ValueStack.emplace_back( new Double( ins.token.dval ) );
427✔
3221
}
427✔
3222

3223
void Executor::ins_classinst( const Instruction& ins )
321✔
3224
{
3225
  ValueStack.emplace_back( new BConstObject(
321✔
3226
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2 ) ) ) );
642✔
3227
}
321✔
3228

3229
void Executor::ins_string( const Instruction& ins )
107,385✔
3230
{
3231
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
107,385✔
3232
}
107,385✔
3233

3234
void Executor::ins_regexp( const Instruction& )
150✔
3235
{
3236
  BObjectRef flags = ValueStack.back();
150✔
3237
  ValueStack.pop_back();
150✔
3238
  BObjectRef& pattern = ValueStack.back();
150✔
3239

3240
  pattern.set(
150✔
3241
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300✔
3242
}
150✔
3243

3244
void Executor::ins_error( const Instruction& /*ins*/ )
4,177✔
3245
{
3246
  ValueStack.emplace_back( new BError() );
4,177✔
3247
}
4,177✔
3248
void Executor::ins_struct( const Instruction& /*ins*/ )
808✔
3249
{
3250
  ValueStack.emplace_back( new BStruct );
808✔
3251
}
808✔
3252
void Executor::ins_spread( const Instruction& ins )
2,125✔
3253
{
3254
  bool spread_into = ins.token.lval;
2,125✔
3255

3256
  // Pops TOP, and spreads it into (new) TOP.
3257
  if ( spread_into )
2,125✔
3258
  {
3259
    BObjectRef rightref = ValueStack.back();
24✔
3260
    ValueStack.pop_back();
24✔
3261
    BObjectRef& leftref = ValueStack.back();
24✔
3262

3263
    BObject& right = *rightref;
24✔
3264
    BObject& left = *leftref;
24✔
3265

3266
    BObjectRef refIter( UninitObject::create() );
24✔
3267

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

3270
    while ( auto next = pIter->step() )
72✔
3271
    {
3272
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48✔
3273
      BObject res_obj( result );
48✔
3274
    }
48✔
3275
  }
24✔
3276
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3277
  else
3278
  {
3279
    auto spread = new BSpread( ValueStack.back() );
2,101✔
3280
    ValueStack.pop_back();
2,101✔
3281
    ValueStack.emplace_back( spread );
2,101✔
3282
  }
3283
}
2,125✔
3284
void Executor::ins_array( const Instruction& /*ins*/ )
3,792✔
3285
{
3286
  ValueStack.emplace_back( new ObjArray );
3,792✔
3287
}
3,792✔
3288
void Executor::ins_dictionary( const Instruction& /*ins*/ )
161✔
3289
{
3290
  ValueStack.emplace_back( new BDictionary );
161✔
3291
}
161✔
3292
void Executor::ins_uninit( const Instruction& /*ins*/ )
113✔
3293
{
3294
  ValueStack.emplace_back( UninitObject::create() );
113✔
3295
}
113✔
3296
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3297
{
3298
  ValueStack.emplace_back( new BError( "Please recompile this script" ) );
×
3299
}
×
3300

3301
// case TOK_UNMINUS:
3302
void Executor::ins_unminus( const Instruction& /*ins*/ )
147✔
3303
{
3304
  BObjectRef ref = getObjRef();
147✔
3305
  BObjectImp* newobj;
3306
  newobj = ref->impref().inverse();
147✔
3307

3308
  ValueStack.emplace_back( newobj );
147✔
3309
}
147✔
3310

3311
// case TOK_UNPLUSPLUS:
3312
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
472✔
3313
{
3314
  BObjectRef& ref = ValueStack.back();
472✔
3315
  ref->impref().selfPlusPlus();
472✔
3316
}
472✔
3317

3318
// case TOK_UNMINUSMINUS:
3319
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
131✔
3320
{
3321
  BObjectRef& ref = ValueStack.back();
131✔
3322
  ref->impref().selfMinusMinus();
131✔
3323
}
131✔
3324

3325
// case TOK_UNPLUSPLUS_POST:
3326
void Executor::ins_unplusplus_post( const Instruction& /*ins*/ )
261✔
3327
{
3328
  BObjectRef& ref = ValueStack.back();
261✔
3329
  BObjectImp* imp = ref->impptr();
261✔
3330
  BObject* n = ref->clone();
261✔
3331
  imp->selfPlusPlus();
261✔
3332
  ref.set( n );
261✔
3333
}
261✔
3334

3335
// case TOK_UNMINUSMINUS_POST:
3336
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
85✔
3337
{
3338
  BObjectRef& ref = ValueStack.back();
85✔
3339
  BObjectImp* imp = ref->impptr();
85✔
3340
  BObject* n = ref->clone();
85✔
3341
  imp->selfMinusMinus();
85✔
3342
  ref.set( n );
85✔
3343
}
85✔
3344

3345
// case INS_SET_MEMBER_ID_UNPLUSPLUS:
3346
void Executor::ins_set_member_id_unplusplus( const Instruction& ins )
10✔
3347
{
3348
  BObjectRef& ref = ValueStack.back();
10✔
3349
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
10✔
3350
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
20✔
3351
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
10✔
3352
  {
3353
    tmp->impref().selfPlusPlus();
10✔
3354
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
10✔
3355
  }
3356
  ref.set( tmp.get() );
10✔
3357
}
10✔
3358

3359
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3360
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
3✔
3361
{
3362
  BObjectRef& ref = ValueStack.back();
3✔
3363
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3364
  BObject* res = tmp->clone();
3✔
3365
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3366
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3367
  {
3368
    tmp->impref().selfPlusPlus();
3✔
3369
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3370
  }
3371
  ref.set( res );
3✔
3372
}
3✔
3373

3374
// case INS_SET_MEMBER_ID_UNMINUSMINUS:
3375
void Executor::ins_set_member_id_unminusminus( const Instruction& ins )
13✔
3376
{
3377
  BObjectRef& ref = ValueStack.back();
13✔
3378
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
13✔
3379
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
26✔
3380
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
13✔
3381
  {
3382
    tmp->impref().selfMinusMinus();
13✔
3383
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
13✔
3384
  }
3385
  ref.set( tmp.get() );
13✔
3386
}
13✔
3387

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

3403
// case TOK_LOG_NOT:
3404
void Executor::ins_logical_not( const Instruction& /*ins*/ )
3,984✔
3405
{
3406
  BObjectRef ref = getObjRef();
3,984✔
3407
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
3,984✔
3408
}
3,984✔
3409

3410
// case TOK_BITWISE_NOT:
3411
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
156✔
3412
{
3413
  BObjectRef ref = getObjRef();
156✔
3414
  ValueStack.emplace_back( ref->impptr()->bitnot() );
156✔
3415
}
156✔
3416

3417
// case TOK_FUNCREF:
3418
void Executor::ins_funcref( const Instruction& ins )
1,489✔
3419
{
3420
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
1,489✔
3421
  {
3422
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3423
                    prog_->function_references.size() );
×
3424
    seterror( true );
×
3425
    return;
×
3426
  }
3427

3428
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
1,489✔
3429

3430
  ValueStack.emplace_back( new BFunctionRef( prog_, funcref_index, Globals2, {} /* captures */ ) );
1,489✔
3431
}
3432

3433
void Executor::ins_functor( const Instruction& ins )
722✔
3434
{
3435
  auto funcref_index = static_cast<int>( ins.token.type );
722✔
3436

3437
  const auto& ep_funcref = prog_->function_references[funcref_index];
722✔
3438

3439
  int capture_count = ep_funcref.capture_count;
722✔
3440

3441
  auto captures = ValueStackCont();
722✔
3442
  while ( capture_count > 0 )
1,354✔
3443
  {
3444
    captures.push_back( ValueStack.back() );
632✔
3445
    ValueStack.pop_back();
632✔
3446
    capture_count--;
632✔
3447
  }
3448

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

3451
  ValueStack.emplace_back( func );
722✔
3452

3453
  PC += ins.token.lval;
722✔
3454
}
722✔
3455

3456
void Executor::ins_logical_jump( const Instruction& ins )
401✔
3457
{
3458
  BObjectRef& objref = ValueStack.back();
401✔
3459
  // jmp if true for ||, jmp if false for &&
3460
  const bool obj_true = objref->impptr()->isTrue();
401✔
3461
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
401✔
3462

3463
  if ( jmp )
401✔
3464
    PC = (unsigned)ins.token.lval;
84✔
3465
  // keep the obj on the stack if it should jump (ShortCircuit)
3466
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3467
  // BLong (original &&/|| convert to BLong bool)
3468
  if ( !jmp )
401✔
3469
    ValueStack.pop_back();
317✔
3470
  else
3471
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
84✔
3472
}
401✔
3473

3474
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
95✔
3475
{
3476
  BObjectRef& objref = ValueStack.back();
95✔
3477
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
95✔
3478
}
95✔
3479

3480
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3481

3482
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
161,479✔
3483
{
3484
  switch ( token.id )
161,479✔
3485
  {
3486
  case INS_INITFOREACH:
404✔
3487
    return &Executor::ins_initforeach;
404✔
3488
  case INS_STEPFOREACH:
404✔
3489
    return &Executor::ins_stepforeach;
404✔
3490
  case INS_INITFOR:
59✔
3491
    return &Executor::ins_initfor;
59✔
3492
  case INS_NEXTFOR:
59✔
3493
    return &Executor::ins_nextfor;
59✔
3494
  case INS_CASEJMP:
95✔
3495
    return &Executor::ins_casejmp;
95✔
3496
  case RSV_JMPIFTRUE:
1,023✔
3497
    return &Executor::ins_jmpiftrue;
1,023✔
3498
  case RSV_JMPIFFALSE:
1,946✔
3499
    return &Executor::ins_jmpiffalse;
1,946✔
3500
  case RSV_LOCAL:
2,634✔
3501
    return &Executor::ins_makeLocal;
2,634✔
3502
  case RSV_GLOBAL:
9,473✔
3503
  case TOK_GLOBALVAR:
3504
    return &Executor::ins_globalvar;
9,473✔
3505
  case TOK_LOCALVAR:
16,640✔
3506
    return &Executor::ins_localvar;
16,640✔
3507
  case TOK_LONG:
13,752✔
3508
    return &Executor::ins_long;
13,752✔
3509
  case TOK_DOUBLE:
424✔
3510
    return &Executor::ins_double;
424✔
3511
  case TOK_STRING:
24,600✔
3512
    return &Executor::ins_string;
24,600✔
3513
  case TOK_REGEXP:
150✔
3514
    return &Executor::ins_regexp;
150✔
3515
  case TOK_ERROR:
190✔
3516
    return &Executor::ins_error;
190✔
3517
  case TOK_STRUCT:
583✔
3518
    return &Executor::ins_struct;
583✔
3519
  case TOK_SPREAD:
670✔
3520
    return &Executor::ins_spread;
670✔
3521
  case TOK_CLASSINST:
242✔
3522
    return &Executor::ins_classinst;
242✔
3523
  case TOK_ARRAY:
2,321✔
3524
    return &Executor::ins_array;
2,321✔
3525
  case TOK_DICTIONARY:
156✔
3526
    return &Executor::ins_dictionary;
156✔
3527
  case TOK_FUNCREF:
734✔
3528
    return &Executor::ins_funcref;
734✔
3529
  case TOK_FUNCTOR:
661✔
3530
    return &Executor::ins_functor;
661✔
3531
  case INS_UNINIT:
106✔
3532
    return &Executor::ins_uninit;
106✔
3533
  case TOK_IDENT:
×
3534
    return &Executor::ins_ident;
×
3535
  case INS_ASSIGN_GLOBALVAR:
732✔
3536
    return &Executor::ins_assign_globalvar;
732✔
3537
  case INS_ASSIGN_LOCALVAR:
906✔
3538
    return &Executor::ins_assign_localvar;
906✔
3539
  case INS_ASSIGN_CONSUME:
63✔
3540
    return &Executor::ins_assign_consume;
63✔
3541
  case TOK_CONSUMER:
18,053✔
3542
    return &Executor::ins_consume;
18,053✔
3543
  case TOK_ASSIGN:
4,276✔
3544
    return &Executor::ins_assign;
4,276✔
3545
  case INS_SUBSCRIPT_ASSIGN:
9✔
3546
    return &Executor::ins_array_assign;
9✔
3547
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
422✔
3548
    return &Executor::ins_array_assign_consume;
422✔
3549
  case INS_MULTISUBSCRIPT:
237✔
3550
    return &Executor::ins_multisubscript;
237✔
3551
  case INS_MULTISUBSCRIPT_ASSIGN:
6✔
3552
    return &Executor::ins_multisubscript_assign;
6✔
3553
  case INS_GET_MEMBER:
1,023✔
3554
    return &Executor::ins_get_member;
1,023✔
3555
  case INS_SET_MEMBER:
3✔
3556
    return &Executor::ins_set_member;
3✔
3557
  case INS_SET_MEMBER_CONSUME:
227✔
3558
    return &Executor::ins_set_member_consume;
227✔
3559

3560
  case INS_UNPACK_SEQUENCE:
106✔
3561
    return &Executor::ins_unpack_sequence;
106✔
3562
  case INS_UNPACK_INDICES:
84✔
3563
    return &Executor::ins_unpack_indices;
84✔
3564
  case INS_TAKE_GLOBAL:
183✔
3565
    return &Executor::ins_take_global;
183✔
3566
  case INS_TAKE_LOCAL:
298✔
3567
    return &Executor::ins_take_local;
298✔
3568

3569
  case INS_GET_MEMBER_ID:
1,524✔
3570
    return &Executor::ins_get_member_id;  // test id
1,524✔
3571
  case INS_SET_MEMBER_ID:
3✔
3572
    return &Executor::ins_set_member_id;  // test id
3✔
3573
  case INS_SET_MEMBER_ID_CONSUME:
199✔
3574
    return &Executor::ins_set_member_id_consume;  // test id
199✔
3575

3576
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15✔
3577
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15✔
3578
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6✔
3579
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6✔
3580
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9✔
3581
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9✔
3582
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6✔
3583
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6✔
3584
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6✔
3585
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6✔
3586

3587
  case TOK_ADD:
2,429✔
3588
    return &Executor::ins_add;
2,429✔
3589
  case TOK_SUBTRACT:
423✔
3590
    return &Executor::ins_subtract;
423✔
3591
  case TOK_DIV:
59✔
3592
    return &Executor::ins_div;
59✔
3593
  case TOK_MULT:
228✔
3594
    return &Executor::ins_mult;
228✔
3595
  case TOK_MODULUS:
30✔
3596
    return &Executor::ins_modulus;
30✔
3597

3598
  case TOK_INSERTINTO:
5,131✔
3599
    return &Executor::ins_insert_into;
5,131✔
3600

3601
  case TOK_PLUSEQUAL:
80✔
3602
    return &Executor::ins_plusequal;
80✔
3603
  case TOK_MINUSEQUAL:
27✔
3604
    return &Executor::ins_minusequal;
27✔
3605
  case TOK_TIMESEQUAL:
24✔
3606
    return &Executor::ins_timesequal;
24✔
3607
  case TOK_DIVIDEEQUAL:
15✔
3608
    return &Executor::ins_divideequal;
15✔
3609
  case TOK_MODULUSEQUAL:
18✔
3610
    return &Executor::ins_modulusequal;
18✔
3611

3612
  case TOK_LESSTHAN:
210✔
3613
    return &Executor::ins_lessthan;
210✔
3614
  case TOK_LESSEQ:
74✔
3615
    return &Executor::ins_lessequal;
74✔
3616
  case RSV_GOTO:
1,762✔
3617
    return &Executor::ins_goto;
1,762✔
3618
  case TOK_ARRAY_SUBSCRIPT:
1,314✔
3619
    return &Executor::ins_arraysubscript;
1,314✔
3620
  case TOK_EQUAL:
946✔
3621
    return &Executor::ins_equal;
946✔
3622
  case TOK_FUNC:
12,065✔
3623
    return &Executor::ins_func;
12,065✔
3624
  case INS_CALL_METHOD:
368✔
3625
    return &Executor::ins_call_method;
368✔
3626
  case INS_CALL_METHOD_ID:
3,364✔
3627
    return &Executor::ins_call_method_id;
3,364✔
3628
  case CTRL_STATEMENTBEGIN:
×
3629
    return &Executor::ins_statementbegin;
×
3630
  case CTRL_MAKELOCAL:
3,886✔
3631
    return &Executor::ins_makelocal;
3,886✔
3632
  case INS_CHECK_MRO:
225✔
3633
    return &Executor::ins_check_mro;
225✔
3634
  case CTRL_JSR_USERFUNC:
3,886✔
3635
    return &Executor::ins_jsr_userfunc;
3,886✔
3636
  case INS_POP_PARAM:
2,903✔
3637
    return &Executor::ins_pop_param;
2,903✔
3638
  case INS_POP_PARAM_BYREF:
1,277✔
3639
    return &Executor::ins_pop_param_byref;
1,277✔
3640
  case INS_GET_ARG:
139✔
3641
    return &Executor::ins_get_arg;
139✔
3642
  case CTRL_LEAVE_BLOCK:
1,516✔
3643
    return &Executor::ins_leave_block;
1,516✔
3644
  case RSV_GOSUB:
×
3645
    return &Executor::ins_gosub;
×
3646
  case RSV_RETURN:
4,506✔
3647
    return &Executor::ins_return;
4,506✔
3648
  case RSV_EXIT:
18✔
3649
    return &Executor::ins_exit;
18✔
3650
  case INS_DECLARE_ARRAY:
69✔
3651
    return &Executor::ins_declareArray;
69✔
3652
  case TOK_UNMINUS:
3✔
3653
    return &Executor::ins_unminus;
3✔
3654
  case TOK_UNPLUS:
×
3655
    return &Executor::ins_nop;
×
3656
  case TOK_LOG_NOT:
190✔
3657
    return &Executor::ins_logical_not;
190✔
3658
  case TOK_BITWISE_NOT:
12✔
3659
    return &Executor::ins_bitwise_not;
12✔
3660
  case TOK_BSRIGHT:
15✔
3661
    return &Executor::ins_bitshift_right;
15✔
3662
  case TOK_BSLEFT:
15✔
3663
    return &Executor::ins_bitshift_left;
15✔
3664
  case TOK_BITAND:
17✔
3665
    return &Executor::ins_bitwise_and;
17✔
3666
  case TOK_BITXOR:
15✔
3667
    return &Executor::ins_bitwise_xor;
15✔
3668
  case TOK_BITOR:
15✔
3669
    return &Executor::ins_bitwise_or;
15✔
3670

3671
  case TOK_NEQ:
772✔
3672
    return &Executor::ins_notequal;
772✔
3673
  case TOK_GRTHAN:
152✔
3674
    return &Executor::ins_greaterthan;
152✔
3675
  case TOK_GREQ:
84✔
3676
    return &Executor::ins_greaterequal;
84✔
3677
  case TOK_AND:
66✔
3678
    return &Executor::ins_logical_and;
66✔
3679
  case TOK_OR:
305✔
3680
    return &Executor::ins_logical_or;
305✔
3681

3682
  case TOK_ADDMEMBER:
96✔
3683
    return &Executor::ins_addmember;
96✔
3684
  case TOK_DELMEMBER:
10✔
3685
    return &Executor::ins_removemember;
10✔
3686
  case TOK_CHKMEMBER:
78✔
3687
    return &Executor::ins_checkmember;
78✔
3688
  case INS_DICTIONARY_ADDMEMBER:
132✔
3689
    return &Executor::ins_dictionary_addmember;
132✔
3690
  case TOK_IN:
49✔
3691
    return &Executor::ins_in;
49✔
3692
  case TOK_IS:
56✔
3693
    return &Executor::ins_is;
56✔
3694
  case INS_ADDMEMBER2:
45✔
3695
    return &Executor::ins_addmember2;
45✔
3696
  case INS_ADDMEMBER_ASSIGN:
1,239✔
3697
    return &Executor::ins_addmember_assign;
1,239✔
3698
  case CTRL_PROGEND:
2,667✔
3699
    return &Executor::ins_progend;
2,667✔
3700
  case TOK_UNPLUSPLUS:
93✔
3701
    return &Executor::ins_unplusplus;
93✔
3702
  case TOK_UNMINUSMINUS:
57✔
3703
    return &Executor::ins_unminusminus;
57✔
3704
  case TOK_UNPLUSPLUS_POST:
100✔
3705
    return &Executor::ins_unplusplus_post;
100✔
3706
  case TOK_UNMINUSMINUS_POST:
49✔
3707
    return &Executor::ins_unminusminus_post;
49✔
3708
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10✔
3709
    return &Executor::ins_set_member_id_unplusplus;  // test id
10✔
3710
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13✔
3711
    return &Executor::ins_set_member_id_unminusminus;  // test id
13✔
3712
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3✔
3713
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3✔
3714
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3✔
3715
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3✔
3716
  case INS_SKIPIFTRUE_ELSE_CONSUME:
196✔
3717
    return &Executor::ins_skipiftrue_else_consume;
196✔
3718
  case TOK_INTERPOLATE_STRING:
1,791✔
3719
    return &Executor::ins_interpolate_string;
1,791✔
3720
  case TOK_FORMAT_EXPRESSION:
89✔
3721
    return &Executor::ins_format_expression;
89✔
3722
  case TOK_BOOL:
218✔
3723
    return &Executor::ins_bool;
218✔
3724
  case INS_LOGICAL_JUMP:
63✔
3725
    return &Executor::ins_logical_jump;
63✔
3726
  case INS_LOGICAL_CONVERT:
47✔
3727
    return &Executor::ins_logical_convert;
47✔
3728
  default:
×
3729
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3730
  }
3731
}
3732

3733
void Executor::sethalt( bool halt )
17✔
3734
{
3735
  halt_ = halt;
17✔
3736

3737
  if ( halt && dbg_env_ )
17✔
3738
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18✔
3739
      listener->on_halt();
9✔
3740

3741
  calcrunnable();
17✔
3742
}
17✔
3743

3744
void Executor::execInstr()
125,460,529✔
3745
{
3746
  unsigned onPC = PC;
125,460,529✔
3747
  try
3748
  {  // this is really more of a class invariant.
3749
    passert( run_ok_ );
125,460,529✔
3750
    passert( PC < nLines );
125,460,529✔
3751
    passert( !error_ );
125,460,529✔
3752
    passert( !done );
125,460,529✔
3753

3754
#ifdef NDEBUG
3755
    const Instruction& ins = prog_->instr[PC];
125,460,529✔
3756
#else
3757
    const Instruction& ins = prog_->instr.at( PC );
3758
#endif
3759
    if ( debug_level >= INSTRUCTIONS )
125,460,529✔
3760
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3761

3762
    // If `on_instruction` returns false, do not execute this instruction.
3763
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
125,460,529✔
3764
    {
3765
      return;
9✔
3766
    }
3767

3768
    ++ins.cycles;
125,460,520✔
3769
    ++prog_->instr_cycles;
125,460,520✔
3770
    ++escript_instr_cycles;
125,460,520✔
3771

3772
    ++PC;
125,460,520✔
3773

3774
    ( this->*( ins.func ) )( ins );
125,460,520✔
3775
  }
3776
  catch ( std::exception& ex )
×
3777
  {
3778
    std::string tmp =
3779
        fmt::format( "Exception in: {} PC={}: {}\n", prog_->name.get(), onPC, ex.what() );
×
3780
    if ( !run_ok_ )
×
3781
      tmp += "run_ok_ = false\n";
×
3782
    if ( PC < nLines )
×
3783
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
×
3784
    if ( error_ )
×
3785
      tmp += "error_ = true\n";
×
3786
    if ( done )
×
3787
      tmp += "done = true\n";
×
3788

3789
    seterror( true );
×
3790
    POLLOG_ERROR( tmp );
×
3791

3792
    show_context( onPC );
×
3793
  }
×
3794
#ifdef __unix__
3795
  catch ( ... )
×
3796
  {
3797
    seterror( true );
×
3798
    POLLOG_ERRORLN( "Exception in {}, PC={}: unclassified", prog_->name.get(), onPC );
×
3799

3800
    show_context( onPC );
×
3801
  }
×
3802
#endif
3803
}
3804

3805
std::string Executor::dbg_get_instruction( size_t atPC ) const
×
3806
{
3807
  std::string out;
×
3808
  dbg_get_instruction( atPC, out );
×
3809
  return out;
×
3810
}
×
3811

3812
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
264✔
3813
{
3814
  bool has_breakpoint =
3815
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
264✔
3816
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
264✔
3817
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
264✔
3818
}
264✔
3819

3820
void Executor::show_context( unsigned atPC )
×
3821
{
3822
  unsigned start, end;
3823
  if ( atPC >= 5 )
×
3824
    start = atPC - 5;
×
3825
  else
3826
    start = 0;
×
3827

3828
  end = atPC + 5;
×
3829

3830
  if ( end >= nLines )
×
3831
    end = nLines - 1;
×
3832

3833
  for ( unsigned i = start; i <= end; ++i )
×
3834
  {
3835
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
×
3836
  }
3837
}
×
3838
void Executor::show_context( std::string& os, unsigned atPC )
24✔
3839
{
3840
  unsigned start, end;
3841
  if ( atPC >= 5 )
24✔
3842
    start = atPC - 5;
24✔
3843
  else
3844
    start = 0;
×
3845

3846
  end = atPC + 5;
24✔
3847

3848
  if ( end >= nLines )
24✔
3849
    end = nLines - 1;
×
3850

3851
  for ( unsigned i = start; i <= end; ++i )
288✔
3852
  {
3853
    dbg_get_instruction( i, os );
264✔
3854
    os += '\n';
264✔
3855
  }
3856
}
24✔
3857

3858
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
38,057✔
3859
                                        const Instruction& jmp )
3860
{
3861
  // params need to be on the stack, without current objectref
3862
  ValueStack.pop_back();
38,057✔
3863

3864
  // Push captured parameters onto the stack prior to function parameters.
3865
  for ( auto& p : funcr->captures )
43,558✔
3866
    ValueStack.push_back( p );
5,501✔
3867

3868
  auto nparams = static_cast<int>( fparams.size() );
38,057✔
3869

3870
  // Handle variadic functions special. Construct an array{} corresponding to
3871
  // the rest parameter (the last parameter for the function). The logic for the
3872
  // condition:
3873
  // - if true, the last argument in the call may not be an array{}, so we need
3874
  //   to construct one (which is why the condition is `>=` and not `>`).
3875
  // - if false, then the address we're jumping to will be a "default argument
3876
  //   address" and _not_ the user function directly, which will create the
3877
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
3878
  if ( funcr->variadic() && nparams >= funcr->numParams() )
38,057✔
3879
  {
3880
    auto num_nonrest_args = funcr->numParams() - 1;
640✔
3881

3882
    auto rest_arg = std::make_unique<ObjArray>();
640✔
3883

3884
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,655✔
3885
    {
3886
      auto& p = fparams[i];
3,015✔
3887

3888
      if ( i < num_nonrest_args )
3,015✔
3889
      {
3890
        ValueStack.push_back( p );
615✔
3891
      }
3892
      else
3893
      {
3894
        rest_arg->ref_arr.push_back( p );
2,400✔
3895
      }
3896
    }
3897
    ValueStack.emplace_back( rest_arg.release() );
640✔
3898
  }
640✔
3899
  // The array{} will be created via the regular default-parameter handling by
3900
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
3901
  // prior to jumping to the user function.
3902
  else
3903
  {
3904
    for ( auto& p : fparams )
77,526✔
3905
      ValueStack.push_back( p );
40,109✔
3906
  }
3907

3908
  // jump to function
3909
  jump( jmp.token.lval, continuation, funcr );
38,057✔
3910
  fparams.clear();
38,057✔
3911
  // switch to new block
3912
  ins_makelocal( jmp );
38,057✔
3913
}
38,057✔
3914

3915
bool Executor::exec()
2,525✔
3916
{
3917
  passert( prog_ok_ );
2,525✔
3918
  passert( !error_ );
2,525✔
3919

3920
  Clib::scripts_thread_script = scriptname();
2,525✔
3921

3922
  set_running_to_completion( true );
2,525✔
3923
  while ( runnable() )
647,885✔
3924
  {
3925
    Clib::scripts_thread_scriptPC = PC;
645,360✔
3926
    execInstr();
645,360✔
3927
  }
3928

3929
  return !error_;
2,525✔
3930
}
3931

3932
void Executor::reinitExec()
×
3933
{
3934
  PC = 0;
×
3935
  done = 0;
×
3936
  seterror( false );
×
3937

3938
  ValueStack.clear();
×
3939
  delete Locals2;
×
3940
  Locals2 = new BObjectRefVec;
×
3941

3942
  if ( !prog_ok_ )
×
3943
  {
3944
    seterror( true );
×
3945
  }
3946
}
×
3947

3948
void Executor::initForFnCall( unsigned in_PC )
831✔
3949
{
3950
#ifdef MEMORYLEAK
3951
  bool data_shown = false;
3952
#endif
3953

3954
  PC = in_PC;
831✔
3955
  done = 0;
831✔
3956
  seterror( false );
831✔
3957

3958
#ifdef MEMORYLEAK
3959
  while ( !ValueStack.empty() )
3960
  {
3961
    if ( Clib::memoryleak_debug )
3962
    {
3963
      if ( !data_shown )
3964
      {
3965
        LEAKLOG( "ValueStack... " );
3966
        data_shown = true;
3967
      }
3968

3969
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
3970
               ValueStack.back()->impptr()->sizeEstimate() );
3971
    }
3972
    ValueStack.pop_back();
3973
  }
3974
  if ( Clib::memoryleak_debug )
3975
    if ( data_shown )
3976
      LEAKLOGLN( " ...deleted" );
3977
#endif
3978

3979
  ValueStack.clear();
831✔
3980
  Locals2->clear();
831✔
3981
}
831✔
3982

3983
void Executor::pushArg( BObjectImp* arg )
1,232✔
3984
{
3985
  passert_always( arg );
1,232✔
3986
  ValueStack.emplace_back( arg );
1,232✔
3987
}
1,232✔
3988

3989
void Executor::pushArg( const BObjectRef& arg )
×
3990
{
3991
  ValueStack.push_back( arg );
×
3992
}
×
3993

3994
void Executor::addModule( ExecutorModule* module )
20,906✔
3995
{
3996
  availmodules.push_back( module );
20,906✔
3997
}
20,906✔
3998

3999

4000
ExecutorModule* Executor::findModule( const std::string& name )
3,557✔
4001
{
4002
  unsigned idx;
4003
  for ( idx = 0; idx < availmodules.size(); idx++ )
19,380✔
4004
  {
4005
    ExecutorModule* module = availmodules[idx];
19,380✔
4006
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
19,380✔
4007
      return module;
3,557✔
4008
  }
4009
  return nullptr;
×
4010
}
4011

4012
bool Executor::attach_debugger( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching )
4✔
4013
{
4014
  // FIXME: a script can be in debugging state but have no debugger attached,
4015
  // eg. a script that called `os::Debugger()`. This needs to check if a
4016
  // debugger is attached. This works for `os::Debugger()` but not for poldbg cmd_attach.
4017
  if ( dbg_env_ )
4✔
4018
  {
4019
    if ( !listener.expired() )
2✔
4020
    {
4021
      auto& dbg_env_listener = dbg_env_->listener;
1✔
4022
      if ( !dbg_env_listener.expired() )
1✔
4023
      {
4024
        return false;
×
4025
      }
4026
      dbg_env_listener = listener;
1✔
4027
    }
4028
    if ( set_attaching )
2✔
4029
      dbg_env_->debug_state = ExecutorDebugState::ATTACHING;
2✔
4030
  }
4031
  else
4032
  {
4033
    dbg_env_ = std::make_unique<ExecutorDebugEnvironment>( listener, set_attaching );
2✔
4034
  }
4035

4036
  return true;
4✔
4037
}
4038

4039
void Executor::detach_debugger()
×
4040
{
4041
  dbg_env_.reset();
×
4042
  sethalt( false );
×
4043
}
×
4044

4045
void Executor::print_to_debugger( const std::string& message )
21,128✔
4046
{
4047
  if ( dbg_env_ )
21,128✔
4048
  {
4049
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2✔
4050
      listener->on_print( message );
1✔
4051
  }
4052
}
21,128✔
4053

4054
void Executor::dbg_ins_trace()
×
4055
{
4056
  if ( dbg_env_ )
×
4057
  {
4058
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4059
  }
4060
  sethalt( false );
×
4061
}
×
4062
void Executor::dbg_step_into()
1✔
4063
{
4064
  if ( dbg_env_ )
1✔
4065
  {
4066
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1✔
4067
  }
4068
  sethalt( false );
1✔
4069
}
1✔
4070
void Executor::dbg_step_over()
2✔
4071
{
4072
  if ( dbg_env_ )
2✔
4073
  {
4074
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2✔
4075
  }
4076
  sethalt( false );
2✔
4077
}
2✔
4078
void Executor::dbg_step_out()
1✔
4079
{
4080
  if ( dbg_env_ )
1✔
4081
  {
4082
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1✔
4083
  }
4084
  sethalt( false );
1✔
4085
}
1✔
4086
void Executor::dbg_run()
4✔
4087
{
4088
  if ( dbg_env_ )
4✔
4089
  {
4090
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4✔
4091
  }
4092
  sethalt( false );
4✔
4093
}
4✔
4094
void Executor::dbg_break()
1✔
4095
{
4096
  if ( dbg_env_ )
1✔
4097
  {
4098
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1✔
4099
  }
4100
}
1✔
4101

4102
void Executor::dbg_setbp( unsigned atPC )
2✔
4103
{
4104
  if ( dbg_env_ )
2✔
4105
  {
4106
    dbg_env_->breakpoints.insert( atPC );
2✔
4107
  }
4108
}
2✔
4109
void Executor::dbg_clrbp( unsigned atPC )
×
4110
{
4111
  if ( dbg_env_ )
×
4112
  {
4113
    dbg_env_->breakpoints.erase( atPC );
×
4114
  }
4115
}
×
4116

4117
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1✔
4118
{
4119
  if ( dbg_env_ )
1✔
4120
  {
4121
    std::set<unsigned> result;
1✔
4122
    auto& breakpoints = dbg_env_->breakpoints;
1✔
4123
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1✔
4124
                         std::inserter( result, result.end() ) );
4125
    breakpoints = result;
1✔
4126
  }
1✔
4127
}
1✔
4128

4129
void Executor::dbg_clrallbp()
×
4130
{
4131
  if ( dbg_env_ )
×
4132
  {
4133
    dbg_env_->breakpoints.clear();
×
4134
  }
4135
}
×
4136

4137
size_t Executor::sizeEstimate() const
1,999✔
4138
{
4139
  size_t size = sizeof( *this );
1,999✔
4140
  size += Clib::memsize( upperLocals2 );
1,999✔
4141
  for ( const auto& bobjectrefvec : upperLocals2 )
1,999✔
4142
  {
4143
    size += Clib::memsize( *bobjectrefvec );
×
4144
    for ( const auto& bojectref : *bobjectrefvec )
×
4145
    {
4146
      if ( bojectref != nullptr )
×
4147
        size += bojectref->sizeEstimate();
×
4148
    }
4149
  }
4150
  size += Clib::memsize( ControlStack );
1,999✔
4151

4152
  size += Clib::memsize( *Locals2 );
1,999✔
4153
  for ( const auto& bojectref : *Locals2 )
2,107✔
4154
  {
4155
    if ( bojectref != nullptr )
108✔
4156
      size += bojectref->sizeEstimate();
108✔
4157
  }
4158
  size += Clib::memsize( *Globals2 );
1,999✔
4159
  for ( const auto& bojectref : *Globals2 )
4,134✔
4160
  {
4161
    if ( bojectref != nullptr )
2,135✔
4162
      size += bojectref->sizeEstimate();
2,135✔
4163
  }
4164
  size += Clib::memsize( ValueStack );
1,999✔
4165
  for ( const auto& bojectref : ValueStack )
2,037✔
4166
  {
4167
    if ( bojectref != nullptr )
38✔
4168
      size += bojectref->sizeEstimate();
38✔
4169
  }
4170
  size += Clib::memsize( fparams );
1,999✔
4171
  for ( const auto& bojectref : fparams )
1,999✔
4172
  {
4173
    if ( bojectref != nullptr )
×
4174
      size += bojectref->sizeEstimate();
×
4175
  }
4176
  for ( const auto& module : availmodules )
16,160✔
4177
  {
4178
    if ( module != nullptr )
14,161✔
4179
      size += module->sizeEstimate();
14,161✔
4180
  }
4181
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
1,999✔
4182
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
1,999✔
4183
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
1,999✔
4184
  size += Clib::memsize( class_methods );
1,999✔
4185
  return size;
1,999✔
4186
}
4187

4188
bool Executor::builtinMethodForced( const char*& methodname )
7✔
4189
{
4190
  if ( methodname[0] == '_' )
7✔
4191
  {
4192
    ++methodname;
×
4193
    return true;
×
4194
  }
4195
  return false;
7✔
4196
}
4197

4198
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
35,100✔
4199
{
4200
  auto* func = continuation->func();
35,100✔
4201

4202
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4203
  // there are too many
4204
  while ( func->numParams() > static_cast<int>( args.size() ) )
35,106✔
4205
  {
4206
    args.emplace_back( UninitObject::create() );
6✔
4207
  }
4208

4209
  // Resize args only for non-varadic functions
4210
  if ( !func->variadic() )
35,100✔
4211
    args.resize( func->numParams() );
35,076✔
4212

4213
  continuation->args = std::move( args );
35,100✔
4214

4215
  return continuation;
35,100✔
4216
}
4217

4218
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
1,620✔
4219
{
4220
  // Compare the program pointers
4221
  if ( prog < other.prog )
1,620✔
4222
    return true;
×
4223
  if ( prog > other.prog )
1,620✔
4224
    return false;
×
4225

4226
  // Compare the indices
4227
  if ( index < other.index )
1,620✔
4228
    return true;
174✔
4229
  if ( index > other.index )
1,446✔
4230
    return false;
69✔
4231

4232
  // Perform a case-insensitive comparison for method_name using stricmp
4233
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
1,377✔
4234
}
4235

4236
#ifdef ESCRIPT_PROFILE
4237
std::map<std::string, EscriptProfiler::profile_instr> EscriptProfiler::escript_profile_map_{};
4238
EscriptProfiler::EscriptProfiler( ExecutorModule* em, const ModuleFunction* modfunc,
4239
                                  const std::vector<BObjectRef>& fparams )
4240
{
4241
  name_ = em->functionName( modfunc->funcidx );
4242
  if ( !fparams.empty() )
4243
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4244
}
4245
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectRef& leftref,
4246
                                  const std::vector<BObjectRef>& fparams )
4247
{
4248
  switch ( ins.token.id )
4249
  {
4250
  case INS_GET_MEMBER:
4251
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.tokval() );
4252
    break;
4253
  case INS_GET_MEMBER_ID:
4254
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4255
    break;
4256
  case INS_CALL_METHOD_ID:
4257
    name_ = fmt::format( "MTHID_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4258
  default:
4259
    break;
4260
  }
4261
  if ( !fparams.empty() )
4262
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4263
}
4264
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectImp* callee,
4265
                                  const char* method_name, const std::vector<BObjectRef>& fparams )
4266
{
4267
  switch ( ins.token.id )
4268
  {
4269
  case INS_CALL_METHOD:
4270
    name_ = fmt::format( "MTH_{} .{}", callee->typeOf(), method_name );
4271
    break;
4272
  default:
4273
    break;
4274
  }
4275
  if ( !fparams.empty() )
4276
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4277
}
4278

4279
EscriptProfiler::~EscriptProfiler()
4280
{
4281
  auto profile_end = timer_.ellapsed().count();
4282
  auto itr = escript_profile_map_.find( name_ );
4283
  if ( itr != escript_profile_map_.end() )
4284
  {
4285
    itr->second.count++;
4286
    itr->second.sum += profile_end;
4287
    if ( itr->second.max < profile_end )
4288
      itr->second.max = profile_end;
4289
    else if ( itr->second.min > profile_end )
4290
      itr->second.min = profile_end;
4291
  }
4292
  else
4293
  {
4294
    escript_profile_map_[name_] = {
4295
        .sum = profile_end, .max = profile_end, .min = profile_end, .count = 1 };
4296
  }
4297
}
4298
std::string EscriptProfiler::result()
4299
{
4300
  std::string buffer = "FuncName,Count,Min,Max,Sum,Avarage\n";
4301
  for ( const auto& [name, profile] : escript_profile_map_ )
4302
  {
4303
    fmt::format_to( std::back_inserter( buffer ), "{},{},{},{},{},{:.2f}\n", name, profile.count,
4304
                    profile.min, profile.max, profile.sum, profile.sum / ( 1.0 * profile.count ) );
4305
  }
4306
  return buffer;
4307
}
4308
#endif
4309
}  // 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