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

polserver / polserver / 31968403210

16 Aug 2026 07:42PM UTC coverage: 78.778% (+0.05%) from 78.729%
31968403210

push

github

web-flow
Improve stacktraces (#943)

* add log reading tests

* refactor stacktrace in script

* add a test for script exceptions

* fix tests waiting too long if POL exited too fast

* improve script stacktraces

* improve stacktraces on windows and linux

* remove windows setthreadname that only worked with debugger

* attempt at making script status thread-safe

* fix comment

158 of 197 new or added lines in 16 files covered. (80.2%)

14 existing lines in 6 files now uncovered.

57160 of 72558 relevant lines covered (78.78%)

1427142.26 hits per line

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

87.73
/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 "bscript/executor.h"
17
#include "bscript/bobject.h"
18
#include "bscript/exectype.h"
19
#include "bscript/executor.inl.h"
20

21
#include "bscript/barray.h"
22
#include "bscript/bboolean.h"
23
#include "bscript/bclassinstance.h"
24
#include "bscript/bcontinuation.h"
25
#include "bscript/bcontiter.h"
26
#include "bscript/bdict.h"
27
#include "bscript/bdouble.h"
28
#include "bscript/berror.h"
29
#include "bscript/blong.h"
30
#include "bscript/bregexp.h"
31
#include "bscript/bspecialjump.h"
32
#include "bscript/bspread.h"
33
#include "bscript/bstring.h"
34
#include "bscript/config.h"
35
#include "bscript/eprog.h"
36
#include "bscript/escriptv.h"
37
#include "bscript/execmodl.h"
38
#include "bscript/fmodule.h"
39
#include "bscript/objmethods.h"
40
#include "bscript/str.h"
41
#include "bscript/token.h"
42
#include "bscript/tokens.h"
43
#include "clib/Debugging/ExceptionParser.h"
44
#include "clib/clib.h"
45
#include "clib/logfacility.h"
46
#include "clib/passert.h"
47
#include "clib/scriptstatus.h"
48
#include "clib/stlutil.h"
49
#include "clib/strutil.h"
50
#include "clib/threadhelp.h"
51
#include <iterator>
52
#include <limits>
53
#ifdef MEMORYLEAK
54
#include "clib/mlog.h"
55
#endif
56

57
#include <boost/core/demangle.hpp>
58
#include <boost/multi_index/ordered_index.hpp>
59
#include <boost/multi_index/sequenced_index.hpp>
60
#include <boost/multi_index_container.hpp>
61

62
#include <cstdlib>
63
#include <cstring>
64
#include <exception>
65
#include <numeric>
66
#include <ranges>
67
#include <string_view>
68
#include <typeinfo>
69

70

71
namespace Pol::Bscript
72
{
73
std::set<Executor*> executor_instances;
74

75
void display_executor_instances()
4 ✔
76
{
77
  for ( const auto& ex : executor_instances )
4 ✔
78
  {
79
    // Fix for crashes due to orphaned script instances.
80
    if ( !ex->empty_scriptname() )
×
81
      INFO_PRINTLN( ex->scriptname() );
×
82
  }
83
}
4 ✔
84

85
ExecutorDebugEnvironment::ExecutorDebugEnvironment( std::weak_ptr<ExecutorDebugListener> listener,
2 ✔
86
                                                    bool set_attaching )
2 ✔
87
    : debug_state( set_attaching ? ExecutorDebugState::ATTACHING : ExecutorDebugState::RUN ),
2 ✔
88
      breakpoints(),
2 ✔
89
      break_on_linechange_from{ ~0u, ~0u },
2 ✔
90
      bp_skip( ~0u ),
2 ✔
91
      listener( listener )
2 ✔
92
{
93
}
2 ✔
94

95
size_t ExecutorDebugEnvironment::sizeEstimate() const
×
96
{
97
  size_t size = sizeof( *this ) + Clib::memsize( breakpoints ) + Clib::memsize( tmpbreakpoints );
×
98
  return size;
×
99
}
100

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

181
  // check for breakpoints on this instruction
182
  if ( ( breakpoints.count( ex.PC ) || tmpbreakpoints.count( ex.PC ) ) && bp_skip != ex.PC &&
304 ✔
183
       !ex.halt() )
4 ✔
184
  {
185
    tmpbreakpoints.erase( ex.PC );
4 ✔
186
    bp_skip = ex.PC;
4 ✔
187
    debug_state = ExecutorDebugState::ATTACHED;
4 ✔
188
    ex.sethalt( true );
4 ✔
189
    return false;
4 ✔
190
  }
191
  bp_skip = ~0u;
296 ✔
192

193
  return true;
296 ✔
194
}
195

196
extern int executor_count;
197
Clib::SpinLock Executor::_executor_lock;
198
Executor::Executor()
2,906 ✔
199
    : done( 0 ),
2,906 ✔
200
      error_( false ),
2,906 ✔
201
      halt_( false ),
2,906 ✔
202
      run_ok_( false ),
2,906 ✔
203
      debug_level( NONE ),
2,906 ✔
204
      PC( 0 ),
2,906 ✔
205
      Globals2( std::make_shared<BObjectRefVec>() ),
2,906 ✔
206
      Locals2( new BObjectRefVec ),
2,906 ✔
207
      nLines( 0 ),
2,906 ✔
208
      current_module_function( nullptr ),
2,906 ✔
209
      prog_ok_( false ),
2,906 ✔
210
      viewmode_( false ),
2,906 ✔
211
      runs_to_completion_( false ),
2,906 ✔
212
      dbg_env_( nullptr ),
2,906 ✔
213
      func_result_( nullptr )
8,718 ✔
214
{
215
  Clib::SpinLockGuard lock( _executor_lock );
2,906 ✔
216
  ++executor_count;
2,906 ✔
217
  executor_instances.insert( this );
2,906 ✔
218

219
  if ( !UninitObject::SharedInstance )
2,906 ✔
220
  {
221
    UninitObject::SharedInstance = new UninitObject;
2,118 ✔
222
    UninitObject::SharedInstanceOwner.set( UninitObject::SharedInstance );
2,118 ✔
223
  }
224
}
2,906 ✔
225

226
Executor::~Executor()
2,906 ✔
227
{
228
  {
229
    Clib::SpinLockGuard lock( _executor_lock );
2,906 ✔
230
    --executor_count;
2,906 ✔
231
    executor_instances.erase( this );
2,906 ✔
232
  }
2,906 ✔
233
  cleanup();
2,906 ✔
234
}
2,906 ✔
235
void Executor::cleanup()
3,698 ✔
236
{
237
  if ( dbg_env_ )
3,698 ✔
238
  {
239
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
8 ✔
240
      listener->on_destroy();
4 ✔
241
  }
242

243
  delete Locals2;
3,698 ✔
244
  Locals2 = nullptr;
3,698 ✔
245

246
  while ( !upperLocals2.empty() )
3,699 ✔
247
  {
248
    delete upperLocals2.back();
1 ✔
249
    upperLocals2.pop_back();
1 ✔
250
  }
251

252
  execmodules.clear();
3,698 ✔
253
  Clib::delete_all( availmodules );
3,698 ✔
254
}
3,698 ✔
255

256
bool Executor::AttachFunctionalityModules()
4,794 ✔
257
{
258
  for ( auto& fm : prog_->modules )
18,497 ✔
259
  {
260
    // if no function in the module is actually called, don't go searching for it.
261
    if ( fm->functions.empty() )
13,703 ✔
262
    {
263
      execmodules.push_back( nullptr );
×
264
      continue;
×
265
    }
266

267
    ExecutorModule* em = findModule( fm->modulename );
13,703 ✔
268
    execmodules.push_back( em );
13,703 ✔
269
    if ( em == nullptr )
13,703 ✔
270
    {
271
      ERROR_PRINTLN( "WARNING: {}: Unable to find module {}", scriptname(), fm->modulename.get() );
×
272
      return false;
×
273
    }
274

275
    if ( !fm->have_indexes )
13,703 ✔
276
    {
277
      /*
278
          FIXE: Possible optimization: store these function indexes in the
279
          EScriptProgram object, since those are cached.  Then, we only
280
          have to find the module index.
281
          */
282
      for ( unsigned fidx = 0; fidx < fm->functions.size(); fidx++ )
8,840 ✔
283
      {
284
        ModuleFunction* func = fm->functions[fidx];
5,402 ✔
285
        // FIXME: should check number of params, blah.
286
        if ( !func->name.get().empty() )
5,402 ✔
287
        {
288
          func->funcidx = em->functionIndex( func->name.get() );
5,402 ✔
289
          if ( func->funcidx == -1 )
5,402 ✔
290
          {
291
            ERROR_PRINTLN( "Unable to find {}::{}", fm->modulename.get(), func->name.get() );
×
292
            return false;
×
293
          }
294
        }
295
      }
296
      fm->have_indexes = true;
3,438 ✔
297
    }
298
  }
299
  return true;
4,794 ✔
300
}
301

302
int Executor::getParams( unsigned howMany )
135,176,590 ✔
303
{
304
  if ( howMany )
135,176,590 ✔
305
  {
306
    fparams.resize( howMany );
134,885,870 ✔
307
    for ( int i = howMany - 1; i >= 0; --i )
269,858,815 ✔
308
    {
309
      if ( ValueStack.empty() )
134,972,945 ✔
310
      {
311
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
312
        seterror( true );
×
313
        return -1;
×
314
      }
315
      fparams[i] = ValueStack.back();
134,972,945 ✔
316
      ValueStack.pop_back();
134,972,945 ✔
317
    }
318
  }
319
  expandParams();
135,176,590 ✔
320
  return 0;
135,176,590 ✔
321
}
322

323
void Executor::expandParams()
135,176,590 ✔
324
{
325
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
270,150,765 ✔
326
  {
327
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
134,974,175 ✔
328
    {
329
      // defer destruction
330
      BObjectRef obj( spread );
629 ✔
331

332
      // Remove the spread
333
      fparams.erase( fparams.begin() + i );
629 ✔
334

335
      BObjectRef refIter( UninitObject::create() );
629 ✔
336

337
      auto pIter = std::unique_ptr<ContIterator>(
338
          spread->object->impptr()->createIterator( refIter.get() ) );
629 ✔
339

340
      BObject* next = pIter->step();
629 ✔
341

342
      int added = 0;
629 ✔
343
      while ( next != nullptr )
1,859 ✔
344
      {
345
        fparams.insert( fparams.begin() + i + added, BObjectRef( next ) );
1,230 ✔
346
        next = pIter->step();
1,230 ✔
347
        added++;
1,230 ✔
348
      }
349
      i += added;
629 ✔
350
    }
629 ✔
351
  }
352
}
135,176,590 ✔
353

354
void Executor::cleanParams()
135,173,339 ✔
355
{
356
  fparams.clear();
135,173,339 ✔
357
}
135,173,339 ✔
358

359
int Executor::makeString( unsigned param )
10,840 ✔
360
{
361
  BObject* obj = getParam( param );
10,840 ✔
362
  if ( !obj )
10,840 ✔
363
    return -1;
×
364
  if ( obj->isa( BObjectImp::OTString ) )
10,840 ✔
365
    return 0;
10,840 ✔
366

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

369
  return 0;
×
370
}
371

372
const char* Executor::paramAsString( unsigned param )
5,479 ✔
373
{
374
  makeString( param );
5,479 ✔
375
  BObjectImp* objimp = fparams[param]->impptr();
5,479 ✔
376

377
  String* str = (String*)objimp;
5,479 ✔
378
  return str ? str->data() : "";
5,479 ✔
379
}
380

381
int Executor::makeDouble( unsigned param )
×
382
{
383
  BObject* obj = getParam( param );
×
384
  if ( !obj )
×
385
    return -1;
×
386
  if ( obj->isa( BObjectImp::OTDouble ) )
×
387
    return 0;
×
388
  if ( auto* v = obj->impptr_if<BLong>() )
×
389
    fparams[param].set( new Double( v->value() ) );
×
390
  else
391
    fparams[param].set( new Double( 0.0 ) );
×
392

393
  return 0;
×
394
}
395

396
double Executor::paramAsDouble( unsigned param )
×
397
{
398
  makeDouble( param );
×
399
  if ( auto* v = getParam( param )->impptr_if<Double>() )
×
400
    return v->value();
×
401
  return 0.0;
×
402
}
403

404
int Executor::paramAsLong( unsigned param )
134,586,008 ✔
405
{
406
  BObjectImp* objimp = getParam( param )->impptr();
134,586,008 ✔
407
  if ( auto* l = impptrIf<BLong>( objimp ) )
134,586,008 ✔
408
    return l->value();
134,586,002 ✔
409
  if ( auto* d = impptrIf<Double>( objimp ) )
6 ✔
410
    return static_cast<int>( d->value() );
6 ✔
411
  return 0;
×
412
}
413
BObject* Executor::getParam( unsigned param )
134,596,900 ✔
414
{
415
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
134,596,900 ✔
416
                                         ": Less Parameter than expected. " +
417
                                         "You should use *.em-files shipped with this Core and "
418
                                         "recompile ALL of your Scripts _now_! RTFM" );
419

420
  return fparams[param].get();
134,596,900 ✔
421
}
422

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

430
  return fparams[param].get()->impptr();
268,539 ✔
431
}
432

433
BObject* Executor::getParamObj( unsigned param )
1,381 ✔
434
{
435
  if ( fparams.size() > param )
1,381 ✔
436
    return fparams[param].get();
1,381 ✔
437
  return nullptr;
×
438
}
439

440
BObjectImp* Executor::getParamImp( unsigned param, BObjectImp::BObjectType type )
83,039 ✔
441
{
442
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
83,039 ✔
443
                                         ": Less Parameter than expected. " +
444
                                         "You should use *.em-files shipped with this Core and "
445
                                         "recompile ALL of your Scripts _now_! RTFM" );
446

447
  BObjectImp* imp = fparams[param].get()->impptr();
83,039 ✔
448

449
  passert( imp != nullptr );
83,039 ✔
450

451
  if ( imp->isa( type ) )
83,039 ✔
452
    return imp;
82,777 ✔
453
  if ( !IS_DEBUGLOG_DISABLED )
262 ✔
454
  {
455
    std::string tmp = fmt::format( "Script Error in '{}' PC={}:\n", scriptname(), PC );
262 ✔
456
    if ( current_module_function )
262 ✔
457
      fmt::format_to( std::back_inserter( tmp ), "\tCall to function {}:\n",
366 ✔
458
                      current_module_function->name.get() );
183 ✔
459
    else
460
      tmp += "\tCall to an object method.\n";
79 ✔
461
    fmt::format_to( std::back_inserter( tmp ),
×
462
                    "\tParameter {}: Expected datatype {}, got datatype {}", param,
463
                    BObjectImp::typestr( type ), BObjectImp::typestr( imp->type() ) );
262 ✔
464
    DEBUGLOGLN( tmp );
262 ✔
465
  }
262 ✔
466
  return nullptr;
262 ✔
467
}
468

469
BObjectImp* Executor::getParamImp2( unsigned param, BObjectImp::BObjectType type )
11,280 ✔
470
{
471
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
11,280 ✔
472
                                         ": Less Parameter than expected. " +
473
                                         "You should use *.em-files shipped with this Core and "
474
                                         "recompile ALL of your Scripts _now_! RTFM" );
475

476
  BObjectImp* imp = fparams[param].get()->impptr();
11,280 ✔
477

478
  passert( imp != nullptr );
11,280 ✔
479

480
  if ( imp->isa( type ) )
11,280 ✔
481
    return imp;
11,234 ✔
482
  std::string report = "Invalid parameter type.  Expected param " + Clib::tostring( param ) +
92 ✔
483
                       " as " + BObjectImp::typestr( type ) + ", got " +
138 ✔
484
                       BObjectImp::typestr( imp->type() );
92 ✔
485
  func_result_ = new BError( report );
46 ✔
486
  return nullptr;
46 ✔
487
}
46 ✔
488

489

490
const String* Executor::getStringParam( unsigned param )
58,978 ✔
491
{
492
  return Clib::explicit_cast<String*, BObjectImp*>( getParamImp( param, BObjectImp::OTString ) );
58,978 ✔
493
}
494

495
const BLong* Executor::getLongParam( unsigned param )
×
496
{
497
  return Clib::explicit_cast<BLong*, BObjectImp*>( getParamImp( param, BObjectImp::OTLong ) );
×
498
}
499

500
bool Executor::getStringParam( unsigned param, const String*& pstr )
58,933 ✔
501
{
502
  pstr = getStringParam( param );
58,933 ✔
503
  return ( pstr != nullptr );
58,933 ✔
504
}
505

506
bool Executor::getParam( unsigned param, int& value )
20,836 ✔
507
{
508
  BLong* plong =
509
      Clib::explicit_cast<BLong*, BObjectImp*>( getParamImp( param, BObjectImp::OTLong ) );
20,836 ✔
510
  if ( plong == nullptr )
20,836 ✔
511
    return false;
61 ✔
512

513
  value = plong->value();
20,775 ✔
514
  return true;
20,775 ✔
515
}
516

517
void Executor::setFunctionResult( BObjectImp* imp )
65 ✔
518
{
519
  func_result_ = imp;
65 ✔
520
}
65 ✔
521

522
void Executor::printStack( const std::string& message = "" )
74,994 ✔
523
{
524
  if ( debug_level < INSTRUCTIONS )
74,994 ✔
525
    return;
74,994 ✔
526

527
  if ( !message.empty() )
×
528
  {
529
    INFO_PRINTLN( message );
×
530
  }
531

532
  size_t i = 0;
×
533
  for ( auto& fparam : std::ranges::reverse_view( fparams ) )
×
534
  {
535
    auto* ptr = fparam.get()->impptr();
×
536
    INFO_PRINTLN( "fparam[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
537
    i++;
×
538
  }
539

540
  i = 0;
×
541
  for ( auto& riter : std::ranges::reverse_view( ValueStack ) )
×
542
  {
543
    auto* ptr = riter.get()->impptr();
×
544
    INFO_PRINTLN( "vstack[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
545
    i++;
×
546
  }
547

548
  INFO_PRINTLN( "---" );
×
549
}
550

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

558
  value = plong->value();
75 ✔
559
  if ( value >= 0 && value <= maxval )
75 ✔
560
    return true;
70 ✔
561
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
5 ✔
562
                                          param, value, maxval ) );
10 ✔
563
  return false;
5 ✔
564
}
565

566
bool Executor::getParam( unsigned param, int& value, int minval, int maxval )
2,209 ✔
567
{
568
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
2,209 ✔
569
  if ( !imp )
2,209 ✔
570
    return false;
22 ✔
571
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
2,187 ✔
572

573
  value = plong->value();
2,187 ✔
574
  if ( value >= minval && value <= maxval )
2,187 ✔
575
    return true;
2,157 ✔
576
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
30 ✔
577
                                          param, value, minval, maxval ) );
60 ✔
578
  return false;
30 ✔
579
}
580

581
bool Executor::getRealParam( unsigned param, double& value )
182 ✔
582
{
583
  BObjectImp* imp = getParamImp( param );
182 ✔
584
  if ( auto* d = impptrIf<Double>( imp ) )
182 ✔
585
  {
586
    value = d->value();
45 ✔
587
    return true;
45 ✔
588
  }
589
  if ( auto* l = impptrIf<BLong>( imp ) )
137 ✔
590
  {
591
    value = l->value();
83 ✔
592
    return true;
83 ✔
593
  }
594
  DEBUGLOGLN(
54 ✔
595
      "Script Error in '{}' PC={}: \n"
596
      "\tCall to function {}:\n"
597
      "\tParameter {}: Expected Integer or Real, got datatype {}",
598
      scriptname(), PC, current_module_function->name.get(), param,
54 ✔
599
      BObjectImp::typestr( imp->type() ) );
108 ✔
600

601
  return false;
54 ✔
602
}
603

604
bool Executor::getObjArrayParam( unsigned param, ObjArray*& pobjarr )
1,574 ✔
605
{
606
  pobjarr =
1,574 ✔
607
      Clib::explicit_cast<ObjArray*, BObjectImp*>( getParamImp( param, BObjectImp::OTArray ) );
1,574 ✔
608
  return ( pobjarr != nullptr );
1,574 ✔
609
}
610

611
BApplicObjBase* Executor::getApplicObjParam( unsigned param, const BApplicObjType* object_type )
466 ✔
612
{
613
  auto aob = static_cast<BApplicObjBase*>( getParamImp( param, BObjectImp::OTApplicObj ) );
466 ✔
614
  if ( aob == nullptr )
466 ✔
615
    return nullptr;
39 ✔
616

617
  if ( aob->object_type() == object_type )
427 ✔
618
    return aob;
426 ✔
619
  DEBUGLOGLN(
1 ✔
620
      "Script Error in '{}' PC={}: \n"
621
      "\tCall to function {}:\n"
622
      "\tParameter {}: Expected datatype, got datatype {}",
623
      scriptname(), PC, current_module_function->name.get(), param, aob->getStringRep() );
1 ✔
624

625
  return nullptr;
1 ✔
626
}
627

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

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

646
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short minval,
562 ✔
647
                         unsigned short maxval )
648
{
649
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
562 ✔
650
  if ( !imp )
562 ✔
651
    return false;
×
652
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
562 ✔
653

654
  int longvalue = plong->value();
562 ✔
655
  if ( longvalue >= minval && longvalue <= maxval )
562 ✔
656
  {
657
    value = static_cast<unsigned short>( longvalue );
552 ✔
658
    return true;
552 ✔
659
  }
660
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
10 ✔
661
                                          param, longvalue, minval, maxval ) );
20 ✔
662
  return false;
10 ✔
663
}
664
bool Executor::getParam( unsigned param, unsigned short& value )
5,666 ✔
665
{
666
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
5,666 ✔
667
  if ( !imp )
5,666 ✔
668
    return false;
19 ✔
669
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
5,647 ✔
670

671
  int longvalue = plong->value();
5,647 ✔
672
  if ( longvalue >= 0 && longvalue <= USHRT_MAX )
5,647 ✔
673
  {
674
    value = static_cast<unsigned short>( longvalue );
5,646 ✔
675
    return true;
5,646 ✔
676
  }
677
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
1 ✔
678
                                          param, longvalue, USHRT_MAX ) );
2 ✔
679
  return false;
1 ✔
680
}
681
bool Executor::getParam( unsigned param, unsigned& value )
154 ✔
682
{
683
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
154 ✔
684
  if ( !imp )
154 ✔
685
    return false;
5 ✔
686
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
149 ✔
687

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

699
bool Executor::getParam( unsigned param, short& value )
1,292 ✔
700
{
701
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
1,292 ✔
702
  if ( !imp )
1,292 ✔
703
    return false;
×
704
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
1,292 ✔
705

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

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

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

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

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

753
bool Executor::getParam( unsigned param, signed char& value )
1,322 ✔
754
{
755
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
1,322 ✔
756
  if ( !imp )
1,322 ✔
757
    return false;
×
758

759
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
1,322 ✔
760

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

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

793
  return false;
3 ✔
794
}
795

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

820
BObjectRef& Executor::LocalVar( unsigned int varnum )
×
821
{
822
  passert( Locals2 );
×
823
  passert( varnum < Locals2->size() );
×
824

825
  return ( *Locals2 )[varnum];
×
826
}
827

828
BObjectRef& Executor::GlobalVar( unsigned int varnum )
×
829
{
830
  if ( varnum >= Globals2->size() )
×
831
  {
832
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
×
833
    seterror( true );
×
834
    UninitObject::SharedInstanceRef.set( UninitObject::SharedInstance );
×
835
    return UninitObject::SharedInstanceRef;
×
836
  }
837
  return ( *Globals2 )[varnum];
×
838
}
839

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

848

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

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

862
  Globals2->clear();
2,906 ✔
863
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
5,593 ✔
864
  {
865
    Globals2->emplace_back( UninitObject::create() );
2,687 ✔
866
  }
867

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

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

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

888

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

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

904
  ExecutorModule* em = execmodules[token.module];
134,947,356 ✔
905

906
  func_result_ = nullptr;
134,947,356 ✔
907
  BObjectImp* resimp;
908
  {
909
    ESCRIPT_PROFILER( em, modfunc, fparams );
910
    resimp = em->execFunc( modfunc->funcidx );
134,947,356 ✔
911
  }
912
  if ( func_result_ )
134,947,356 ✔
913
  {
914
    if ( resimp )
111 ✔
915
    {
916
      BObject obj( resimp );
111 ✔
917
    }
111 ✔
918
    ValueStack.emplace_back( func_result_ );
111 ✔
919
    func_result_ = nullptr;
111 ✔
920
  }
921
  else if ( resimp )
134,947,245 ✔
922
  {
923
    ValueStack.emplace_back( resimp );
134,947,220 ✔
924
  }
925
  else
926
  {
927
    ValueStack.emplace_back( UninitObject::create() );
25 ✔
928
  }
929

930
  current_module_function = nullptr;
134,947,356 ✔
931
}
134,947,356 ✔
932

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

938
  Locals2->emplace_back( UninitObject::create() );
203,756 ✔
939

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

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

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

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

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

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

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

971
  Locals2->emplace_back( objref );
29,012 ✔
972
}
29,012 ✔
973

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

987

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

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

997
  return left.impref().operDotPlus( varname.data() );
4,559 ✔
998
}
999

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

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

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

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

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

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

1024

1025
/* Coming into initforeach, the expr to be iterated through is on the value stack.
1026
   Initforeach must create three local variables:
1027
   0. the iterator
1028
   1. the expression
1029
   2. the counter
1030
   and remove the expression from the value stack.
1031
   It then jumps to the STEPFOREACH instruction.
1032
   */
1033
void Executor::ins_initforeach( const Instruction& ins )
2,540 ✔
1034
{
1035
  Locals2->emplace_back( UninitObject::create() );  // the iterator
2,540 ✔
1036

1037
  auto pIterVal = new BObject( UninitObject::create() );
2,540 ✔
1038

1039
  // this is almost like popParam, only we don't want a copy.
1040
  BObjectRef objref = getObjRef();
2,540 ✔
1041
  ContIterator* pIter = objref->impptr()->createIterator( pIterVal );
2,540 ✔
1042
  Locals2->emplace_back( pIter );
2,540 ✔
1043

1044
  Locals2->emplace_back( pIterVal );
2,540 ✔
1045

1046
  // Jump to to the corresponding `stepforeach` instruction, advancing the iterator.
1047
  PC = ins.token.lval;
2,540 ✔
1048
}
2,540 ✔
1049

1050
void Executor::ins_stepforeach( const Instruction& ins )
30,625 ✔
1051
{
1052
  size_t locsize = Locals2->size();
30,625 ✔
1053
  ContIterator* pIter = ( *Locals2 )[locsize - 2]->impptr<ContIterator>();
30,625 ✔
1054

1055
  BObject* next = pIter->step();
30,625 ✔
1056
  // If iterator has a value, set it on the locals stack and jump to the
1057
  // corresponding instruction after `initforeach`.
1058
  if ( next != nullptr )
30,625 ✔
1059
  {
1060
    ( *Locals2 )[locsize - 3].set( next );
28,498 ✔
1061
    PC = ins.token.lval;
28,498 ✔
1062
  }
1063
}
30,625 ✔
1064

1065
/*
1066
    Coming into the INITFOR, there will be two values on the value stack:
1067
    START VALUE
1068
    END VALUE
1069

1070
    If START VALUE > END VALUE, we skip the whole for loop.
1071
    (the INITFOR's lval has the instr to jump to)
1072
    */
1073
void Executor::ins_initfor( const Instruction& ins )
112 ✔
1074
{
1075
  BObjectRef endref = getObjRef();
112 ✔
1076
  BObjectRef startref = getObjRef();
112 ✔
1077
  if ( *startref.get() > *endref.get() )
112 ✔
1078
  {
1079
    PC = ins.token.lval;
×
1080
    return;
×
1081
  }
1082

1083
  Locals2->emplace_back( startref->clone() );  // the iterator
112 ✔
1084
  Locals2->emplace_back( endref->clone() );
112 ✔
1085
}
112 ✔
1086

1087
void Executor::ins_nextfor( const Instruction& ins )
1,552 ✔
1088
{
1089
  size_t locsize = Locals2->size();
1,552 ✔
1090
  BObjectImp* itr = ( *Locals2 )[locsize - 2]->impptr();
1,552 ✔
1091
  BObjectImp* end = ( *Locals2 )[locsize - 1]->impptr();
1,552 ✔
1092

1093
  if ( auto* l = impptrIf<BLong>( itr ) )
1,552 ✔
1094
    l->increment();
1,552 ✔
1095
  else if ( auto* d = impptrIf<Double>( itr ) )
×
1096
    d->increment();
×
1097

1098
  if ( *end >= *itr )
1,552 ✔
1099
  {
1100
    PC = ins.token.lval;
1,454 ✔
1101
  }
1102
}
1,552 ✔
1103

1104
int Executor::ins_casejmp_findlong( const Token& token, BLong* blong )
148 ✔
1105
{
1106
  const unsigned char* dataptr = token.dataptr;
148 ✔
1107
  for ( ;; )
1108
  {
1109
    unsigned short offset;
1110
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
329 ✔
1111
    dataptr += 2;
329 ✔
1112
    unsigned char type = *dataptr;
329 ✔
1113
    dataptr += 1;
329 ✔
1114
    if ( type == CASE_TYPE_LONG )
329 ✔
1115
    {
1116
      int v = blong->value();
248 ✔
1117
      if ( std::memcmp( &v, dataptr, sizeof( int ) ) == 0 )
248 ✔
1118
        return offset;
115 ✔
1119
      dataptr += 4;
133 ✔
1120
    }
1121
    else if ( type == CASE_TYPE_DEFAULT )
81 ✔
1122
    {
1123
      return offset;
33 ✔
1124
    }
1125
    else if ( type == CASE_TYPE_UNINIT )
48 ✔
1126
    {
1127
      /* nothing */
1128
    }
1129
    else if ( type == CASE_TYPE_BOOL )
48 ✔
1130
    {
1131
      dataptr += 1;
×
1132
    }
1133
    else if ( type == CASE_TYPE_STRING )
48 ✔
1134
    {
1135
      unsigned char len = *dataptr;
48 ✔
1136
      dataptr += 1 + len;
48 ✔
1137
    }
1138
  }
181 ✔
1139
}
1140

1141
int Executor::ins_casejmp_findbool( const Token& token, BBoolean* bbool )
15 ✔
1142
{
1143
  const unsigned char* dataptr = token.dataptr;
15 ✔
1144
  for ( ;; )
1145
  {
1146
    unsigned short offset;
1147
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
45 ✔
1148
    dataptr += 2;
45 ✔
1149
    unsigned char type = *dataptr;
45 ✔
1150
    dataptr += 1;
45 ✔
1151
    if ( type == CASE_TYPE_LONG )
45 ✔
1152
    {
1153
      dataptr += 4;
24 ✔
1154
    }
1155
    else if ( type == CASE_TYPE_DEFAULT )
21 ✔
1156
    {
1157
      return offset;
15 ✔
1158
    }
1159
    else if ( type == CASE_TYPE_UNINIT )
21 ✔
1160
    {
1161
      /* nothing */
1162
    }
1163
    else if ( type == CASE_TYPE_BOOL )
21 ✔
1164
    {
1165
      bool value = static_cast<bool>( *dataptr );
21 ✔
1166
      dataptr += 1;
21 ✔
1167
      if ( value == bbool->value() )
21 ✔
1168
      {
1169
        return offset;
15 ✔
1170
      }
1171
    }
1172
    else if ( type == CASE_TYPE_STRING )
×
1173
    {
1174
      unsigned char len = *dataptr;
×
1175
      dataptr += 1 + len;
×
1176
    }
1177
  }
30 ✔
1178
}
1179

1180
int Executor::ins_casejmp_finduninit( const Token& token )
6 ✔
1181
{
1182
  const unsigned char* dataptr = token.dataptr;
6 ✔
1183
  for ( ;; )
1184
  {
1185
    unsigned short offset;
1186
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
30 ✔
1187
    dataptr += 2;
30 ✔
1188
    unsigned char type = *dataptr;
30 ✔
1189
    dataptr += 1;
30 ✔
1190
    if ( type == CASE_TYPE_LONG )
30 ✔
1191
    {
1192
      dataptr += 4;
12 ✔
1193
    }
1194
    else if ( type == CASE_TYPE_DEFAULT )
18 ✔
1195
    {
1196
      return offset;
6 ✔
1197
    }
1198
    else if ( type == CASE_TYPE_UNINIT )
18 ✔
1199
    {
1200
      return offset;
6 ✔
1201
    }
1202
    else if ( type == CASE_TYPE_BOOL )
12 ✔
1203
    {
1204
      dataptr += 1;
12 ✔
1205
    }
1206
    else if ( type == CASE_TYPE_STRING )
×
1207
    {
1208
      unsigned char len = *dataptr;
×
1209
      dataptr += 1 + len;
×
1210
    }
1211
  }
24 ✔
1212
}
1213

1214
int Executor::ins_casejmp_findstring( const Token& token, String* bstringimp )
389 ✔
1215
{
1216
  const std::string& bstring = bstringimp->value();
389 ✔
1217
  const unsigned char* dataptr = token.dataptr;
389 ✔
1218
  for ( ;; )
1219
  {
1220
    unsigned short offset;
1221
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
2,416 ✔
1222
    dataptr += 2;
2,416 ✔
1223
    unsigned char type = *dataptr;
2,416 ✔
1224
    dataptr += 1;
2,416 ✔
1225
    if ( type == CASE_TYPE_LONG )
2,416 ✔
1226
    {
1227
      dataptr += 4;
36 ✔
1228
    }
1229
    else if ( type == CASE_TYPE_DEFAULT )
2,380 ✔
1230
    {
1231
      return offset;
389 ✔
1232
    }
1233
    else if ( type == CASE_TYPE_BOOL )
2,358 ✔
1234
    {
1235
      dataptr += 1;
12 ✔
1236
    }
1237
    else if ( type == CASE_TYPE_UNINIT )
2,346 ✔
1238
    {
1239
      /* nothing */
1240
    }
1241
    else if ( type == CASE_TYPE_STRING )
2,340 ✔
1242
    {
1243
      unsigned char len = *dataptr;
2,340 ✔
1244
      dataptr += 1;
2,340 ✔
1245
      if ( bstring.size() == len && memcmp( bstring.data(), dataptr, len ) == 0 )
2,340 ✔
1246
      {
1247
        return offset;
367 ✔
1248
      }
1249
      dataptr += len;
1,973 ✔
1250
    }
1251
  }
2,027 ✔
1252
}
1253

1254
int Executor::ins_casejmp_finddefault( const Token& token )
19 ✔
1255
{
1256
  const unsigned char* dataptr = token.dataptr;
19 ✔
1257
  for ( ;; )
1258
  {
1259
    unsigned short offset;
1260
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
68 ✔
1261
    dataptr += 2;
68 ✔
1262
    unsigned char type = *dataptr;
68 ✔
1263
    dataptr += 1;
68 ✔
1264
    if ( type == CASE_TYPE_LONG )
68 ✔
1265
    {
1266
      dataptr += 4;
25 ✔
1267
    }
1268
    else if ( type == CASE_TYPE_DEFAULT )
43 ✔
1269
    {
1270
      return offset;
19 ✔
1271
    }
1272
    else if ( type == CASE_TYPE_UNINIT )
24 ✔
1273
    {
1274
      /* nothing */
1275
    }
1276
    else if ( type == CASE_TYPE_BOOL )
18 ✔
1277
    {
1278
      dataptr += 1;
12 ✔
1279
    }
1280
    else if ( type == CASE_TYPE_STRING )
6 ✔
1281
    {
1282
      unsigned char len = *dataptr;
6 ✔
1283
      dataptr += 1 + len;
6 ✔
1284
    }
1285
  }
49 ✔
1286
}
1287

1288
void Executor::ins_casejmp( const Instruction& ins )
577 ✔
1289
{
1290
  BObjectRef& objref = ValueStack.back();
577 ✔
1291
  BObjectImp* objimp = objref->impptr();
577 ✔
1292
  if ( auto* l = impptrIf<BLong>( objimp ) )
577 ✔
1293
    PC = ins_casejmp_findlong( ins.token, l );
148 ✔
1294
  else if ( auto* s = impptrIf<String>( objimp ) )
429 ✔
1295
    PC = ins_casejmp_findstring( ins.token, s );
389 ✔
1296
  else if ( auto* b = impptrIf<BBoolean>( objimp ) )
40 ✔
1297
    PC = ins_casejmp_findbool( ins.token, b );
15 ✔
1298
  else if ( impptrIf<UninitObject>( objimp ) )
25 ✔
1299
    PC = ins_casejmp_finduninit( ins.token );
6 ✔
1300
  else
1301
    PC = ins_casejmp_finddefault( ins.token );
19 ✔
1302
  ValueStack.pop_back();
577 ✔
1303
}
577 ✔
1304

1305

1306
void Executor::ins_jmpiftrue( const Instruction& ins )
26,095 ✔
1307
{
1308
  BObjectRef& objref = ValueStack.back();
26,095 ✔
1309

1310
  if ( objref->impptr()->isTrue() )
26,095 ✔
1311
    PC = (unsigned)ins.token.lval;
23,086 ✔
1312

1313
  ValueStack.pop_back();
26,095 ✔
1314
}
26,095 ✔
1315

1316
void Executor::ins_jmpiffalse( const Instruction& ins )
527,438 ✔
1317
{
1318
  BObjectRef& objref = ValueStack.back();
527,438 ✔
1319

1320
  if ( !objref->impptr()->isTrue() )
527,438 ✔
1321
    PC = (unsigned)ins.token.lval;
348,145 ✔
1322

1323
  ValueStack.pop_back();
527,438 ✔
1324
}
527,438 ✔
1325

1326
void Executor::ins_interpolate_string( const Instruction& ins )
15,076 ✔
1327
{
1328
  auto count = ins.token.lval;
15,076 ✔
1329
  if ( count == 0 )
15,076 ✔
1330
  {
1331
    ValueStack.emplace_back( new String( "" ) );
3 ✔
1332
  }
1333
  else
1334
  {
1335
    size_t length = 0;
15,073 ✔
1336

1337
    std::vector<std::string> contents;
15,073 ✔
1338
    contents.reserve( count );
15,073 ✔
1339

1340
    while ( count-- )
55,890 ✔
1341
    {
1342
      BObjectRef rightref = ValueStack.back();
40,817 ✔
1343
      ValueStack.pop_back();
40,817 ✔
1344
      auto str = rightref->impptr()->getStringRep();
40,817 ✔
1345
      length += str.length();
40,817 ✔
1346
      contents.push_back( std::move( str ) );
40,817 ✔
1347
    }
40,817 ✔
1348

1349
    std::string joined;
15,073 ✔
1350
    joined.reserve( length );
15,073 ✔
1351

1352
    while ( !contents.empty() )
55,890 ✔
1353
    {
1354
      joined += contents.back();
40,817 ✔
1355
      contents.pop_back();
40,817 ✔
1356
    }
1357

1358
    ValueStack.emplace_back( new String( joined ) );
15,073 ✔
1359
  }
15,073 ✔
1360
}
15,076 ✔
1361

1362
void Executor::ins_format_expression( const Instruction& )
2,315 ✔
1363
{
1364
  BObjectRef formatref = ValueStack.back();
2,315 ✔
1365
  ValueStack.pop_back();
2,315 ✔
1366
  BObjectRef& exprref = ValueStack.back();
2,315 ✔
1367
  BObject& expr = *exprref;
2,315 ✔
1368

1369
  auto format = formatref->impptr()->getFormattedStringRep();
2,315 ✔
1370
  auto formatted = Bscript::get_formatted( expr.impptr(), format );
2,315 ✔
1371

1372
  exprref.set( new String( formatted ) );
2,315 ✔
1373
}
2,315 ✔
1374

1375
void Executor::ins_skipiftrue_else_consume( const Instruction& ins )
2,091 ✔
1376
{
1377
  // This is for short-circuit evaluation of the elvis operator [expr_a] ?: [expr_b]
1378
  //
1379
  // Program instructions look like this:
1380
  //   [expr_a instructions] INS_SKIPIFTRUE_ELSE_CONSUME [expr_b instructions]
1381
  //
1382
  // The result value of expr_a is on the top of the value stack when this instruction executes.
1383
  //
1384
  // If [expr_a] evaluated to true, leave its result and skip over the expr_b instructions
1385
  // otherwise, consume the false value and continue so that expr_b can replace it.
1386
  //
1387
  BObjectRef& objref = ValueStack.back();
2,091 ✔
1388

1389
  if ( objref->impptr()->isTrue() )
2,091 ✔
1390
  {
1391
    PC = PC + (unsigned)( ins.token.lval );
887 ✔
1392
  }
1393
  else
1394
  {
1395
    ValueStack.pop_back();
1,204 ✔
1396
  }
1397
}
2,091 ✔
1398

1399

1400
// case TOK_LOCALVAR:
1401
void Executor::ins_localvar( const Instruction& ins )
1,346,751 ✔
1402
{
1403
  ValueStack.push_back( ( *Locals2 )[ins.token.lval] );
1,346,751 ✔
1404
}
1,346,751 ✔
1405

1406
// case RSV_GLOBAL:
1407
// case TOK_GLOBALVAR:
1408
void Executor::ins_globalvar( const Instruction& ins )
68,335 ✔
1409
{
1410
  if ( (unsigned)ins.token.lval >= Globals2->size() )
68,335 ✔
1411
  {
1412
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
1 ✔
1413
    seterror( true );
1 ✔
1414
    ValueStack.emplace_back( UninitObject::create() );
1 ✔
1415
    return;
1 ✔
1416
  }
1417
  ValueStack.push_back( ( *Globals2 )[ins.token.lval] );
68,334 ✔
1418
}
1419

1420
// case TOK_LONG:
1421
void Executor::ins_long( const Instruction& ins )
134,901,876 ✔
1422
{
1423
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
134,901,876 ✔
1424
}
134,901,876 ✔
1425

1426
// case TOK_BOOL:
1427
void Executor::ins_bool( const Instruction& ins )
1,459 ✔
1428
{
1429
  ValueStack.emplace_back( new BBoolean( ins.token.lval ) );
1,459 ✔
1430
}
1,459 ✔
1431

1432
// case TOK_CONSUMER:
1433
void Executor::ins_consume( const Instruction& /*ins*/ )
134,910,372 ✔
1434
{
1435
  ValueStack.pop_back();
134,910,372 ✔
1436
}
134,910,372 ✔
1437

1438
void Executor::ins_set_member( const Instruction& ins )
3 ✔
1439
{
1440
  BObjectRef rightref = ValueStack.back();
3 ✔
1441
  ValueStack.pop_back();
3 ✔
1442
  BObjectRef& leftref = ValueStack.back();
3 ✔
1443

1444
  BObject& right = *rightref;
3 ✔
1445
  BObject& left = *leftref;
3 ✔
1446

1447
  BObjectImp& rightimpref = right.impref();
3 ✔
1448
  left.impref().set_member( ins.token.tokval(), &rightimpref,
6 ✔
1449
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
3 ✔
1450
}
3 ✔
1451

1452
void Executor::ins_set_member_id( const Instruction& ins )
3 ✔
1453
{
1454
  BObjectRef rightref = ValueStack.back();
3 ✔
1455
  ValueStack.pop_back();
3 ✔
1456
  BObjectRef& leftref = ValueStack.back();
3 ✔
1457

1458
  BObject& right = *rightref;
3 ✔
1459
  BObject& left = *leftref;
3 ✔
1460

1461
  BObjectImp& rightimpref = right.impref();
3 ✔
1462
  left.impref().set_member_id( ins.token.lval, &rightimpref,
6 ✔
1463
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
3 ✔
1464
}
3 ✔
1465

1466
void Executor::ins_set_member_consume( const Instruction& ins )
610 ✔
1467
{
1468
  BObjectRef rightref = ValueStack.back();
610 ✔
1469
  ValueStack.pop_back();
610 ✔
1470
  BObjectRef& leftref = ValueStack.back();
610 ✔
1471

1472
  BObject& right = *rightref;
610 ✔
1473
  BObject& left = *leftref;
610 ✔
1474

1475
  BObjectImp& rightimpref = right.impref();
610 ✔
1476
  left.impref().set_member( ins.token.tokval(), &rightimpref,
1,220 ✔
1477
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
610 ✔
1478
  ValueStack.pop_back();
610 ✔
1479
}
610 ✔
1480

1481
void Executor::ins_set_member_id_consume( const Instruction& ins )
892 ✔
1482
{
1483
  BObjectRef rightref = ValueStack.back();
892 ✔
1484
  ValueStack.pop_back();
892 ✔
1485
  BObjectRef& leftref = ValueStack.back();
892 ✔
1486

1487
  BObject& right = *rightref;
892 ✔
1488
  BObject& left = *leftref;
892 ✔
1489

1490
  BObjectImp& rightimpref = right.impref();
892 ✔
1491

1492
  left.impref().set_member_id( ins.token.lval, &rightimpref,
1,784 ✔
1493
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
892 ✔
1494
  ValueStack.pop_back();
892 ✔
1495
}
892 ✔
1496

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

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

1506
  BObjectImp& leftimpref = left.impref();
15 ✔
1507

1508
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15 ✔
1509

1510
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
21 ✔
1511
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6 ✔
1512
  {
1513
    tmp->impref().operPlusEqual( *tmp, right.impref() );
6 ✔
1514
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6 ✔
1515
  }
1516
  ValueStack.pop_back();
15 ✔
1517
}
15 ✔
1518

1519
void Executor::ins_set_member_id_consume_minusequal( const Instruction& ins )
6 ✔
1520
{
1521
  BObjectRef rightref = ValueStack.back();
6 ✔
1522
  ValueStack.pop_back();
6 ✔
1523
  BObjectRef& leftref = ValueStack.back();
6 ✔
1524

1525
  BObject& right = *rightref;
6 ✔
1526
  BObject& left = *leftref;
6 ✔
1527

1528
  BObjectImp& leftimpref = left.impref();
6 ✔
1529

1530
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6 ✔
1531

1532
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12 ✔
1533
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6 ✔
1534
  {
1535
    tmp->impref().operMinusEqual( *tmp, right.impref() );
6 ✔
1536
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6 ✔
1537
  }
1538
  ValueStack.pop_back();
6 ✔
1539
}
6 ✔
1540

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

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

1550
  BObjectImp& leftimpref = left.impref();
9 ✔
1551

1552
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
9 ✔
1553

1554
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
18 ✔
1555
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
9 ✔
1556
  {
1557
    tmp->impref().operTimesEqual( *tmp, right.impref() );
9 ✔
1558
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
9 ✔
1559
  }
1560
  ValueStack.pop_back();
9 ✔
1561
}
9 ✔
1562

1563
void Executor::ins_set_member_id_consume_divideequal( const Instruction& ins )
6 ✔
1564
{
1565
  BObjectRef rightref = ValueStack.back();
6 ✔
1566
  ValueStack.pop_back();
6 ✔
1567
  BObjectRef& leftref = ValueStack.back();
6 ✔
1568

1569
  BObject& right = *rightref;
6 ✔
1570
  BObject& left = *leftref;
6 ✔
1571

1572
  BObjectImp& leftimpref = left.impref();
6 ✔
1573

1574
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6 ✔
1575

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

1585
void Executor::ins_set_member_id_consume_modulusequal( const Instruction& ins )
6 ✔
1586
{
1587
  BObjectRef rightref = ValueStack.back();
6 ✔
1588
  ValueStack.pop_back();
6 ✔
1589
  BObjectRef& leftref = ValueStack.back();
6 ✔
1590

1591
  BObject& right = *rightref;
6 ✔
1592
  BObject& left = *leftref;
6 ✔
1593

1594
  BObjectImp& leftimpref = left.impref();
6 ✔
1595

1596
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6 ✔
1597

1598
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12 ✔
1599
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6 ✔
1600
  {
1601
    tmp->impref().operModulusEqual( *tmp, right.impref() );
6 ✔
1602
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6 ✔
1603
  }
1604
  ValueStack.pop_back();
6 ✔
1605
}
6 ✔
1606

1607
void Executor::ins_get_member( const Instruction& ins )
189,562 ✔
1608
{
1609
  BObjectRef& leftref = ValueStack.back();
189,562 ✔
1610
  BObject& left = *leftref;
189,562 ✔
1611
  {
1612
    ESCRIPT_PROFILER( ins, leftref, fparams );
1613
    leftref = left->get_member( ins.token.tokval() );
189,562 ✔
1614
  }
1615
}
189,562 ✔
1616

1617
void Executor::ins_get_member_id( const Instruction& ins )
169,303 ✔
1618
{
1619
  BObjectRef& leftref = ValueStack.back();
169,303 ✔
1620
  BObject& left = *leftref;
169,303 ✔
1621
  {
1622
    ESCRIPT_PROFILER( ins, leftref, fparams );
1623
    leftref = left->get_member_id( ins.token.lval );
169,303 ✔
1624
  }
1625
}
169,303 ✔
1626

1627
void Executor::ins_assign_localvar( const Instruction& ins )
19,636 ✔
1628
{
1629
  BObjectRef& lvar = ( *Locals2 )[ins.token.lval];
19,636 ✔
1630

1631
  BObjectRef& rightref = ValueStack.back();
19,636 ✔
1632

1633
  BObject& right = *rightref;
19,636 ✔
1634

1635
  BObjectImp& rightimpref = right.impref();
19,636 ✔
1636

1637
  if ( right.count() == 1 && rightimpref.count() == 1 )
19,636 ✔
1638
  {
1639
    lvar->setimp( &rightimpref );
18,748 ✔
1640
  }
1641
  else
1642
  {
1643
    lvar->setimp( rightimpref.copy() );
888 ✔
1644
  }
1645
  ValueStack.pop_back();
19,636 ✔
1646
}
19,636 ✔
1647
void Executor::ins_assign_globalvar( const Instruction& ins )
2,888 ✔
1648
{
1649
  if ( (unsigned)ins.token.lval >= Globals2->size() )
2,888 ✔
1650
  {
1651
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
×
1652
    seterror( true );
×
1653
    ValueStack.pop_back();
×
1654
    return;
×
1655
  }
1656
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
2,888 ✔
1657

1658
  BObjectRef& rightref = ValueStack.back();
2,888 ✔
1659

1660
  BObject& right = *rightref;
2,888 ✔
1661

1662
  BObjectImp& rightimpref = right.impref();
2,888 ✔
1663

1664
  if ( right.count() == 1 && rightimpref.count() == 1 )
2,888 ✔
1665
  {
1666
    gvar->setimp( &rightimpref );
2,809 ✔
1667
  }
1668
  else
1669
  {
1670
    gvar->setimp( rightimpref.copy() );
79 ✔
1671
  }
1672
  ValueStack.pop_back();
2,888 ✔
1673
}
1674

1675
// case INS_ASSIGN_CONSUME:
1676
void Executor::ins_assign_consume( const Instruction& /*ins*/ )
4,439 ✔
1677
{
1678
  BObjectRef rightref = ValueStack.back();
4,439 ✔
1679
  ValueStack.pop_back();
4,439 ✔
1680
  BObjectRef& leftref = ValueStack.back();
4,439 ✔
1681

1682
  BObject& right = *rightref;
4,439 ✔
1683
  BObject& left = *leftref;
4,439 ✔
1684

1685
  BObjectImp& rightimpref = right.impref();
4,439 ✔
1686

1687
  if ( right.count() == 1 && rightimpref.count() == 1 )
4,439 ✔
1688
  {
1689
    left.setimp( &rightimpref );
3,726 ✔
1690
  }
1691
  else
1692
  {
1693
    left.setimp( rightimpref.copy() );
713 ✔
1694
  }
1695
  ValueStack.pop_back();
4,439 ✔
1696
}
4,439 ✔
1697

1698
void Executor::ins_assign( const Instruction& /*ins*/ )
209,215 ✔
1699
{
1700
  /*
1701
      These each take two operands, and replace them with one.
1702
      We'll leave the second one on the value stack, and
1703
      just replace its object with the result
1704
      */
1705
  BObjectRef rightref = ValueStack.back();
209,215 ✔
1706
  ValueStack.pop_back();
209,215 ✔
1707
  BObjectRef& leftref = ValueStack.back();
209,215 ✔
1708

1709
  BObject& right = *rightref;
209,215 ✔
1710
  BObject& left = *leftref;
209,215 ✔
1711

1712
  BObjectImp& rightimpref = right.impref();
209,215 ✔
1713

1714
  if ( right.count() == 1 && rightimpref.count() == 1 )
209,215 ✔
1715
  {
1716
    left.setimp( &rightimpref );
204,546 ✔
1717
  }
1718
  else
1719
  {
1720
    left.setimp( rightimpref.copy() );
4,669 ✔
1721
  }
1722
}
209,215 ✔
1723

1724
void Executor::ins_array_assign( const Instruction& /*ins*/ )
9 ✔
1725
{
1726
  /*
1727
      on the value stack:
1728
      x[i] := y;
1729
      (top)
1730
      y
1731
      i
1732
      x
1733
      upon exit:
1734
      (x[i])
1735
      */
1736
  BObjectRef y_ref = ValueStack.back();
9 ✔
1737
  ValueStack.pop_back();
9 ✔
1738
  BObjectRef i_ref = ValueStack.back();
9 ✔
1739
  ValueStack.pop_back();
9 ✔
1740
  BObjectRef& x_ref = ValueStack.back();
9 ✔
1741

1742
  BObject& y = *y_ref;
9 ✔
1743
  BObject& i = *i_ref;
9 ✔
1744
  BObject& x = *x_ref;
9 ✔
1745

1746
  BObjectImp* result;
1747
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
9 ✔
1748

1749
  x_ref.set( result );
9 ✔
1750
}
9 ✔
1751
void Executor::ins_array_assign_consume( const Instruction& /*ins*/ )
963 ✔
1752
{
1753
  /*
1754
      on the value stack:
1755
      x[i] := y;
1756
      (top)
1757
      y
1758
      i
1759
      x
1760
      upon exit:
1761
      (x[i])
1762
      */
1763
  BObjectRef y_ref = ValueStack.back();
963 ✔
1764
  ValueStack.pop_back();
963 ✔
1765
  BObjectRef i_ref = ValueStack.back();
963 ✔
1766
  ValueStack.pop_back();
963 ✔
1767
  BObjectRef& x_ref = ValueStack.back();
963 ✔
1768

1769
  BObject& y = *y_ref;
963 ✔
1770
  BObject& i = *i_ref;
963 ✔
1771
  BObject& x = *x_ref;
963 ✔
1772

1773
  BObjectImp* result;
1774
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
963 ✔
1775

1776
  BObject obj( result );
963 ✔
1777
  ValueStack.pop_back();
963 ✔
1778
}
963 ✔
1779

1780
// TOK_ADD:
1781
void Executor::ins_add( const Instruction& /*ins*/ )
61,515 ✔
1782
{
1783
  /*
1784
      These each take two operands, and replace them with one.
1785
      We'll leave the second one on the value stack, and
1786
      just replace its object with the result
1787
      */
1788
  BObjectRef rightref = ValueStack.back();
61,515 ✔
1789
  ValueStack.pop_back();
61,515 ✔
1790
  BObjectRef& leftref = ValueStack.back();
61,515 ✔
1791

1792
  BObject& right = *rightref;
61,515 ✔
1793
  BObject& left = *leftref;
61,515 ✔
1794

1795
  leftref.set( right.impref().selfPlusObjImp( left.impref() ) );
61,515 ✔
1796
}
61,515 ✔
1797

1798
// TOK_SUBTRACT
1799
void Executor::ins_subtract( const Instruction& /*ins*/ )
6,269 ✔
1800
{
1801
  /*
1802
      These each take two operands, and replace them with one.
1803
      We'll leave the second one on the value stack, and
1804
      just replace its object with the result
1805
      */
1806
  BObjectRef rightref = ValueStack.back();
6,269 ✔
1807
  ValueStack.pop_back();
6,269 ✔
1808
  BObjectRef& leftref = ValueStack.back();
6,269 ✔
1809

1810
  BObject& right = *rightref;
6,269 ✔
1811
  BObject& left = *leftref;
6,269 ✔
1812

1813
  leftref.set( right.impref().selfMinusObjImp( left.impref() ) );
6,269 ✔
1814
}
6,269 ✔
1815

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

1828
  BObject& right = *rightref;
3,480 ✔
1829
  BObject& left = *leftref;
3,480 ✔
1830

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

1845
  BObject& right = *rightref;
387 ✔
1846
  BObject& left = *leftref;
387 ✔
1847

1848
  leftref.set( right.impref().selfDividedByObjImp( left.impref() ) );
387 ✔
1849
}
387 ✔
1850
// TOK_MODULUS:
1851
void Executor::ins_modulus( const Instruction& /*ins*/ )
516 ✔
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();
516 ✔
1859
  ValueStack.pop_back();
516 ✔
1860
  BObjectRef& leftref = ValueStack.back();
516 ✔
1861

1862
  BObject& right = *rightref;
516 ✔
1863
  BObject& left = *leftref;
516 ✔
1864

1865
  leftref.set( right.impref().selfModulusObjImp( left.impref() ) );
516 ✔
1866
}
516 ✔
1867

1868
// TOK_IS:
1869
void Executor::ins_is( const Instruction& /*ins*/ )
100 ✔
1870
{
1871
  BObjectRef rightref = ValueStack.back();
100 ✔
1872
  ValueStack.pop_back();
100 ✔
1873
  BObjectRef& leftref = ValueStack.back();
100 ✔
1874

1875
  BObject& right = *rightref;
100 ✔
1876
  BObject& left = *leftref;
100 ✔
1877

1878
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100 ✔
1879
}
100 ✔
1880

1881
// TOK_BSRIGHT:
1882
void Executor::ins_bitshift_right( const Instruction& /*ins*/ )
312 ✔
1883
{
1884
  /*
1885
      These each take two operands, and replace them with one.
1886
      We'll leave the second one on the value stack, and
1887
      just replace its object with the result
1888
      */
1889
  BObjectRef rightref = ValueStack.back();
312 ✔
1890
  ValueStack.pop_back();
312 ✔
1891
  BObjectRef& leftref = ValueStack.back();
312 ✔
1892

1893
  BObject& right = *rightref;
312 ✔
1894
  BObject& left = *leftref;
312 ✔
1895

1896
  leftref.set( right.impref().selfBitShiftRightObjImp( left.impref() ) );
312 ✔
1897
}
312 ✔
1898
// TOK_BSLEFT:
1899
void Executor::ins_bitshift_left( const Instruction& /*ins*/ )
316 ✔
1900
{
1901
  /*
1902
      These each take two operands, and replace them with one.
1903
      We'll leave the second one on the value stack, and
1904
      just replace its object with the result
1905
      */
1906
  BObjectRef rightref = ValueStack.back();
316 ✔
1907
  ValueStack.pop_back();
316 ✔
1908
  BObjectRef& leftref = ValueStack.back();
316 ✔
1909

1910
  BObject& right = *rightref;
316 ✔
1911
  BObject& left = *leftref;
316 ✔
1912

1913
  leftref.set( right.impref().selfBitShiftLeftObjImp( left.impref() ) );
316 ✔
1914
}
316 ✔
1915
// TOK_BITAND:
1916
void Executor::ins_bitwise_and( const Instruction& /*ins*/ )
342 ✔
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();
342 ✔
1924
  ValueStack.pop_back();
342 ✔
1925
  BObjectRef& leftref = ValueStack.back();
342 ✔
1926

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

1930
  leftref.set( right.impref().selfBitAndObjImp( left.impref() ) );
342 ✔
1931
}
342 ✔
1932
// TOK_BITXOR:
1933
void Executor::ins_bitwise_xor( const Instruction& /*ins*/ )
309 ✔
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();
309 ✔
1941
  ValueStack.pop_back();
309 ✔
1942
  BObjectRef& leftref = ValueStack.back();
309 ✔
1943

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

1947
  leftref.set( right.impref().selfBitXorObjImp( left.impref() ) );
309 ✔
1948
}
309 ✔
1949
// TOK_BITOR:
1950
void Executor::ins_bitwise_or( const Instruction& /*ins*/ )
309 ✔
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();
309 ✔
1958
  ValueStack.pop_back();
309 ✔
1959
  BObjectRef& leftref = ValueStack.back();
309 ✔
1960

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

1964
  leftref.set( right.impref().selfBitOrObjImp( left.impref() ) );
309 ✔
1965
}
309 ✔
1966

1967
void Executor::ins_logical_and( const Instruction& /*ins*/ )
280,527 ✔
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();
280,527 ✔
1975
  ValueStack.pop_back();
280,527 ✔
1976
  BObjectRef& leftref = ValueStack.back();
280,527 ✔
1977

1978
  BObject& right = *rightref;
280,527 ✔
1979
  BObject& left = *leftref;
280,527 ✔
1980

1981
  int _true = ( left.isTrue() && right.isTrue() );
280,527 ✔
1982
  leftref.set( new BLong( _true ) );
280,527 ✔
1983
}
280,527 ✔
1984
void Executor::ins_logical_or( const Instruction& /*ins*/ )
12,797 ✔
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();
12,797 ✔
1992
  ValueStack.pop_back();
12,797 ✔
1993
  BObjectRef& leftref = ValueStack.back();
12,797 ✔
1994

1995
  BObject& right = *rightref;
12,797 ✔
1996
  BObject& left = *leftref;
12,797 ✔
1997

1998
  int _true = ( left.isTrue() || right.isTrue() );
12,797 ✔
1999
  leftref.set( new BLong( _true ) );
12,797 ✔
2000
}
12,797 ✔
2001

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

2013
  BObject& right = *rightref;
47,058 ✔
2014
  BObject& left = *leftref;
47,058 ✔
2015

2016
  int _true = ( left != right );
47,058 ✔
2017
  leftref.set( new BLong( _true ) );
47,058 ✔
2018
}
47,058 ✔
2019

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

2031
  BObject& right = *rightref;
164,031 ✔
2032
  BObject& left = *leftref;
164,031 ✔
2033

2034
  int _true = ( left == right );
164,031 ✔
2035
  leftref.set( new BLong( _true ) );
164,031 ✔
2036
}
164,031 ✔
2037

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

2049
  BObject& right = *rightref;
151,850 ✔
2050
  BObject& left = *leftref;
151,850 ✔
2051

2052
  int _true = ( left < right );
151,850 ✔
2053
  leftref.set( new BLong( _true ) );
151,850 ✔
2054
}
151,850 ✔
2055

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

2067
  BObject& right = *rightref;
9,576 ✔
2068
  BObject& left = *leftref;
9,576 ✔
2069
  int _true = ( left <= right );
9,576 ✔
2070
  leftref.set( new BLong( _true ) );
9,576 ✔
2071
}
9,576 ✔
2072
void Executor::ins_greaterthan( const Instruction& /*ins*/ )
17,723 ✔
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();
17,723 ✔
2080
  ValueStack.pop_back();
17,723 ✔
2081
  BObjectRef& leftref = ValueStack.back();
17,723 ✔
2082

2083
  BObject& right = *rightref;
17,723 ✔
2084
  BObject& left = *leftref;
17,723 ✔
2085

2086
  int _true = ( left > right );
17,723 ✔
2087
  leftref.set( new BLong( _true ) );
17,723 ✔
2088
}
17,723 ✔
2089
void Executor::ins_greaterequal( const Instruction& /*ins*/ )
125,533 ✔
2090
{
2091
  /*
2092
      These each take two operands, and replace them with one.
2093
      We'll leave the second one on the value stack, and
2094
      just replace its object with the result
2095
      */
2096
  BObjectRef rightref = ValueStack.back();
125,533 ✔
2097
  ValueStack.pop_back();
125,533 ✔
2098
  BObjectRef& leftref = ValueStack.back();
125,533 ✔
2099

2100
  BObject& right = *rightref;
125,533 ✔
2101
  BObject& left = *leftref;
125,533 ✔
2102

2103
  int _true = ( left >= right );
125,533 ✔
2104
  leftref.set( new BLong( _true ) );
125,533 ✔
2105
}
125,533 ✔
2106

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

2119
  leftref = ( *leftref )->OperSubscript( *rightref );
35,916 ✔
2120
}
35,916 ✔
2121

2122
void Executor::ins_multisubscript( const Instruction& ins )
10,945 ✔
2123
{
2124
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2125
  std::stack<BObjectRef> indices;
10,945 ✔
2126
  for ( int i = 0; i < ins.token.lval; ++i )
32,838 ✔
2127
  {
2128
    indices.push( ValueStack.back() );
21,893 ✔
2129
    ValueStack.pop_back();
21,893 ✔
2130
  }
2131

2132
  BObjectRef& leftref = ValueStack.back();
10,945 ✔
2133
  leftref = ( *leftref )->OperMultiSubscript( indices );
10,945 ✔
2134
}
10,945 ✔
2135

2136
void Executor::ins_unpack_sequence( const Instruction& ins )
250 ✔
2137
{
2138
  bool rest = ins.token.lval >> 14;
250 ✔
2139
  auto count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
250 ✔
2140

2141
  BObjectRef refIter( new BObject( UninitObject::create() ) );
250 ✔
2142

2143
  BObjectRef rightref = ValueStack.back();
250 ✔
2144
  ValueStack.pop_back();
250 ✔
2145

2146
  // Reserve to keep the insert_at iterator valid
2147
  ValueStack.reserve( ValueStack.size() + count );
250 ✔
2148
  auto insert_at = ValueStack.begin() + ValueStack.size();
250 ✔
2149
  auto pIter = std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
250 ✔
2150

2151
  // The default iterator is used for non-iterable objects, so we can't unpack
2152
  // them. Simply add errors for each value to unpack.
2153
  if ( pIter->is_default() )
250 ✔
2154
  {
2155
    if ( rest )
6 ✔
2156
    {
2157
      auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
3 ✔
2158
      for ( u8 i = 0; i < count; ++i )
12 ✔
2159
        ValueStack.emplace( insert_at, new BError( i == rest_index ? "Invalid type for rest binding"
9 ✔
2160
                                                                   : "Index out of bounds" ) );
9 ✔
2161
    }
2162
    else
2163
    {
2164
      for ( u8 i = 0; i < count; ++i )
9 ✔
2165
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6 ✔
2166
    }
2167
    return;
6 ✔
2168
  }
2169

2170
  if ( rest )
244 ✔
2171
  {
2172
    auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
66 ✔
2173

2174
    // Add all elements up to the rest index
2175
    for ( u8 i = 0; i < rest_index; ++i )
147 ✔
2176
    {
2177
      if ( auto res = pIter->step() )
81 ✔
2178
        ValueStack.emplace( insert_at, res );
75 ✔
2179
      else
2180
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6 ✔
2181
    }
2182

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

2186
    while ( auto res = pIter->step() )
306 ✔
2187
      rest_array->addElement( res->impptr() );
240 ✔
2188

2189
    // Take the remaining elements from the rest array to fill the rest of the bindings.
2190
    auto left = count - rest_index - 1;
66 ✔
2191

2192
    for ( u8 i = 0; i < left; ++i )
138 ✔
2193
    {
2194
      if ( rest_array->ref_arr.empty() )
72 ✔
2195
        ValueStack.emplace( insert_at + i, new BError( "Index out of bounds" ) );
12 ✔
2196
      else
2197
      {
2198
        ValueStack.insert( insert_at + i, rest_array->ref_arr.back() );
60 ✔
2199

2200
        rest_array->ref_arr.pop_back();
60 ✔
2201
      }
2202
    }
2203
  }
2204
  else
2205
  {
2206
    for ( u8 i = 0; i < count; ++i )
608 ✔
2207
    {
2208
      if ( auto res = pIter->step() )
430 ✔
2209
        ValueStack.emplace( insert_at, res );
430 ✔
2210
      else
2211
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
×
2212
    }
2213
  }
2214
}
262 ✔
2215

2216
void Executor::ins_unpack_indices( const Instruction& ins )
135 ✔
2217
{
2218
  bool rest = ins.token.lval >> 14;
135 ✔
2219
  auto binding_count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
135 ✔
2220
  // If there is a rest binding, there will be one less index than the binding
2221
  // count, as the rest binding has no corresponding element index access.
2222
  auto index_count = rest ? Clib::clamp_convert<u8>( binding_count - 1 ) : binding_count;
135 ✔
2223

2224
  // Ensure there is a ValueStack entry for each index, + 1 for the unpacking object ('rightref').
2225
  passert_r( static_cast<int>( ValueStack.size() ) >= index_count + 1,
135 ✔
2226
             "Not enough values to unpack" );
2227

2228
  if ( rest )
135 ✔
2229
  {
2230
    // Use a multi_index because we need to (1) iterate over the indexes in
2231
    // order of the bindings in the script, and (2) keep track of which indexes
2232
    // have been used.
2233
    using namespace boost::multi_index;
2234
    using OrderedSet =
2235
        multi_index_container<BObject,
2236
                              indexed_by<sequenced<>,  // Maintains insertion order
2237
                                         ordered_unique<identity<BObject>>  // Ensures uniqueness
2238
                                         >>;
2239

2240
    OrderedSet indexes;
39 ✔
2241

2242
    for ( u8 i = 0; i < index_count; ++i )
120 ✔
2243
    {
2244
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
81 ✔
2245
      ValueStack.pop_back();
81 ✔
2246
    }
2247

2248
    BObjectRef rightref = ValueStack.back();
39 ✔
2249
    ValueStack.pop_back();
39 ✔
2250

2251
    // Reserve to keep the insert_at iterator valid
2252
    ValueStack.reserve( ValueStack.size() + binding_count );
39 ✔
2253
    auto insert_at = ValueStack.end();
39 ✔
2254

2255
    for ( const auto& index : indexes )
120 ✔
2256
    {
2257
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
81 ✔
2258
    }
2259

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

2264
    BObjectRef refIter( UninitObject::create() );
39 ✔
2265
    auto pIter =
2266
        std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
39 ✔
2267

2268
    // The default iterator is used for non-iterable objects, so we can't unpack them.
2269
    if ( pIter->is_default() )
39 ✔
2270
    {
2271
      ValueStack.emplace( insert_at, new BError( "Invalid type for rest binding" ) );
15 ✔
2272
      return;
15 ✔
2273
    }
2274
    rest_obj = std::make_unique<BDictionary>();
24 ✔
2275

2276
    auto& unique_index = indexes.get<1>();
24 ✔
2277

2278
    while ( auto res = pIter->step() )
120 ✔
2279
    {
2280
      auto itr = unique_index.find( *refIter.get() );
96 ✔
2281

2282
      if ( itr == unique_index.end() )
96 ✔
2283
        rest_obj->array_assign( refIter->impptr(), res->impptr(), true );
45 ✔
2284
    }
96 ✔
2285
    ValueStack.emplace( insert_at, rest_obj.release() );
24 ✔
2286
  }
99 ✔
2287
  else
2288
  {
2289
    // If not using a rest binding, only keep track of binding order.
2290
    std::list<BObject> indexes;
96 ✔
2291

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

2298
    BObjectRef rightref = ValueStack.back();
96 ✔
2299
    ValueStack.pop_back();
96 ✔
2300

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

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

2312
void Executor::ins_take_local( const Instruction& )
771 ✔
2313
{
2314
  passert( Locals2 != nullptr );
771 ✔
2315
  passert( !ValueStack.empty() );
771 ✔
2316

2317
  // There is no entry in the locals vector, so create a new one.
2318
  Locals2->emplace_back( UninitObject::create() );
771 ✔
2319
  BObjectRef& lvar = ( *Locals2 ).back();
771 ✔
2320

2321
  BObjectRef& rightref = ValueStack.back();
771 ✔
2322

2323
  BObject& right = *rightref;
771 ✔
2324

2325
  BObjectImp& rightimpref = right.impref();
771 ✔
2326

2327
  if ( right.count() == 1 && rightimpref.count() == 1 )
771 ✔
2328
  {
2329
    lvar->setimp( &rightimpref );
244 ✔
2330
  }
2331
  else
2332
  {
2333
    lvar->setimp( rightimpref.copy() );
527 ✔
2334
  }
2335
  ValueStack.pop_back();
771 ✔
2336
}
771 ✔
2337

2338
void Executor::ins_take_global( const Instruction& ins )
183 ✔
2339
{
2340
  passert( !ValueStack.empty() );
183 ✔
2341

2342
  if ( (unsigned)ins.token.lval >= Globals2->size() )
183 ✔
2343
  {
2344
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={})", prog_->name, PC );
×
2345
    seterror( true );
×
2346
    ValueStack.pop_back();
×
2347
    return;
×
2348
  }
2349
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
183 ✔
2350

2351
  BObjectRef& rightref = ValueStack.back();
183 ✔
2352

2353
  BObject& right = *rightref;
183 ✔
2354

2355
  BObjectImp& rightimpref = right.impref();
183 ✔
2356

2357
  if ( right.count() == 1 && rightimpref.count() == 1 )
183 ✔
2358
  {
2359
    gvar->setimp( &rightimpref );
150 ✔
2360
  }
2361
  else
2362
  {
2363
    gvar->setimp( rightimpref.copy() );
33 ✔
2364
  }
2365
  ValueStack.pop_back();
183 ✔
2366
}
2367

2368
void Executor::ins_multisubscript_assign( const Instruction& ins )
30 ✔
2369
{
2370
  BObjectRef target_ref = ValueStack.back();
30 ✔
2371
  ValueStack.pop_back();
30 ✔
2372
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2373
  std::stack<BObjectRef> indices;
30 ✔
2374
  for ( int i = 0; i < ins.token.lval; ++i )
93 ✔
2375
  {
2376
    indices.push( ValueStack.back() );
63 ✔
2377
    ValueStack.pop_back();
63 ✔
2378
  }
2379

2380
  BObjectRef& leftref = ValueStack.back();
30 ✔
2381
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
30 ✔
2382
}
30 ✔
2383

2384
void Executor::ins_addmember( const Instruction& /*ins*/ )
4,559 ✔
2385
{
2386
  /*
2387
      These each take two operands, and replace them with one.
2388
      We'll leave the second one on the value stack, and
2389
      just replace its object with the result
2390
      */
2391
  BObjectRef rightref = ValueStack.back();
4,559 ✔
2392
  ValueStack.pop_back();
4,559 ✔
2393
  BObjectRef& leftref = ValueStack.back();
4,559 ✔
2394

2395
  BObject& right = *rightref;
4,559 ✔
2396
  BObject& left = *leftref;
4,559 ✔
2397

2398
  leftref = addmember( left, right );
4,559 ✔
2399
}
4,559 ✔
2400

2401
void Executor::ins_removemember( const Instruction& /*ins*/ )
94 ✔
2402
{
2403
  /*
2404
      These each take two operands, and replace them with one.
2405
      We'll leave the second one on the value stack, and
2406
      just replace its object with the result
2407
      */
2408
  BObjectRef rightref = ValueStack.back();
94 ✔
2409
  ValueStack.pop_back();
94 ✔
2410
  BObjectRef& leftref = ValueStack.back();
94 ✔
2411

2412
  BObject& right = *rightref;
94 ✔
2413
  BObject& left = *leftref;
94 ✔
2414

2415
  leftref = removemember( left, right );
94 ✔
2416
}
94 ✔
2417

2418
void Executor::ins_checkmember( const Instruction& /*ins*/ )
39,507 ✔
2419
{
2420
  /*
2421
      These each take two operands, and replace them with one.
2422
      We'll leave the second one on the value stack, and
2423
      just replace its object with the result
2424
      */
2425
  BObjectRef rightref = ValueStack.back();
39,507 ✔
2426
  ValueStack.pop_back();
39,507 ✔
2427
  BObjectRef& leftref = ValueStack.back();
39,507 ✔
2428

2429
  BObject& right = *rightref;
39,507 ✔
2430
  BObject& left = *leftref;
39,507 ✔
2431

2432
  leftref = checkmember( left, right );
39,507 ✔
2433
}
39,507 ✔
2434

2435
void Executor::ins_addmember2( const Instruction& ins )
45 ✔
2436
{
2437
  BObjectRef& obref = ValueStack.back();
45 ✔
2438

2439
  BObject& ob = *obref;
45 ✔
2440

2441
  ob.impref().operDotPlus( ins.token.tokval() );
45 ✔
2442
}
45 ✔
2443

2444
void Executor::ins_addmember_assign( const Instruction& ins )
9,405 ✔
2445
{
2446
  BObjectRef valref = ValueStack.back();
9,405 ✔
2447
  BObject& valob = *valref;
9,405 ✔
2448
  BObjectImp* valimp = valref->impptr();
9,405 ✔
2449

2450
  ValueStack.pop_back();
9,405 ✔
2451

2452
  BObjectRef& obref = ValueStack.back();
9,405 ✔
2453
  BObject& ob = *obref;
9,405 ✔
2454

2455
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
9,405 ✔
2456
  BObject& mem = *memref;
9,405 ✔
2457

2458
  if ( valob.count() == 1 && valimp->count() == 1 )
9,405 ✔
2459
  {
2460
    mem.setimp( valimp );
6,329 ✔
2461
  }
2462
  else
2463
  {
2464
    mem.setimp( valimp->copy() );
3,076 ✔
2465
  }
2466
  // the struct is at the top of the stack
2467
}
9,405 ✔
2468

2469
void Executor::ins_dictionary_addmember( const Instruction& /*ins*/ )
191 ✔
2470
{
2471
  /*
2472
      ENTRANCE: value stack:
2473
      dictionary
2474
      key
2475
      value
2476
      EXIT: value stack:
2477
      dictionary
2478
      FUNCTION:
2479
      adds the (key, value) pair to the dictionary
2480
      */
2481

2482
  BObjectRef valref = ValueStack.back();
191 ✔
2483
  ValueStack.pop_back();
191 ✔
2484
  BObject& valob = *valref;
191 ✔
2485
  BObjectImp* valimp = valob.impptr();
191 ✔
2486

2487
  BObjectRef keyref = ValueStack.back();
191 ✔
2488
  ValueStack.pop_back();
191 ✔
2489
  BObject& keyob = *keyref;
191 ✔
2490
  BObjectImp* keyimp = keyob.impptr();
191 ✔
2491

2492
  BObjectRef& dictref = ValueStack.back();
191 ✔
2493
  BObject& dictob = *dictref;
191 ✔
2494
  BDictionary* dict = dictob.impptr<BDictionary>();
191 ✔
2495

2496
  if ( keyob.count() != 1 || keyimp->count() != 1 )
191 ✔
2497
  {
2498
    keyimp = keyimp->copy();
6 ✔
2499
  }
2500
  if ( valob.count() != 1 || valimp->count() != 1 )
191 ✔
2501
  {
2502
    valimp = valimp->copy();
18 ✔
2503
  }
2504

2505
  dict->addMember( keyimp, valimp );
191 ✔
2506
  // the dictionary remains at the top of the stack.
2507
}
191 ✔
2508

2509
void Executor::ins_in( const Instruction& /*ins*/ )
4,442 ✔
2510
{
2511
  /*
2512
      These each take two operands, and replace them with one.
2513
      We'll leave the second one on the value stack, and
2514
      just replace its object with the result
2515
      */
2516
  BObjectRef rightref = ValueStack.back();
4,442 ✔
2517
  ValueStack.pop_back();
4,442 ✔
2518
  BObjectRef& leftref = ValueStack.back();
4,442 ✔
2519

2520
  BObject& right = *rightref;
4,442 ✔
2521
  BObject& left = *leftref;
4,442 ✔
2522

2523
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
4,442 ✔
2524
}
4,442 ✔
2525

2526
void Executor::ins_insert_into( const Instruction& /*ins*/ )
13,476 ✔
2527
{
2528
  BObjectRef rightref = ValueStack.back();
13,476 ✔
2529
  ValueStack.pop_back();
13,476 ✔
2530
  BObjectRef& leftref = ValueStack.back();
13,476 ✔
2531

2532
  BObject& right = *rightref;
13,476 ✔
2533
  BObject& left = *leftref;
13,476 ✔
2534

2535
  if ( auto* spread = right.impptr_if<BSpread>() )
13,476 ✔
2536
  {
2537
    BObjectRef refIter( UninitObject::create() );
1,473 ✔
2538

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

2542
    BObject* next = pIter->step();
1,473 ✔
2543
    while ( next != nullptr )
4,317 ✔
2544
    {
2545
      left.impref().operInsertInto( left, next->impref() );
2,844 ✔
2546
      next = pIter->step();
2,844 ✔
2547
    }
2548
  }
1,473 ✔
2549
  else
2550
  {
2551
    left.impref().operInsertInto( left, right.impref() );
12,003 ✔
2552
  }
2553
}
13,476 ✔
2554

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

2566
  BObject& right = *rightref;
8,683 ✔
2567
  BObject& left = *leftref;
8,683 ✔
2568

2569
  left.impref().operPlusEqual( left, right.impref() );
8,683 ✔
2570
}
8,683 ✔
2571

2572
void Executor::ins_minusequal( const Instruction& /*ins*/ )
413 ✔
2573
{
2574
  /*
2575
      These each take two operands, and replace them with one.
2576
      We'll leave the second one on the value stack, and
2577
      just replace its object with the result
2578
      */
2579
  BObjectRef rightref = ValueStack.back();
413 ✔
2580
  ValueStack.pop_back();
413 ✔
2581
  BObjectRef& leftref = ValueStack.back();
413 ✔
2582

2583
  BObject& right = *rightref;
413 ✔
2584
  BObject& left = *leftref;
413 ✔
2585

2586
  left.impref().operMinusEqual( left, right.impref() );
413 ✔
2587
}
413 ✔
2588

2589
void Executor::ins_timesequal( const Instruction& /*ins*/ )
237 ✔
2590
{
2591
  /*
2592
      These each take two operands, and replace them with one.
2593
      We'll leave the second one on the value stack, and
2594
      just replace its object with the result
2595
      */
2596
  BObjectRef rightref = ValueStack.back();
237 ✔
2597
  ValueStack.pop_back();
237 ✔
2598
  BObjectRef& leftref = ValueStack.back();
237 ✔
2599

2600
  BObject& right = *rightref;
237 ✔
2601
  BObject& left = *leftref;
237 ✔
2602

2603
  left.impref().operTimesEqual( left, right.impref() );
237 ✔
2604
}
237 ✔
2605

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

2617
  BObject& right = *rightref;
231 ✔
2618
  BObject& left = *leftref;
231 ✔
2619

2620
  left.impref().operDivideEqual( left, right.impref() );
231 ✔
2621
}
231 ✔
2622

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

2634
  BObject& right = *rightref;
378 ✔
2635
  BObject& left = *leftref;
378 ✔
2636

2637
  left.impref().operModulusEqual( left, right.impref() );
378 ✔
2638
}
378 ✔
2639

2640
// case RSV_GOTO:
2641
void Executor::ins_goto( const Instruction& ins )
134,753,725 ✔
2642
{
2643
  PC = (unsigned)ins.token.lval;
134,753,725 ✔
2644
}
134,753,725 ✔
2645

2646
// TOK_FUNC:
2647
void Executor::ins_func( const Instruction& ins )
134,947,356 ✔
2648
{
2649
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
134,947,356 ✔
2650
  getParams( nparams );
134,947,356 ✔
2651
  execFunc( ins.token );
134,947,356 ✔
2652
  cleanParams();
134,947,356 ✔
2653
  return;
134,947,356 ✔
2654
}
2655

2656
void Executor::ins_call_method_id( const Instruction& ins )
224,990 ✔
2657
{
2658
  BContinuation* continuation = nullptr;
224,990 ✔
2659
  unsigned nparams = ins.token.type;
224,990 ✔
2660

2661
  do
2662
  {
2663
    getParams( nparams );
225,837 ✔
2664
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
225,837 ✔
2665
    {
2666
      Instruction jmp;
3,355 ✔
2667
      bool add_new_classinst = ins.token.lval == MTH_NEW;
3,355 ✔
2668

2669
      if ( add_new_classinst )
3,355 ✔
2670
      {
2671
        if ( funcr->constructor() )
108 ✔
2672
        {
2673
          fparams.insert( fparams.begin(),
81 ✔
2674
                          BObjectRef( new BConstObject( new BClassInstanceRef( new BClassInstance(
162 ✔
2675
                              prog_, funcr->class_index(), Globals2, pid() ) ) ) ) );
162 ✔
2676
        }
2677
      }
2678

2679
      if ( funcr->validCall( continuation ? MTH_CALL : ins.token.lval, *this, &jmp ) )
3,355 ✔
2680
      {
2681
        BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
3,250 ✔
2682
        call_function_reference( funcr, continuation, jmp );
3,250 ✔
2683
        return;
3,250 ✔
2684
      }
3,250 ✔
2685
    }
3,355 ✔
2686
    // If there _was_ a continuation to be handled (from previous loop
2687
    // iteration), there must have been a FuncRef on the stack. Otherwise,
2688
    // `continuation` may leak.
2689
    passert_always( continuation == nullptr );
222,587 ✔
2690

2691
    size_t stacksize = ValueStack.size();  // ValueStack can grow
222,587 ✔
2692
    BObjectImp* imp;
2693
    {
2694
      ESCRIPT_PROFILER( ins, ValueStack.back(), fparams );
2695
      imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
222,587 ✔
2696

2697
      if ( auto* cont = impptrIf<BContinuation>( imp ) )
222,587 ✔
2698
      {
2699
        continuation = cont;
847 ✔
2700
        // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2701
        // move.
2702
        nparams = static_cast<unsigned int>( continuation->args.size() );
847 ✔
2703

2704
        // Add function reference to stack
2705
        ValueStack.emplace_back( continuation->func() );
847 ✔
2706

2707
        // Move all arguments to the value stack
2708
        ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
847 ✔
2709
                           std::make_move_iterator( continuation->args.end() ) );
2710

2711
        continuation->args.clear();
847 ✔
2712

2713
        cleanParams();
847 ✔
2714

2715
        printStack( fmt::format(
1,694 ✔
2716
            "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2717
            "funcref.call()",
2718
            nparams ) );
2719

2720
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2721
        // handle it.
2722

2723
        // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2724
        // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2725
        // jumps.
2726

2727
        continue;
847 ✔
2728
      }
2729
    }
2730

2731
    if ( impptrIf<BSpecialUserFuncJump>( imp ) )
221,740 ✔
2732
    {
2733
      cleanParams();
3 ✔
2734
      return;
3 ✔
2735
    }
2736
    BObjectRef& objref = ValueStack[stacksize - 1];
221,737 ✔
2737
    if ( func_result_ )
221,737 ✔
2738
    {
2739
      if ( imp )
48 ✔
2740
      {
2741
        BObject obj( imp );
36 ✔
2742
      }
36 ✔
2743

2744
      objref.set( func_result_ );
48 ✔
2745
      func_result_ = nullptr;
48 ✔
2746
    }
2747
    else if ( imp )
221,689 ✔
2748
    {
2749
      objref.set( imp );
221,627 ✔
2750
    }
2751
    else
2752
    {
2753
      objref.set( UninitObject::create() );
62 ✔
2754
    }
2755

2756
    cleanParams();
221,737 ✔
2757
    return;
221,737 ✔
2758
  }
2759
  // This condition should only ever evaluate to `true` once. In the second loop
2760
  // iteration, handling the FuncRef will return out of this method.
2761
  while ( continuation != nullptr );
847 ✔
2762
}
2763

2764
void Executor::ins_call_method( const Instruction& ins )
3,397 ✔
2765
{
2766
  unsigned nparams = ins.token.lval;
3,397 ✔
2767
  auto method_name = ins.token.tokval();
3,397 ✔
2768

2769
  getParams( nparams );
3,397 ✔
2770
  BObjectImp* callee = ValueStack.back()->impptr();
3,397 ✔
2771

2772
  if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
3,397 ✔
2773
  {
2774
    Instruction jmp;
×
2775
    if ( funcr->validCall( method_name, *this, &jmp ) )
×
2776
    {
2777
      BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
×
2778
      call_function_reference( funcr, nullptr, jmp );
×
2779
      return;
×
2780
    }
×
2781
  }
×
2782

2783
  size_t stacksize = ValueStack.size();  // ValueStack can grow
3,397 ✔
2784
  BObjectImp* imp;
2785
  {
2786
    ESCRIPT_PROFILER( ins, callee, method_name, fparams );
2787
#ifdef BOBJECTIMP_DEBUG
2788
    if ( strcmp( method_name, "impptr" ) == 0 )
3,397 ✔
2789
      imp = new String( fmt::format( "{}", static_cast<void*>( callee ) ) );
×
2790
    else
2791
      imp = callee->call_method( method_name, *this );
3,397 ✔
2792
#else
2793
    imp = callee->call_method( method_name, *this );
2794
#endif
2795
  }
2796

2797
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
3,396 ✔
2798
  {
2799
    cleanParams();
2,327 ✔
2800
    return;
2,327 ✔
2801
  }
2802

2803
  BObjectRef& objref = ValueStack[stacksize - 1];
1,069 ✔
2804
  if ( func_result_ )
1,069 ✔
2805
  {
2806
    if ( imp )
1 ✔
2807
    {
2808
      BObject obj( imp );
×
2809
    }
×
2810

2811
    objref.set( func_result_ );
1 ✔
2812
    func_result_ = nullptr;
1 ✔
2813
  }
2814
  else if ( imp )
1,068 ✔
2815
  {
2816
    objref.set( imp );
213 ✔
2817
  }
2818
  else
2819
  {
2820
    objref.set( UninitObject::create() );
855 ✔
2821
  }
2822
  cleanParams();
1,069 ✔
2823
}
2824

2825
// CTRL_STATEMENTBEGIN:
2826
void Executor::ins_statementbegin( const Instruction& ins )
×
2827
{
2828
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
2829
    INFO_PRINTLN( ins.token.tokval() );
×
2830
}
×
2831

2832
// case CTRL_PROGEND:
2833
void Executor::ins_progend( const Instruction& /*ins*/ )
9,779 ✔
2834
{
2835
  done = 1;
9,779 ✔
2836
  run_ok_ = false;
9,779 ✔
2837
  PC = 0;
9,779 ✔
2838
}
9,779 ✔
2839

2840

2841
// case CTRL_MAKELOCAL:
2842
void Executor::ins_makelocal( const Instruction& /*ins*/ )
85,438 ✔
2843
{
2844
  if ( Locals2 )
85,438 ✔
2845
    upperLocals2.push_back( Locals2 );
85,438 ✔
2846
  Locals2 = new BObjectRefVec;
85,438 ✔
2847
}
85,438 ✔
2848

2849
void Executor::ins_check_mro( const Instruction& ins )
280 ✔
2850
{
2851
  auto classinst_offset = ins.token.lval;
280 ✔
2852

2853
  if ( classinst_offset > static_cast<int>( ValueStack.size() ) || ValueStack.empty() )
280 ✔
2854
  {
2855
    POLLOG_ERRORLN( "Fatal error: Check MRO offset error! offset={}, ValueStack.size={} ({},PC={})",
×
2856
                    classinst_offset, ValueStack.size(), prog_->name, PC );
×
2857
    seterror( true );
×
2858
    return;
×
2859
  }
2860

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

2863
  if ( nLines < PC + 1 )
280 ✔
2864
  {
2865
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction out of bounds! nLines={} ({},PC={})",
×
2866
                    nLines, prog_->name, PC );
×
2867
    seterror( true );
×
2868
    return;
×
2869
  }
2870

2871
  const Instruction& jsr_ins = prog_->instr.at( PC + 1 );
280 ✔
2872
  if ( jsr_ins.func != &Executor::ins_jsr_userfunc )
280 ✔
2873
  {
2874
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction not followed by JSR_USERFUNC! ({},PC={})",
×
2875
                    prog_->name, PC );
×
2876
    seterror( true );
×
2877
    return;
×
2878
  }
2879

2880
  auto ctor_addr = jsr_ins.token.lval;
280 ✔
2881

2882
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280 ✔
2883

2884
  if ( classinstref != nullptr && classinstref->instance()->constructors_called.find( ctor_addr ) ==
557 ✔
2885
                                      classinstref->instance()->constructors_called.end() )
557 ✔
2886
  {
2887
    classinstref->instance()->constructors_called.insert( ctor_addr );
224 ✔
2888
  }
2889
  else
2890
  {
2891
    // Constructor has been called, or `this` is not a class instance: clear
2892
    // arguments and skip jump instructions (makelocal, jsr_userfunc)
2893
    ValueStack.resize( ValueStack.size() - ins.token.lval );
56 ✔
2894
    PC += 2;
56 ✔
2895
  }
2896
}
2897

2898
// CTRL_JSR_USERFUNC:
2899
void Executor::ins_jsr_userfunc( const Instruction& ins )
43,208 ✔
2900
{
2901
  jump( ins.token.lval, nullptr, nullptr );
43,208 ✔
2902
}
43,208 ✔
2903

2904
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
85,438 ✔
2905
{
2906
  ReturnContext rc;
85,438 ✔
2907
  rc.PC = PC;
85,438 ✔
2908
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
85,438 ✔
2909
  if ( continuation )
85,438 ✔
2910
  {
2911
    rc.Continuation.set( continuation );
37,497 ✔
2912
  }
2913

2914
  // Only store our global context if the function is external to the current program.
2915
  if ( funcref != nullptr && funcref->pid() != pid() )
85,438 ✔
2916
  {
2917
    // Store external context for the return path.
2918
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
1,888 ✔
2919

2920
    if ( auto shared = funcref->globals.lock() )
3,776 ✔
2921
      Globals2 = shared;
1,886 ✔
2922
    else
2923
      Globals2 =
2924
          std::make_shared<BObjectRefVec>();  // empty but valid, access would stop the executor
1,888 ✔
2925

2926
    // Set the prog and globals to the external function's, updating nLines and
2927
    // execmodules.
2928
    prog_ = funcref->prog();
1,888 ✔
2929

2930

2931
    nLines = static_cast<unsigned int>( prog_->instr.size() );
1,888 ✔
2932

2933
    // Re-attach modules, as the external user function's module function call
2934
    // instructions refer to modules by index.
2935
    execmodules.clear();
1,888 ✔
2936

2937
    if ( !viewmode_ )
1,888 ✔
2938
    {
2939
      if ( !AttachFunctionalityModules() )
1,888 ✔
2940
      {
2941
        POLLOGLN( "Could not attach modules for external function call jump" );
×
2942
        seterror( true );
×
2943
      }
2944
    }
2945
  }
2946

2947
  ControlStack.push_back( rc );
85,438 ✔
2948

2949
  PC = target_PC;
85,438 ✔
2950
  if ( ControlStack.size() >= escript_config.max_call_depth )
85,438 ✔
2951
  {
2952
    std::string tmp = fmt::format(
2953
        "Script {} exceeded maximum call depth\n"
2954
        "Return path PCs: ",
2955
        scriptname() );
×
2956
    while ( !ControlStack.empty() )
×
2957
    {
2958
      rc = ControlStack.back();
×
2959
      ControlStack.pop_back();
×
2960
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
2961
    }
2962
    POLLOGLN( tmp );
×
2963
    seterror( true );
×
2964
  }
×
2965
}
85,438 ✔
2966

2967

2968
namespace
2969
{
2970
// Runs `handler` for every frame of `ex`'s call stack with the frame's PC
2971
// resolved against the program's debug symbols. Only meaningful once
2972
// read_dbg_file() has succeeded; callers pick the symbol-less walk otherwise.
2973
void walk_call_stack_with_symbols(
77 ✔
2974
    Executor& ex,
2975
    const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
2976
                              unsigned int /*line*/, const std::string& /*functionName*/ )>&
2977
        handler )
2978
{
2979
  ex.walkCallStack(
77 ✔
2980
      [&]( unsigned int pc )
77 ✔
2981
      {
2982
        auto filename = ex.prog()->dbg_filenames[ex.prog()->dbg_filenum[pc]];
294 ✔
2983
        auto line = ex.prog()->dbg_linenum[pc];
294 ✔
2984
        auto dbgFunction =
2985
            std::find_if( ex.prog()->dbg_functions.begin(), ex.prog()->dbg_functions.end(),
294 ✔
2986
                          [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
6,697 ✔
2987

2988
        std::string functionName =
2989
            dbgFunction != ex.prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
294 ✔
2990

2991
        handler( pc, filename, line, functionName );
294 ✔
2992
      } );
294 ✔
2993
}
77 ✔
2994
}  // namespace
2995

2996
BObjectImp* Executor::get_stacktrace( bool as_array )
78 ✔
2997
{
2998
  if ( !as_array )
78 ✔
2999
    return new String( stacktrace_string() );
74 ✔
3000

3001
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
4 ✔
3002

3003
  std::unique_ptr<ObjArray> result( new ObjArray );
4 ✔
3004

3005
  if ( has_symbols )
4 ✔
3006
  {
3007
    walk_call_stack_with_symbols( *this,
2 ✔
3008
                                  [&]( unsigned int pc, const std::string& filename,
4 ✔
3009
                                       unsigned int line, const std::string& functionName )
3010
                                  {
3011
                                    std::unique_ptr<BStruct> entry( new BStruct );
8 ✔
3012
                                    entry->addMember( "file", new String( filename ) );
8 ✔
3013
                                    entry->addMember( "line", new BLong( line ) );
8 ✔
3014
                                    entry->addMember( "name", new String( functionName ) );
8 ✔
3015
                                    entry->addMember( "pc", new BLong( pc ) );
8 ✔
3016
                                    result->addElement( entry.release() );
8 ✔
3017
                                  } );
8 ✔
3018
  }
3019
  else
3020
  {
3021
    walkCallStack(
2 ✔
3022
        [&]( unsigned int pc )
2 ✔
3023
        {
3024
          std::unique_ptr<BStruct> entry( new BStruct );
3 ✔
3025
          entry->addMember( "file", new String( scriptname() ) );
3 ✔
3026
          entry->addMember( "pc", new BLong( pc ) );
3 ✔
3027
          result->addElement( entry.release() );
3 ✔
3028
        } );
3 ✔
3029
  }
3030

3031
  return result.release();
4 ✔
3032
}
4 ✔
3033

3034
std::string Executor::stacktrace_string()
75 ✔
3035
{
3036
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
75 ✔
3037

3038
  std::string result;
75 ✔
3039

3040
  if ( has_symbols )
75 ✔
3041
  {
3042
    walk_call_stack_with_symbols(
75 ✔
3043
        *this,
3044
        [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
150 ✔
3045
             const std::string& functionName )
3046
        {
3047
          result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
572 ✔
3048
                                      filename, line ) );
3049
        } );
286 ✔
3050
  }
3051
  else
3052
  {
3053
    walkCallStack(
×
3054
        [&]( unsigned int pc )
×
3055
        {
3056
          result.append(
×
3057
              fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
3058
        } );
×
3059
  }
3060

3061
  return result;
75 ✔
NEW
3062
}
×
3063

3064
namespace
3065
{
3066
// Script-controlled recursion is unbounded, so this block is capped tighter than
3067
// the native one.
3068
constexpr size_t MAX_REPORTED_ESCRIPT_FRAMES = 30;
3069
}  // namespace
3070

3071
std::string Executor::execution_error_report( size_t onPC, const std::string& what )
1 ✔
3072
{
3073
  try
3074
  {
3075
    return format_execution_error( prog_->name.get(), pid(), onPC,
2 ✔
3076
                                   threadhelp::current_thread_name(), what, stacktrace_string(),
1 ✔
3077
                                   Clib::ExceptionParser::getTrace() );
3 ✔
3078
  }
NEW
3079
  catch ( ... )
×
3080
  {
3081
    // Collecting the stacks reads the debug file and symbolizes the native
3082
    // trace, either of which can fail. Losing the detail is acceptable; losing
3083
    // the message -- turning a script error into a second exception escaping an
3084
    // already-failing catch block -- is not.
3085
    return fmt::format( "Exception in {} PC {}\n  {}\n  (no stacks: reporting them failed)\n",
NEW
3086
                        prog_->name.get(), onPC, what );
×
NEW
3087
  }
×
3088
}
3089

3090
std::string format_execution_error( const std::string& script, unsigned int pid, size_t pc,
19 ✔
3091
                                    const std::string& thread_name, const std::string& what,
3092
                                    const std::string& escript_stack,
3093
                                    const std::string& native_stack )
3094
{
3095
  std::string result = fmt::format( "Exception in {}", script );
19 ✔
3096
  // A plain Executor has no pid; only scripts scheduled by the core do.
3097
  if ( pid )
19 ✔
3098
    fmt::format_to( std::back_inserter( result ), " (pid {})", pid );
34 ✔
3099
  fmt::format_to( std::back_inserter( result ), " PC {}", pc );
19 ✔
3100
  if ( !thread_name.empty() )
19 ✔
3101
    fmt::format_to( std::back_inserter( result ), ", thread {}", thread_name );
36 ✔
3102
  fmt::format_to( std::back_inserter( result ), "\n  {}\n", what );
19 ✔
3103

3104
  // Script block first: it is short, it is the answer for most readers, and it
3105
  // survives log truncation that would eat the tail of the native one.
3106
  auto escript = Clib::indent_stack_block( escript_stack, MAX_REPORTED_ESCRIPT_FRAMES );
19 ✔
3107
  if ( !escript.empty() )
19 ✔
3108
    result += "\neScript stack (innermost first):\n" + escript;
14 ✔
3109

3110
  auto native = Clib::indent_stack_block( native_stack, Clib::MAX_REPORTED_NATIVE_FRAMES );
19 ✔
3111
  if ( !native.empty() )
19 ✔
3112
    result += "\nNative stack (throw site, innermost first):\n" + native;
11 ✔
3113

3114
  return result;
38 ✔
3115
}
19 ✔
3116

3117
void Executor::ins_pop_param( const Instruction& ins )
111,610 ✔
3118
{
3119
  popParam( ins.token );
111,610 ✔
3120
}
111,610 ✔
3121

3122
void Executor::ins_pop_param_byref( const Instruction& ins )
29,012 ✔
3123
{
3124
  popParamByRef( ins.token );
29,012 ✔
3125
}
29,012 ✔
3126

3127
void Executor::ins_get_arg( const Instruction& ins )
1,140 ✔
3128
{
3129
  getArg( ins.token );
1,140 ✔
3130
}
1,140 ✔
3131

3132
// CTRL_LEAVE_BLOCK:
3133
void Executor::ins_leave_block( const Instruction& ins )
160,953 ✔
3134
{
3135
  if ( Locals2 )
160,953 ✔
3136
  {
3137
    for ( int i = 0; i < ins.token.lval; i++ )
336,751 ✔
3138
      Locals2->pop_back();
175,798 ✔
3139
  }
3140
  else  // at global level.  ick.
3141
  {
3142
    for ( int i = 0; i < ins.token.lval; i++ )
×
3143
      Globals2->pop_back();
×
3144
  }
3145
}
160,953 ✔
3146

3147
void Executor::ins_gosub( const Instruction& ins )
×
3148
{
3149
  ReturnContext rc;
×
3150
  rc.PC = PC;
×
3151
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3152
  ControlStack.push_back( rc );
×
3153
  if ( Locals2 )
×
3154
    upperLocals2.push_back( Locals2 );
×
3155
  Locals2 = new BObjectRefVec;
×
3156

3157
  PC = (unsigned)ins.token.lval;
×
3158
}
×
3159

3160
// case RSV_RETURN
3161
void Executor::ins_return( const Instruction& /*ins*/ )
85,437 ✔
3162
{
3163
  if ( ControlStack.empty() )
85,437 ✔
3164
  {
3165
    ERROR_PRINTLN( "Return without GOSUB! (PC={}, {})", PC, scriptname() );
×
3166

3167
    seterror( true );
×
3168
    return;
×
3169
  }
3170
  BObjectRef continuation;
85,437 ✔
3171
  ReturnContext& rc = ControlStack.back();
85,437 ✔
3172
  PC = rc.PC;
85,437 ✔
3173

3174
  if ( rc.Continuation.get() != nullptr )
85,437 ✔
3175
  {
3176
    continuation = rc.Continuation;
37,497 ✔
3177
  }
3178

3179
  if ( Locals2 )
85,437 ✔
3180
  {
3181
    delete Locals2;
85,437 ✔
3182
    Locals2 = nullptr;
85,437 ✔
3183
  }
3184
  if ( !upperLocals2.empty() )
85,437 ✔
3185
  {
3186
    Locals2 = upperLocals2.back();
85,437 ✔
3187
    upperLocals2.pop_back();
85,437 ✔
3188
  }
3189

3190
  if ( rc.ExternalContext.has_value() )
85,437 ✔
3191
  {
3192
    prog_ = std::move( rc.ExternalContext->Program );
1,887 ✔
3193
    nLines = static_cast<unsigned int>( prog_->instr.size() );
1,887 ✔
3194
    execmodules = std::move( rc.ExternalContext->Modules );
1,887 ✔
3195
    Globals2 = std::move( rc.ExternalContext->Globals );
1,887 ✔
3196
  }
3197

3198
  // FIXME do something with rc.ValueStackDepth
3199
  ControlStack.pop_back();
85,437 ✔
3200

3201
  if ( continuation != nullptr )
85,437 ✔
3202
  {
3203
    auto result = ValueStack.back();
37,497 ✔
3204
    ValueStack.pop_back();
37,497 ✔
3205

3206
    // Do not move the `result` object, as the continuation callback may return
3207
    // the result's BObjectImp*. If we move `result`, the BObjectImp* will be
3208
    // deleted when the callback ends.
3209
    auto* imp = continuation->impptr<BContinuation>()->continueWith( *this, result );
37,497 ✔
3210

3211
    // If the the continuation callback returned a continuation, handle the jump.
3212
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
37,497 ✔
3213
    {
3214
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3215
      // takes ownership.
3216

3217
      // Add function reference to stack
3218
      ValueStack.emplace_back( cont->func() );
36,650 ✔
3219

3220
      // Move all arguments to the fparams stack
3221
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
36,650 ✔
3222
                      std::make_move_iterator( cont->args.end() ) );
3223

3224
      cont->args.clear();
36,650 ✔
3225

3226
      printStack(
36,650 ✔
3227
          "continuation callback returned a continuation; continuation args added to fparams" );
3228

3229
      BObjectRef objref = ValueStack.back();
36,650 ✔
3230
      auto funcr = objref->impptr<BFunctionRef>();
36,650 ✔
3231
      Instruction jmp;
36,650 ✔
3232
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
36,650 ✔
3233
      {
3234
        call_function_reference( funcr, cont, jmp );
36,650 ✔
3235
      }
3236
      else
3237
      {
3238
        // Delete `imp` since a ReturnContext was not created.
3239
        BObject bobj( imp );
×
3240
      }
×
3241
    }
36,650 ✔
3242
    else
3243
    {
3244
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3245
      // `array{}.filter(...)`
3246
      ValueStack.pop_back();
847 ✔
3247

3248
      // Add the result to the stack.
3249
      ValueStack.emplace_back( imp );
847 ✔
3250
    }
3251
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
74,994 ✔
3252
  }
37,497 ✔
3253
}
85,437 ✔
3254

3255
void Executor::ins_exit( const Instruction& /*ins*/ )
3 ✔
3256
{
3257
  done = 1;
3 ✔
3258
  run_ok_ = false;
3 ✔
3259
}
3 ✔
3260

3261
void Executor::ins_double( const Instruction& ins )
2,811 ✔
3262
{
3263
  ValueStack.emplace_back( new Double( ins.token.dval ) );
2,811 ✔
3264
}
2,811 ✔
3265

3266
void Executor::ins_classinst( const Instruction& ins )
451 ✔
3267
{
3268
  ValueStack.emplace_back( new BConstObject(
451 ✔
3269
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2, pid() ) ) ) );
902 ✔
3270
}
451 ✔
3271

3272
void Executor::ins_string( const Instruction& ins )
368,326 ✔
3273
{
3274
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
368,326 ✔
3275
}
368,326 ✔
3276

3277
void Executor::ins_regexp( const Instruction& )
150 ✔
3278
{
3279
  BObjectRef flags = ValueStack.back();
150 ✔
3280
  ValueStack.pop_back();
150 ✔
3281
  BObjectRef& pattern = ValueStack.back();
150 ✔
3282

3283
  pattern.set(
150 ✔
3284
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300 ✔
3285
}
150 ✔
3286

3287
void Executor::ins_error( const Instruction& /*ins*/ )
5,046 ✔
3288
{
3289
  ValueStack.emplace_back( new BError() );
5,046 ✔
3290
}
5,046 ✔
3291
void Executor::ins_struct( const Instruction& /*ins*/ )
5,095 ✔
3292
{
3293
  ValueStack.emplace_back( new BStruct );
5,095 ✔
3294
}
5,095 ✔
3295
void Executor::ins_spread( const Instruction& ins )
2,126 ✔
3296
{
3297
  bool spread_into = ins.token.lval;
2,126 ✔
3298

3299
  // Pops TOP, and spreads it into (new) TOP.
3300
  if ( spread_into )
2,126 ✔
3301
  {
3302
    BObjectRef rightref = ValueStack.back();
24 ✔
3303
    ValueStack.pop_back();
24 ✔
3304
    BObjectRef& leftref = ValueStack.back();
24 ✔
3305

3306
    BObject& right = *rightref;
24 ✔
3307
    BObject& left = *leftref;
24 ✔
3308

3309
    BObjectRef refIter( UninitObject::create() );
24 ✔
3310

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

3313
    while ( auto next = pIter->step() )
72 ✔
3314
    {
3315
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48 ✔
3316
      BObject res_obj( result );
48 ✔
3317
    }
48 ✔
3318
  }
24 ✔
3319
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3320
  else
3321
  {
3322
    auto spread = new BSpread( ValueStack.back() );
2,102 ✔
3323
    ValueStack.pop_back();
2,102 ✔
3324
    ValueStack.emplace_back( spread );
2,102 ✔
3325
  }
3326
}
2,126 ✔
3327
void Executor::ins_array( const Instruction& /*ins*/ )
8,308 ✔
3328
{
3329
  ValueStack.emplace_back( new ObjArray );
8,308 ✔
3330
}
8,308 ✔
3331
void Executor::ins_dictionary( const Instruction& /*ins*/ )
211 ✔
3332
{
3333
  ValueStack.emplace_back( new BDictionary );
211 ✔
3334
}
211 ✔
3335
void Executor::ins_uninit( const Instruction& /*ins*/ )
127 ✔
3336
{
3337
  ValueStack.emplace_back( UninitObject::create() );
127 ✔
3338
}
127 ✔
3339
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3340
{
3341
  ValueStack.emplace_back( new BError( "Please recompile this script" ) );
×
3342
}
×
3343

3344
// case TOK_UNMINUS:
3345
void Executor::ins_unminus( const Instruction& /*ins*/ )
147 ✔
3346
{
3347
  BObjectRef ref = getObjRef();
147 ✔
3348
  BObjectImp* newobj;
3349
  newobj = ref->impref().inverse();
147 ✔
3350

3351
  ValueStack.emplace_back( newobj );
147 ✔
3352
}
147 ✔
3353

3354
// case TOK_UNPLUSPLUS:
3355
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
8,875 ✔
3356
{
3357
  BObjectRef& ref = ValueStack.back();
8,875 ✔
3358
  ref->impref().selfPlusPlus();
8,875 ✔
3359
}
8,875 ✔
3360

3361
// case TOK_UNMINUSMINUS:
3362
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
132 ✔
3363
{
3364
  BObjectRef& ref = ValueStack.back();
132 ✔
3365
  ref->impref().selfMinusMinus();
132 ✔
3366
}
132 ✔
3367

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

3378
// case TOK_UNMINUSMINUS_POST:
3379
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
100 ✔
3380
{
3381
  BObjectRef& ref = ValueStack.back();
100 ✔
3382
  BObjectImp* imp = ref->impptr();
100 ✔
3383
  BObject* n = ref->clone();
100 ✔
3384
  imp->selfMinusMinus();
100 ✔
3385
  ref.set( n );
100 ✔
3386
}
100 ✔
3387

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

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

3417
// case INS_SET_MEMBER_ID_UNMINUSMINUS:
3418
void Executor::ins_set_member_id_unminusminus( const Instruction& ins )
13 ✔
3419
{
3420
  BObjectRef& ref = ValueStack.back();
13 ✔
3421
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
13 ✔
3422
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
26 ✔
3423
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
13 ✔
3424
  {
3425
    tmp->impref().selfMinusMinus();
13 ✔
3426
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
13 ✔
3427
  }
3428
  ref.set( tmp.get() );
13 ✔
3429
}
13 ✔
3430

3431
// case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3432
void Executor::ins_set_member_id_unminusminus_post( const Instruction& ins )
3 ✔
3433
{
3434
  BObjectRef& ref = ValueStack.back();
3 ✔
3435
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3 ✔
3436
  BObject* res = tmp->clone();
3 ✔
3437
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6 ✔
3438
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3 ✔
3439
  {
3440
    tmp->impref().selfMinusMinus();
3 ✔
3441
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3 ✔
3442
  }
3443
  ref.set( res );
3 ✔
3444
}
3 ✔
3445

3446
// case TOK_LOG_NOT:
3447
void Executor::ins_logical_not( const Instruction& /*ins*/ )
13,061 ✔
3448
{
3449
  BObjectRef ref = getObjRef();
13,061 ✔
3450
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
13,061 ✔
3451
}
13,061 ✔
3452

3453
// case TOK_BITWISE_NOT:
3454
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
156 ✔
3455
{
3456
  BObjectRef ref = getObjRef();
156 ✔
3457
  ValueStack.emplace_back( ref->impptr()->bitnot() );
156 ✔
3458
}
156 ✔
3459

3460
// case TOK_FUNCREF:
3461
void Executor::ins_funcref( const Instruction& ins )
2,343 ✔
3462
{
3463
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
2,343 ✔
3464
  {
3465
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3466
                    prog_->function_references.size() );
×
3467
    seterror( true );
×
3468
    return;
×
3469
  }
3470

3471
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
2,343 ✔
3472

3473
  ValueStack.emplace_back(
4,686 ✔
3474
      new BFunctionRef( prog_, pid(), funcref_index, Globals2, {} /* captures */ ) );
2,343 ✔
3475
}
3476

3477
void Executor::ins_functor( const Instruction& ins )
795 ✔
3478
{
3479
  auto funcref_index = static_cast<int>( ins.token.type );
795 ✔
3480

3481
  const auto& ep_funcref = prog_->function_references[funcref_index];
795 ✔
3482

3483
  int capture_count = ep_funcref.capture_count;
795 ✔
3484

3485
  auto captures = ValueStackCont();
795 ✔
3486
  while ( capture_count > 0 )
1,505 ✔
3487
  {
3488
    captures.push_back( ValueStack.back() );
710 ✔
3489
    ValueStack.pop_back();
710 ✔
3490
    capture_count--;
710 ✔
3491
  }
3492

3493
  auto func = new BFunctionRef( prog_, pid(), funcref_index, Globals2, std::move( captures ) );
795 ✔
3494

3495
  ValueStack.emplace_back( func );
795 ✔
3496

3497
  PC += ins.token.lval;
795 ✔
3498
}
795 ✔
3499

3500
void Executor::ins_logical_jump( const Instruction& ins )
652 ✔
3501
{
3502
  BObjectRef& objref = ValueStack.back();
652 ✔
3503
  // jmp if true for ||, jmp if false for &&
3504
  const bool obj_true = objref->impptr()->isTrue();
652 ✔
3505
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
652 ✔
3506

3507
  if ( jmp )
652 ✔
3508
    PC = (unsigned)ins.token.lval;
146 ✔
3509
  // keep the obj on the stack if it should jump (ShortCircuit)
3510
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3511
  // BLong (original &&/|| convert to BLong bool)
3512
  if ( !jmp )
652 ✔
3513
    ValueStack.pop_back();
506 ✔
3514
  else
3515
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
146 ✔
3516
}
652 ✔
3517

3518
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
196 ✔
3519
{
3520
  BObjectRef& objref = ValueStack.back();
196 ✔
3521
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
196 ✔
3522
}
196 ✔
3523

3524
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3525

3526
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
325,771 ✔
3527
{
3528
  switch ( token.id )
325,771 ✔
3529
  {
3530
  case INS_INITFOREACH:
684 ✔
3531
    return &Executor::ins_initforeach;
684 ✔
3532
  case INS_STEPFOREACH:
684 ✔
3533
    return &Executor::ins_stepforeach;
684 ✔
3534
  case INS_INITFOR:
79 ✔
3535
    return &Executor::ins_initfor;
79 ✔
3536
  case INS_NEXTFOR:
79 ✔
3537
    return &Executor::ins_nextfor;
79 ✔
3538
  case INS_CASEJMP:
97 ✔
3539
    return &Executor::ins_casejmp;
97 ✔
3540
  case RSV_JMPIFTRUE:
3,834 ✔
3541
    return &Executor::ins_jmpiftrue;
3,834 ✔
3542
  case RSV_JMPIFFALSE:
6,019 ✔
3543
    return &Executor::ins_jmpiffalse;
6,019 ✔
3544
  case RSV_LOCAL:
7,116 ✔
3545
    return &Executor::ins_makeLocal;
7,116 ✔
3546
  case RSV_GLOBAL:
13,977 ✔
3547
  case TOK_GLOBALVAR:
3548
    return &Executor::ins_globalvar;
13,977 ✔
3549
  case TOK_LOCALVAR:
40,871 ✔
3550
    return &Executor::ins_localvar;
40,871 ✔
3551
  case TOK_LONG:
27,877 ✔
3552
    return &Executor::ins_long;
27,877 ✔
3553
  case TOK_DOUBLE:
696 ✔
3554
    return &Executor::ins_double;
696 ✔
3555
  case TOK_STRING:
43,983 ✔
3556
    return &Executor::ins_string;
43,983 ✔
3557
  case TOK_REGEXP:
150 ✔
3558
    return &Executor::ins_regexp;
150 ✔
3559
  case TOK_ERROR:
581 ✔
3560
    return &Executor::ins_error;
581 ✔
3561
  case TOK_STRUCT:
1,184 ✔
3562
    return &Executor::ins_struct;
1,184 ✔
3563
  case TOK_SPREAD:
670 ✔
3564
    return &Executor::ins_spread;
670 ✔
3565
  case TOK_CLASSINST:
248 ✔
3566
    return &Executor::ins_classinst;
248 ✔
3567
  case TOK_ARRAY:
3,391 ✔
3568
    return &Executor::ins_array;
3,391 ✔
3569
  case TOK_DICTIONARY:
206 ✔
3570
    return &Executor::ins_dictionary;
206 ✔
3571
  case TOK_FUNCREF:
760 ✔
3572
    return &Executor::ins_funcref;
760 ✔
3573
  case TOK_FUNCTOR:
705 ✔
3574
    return &Executor::ins_functor;
705 ✔
3575
  case INS_UNINIT:
114 ✔
3576
    return &Executor::ins_uninit;
114 ✔
3577
  case TOK_IDENT:
×
3578
    return &Executor::ins_ident;
×
3579
  case INS_ASSIGN_GLOBALVAR:
1,583 ✔
3580
    return &Executor::ins_assign_globalvar;
1,583 ✔
3581
  case INS_ASSIGN_LOCALVAR:
2,220 ✔
3582
    return &Executor::ins_assign_localvar;
2,220 ✔
3583
  case INS_ASSIGN_CONSUME:
277 ✔
3584
    return &Executor::ins_assign_consume;
277 ✔
3585
  case TOK_CONSUMER:
29,234 ✔
3586
    return &Executor::ins_consume;
29,234 ✔
3587
  case TOK_ASSIGN:
9,132 ✔
3588
    return &Executor::ins_assign;
9,132 ✔
3589
  case INS_SUBSCRIPT_ASSIGN:
9 ✔
3590
    return &Executor::ins_array_assign;
9 ✔
3591
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
490 ✔
3592
    return &Executor::ins_array_assign_consume;
490 ✔
3593
  case INS_MULTISUBSCRIPT:
316 ✔
3594
    return &Executor::ins_multisubscript;
316 ✔
3595
  case INS_MULTISUBSCRIPT_ASSIGN:
30 ✔
3596
    return &Executor::ins_multisubscript_assign;
30 ✔
3597
  case INS_GET_MEMBER:
3,170 ✔
3598
    return &Executor::ins_get_member;
3,170 ✔
3599
  case INS_SET_MEMBER:
3 ✔
3600
    return &Executor::ins_set_member;
3 ✔
3601
  case INS_SET_MEMBER_CONSUME:
242 ✔
3602
    return &Executor::ins_set_member_consume;
242 ✔
3603

3604
  case INS_UNPACK_SEQUENCE:
148 ✔
3605
    return &Executor::ins_unpack_sequence;
148 ✔
3606
  case INS_UNPACK_INDICES:
91 ✔
3607
    return &Executor::ins_unpack_indices;
91 ✔
3608
  case INS_TAKE_GLOBAL:
183 ✔
3609
    return &Executor::ins_take_global;
183 ✔
3610
  case INS_TAKE_LOCAL:
406 ✔
3611
    return &Executor::ins_take_local;
406 ✔
3612

3613
  case INS_GET_MEMBER_ID:
5,045 ✔
3614
    return &Executor::ins_get_member_id;  // test id
5,045 ✔
3615
  case INS_SET_MEMBER_ID:
3 ✔
3616
    return &Executor::ins_set_member_id;  // test id
3 ✔
3617
  case INS_SET_MEMBER_ID_CONSUME:
444 ✔
3618
    return &Executor::ins_set_member_id_consume;  // test id
444 ✔
3619

3620
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15 ✔
3621
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15 ✔
3622
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6 ✔
3623
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6 ✔
3624
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9 ✔
3625
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9 ✔
3626
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6 ✔
3627
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6 ✔
3628
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6 ✔
3629
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6 ✔
3630

3631
  case TOK_ADD:
4,673 ✔
3632
    return &Executor::ins_add;
4,673 ✔
3633
  case TOK_SUBTRACT:
550 ✔
3634
    return &Executor::ins_subtract;
550 ✔
3635
  case TOK_DIV:
86 ✔
3636
    return &Executor::ins_div;
86 ✔
3637
  case TOK_MULT:
377 ✔
3638
    return &Executor::ins_mult;
377 ✔
3639
  case TOK_MODULUS:
41 ✔
3640
    return &Executor::ins_modulus;
41 ✔
3641

3642
  case TOK_INSERTINTO:
6,976 ✔
3643
    return &Executor::ins_insert_into;
6,976 ✔
3644

3645
  case TOK_PLUSEQUAL:
106 ✔
3646
    return &Executor::ins_plusequal;
106 ✔
3647
  case TOK_MINUSEQUAL:
33 ✔
3648
    return &Executor::ins_minusequal;
33 ✔
3649
  case TOK_TIMESEQUAL:
24 ✔
3650
    return &Executor::ins_timesequal;
24 ✔
3651
  case TOK_DIVIDEEQUAL:
18 ✔
3652
    return &Executor::ins_divideequal;
18 ✔
3653
  case TOK_MODULUSEQUAL:
21 ✔
3654
    return &Executor::ins_modulusequal;
21 ✔
3655

3656
  case TOK_LESSTHAN:
422 ✔
3657
    return &Executor::ins_lessthan;
422 ✔
3658
  case TOK_LESSEQ:
137 ✔
3659
    return &Executor::ins_lessequal;
137 ✔
3660
  case RSV_GOTO:
2,935 ✔
3661
    return &Executor::ins_goto;
2,935 ✔
3662
  case TOK_ARRAY_SUBSCRIPT:
2,112 ✔
3663
    return &Executor::ins_arraysubscript;
2,112 ✔
3664
  case TOK_EQUAL:
1,325 ✔
3665
    return &Executor::ins_equal;
1,325 ✔
3666
  case TOK_FUNC:
19,996 ✔
3667
    return &Executor::ins_func;
19,996 ✔
3668
  case INS_CALL_METHOD:
859 ✔
3669
    return &Executor::ins_call_method;
859 ✔
3670
  case INS_CALL_METHOD_ID:
7,178 ✔
3671
    return &Executor::ins_call_method_id;
7,178 ✔
3672
  case CTRL_STATEMENTBEGIN:
×
3673
    return &Executor::ins_statementbegin;
×
3674
  case CTRL_MAKELOCAL:
13,273 ✔
3675
    return &Executor::ins_makelocal;
13,273 ✔
3676
  case INS_CHECK_MRO:
225 ✔
3677
    return &Executor::ins_check_mro;
225 ✔
3678
  case CTRL_JSR_USERFUNC:
13,273 ✔
3679
    return &Executor::ins_jsr_userfunc;
13,273 ✔
3680
  case INS_POP_PARAM:
5,774 ✔
3681
    return &Executor::ins_pop_param;
5,774 ✔
3682
  case INS_POP_PARAM_BYREF:
1,364 ✔
3683
    return &Executor::ins_pop_param_byref;
1,364 ✔
3684
  case INS_GET_ARG:
200 ✔
3685
    return &Executor::ins_get_arg;
200 ✔
3686
  case CTRL_LEAVE_BLOCK:
2,446 ✔
3687
    return &Executor::ins_leave_block;
2,446 ✔
3688
  case RSV_GOSUB:
×
3689
    return &Executor::ins_gosub;
×
3690
  case RSV_RETURN:
11,918 ✔
3691
    return &Executor::ins_return;
11,918 ✔
3692
  case RSV_EXIT:
18 ✔
3693
    return &Executor::ins_exit;
18 ✔
3694
  case INS_DECLARE_ARRAY:
72 ✔
3695
    return &Executor::ins_declareArray;
72 ✔
3696
  case TOK_UNMINUS:
4 ✔
3697
    return &Executor::ins_unminus;
4 ✔
3698
  case TOK_UNPLUS:
×
3699
    return &Executor::ins_nop;
×
3700
  case TOK_LOG_NOT:
548 ✔
3701
    return &Executor::ins_logical_not;
548 ✔
3702
  case TOK_BITWISE_NOT:
12 ✔
3703
    return &Executor::ins_bitwise_not;
12 ✔
3704
  case TOK_BSRIGHT:
30 ✔
3705
    return &Executor::ins_bitshift_right;
30 ✔
3706
  case TOK_BSLEFT:
31 ✔
3707
    return &Executor::ins_bitshift_left;
31 ✔
3708
  case TOK_BITAND:
37 ✔
3709
    return &Executor::ins_bitwise_and;
37 ✔
3710
  case TOK_BITXOR:
27 ✔
3711
    return &Executor::ins_bitwise_xor;
27 ✔
3712
  case TOK_BITOR:
27 ✔
3713
    return &Executor::ins_bitwise_or;
27 ✔
3714

3715
  case TOK_NEQ:
3,360 ✔
3716
    return &Executor::ins_notequal;
3,360 ✔
3717
  case TOK_GRTHAN:
215 ✔
3718
    return &Executor::ins_greaterthan;
215 ✔
3719
  case TOK_GREQ:
112 ✔
3720
    return &Executor::ins_greaterequal;
112 ✔
3721
  case TOK_AND:
413 ✔
3722
    return &Executor::ins_logical_and;
413 ✔
3723
  case TOK_OR:
1,166 ✔
3724
    return &Executor::ins_logical_or;
1,166 ✔
3725

3726
  case TOK_ADDMEMBER:
328 ✔
3727
    return &Executor::ins_addmember;
328 ✔
3728
  case TOK_DELMEMBER:
25 ✔
3729
    return &Executor::ins_removemember;
25 ✔
3730
  case TOK_CHKMEMBER:
132 ✔
3731
    return &Executor::ins_checkmember;
132 ✔
3732
  case INS_DICTIONARY_ADDMEMBER:
185 ✔
3733
    return &Executor::ins_dictionary_addmember;
185 ✔
3734
  case TOK_IN:
114 ✔
3735
    return &Executor::ins_in;
114 ✔
3736
  case TOK_IS:
56 ✔
3737
    return &Executor::ins_is;
56 ✔
3738
  case INS_ADDMEMBER2:
45 ✔
3739
    return &Executor::ins_addmember2;
45 ✔
3740
  case INS_ADDMEMBER_ASSIGN:
2,571 ✔
3741
    return &Executor::ins_addmember_assign;
2,571 ✔
3742
  case CTRL_PROGEND:
4,375 ✔
3743
    return &Executor::ins_progend;
4,375 ✔
3744
  case TOK_UNPLUSPLUS:
110 ✔
3745
    return &Executor::ins_unplusplus;
110 ✔
3746
  case TOK_UNMINUSMINUS:
57 ✔
3747
    return &Executor::ins_unminusminus;
57 ✔
3748
  case TOK_UNPLUSPLUS_POST:
100 ✔
3749
    return &Executor::ins_unplusplus_post;
100 ✔
3750
  case TOK_UNMINUSMINUS_POST:
57 ✔
3751
    return &Executor::ins_unminusminus_post;
57 ✔
3752
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10 ✔
3753
    return &Executor::ins_set_member_id_unplusplus;  // test id
10 ✔
3754
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13 ✔
3755
    return &Executor::ins_set_member_id_unminusminus;  // test id
13 ✔
3756
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3 ✔
3757
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3 ✔
3758
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3 ✔
3759
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3 ✔
3760
  case INS_SKIPIFTRUE_ELSE_CONSUME:
212 ✔
3761
    return &Executor::ins_skipiftrue_else_consume;
212 ✔
3762
  case TOK_INTERPOLATE_STRING:
5,469 ✔
3763
    return &Executor::ins_interpolate_string;
5,469 ✔
3764
  case TOK_FORMAT_EXPRESSION:
521 ✔
3765
    return &Executor::ins_format_expression;
521 ✔
3766
  case TOK_BOOL:
1,513 ✔
3767
    return &Executor::ins_bool;
1,513 ✔
3768
  case INS_LOGICAL_JUMP:
268 ✔
3769
    return &Executor::ins_logical_jump;
268 ✔
3770
  case INS_LOGICAL_CONVERT:
117 ✔
3771
    return &Executor::ins_logical_convert;
117 ✔
3772
  default:
×
3773
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3774
  }
3775
}
3776

3777
void Executor::sethalt( bool halt )
17 ✔
3778
{
3779
  halt_ = halt;
17 ✔
3780

3781
  if ( halt && dbg_env_ )
17 ✔
3782
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18 ✔
3783
      listener->on_halt();
9 ✔
3784

3785
  calcrunnable();
17 ✔
3786
}
17 ✔
3787

3788
void Executor::execInstr()
544,484,953 ✔
3789
{
3790
  unsigned onPC = PC;
544,484,953 ✔
3791
  try
3792
  {  // this is really more of a class invariant.
3793
    passert( run_ok_ );
544,484,953 ✔
3794
    passert( PC < nLines );
544,484,953 ✔
3795
    passert( !error_ );
544,484,953 ✔
3796
    passert( !done );
544,484,953 ✔
3797

3798
#ifdef NDEBUG
3799
    const Instruction& ins = prog_->instr[PC];
544,484,953 ✔
3800
#else
3801
    const Instruction& ins = prog_->instr.at( PC );
3802
#endif
3803
    if ( debug_level >= INSTRUCTIONS )
544,484,953 ✔
3804
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3805

3806
    // If `on_instruction` returns false, do not execute this instruction.
3807
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
544,484,953 ✔
3808
    {
3809
      return;
9 ✔
3810
    }
3811

3812
    ++ins.cycles;
544,484,944 ✔
3813
    ++prog_->instr_cycles;
544,484,944 ✔
3814
    count_instr_cycle();
544,484,944 ✔
3815

3816
    ++PC;
544,484,944 ✔
3817

3818
    ( this->*( ins.func ) )( ins );
544,484,944 ✔
3819
  }
3820
  catch ( std::exception& ex )
1 ✔
3821
  {
3822
    std::string tmp = execution_error_report(
3823
        onPC, boost::core::demangle( typeid( ex ).name() ) + ": " + ex.what() );
1 ✔
3824
    if ( !run_ok_ )
1 ✔
3825
      tmp += "run_ok_ = false\n";
×
3826
    if ( PC < nLines )
1 ✔
3827
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
2 ✔
3828
    if ( error_ )
1 ✔
3829
      tmp += "error_ = true\n";
×
3830
    if ( done )
1 ✔
3831
      tmp += "done = true\n";
×
3832

3833
    seterror( true );
1 ✔
3834
    POLLOG_ERROR( tmp );
1 ✔
3835

3836
    show_context( onPC );
1 ✔
3837
  }
1 ✔
3838
#ifdef __unix__
3839
  catch ( ... )
×
3840
  {
3841
    // No exception object to describe, but the throw site is still recorded --
3842
    // boost's hook does not care what was thrown.
NEW
3843
    std::string tmp = execution_error_report( onPC, "unclassified" );
×
3844

3845
    seterror( true );
×
NEW
3846
    POLLOG_ERROR( tmp );
×
3847

3848
    show_context( onPC );
×
3849
  }
×
3850
#endif
3851
}
3852

3853
std::string Executor::dbg_get_instruction( size_t atPC ) const
7 ✔
3854
{
3855
  std::string out;
7 ✔
3856
  dbg_get_instruction( atPC, out );
7 ✔
3857
  return out;
7 ✔
3858
}
×
3859

3860
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
3,868 ✔
3861
{
3862
  bool has_breakpoint =
3863
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
3,868 ✔
3864
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
3,868 ✔
3865
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
3,868 ✔
3866
}
3,868 ✔
3867

3868
void Executor::show_context( unsigned atPC )
1 ✔
3869
{
3870
  unsigned start, end;
3871
  if ( atPC >= 5 )
1 ✔
3872
    start = atPC - 5;
×
3873
  else
3874
    start = 0;
1 ✔
3875

3876
  end = atPC + 5;
1 ✔
3877

3878
  if ( end >= nLines )
1 ✔
3879
    end = nLines - 1;
1 ✔
3880

3881
  for ( unsigned i = start; i <= end; ++i )
8 ✔
3882
  {
3883
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
7 ✔
3884
  }
3885
}
1 ✔
3886
void Executor::show_context( std::string& os, unsigned atPC )
351 ✔
3887
{
3888
  unsigned start, end;
3889
  if ( atPC >= 5 )
351 ✔
3890
    start = atPC - 5;
351 ✔
3891
  else
3892
    start = 0;
×
3893

3894
  end = atPC + 5;
351 ✔
3895

3896
  if ( end >= nLines )
351 ✔
3897
    end = nLines - 1;
×
3898

3899
  for ( unsigned i = start; i <= end; ++i )
4,212 ✔
3900
  {
3901
    dbg_get_instruction( i, os );
3,861 ✔
3902
    os += '\n';
3,861 ✔
3903
  }
3904
}
351 ✔
3905

3906
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
42,230 ✔
3907
                                        const Instruction& jmp )
3908
{
3909
  // params need to be on the stack, without current objectref
3910
  ValueStack.pop_back();
42,230 ✔
3911
  // Push captured parameters onto the stack prior to function parameters.
3912
  for ( auto& p : funcr->captures )
49,339 ✔
3913
    ValueStack.push_back( p );
7,109 ✔
3914

3915
  auto nparams = static_cast<int>( fparams.size() );
42,230 ✔
3916

3917
  // Handle variadic functions special. Construct an array{} corresponding to
3918
  // the rest parameter (the last parameter for the function). The logic for the
3919
  // condition:
3920
  // - if true, the last argument in the call may not be an array{}, so we need
3921
  //   to construct one (which is why the condition is `>=` and not `>`).
3922
  // - if false, then the address we're jumping to will be a "default argument
3923
  //   address" and _not_ the user function directly, which will create the
3924
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
3925
  if ( funcr->variadic() && nparams >= funcr->numParams() )
42,230 ✔
3926
  {
3927
    auto num_nonrest_args = funcr->numParams() - 1;
641 ✔
3928

3929
    auto rest_arg = std::make_unique<ObjArray>();
641 ✔
3930

3931
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,661 ✔
3932
    {
3933
      auto& p = fparams[i];
3,020 ✔
3934

3935
      if ( i < num_nonrest_args )
3,020 ✔
3936
      {
3937
        ValueStack.push_back( p );
617 ✔
3938
      }
3939
      else
3940
      {
3941
        rest_arg->ref_arr.push_back( p );
2,403 ✔
3942
      }
3943
    }
3944
    ValueStack.emplace_back( rest_arg.release() );
641 ✔
3945
  }
641 ✔
3946
  // The array{} will be created via the regular default-parameter handling by
3947
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
3948
  // prior to jumping to the user function.
3949
  else
3950
  {
3951
    for ( auto& p : fparams )
89,088 ✔
3952
      ValueStack.push_back( p );
47,499 ✔
3953
  }
3954

3955
  // jump to function
3956
  jump( jmp.token.lval, continuation, funcr );
42,230 ✔
3957
  fparams.clear();
42,230 ✔
3958
  // switch to new block
3959
  ins_makelocal( jmp );
42,230 ✔
3960
}
42,230 ✔
3961

3962
bool Executor::exec()
7,931 ✔
3963
{
3964
  passert( prog_ok_ );
7,931 ✔
3965
  passert( !error_ );
7,931 ✔
3966

3967
  Clib::script_status.set_script( scriptname() );
7,931 ✔
3968

3969
  set_running_to_completion( true );
7,931 ✔
3970
  while ( runnable() )
806,773 ✔
3971
  {
3972
    Clib::script_status.set_pc( PC );
798,842 ✔
3973
    execInstr();
798,842 ✔
3974
  }
3975

3976
  return !error_;
7,931 ✔
3977
}
3978

3979
void Executor::reinitExec()
×
3980
{
3981
  PC = 0;
×
3982
  done = 0;
×
3983
  seterror( false );
×
3984

3985
  ValueStack.clear();
×
3986
  delete Locals2;
×
3987
  Locals2 = new BObjectRefVec;
×
3988

3989
  if ( !prog_ok_ )
×
3990
  {
3991
    seterror( true );
×
3992
  }
3993
}
×
3994

3995
void Executor::initForFnCall( unsigned in_PC )
6,984 ✔
3996
{
3997
#ifdef MEMORYLEAK
3998
  bool data_shown = false;
3999
#endif
4000

4001
  PC = in_PC;
6,984 ✔
4002
  done = 0;
6,984 ✔
4003
  seterror( false );
6,984 ✔
4004

4005
#ifdef MEMORYLEAK
4006
  while ( !ValueStack.empty() )
4007
  {
4008
    if ( Clib::memoryleak_debug )
4009
    {
4010
      if ( !data_shown )
4011
      {
4012
        LEAKLOG( "ValueStack... " );
4013
        data_shown = true;
4014
      }
4015

4016
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
4017
               ValueStack.back()->impptr()->sizeEstimate() );
4018
    }
4019
    ValueStack.pop_back();
4020
  }
4021
  if ( Clib::memoryleak_debug )
4022
    if ( data_shown )
4023
      LEAKLOGLN( " ...deleted" );
4024
#endif
4025

4026
  ValueStack.clear();
6,984 ✔
4027
  Locals2->clear();
6,984 ✔
4028
}
6,984 ✔
4029

4030
void Executor::pushArg( BObjectImp* arg )
10,305 ✔
4031
{
4032
  passert_always( arg );
10,305 ✔
4033
  ValueStack.emplace_back( arg );
10,305 ✔
4034
}
10,305 ✔
4035

4036
void Executor::pushArg( const BObjectRef& arg )
2 ✔
4037
{
4038
  ValueStack.push_back( arg );
2 ✔
4039
}
2 ✔
4040

4041
void Executor::addModule( ExecutorModule* module )
33,590 ✔
4042
{
4043
  availmodules.push_back( module );
33,590 ✔
4044
}
33,590 ✔
4045

4046

4047
ExecutorModule* Executor::findModule( const std::string& name )
14,422 ✔
4048
{
4049
  unsigned idx;
4050
  for ( idx = 0; idx < availmodules.size(); idx++ )
147,284 ✔
4051
  {
4052
    ExecutorModule* module = availmodules[idx];
147,284 ✔
4053
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
147,284 ✔
4054
      return module;
14,422 ✔
4055
  }
4056
  return nullptr;
×
4057
}
4058

4059
bool Executor::attach_debugger( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching )
4 ✔
4060
{
4061
  // FIXME: a script can be in debugging state but have no debugger attached,
4062
  // eg. a script that called `os::Debugger()`. This needs to check if a
4063
  // debugger is attached. This works for `os::Debugger()` but not for poldbg cmd_attach.
4064
  if ( dbg_env_ )
4 ✔
4065
  {
4066
    if ( !listener.expired() )
2 ✔
4067
    {
4068
      auto& dbg_env_listener = dbg_env_->listener;
1 ✔
4069
      if ( !dbg_env_listener.expired() )
1 ✔
4070
      {
4071
        return false;
×
4072
      }
4073
      dbg_env_listener = listener;
1 ✔
4074
    }
4075
    if ( set_attaching )
2 ✔
4076
      dbg_env_->debug_state = ExecutorDebugState::ATTACHING;
2 ✔
4077
  }
4078
  else
4079
  {
4080
    dbg_env_ = std::make_unique<ExecutorDebugEnvironment>( listener, set_attaching );
2 ✔
4081
  }
4082

4083
  return true;
4 ✔
4084
}
4085

4086
void Executor::detach_debugger()
×
4087
{
4088
  dbg_env_.reset();
×
4089
  sethalt( false );
×
4090
}
×
4091

4092
void Executor::print_to_debugger( const std::string& message )
24,181 ✔
4093
{
4094
  if ( dbg_env_ )
24,181 ✔
4095
  {
4096
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2 ✔
4097
      listener->on_print( message );
1 ✔
4098
  }
4099
}
24,181 ✔
4100

4101
void Executor::dbg_ins_trace()
×
4102
{
4103
  if ( dbg_env_ )
×
4104
  {
4105
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4106
  }
4107
  sethalt( false );
×
4108
}
×
4109
void Executor::dbg_step_into()
1 ✔
4110
{
4111
  if ( dbg_env_ )
1 ✔
4112
  {
4113
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1 ✔
4114
  }
4115
  sethalt( false );
1 ✔
4116
}
1 ✔
4117
void Executor::dbg_step_over()
2 ✔
4118
{
4119
  if ( dbg_env_ )
2 ✔
4120
  {
4121
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2 ✔
4122
  }
4123
  sethalt( false );
2 ✔
4124
}
2 ✔
4125
void Executor::dbg_step_out()
1 ✔
4126
{
4127
  if ( dbg_env_ )
1 ✔
4128
  {
4129
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1 ✔
4130
  }
4131
  sethalt( false );
1 ✔
4132
}
1 ✔
4133
void Executor::dbg_run()
4 ✔
4134
{
4135
  if ( dbg_env_ )
4 ✔
4136
  {
4137
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4 ✔
4138
  }
4139
  sethalt( false );
4 ✔
4140
}
4 ✔
4141
void Executor::dbg_break()
1 ✔
4142
{
4143
  if ( dbg_env_ )
1 ✔
4144
  {
4145
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1 ✔
4146
  }
4147
}
1 ✔
4148

4149
void Executor::dbg_setbp( unsigned atPC )
2 ✔
4150
{
4151
  if ( dbg_env_ )
2 ✔
4152
  {
4153
    dbg_env_->breakpoints.insert( atPC );
2 ✔
4154
  }
4155
}
2 ✔
4156
void Executor::dbg_clrbp( unsigned atPC )
×
4157
{
4158
  if ( dbg_env_ )
×
4159
  {
4160
    dbg_env_->breakpoints.erase( atPC );
×
4161
  }
4162
}
×
4163

4164
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1 ✔
4165
{
4166
  if ( dbg_env_ )
1 ✔
4167
  {
4168
    std::set<unsigned> result;
1 ✔
4169
    auto& breakpoints = dbg_env_->breakpoints;
1 ✔
4170
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1 ✔
4171
                         std::inserter( result, result.end() ) );
4172
    breakpoints = result;
1 ✔
4173
  }
1 ✔
4174
}
1 ✔
4175

4176
void Executor::dbg_clrallbp()
×
4177
{
4178
  if ( dbg_env_ )
×
4179
  {
4180
    dbg_env_->breakpoints.clear();
×
4181
  }
4182
}
×
4183

4184
size_t Executor::sizeEstimate() const
2,139 ✔
4185
{
4186
  size_t size = sizeof( *this );
2,139 ✔
4187
  size += Clib::memsize( upperLocals2 );
2,139 ✔
4188
  for ( const auto& bobjectrefvec : upperLocals2 )
2,140 ✔
4189
  {
4190
    size += Clib::memsize( *bobjectrefvec );
1 ✔
4191
    for ( const auto& bojectref : *bobjectrefvec )
1 ✔
4192
    {
4193
      if ( bojectref != nullptr )
×
4194
        size += bojectref->sizeEstimate();
×
4195
    }
4196
  }
4197
  size += Clib::memsize( ControlStack );
2,139 ✔
4198

4199
  size += Clib::memsize( *Locals2 );
2,139 ✔
4200
  for ( const auto& bojectref : *Locals2 )
2,250 ✔
4201
  {
4202
    if ( bojectref != nullptr )
111 ✔
4203
      size += bojectref->sizeEstimate();
111 ✔
4204
  }
4205
  size += Clib::memsize( *Globals2 );
2,139 ✔
4206
  for ( const auto& bojectref : *Globals2 )
4,520 ✔
4207
  {
4208
    if ( bojectref != nullptr )
2,381 ✔
4209
      size += bojectref->sizeEstimate();
2,381 ✔
4210
  }
4211
  size += Clib::memsize( ValueStack );
2,139 ✔
4212
  for ( const auto& bojectref : ValueStack )
2,178 ✔
4213
  {
4214
    if ( bojectref != nullptr )
39 ✔
4215
      size += bojectref->sizeEstimate();
39 ✔
4216
  }
4217
  size += Clib::memsize( fparams );
2,139 ✔
4218
  for ( const auto& bojectref : fparams )
2,139 ✔
4219
  {
4220
    if ( bojectref != nullptr )
×
4221
      size += bojectref->sizeEstimate();
×
4222
  }
4223
  for ( const auto& module : availmodules )
17,429 ✔
4224
  {
4225
    if ( module != nullptr )
15,290 ✔
4226
      size += module->sizeEstimate();
15,290 ✔
4227
  }
4228
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
2,139 ✔
4229
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
2,139 ✔
4230
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
2,139 ✔
4231
  size += Clib::memsize( class_methods );
2,139 ✔
4232
  return size;
2,139 ✔
4233
}
4234

4235
bool Executor::builtinMethodForced( const char*& methodname )
27 ✔
4236
{
4237
  if ( methodname[0] == '_' )
27 ✔
4238
  {
4239
    ++methodname;
×
4240
    return true;
×
4241
  }
4242
  return false;
27 ✔
4243
}
4244

4245
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
36,650 ✔
4246
{
4247
  auto* func = continuation->func();
36,650 ✔
4248

4249
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4250
  // there are too many
4251
  while ( func->numParams() > static_cast<int>( args.size() ) )
36,656 ✔
4252
  {
4253
    args.emplace_back( UninitObject::create() );
6 ✔
4254
  }
4255

4256
  // Resize args only for non-varadic functions
4257
  if ( !func->variadic() )
36,650 ✔
4258
    args.resize( func->numParams() );
36,626 ✔
4259

4260
  continuation->args = std::move( args );
36,650 ✔
4261

4262
  return continuation;
36,650 ✔
4263
}
4264

4265
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
5,352 ✔
4266
{
4267
  // Compare the program pointers
4268
  if ( prog < other.prog )
5,352 ✔
4269
    return true;
×
4270
  if ( prog > other.prog )
5,352 ✔
4271
    return false;
×
4272

4273
  // Compare the indices
4274
  if ( index < other.index )
5,352 ✔
4275
    return true;
174 ✔
4276
  if ( index > other.index )
5,178 ✔
4277
    return false;
69 ✔
4278

4279
  // Perform a case-insensitive comparison for method_name using stricmp
4280
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
5,109 ✔
4281
}
4282

4283
#ifdef ESCRIPT_PROFILE
4284
std::map<std::string, EscriptProfiler::profile_instr> EscriptProfiler::escript_profile_map_{};
4285
EscriptProfiler::EscriptProfiler( ExecutorModule* em, const ModuleFunction* modfunc,
4286
                                  const std::vector<BObjectRef>& fparams )
4287
{
4288
  name_ = em->functionName( modfunc->funcidx );
4289
  if ( !fparams.empty() )
4290
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4291
}
4292
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectRef& leftref,
4293
                                  const std::vector<BObjectRef>& fparams )
4294
{
4295
  switch ( ins.token.id )
4296
  {
4297
  case INS_GET_MEMBER:
4298
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.tokval() );
4299
    break;
4300
  case INS_GET_MEMBER_ID:
4301
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4302
    break;
4303
  case INS_CALL_METHOD_ID:
4304
    name_ = fmt::format( "MTHID_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4305
  default:
4306
    break;
4307
  }
4308
  if ( !fparams.empty() )
4309
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4310
}
4311
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectImp* callee,
4312
                                  const char* method_name, const std::vector<BObjectRef>& fparams )
4313
{
4314
  switch ( ins.token.id )
4315
  {
4316
  case INS_CALL_METHOD:
4317
    name_ = fmt::format( "MTH_{} .{}", callee->typeOf(), method_name );
4318
    break;
4319
  default:
4320
    break;
4321
  }
4322
  if ( !fparams.empty() )
4323
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4324
}
4325

4326
EscriptProfiler::~EscriptProfiler()
4327
{
4328
  auto profile_end = timer_.ellapsed().count();
4329
  auto itr = escript_profile_map_.find( name_ );
4330
  if ( itr != escript_profile_map_.end() )
4331
  {
4332
    itr->second.count++;
4333
    itr->second.sum += profile_end;
4334
    if ( itr->second.max < profile_end )
4335
      itr->second.max = profile_end;
4336
    else if ( itr->second.min > profile_end )
4337
      itr->second.min = profile_end;
4338
  }
4339
  else
4340
  {
4341
    escript_profile_map_[name_] = {
4342
        .sum = profile_end, .max = profile_end, .min = profile_end, .count = 1 };
4343
  }
4344
}
4345
std::string EscriptProfiler::result()
4346
{
4347
  std::string buffer = "FuncName,Count,Min,Max,Sum,Avarage\n";
4348
  for ( const auto& [name, profile] : escript_profile_map_ )
4349
  {
4350
    fmt::format_to( std::back_inserter( buffer ), "{},{},{},{},{},{:.2f}\n", name, profile.count,
4351
                    profile.min, profile.max, profile.sum, profile.sum / ( 1.0 * profile.count ) );
4352
  }
4353
  return buffer;
4354
}
4355
#endif
4356
}  // namespace Pol::Bscript
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc