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

polserver / polserver / 34749158714

13 Sep 2026 09:13AM UTC coverage: 81.349% (+0.04%) from 81.313%
34749158714

push

github

web-flow
add stack traces to more error log entries (#963)

49 of 97 new or added lines in 10 files covered. (50.52%)

26 existing lines in 11 files now uncovered.

59093 of 72641 relevant lines covered (81.35%)

1950060.76 hits per line

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

87.64
/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
namespace
74
{
75
// Script-controlled recursion is unbounded, so this block is capped tighter than
76
// the native one.
77
constexpr size_t MAX_REPORTED_ESCRIPT_FRAMES = 30;
78
}  // namespace
79

80
std::set<Executor*> executor_instances;
81

82
void display_executor_instances()
4 ✔
83
{
84
  for ( const auto& ex : executor_instances )
4 ✔
85
  {
86
    // Fix for crashes due to orphaned script instances.
87
    if ( !ex->empty_scriptname() )
×
88
      INFO_PRINTLN( ex->scriptname() );
×
89
  }
90
}
4 ✔
91

92
ExecutorDebugEnvironment::ExecutorDebugEnvironment( std::weak_ptr<ExecutorDebugListener> listener,
2 ✔
93
                                                    bool set_attaching )
2 ✔
94
    : debug_state( set_attaching ? ExecutorDebugState::ATTACHING : ExecutorDebugState::RUN ),
2 ✔
95
      breakpoints(),
2 ✔
96
      break_on_linechange_from{ ~0u, ~0u },
2 ✔
97
      bp_skip( ~0u ),
2 ✔
98
      listener( listener )
2 ✔
99
{
100
}
2 ✔
101

102
size_t ExecutorDebugEnvironment::sizeEstimate() const
×
103
{
104
  size_t size = sizeof( *this ) + Clib::memsize( breakpoints ) + Clib::memsize( tmpbreakpoints );
×
105
  return size;
×
106
}
107

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

188
  // check for breakpoints on this instruction
189
  if ( ( breakpoints.count( ex.PC ) || tmpbreakpoints.count( ex.PC ) ) && bp_skip != ex.PC &&
304 ✔
190
       !ex.halt() )
4 ✔
191
  {
192
    tmpbreakpoints.erase( ex.PC );
4 ✔
193
    bp_skip = ex.PC;
4 ✔
194
    debug_state = ExecutorDebugState::ATTACHED;
4 ✔
195
    ex.sethalt( true );
4 ✔
196
    return false;
4 ✔
197
  }
198
  bp_skip = ~0u;
296 ✔
199

200
  return true;
296 ✔
201
}
202

203
extern int executor_count;
204
Clib::SpinLock Executor::_executor_lock;
205
Executor::Executor()
2,973 ✔
206
    : done( 0 ),
2,973 ✔
207
      error_( false ),
2,973 ✔
208
      halt_( false ),
2,973 ✔
209
      run_ok_( false ),
2,973 ✔
210
      debug_level( NONE ),
2,973 ✔
211
      PC( 0 ),
2,973 ✔
212
      Globals2( std::make_shared<BObjectRefVec>() ),
2,973 ✔
213
      Locals2( new BObjectRefVec ),
2,973 ✔
214
      nLines( 0 ),
2,973 ✔
215
      current_module_function( nullptr ),
2,973 ✔
216
      prog_ok_( false ),
2,973 ✔
217
      viewmode_( false ),
2,973 ✔
218
      runs_to_completion_( false ),
2,973 ✔
219
      dbg_env_( nullptr ),
2,973 ✔
220
      func_result_( nullptr )
8,919 ✔
221
{
222
  Clib::SpinLockGuard lock( _executor_lock );
2,973 ✔
223
  ++executor_count;
2,973 ✔
224
  executor_instances.insert( this );
2,973 ✔
225

226
  if ( !UninitObject::SharedInstance )
2,973 ✔
227
  {
228
    UninitObject::SharedInstance = new UninitObject;
2,139 ✔
229
    UninitObject::SharedInstanceOwner.set( UninitObject::SharedInstance );
2,139 ✔
230
  }
231
}
2,973 ✔
232

233
Executor::~Executor()
2,973 ✔
234
{
235
  {
236
    Clib::SpinLockGuard lock( _executor_lock );
2,973 ✔
237
    --executor_count;
2,973 ✔
238
    executor_instances.erase( this );
2,973 ✔
239
  }
2,973 ✔
240
  cleanup();
2,973 ✔
241
}
2,973 ✔
242
void Executor::cleanup()
3,811 ✔
243
{
244
  if ( dbg_env_ )
3,811 ✔
245
  {
246
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
8 ✔
247
      listener->on_destroy();
4 ✔
248
  }
249

250
  delete Locals2;
3,811 ✔
251
  Locals2 = nullptr;
3,811 ✔
252

253
  while ( !upperLocals2.empty() )
3,812 ✔
254
  {
255
    delete upperLocals2.back();
1 ✔
256
    upperLocals2.pop_back();
1 ✔
257
  }
258

259
  execmodules.clear();
3,811 ✔
260
  Clib::delete_all( availmodules );
3,811 ✔
261
}
3,811 ✔
262

263
bool Executor::AttachFunctionalityModules()
4,977 ✔
264
{
265
  for ( auto& fm : prog_->modules )
19,384 ✔
266
  {
267
    // if no function in the module is actually called, don't go searching for it.
268
    if ( fm->functions.empty() )
14,407 ✔
269
    {
270
      execmodules.push_back( nullptr );
×
271
      continue;
×
272
    }
273

274
    ExecutorModule* em = findModule( fm->modulename );
14,407 ✔
275
    execmodules.push_back( em );
14,407 ✔
276
    if ( em == nullptr )
14,407 ✔
277
    {
278
      ERROR_PRINTLN( "WARNING: {}: Unable to find module {}", scriptname(), fm->modulename.get() );
×
279
      return false;
×
280
    }
281

282
    if ( !fm->have_indexes )
14,407 ✔
283
    {
284
      /*
285
          FIXE: Possible optimization: store these function indexes in the
286
          EScriptProgram object, since those are cached.  Then, we only
287
          have to find the module index.
288
          */
289
      for ( unsigned fidx = 0; fidx < fm->functions.size(); fidx++ )
9,181 ✔
290
      {
291
        ModuleFunction* func = fm->functions[fidx];
5,637 ✔
292
        // FIXME: should check number of params, blah.
293
        if ( !func->name.get().empty() )
5,637 ✔
294
        {
295
          func->funcidx = em->functionIndex( func->name.get() );
5,637 ✔
296
          if ( func->funcidx == -1 )
5,637 ✔
297
          {
298
            ERROR_PRINTLN( "Unable to find {}::{}", fm->modulename.get(), func->name.get() );
×
299
            return false;
×
300
          }
301
        }
302
      }
303
      fm->have_indexes = true;
3,544 ✔
304
    }
305
  }
306
  return true;
4,977 ✔
307
}
308

309
int Executor::getParams( unsigned howMany )
188,669,762 ✔
310
{
311
  if ( howMany )
188,669,762 ✔
312
  {
313
    fparams.resize( howMany );
188,319,300 ✔
314
    for ( int i = howMany - 1; i >= 0; --i )
376,733,156 ✔
315
    {
316
      if ( ValueStack.empty() )
188,413,856 ✔
317
      {
NEW
318
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={}){}", prog_->name, PC,
×
NEW
319
                        script_stack_block() );
×
320
        seterror( true );
×
321
        return -1;
×
322
      }
323
      fparams[i] = ValueStack.back();
188,413,856 ✔
324
      ValueStack.pop_back();
188,413,856 ✔
325
    }
326
  }
327
  expandParams();
188,669,762 ✔
328
  return 0;
188,669,762 ✔
329
}
330

331
void Executor::expandParams()
188,669,762 ✔
332
{
333
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
377,084,848 ✔
334
  {
335
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
188,415,086 ✔
336
    {
337
      // defer destruction
338
      BObjectRef obj( spread );
629 ✔
339

340
      // Remove the spread
341
      fparams.erase( fparams.begin() + i );
629 ✔
342

343
      BObjectRef refIter( UninitObject::create() );
629 ✔
344

345
      auto pIter = std::unique_ptr<ContIterator>(
346
          spread->object->impptr()->createIterator( refIter.get() ) );
629 ✔
347

348
      BObject* next = pIter->step();
629 ✔
349

350
      int added = 0;
629 ✔
351
      while ( next != nullptr )
1,859 ✔
352
      {
353
        fparams.insert( fparams.begin() + i + added, BObjectRef( next ) );
1,230 ✔
354
        next = pIter->step();
1,230 ✔
355
        added++;
1,230 ✔
356
      }
357
      i += added;
629 ✔
358
    }
629 ✔
359
  }
360
}
188,669,762 ✔
361

362
void Executor::cleanParams()
188,666,457 ✔
363
{
364
  fparams.clear();
188,666,457 ✔
365
}
188,666,457 ✔
366

367
int Executor::makeString( unsigned param )
10,844 ✔
368
{
369
  BObject* obj = getParam( param );
10,844 ✔
370
  if ( !obj )
10,844 ✔
371
    return -1;
×
372
  if ( obj->isa( BObjectImp::OTString ) )
10,844 ✔
373
    return 0;
10,844 ✔
374

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

377
  return 0;
×
378
}
379

380
const char* Executor::paramAsString( unsigned param )
5,483 ✔
381
{
382
  makeString( param );
5,483 ✔
383
  BObjectImp* objimp = fparams[param]->impptr();
5,483 ✔
384

385
  String* str = (String*)objimp;
5,483 ✔
386
  return str ? str->data() : "";
5,483 ✔
387
}
388

389
int Executor::makeDouble( unsigned param )
×
390
{
391
  BObject* obj = getParam( param );
×
392
  if ( !obj )
×
393
    return -1;
×
394
  if ( obj->isa( BObjectImp::OTDouble ) )
×
395
    return 0;
×
396
  if ( auto* v = obj->impptr_if<BLong>() )
×
397
    fparams[param].set( new Double( v->value() ) );
×
398
  else
399
    fparams[param].set( new Double( 0.0 ) );
×
400

401
  return 0;
×
402
}
403

404
double Executor::paramAsDouble( unsigned param )
×
405
{
406
  makeDouble( param );
×
407
  if ( auto* v = getParam( param )->impptr_if<Double>() )
×
408
    return v->value();
×
409
  return 0.0;
×
410
}
411

412
int Executor::paramAsLong( unsigned param )
187,963,672 ✔
413
{
414
  BObjectImp* objimp = getParam( param )->impptr();
187,963,672 ✔
415
  if ( auto* l = impptrIf<BLong>( objimp ) )
187,963,672 ✔
416
    return l->value();
187,963,662 ✔
417
  if ( auto* d = impptrIf<Double>( objimp ) )
10 ✔
418
    return static_cast<int>( d->value() );
10 ✔
419
  return 0;
×
420
}
421
BObject* Executor::getParam( unsigned param )
187,974,578 ✔
422
{
423
  passert_r( param < fparams.size(),
187,974,578 ✔
424
             "Script Error in '" + scriptname() + ": Less Parameter than expected. " +
425
                 "You should use *.em-files shipped with this Core and "
426
                 "recompile ALL of your Scripts _now_! RTFM" +
427
                 script_stack_block() );
428

429
  return fparams[param].get();
187,974,578 ✔
430
}
431

432
BObjectImp* Executor::getParamImp( unsigned param )
318,682 ✔
433
{
434
  passert_r( param < fparams.size(),
318,682 ✔
435
             "Script Error in '" + scriptname() + ": Less Parameter than expected. " +
436
                 "You should use *.em-files shipped with this Core and "
437
                 "recompile ALL of your Scripts _now_! RTFM" +
438
                 script_stack_block() );
439

440
  return fparams[param].get()->impptr();
318,682 ✔
441
}
442

443
BObject* Executor::getParamObj( unsigned param )
1,686 ✔
444
{
445
  if ( fparams.size() > param )
1,686 ✔
446
    return fparams[param].get();
1,686 ✔
447
  return nullptr;
×
448
}
449

450
BObjectImp* Executor::getParamImp( unsigned param, BObjectImp::BObjectType type )
92,935 ✔
451
{
452
  passert_r( param < fparams.size(),
92,935 ✔
453
             "Script Error in '" + scriptname() + ": Less Parameter than expected. " +
454
                 "You should use *.em-files shipped with this Core and "
455
                 "recompile ALL of your Scripts _now_! RTFM" +
456
                 script_stack_block() );
457

458
  BObjectImp* imp = fparams[param].get()->impptr();
92,935 ✔
459

460
  passert( imp != nullptr );
92,935 ✔
461

462
  if ( imp->isa( type ) )
92,935 ✔
463
    return imp;
92,670 ✔
464
  if ( !IS_DEBUGLOG_DISABLED )
265 ✔
465
  {
466
    std::string tmp = fmt::format( "Script Error in '{}' PC={}:\n", scriptname(), PC );
265 ✔
467
    if ( current_module_function )
265 ✔
468
      fmt::format_to( std::back_inserter( tmp ), "\tCall to function {}:\n",
368 ✔
469
                      current_module_function->name.get() );
184 ✔
470
    else
471
      tmp += "\tCall to an object method.\n";
81 ✔
472
    fmt::format_to( std::back_inserter( tmp ),
×
473
                    "\tParameter {}: Expected datatype {}, got datatype {}", param,
474
                    BObjectImp::typestr( type ), BObjectImp::typestr( imp->type() ) );
265 ✔
475
    tmp += script_stack_block();
265 ✔
476
    DEBUGLOGLN( tmp );
265 ✔
477
  }
265 ✔
478
  return nullptr;
265 ✔
479
}
480

481
BObjectImp* Executor::getParamImp2( unsigned param, BObjectImp::BObjectType type )
11,951 ✔
482
{
483
  passert_r( param < fparams.size(),
11,951 ✔
484
             "Script Error in '" + scriptname() + ": Less Parameter than expected. " +
485
                 "You should use *.em-files shipped with this Core and "
486
                 "recompile ALL of your Scripts _now_! RTFM" +
487
                 script_stack_block() );
488

489
  BObjectImp* imp = fparams[param].get()->impptr();
11,951 ✔
490

491
  passert( imp != nullptr );
11,951 ✔
492

493
  if ( imp->isa( type ) )
11,951 ✔
494
    return imp;
11,905 ✔
495
  std::string report = "Invalid parameter type.  Expected param " + Clib::tostring( param ) +
92 ✔
496
                       " as " + BObjectImp::typestr( type ) + ", got " +
138 ✔
497
                       BObjectImp::typestr( imp->type() );
92 ✔
498
  func_result_ = new BError( report );
46 ✔
499
  return nullptr;
46 ✔
500
}
46 ✔
501

502

503
const String* Executor::getStringParam( unsigned param )
65,591 ✔
504
{
505
  return Clib::explicit_cast<String*, BObjectImp*>( getParamImp( param, BObjectImp::OTString ) );
65,591 ✔
506
}
507

508
const BLong* Executor::getLongParam( unsigned param )
×
509
{
510
  return Clib::explicit_cast<BLong*, BObjectImp*>( getParamImp( param, BObjectImp::OTLong ) );
×
511
}
512

513
bool Executor::getStringParam( unsigned param, const String*& pstr )
65,522 ✔
514
{
515
  pstr = getStringParam( param );
65,522 ✔
516
  return ( pstr != nullptr );
65,522 ✔
517
}
518

519
bool Executor::getParam( unsigned param, int& value )
23,907 ✔
520
{
521
  BLong* plong =
522
      Clib::explicit_cast<BLong*, BObjectImp*>( getParamImp( param, BObjectImp::OTLong ) );
23,907 ✔
523
  if ( plong == nullptr )
23,907 ✔
524
    return false;
62 ✔
525

526
  value = plong->value();
23,845 ✔
527
  return true;
23,845 ✔
528
}
529

530
void Executor::setFunctionResult( BObjectImp* imp )
66 ✔
531
{
532
  func_result_ = imp;
66 ✔
533
}
66 ✔
534

535
void Executor::printStack( const std::string& message = "" )
74,994 ✔
536
{
537
  if ( debug_level < INSTRUCTIONS )
74,994 ✔
538
    return;
74,994 ✔
539

540
  if ( !message.empty() )
×
541
  {
542
    INFO_PRINTLN( message );
×
543
  }
544

545
  size_t i = 0;
×
546
  for ( auto& fparam : std::ranges::reverse_view( fparams ) )
×
547
  {
548
    auto* ptr = fparam.get()->impptr();
×
549
    INFO_PRINTLN( "fparam[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
550
    i++;
×
551
  }
552

553
  i = 0;
×
554
  for ( auto& riter : std::ranges::reverse_view( ValueStack ) )
×
555
  {
556
    auto* ptr = riter.get()->impptr();
×
557
    INFO_PRINTLN( "vstack[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
558
    i++;
×
559
  }
560

561
  INFO_PRINTLN( "---" );
×
562
}
563

564
bool Executor::getParam( unsigned param, int& value, int maxval )
101 ✔
565
{
566
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
101 ✔
567
  if ( !imp )
101 ✔
568
    return false;
×
569
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
101 ✔
570

571
  value = plong->value();
101 ✔
572
  if ( value >= 0 && value <= maxval )
101 ✔
573
    return true;
96 ✔
574
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
5 ✔
575
                                          param, value, maxval ) );
10 ✔
576
  return false;
5 ✔
577
}
578

579
bool Executor::getParam( unsigned param, int& value, int minval, int maxval )
2,317 ✔
580
{
581
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
2,317 ✔
582
  if ( !imp )
2,317 ✔
583
    return false;
22 ✔
584
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
2,295 ✔
585

586
  value = plong->value();
2,295 ✔
587
  if ( value >= minval && value <= maxval )
2,295 ✔
588
    return true;
2,265 ✔
589
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
30 ✔
590
                                          param, value, minval, maxval ) );
60 ✔
591
  return false;
30 ✔
592
}
593

594
bool Executor::getRealParam( unsigned param, double& value )
182 ✔
595
{
596
  BObjectImp* imp = getParamImp( param );
182 ✔
597
  if ( auto* d = impptrIf<Double>( imp ) )
182 ✔
598
  {
599
    value = d->value();
45 ✔
600
    return true;
45 ✔
601
  }
602
  if ( auto* l = impptrIf<BLong>( imp ) )
137 ✔
603
  {
604
    value = l->value();
83 ✔
605
    return true;
83 ✔
606
  }
607
  DEBUGLOGLN(
54 ✔
608
      "Script Error in '{}' PC={}: \n"
609
      "\tCall to function {}:\n"
610
      "\tParameter {}: Expected Integer or Real, got datatype {}{}",
611
      scriptname(), PC, current_module_function->name.get(), param,
54 ✔
612
      BObjectImp::typestr( imp->type() ), script_stack_block() );
108 ✔
613

614
  return false;
54 ✔
615
}
616

617
bool Executor::getObjArrayParam( unsigned param, ObjArray*& pobjarr )
1,745 ✔
618
{
619
  pobjarr =
1,745 ✔
620
      Clib::explicit_cast<ObjArray*, BObjectImp*>( getParamImp( param, BObjectImp::OTArray ) );
1,745 ✔
621
  return ( pobjarr != nullptr );
1,745 ✔
622
}
623

624
BApplicObjBase* Executor::getApplicObjParam( unsigned param, const BApplicObjType* object_type )
473 ✔
625
{
626
  auto aob = static_cast<BApplicObjBase*>( getParamImp( param, BObjectImp::OTApplicObj ) );
473 ✔
627
  if ( aob == nullptr )
473 ✔
628
    return nullptr;
39 ✔
629

630
  if ( aob->object_type() == object_type )
434 ✔
631
    return aob;
433 ✔
632
  DEBUGLOGLN(
1 ✔
633
      "Script Error in '{}' PC={}: \n"
634
      "\tCall to function {}:\n"
635
      "\tParameter {}: Expected datatype, got datatype {}{}",
636
      scriptname(), PC, current_module_function->name.get(), param, aob->getStringRep(),
1 ✔
637
      script_stack_block() );
2 ✔
638

639
  return nullptr;
1 ✔
640
}
641

642
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short maxval )
×
643
{
644
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
645
  if ( !imp )
×
646
    return false;
×
647
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
648

649
  int longvalue = plong->value();
×
650
  if ( longvalue >= 0 && longvalue <= maxval )
×
651
  {
652
    value = static_cast<unsigned short>( longvalue );
×
653
    return true;
×
654
  }
655
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
656
                                          param, longvalue, maxval ) );
×
657
  return false;
×
658
}
659

660
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short minval,
585 ✔
661
                         unsigned short maxval )
662
{
663
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
585 ✔
664
  if ( !imp )
585 ✔
665
    return false;
×
666
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
585 ✔
667

668
  int longvalue = plong->value();
585 ✔
669
  if ( longvalue >= minval && longvalue <= maxval )
585 ✔
670
  {
671
    value = static_cast<unsigned short>( longvalue );
575 ✔
672
    return true;
575 ✔
673
  }
674
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
10 ✔
675
                                          param, longvalue, minval, maxval ) );
20 ✔
676
  return false;
10 ✔
677
}
678
bool Executor::getParam( unsigned param, unsigned short& value )
6,025 ✔
679
{
680
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,025 ✔
681
  if ( !imp )
6,025 ✔
682
    return false;
19 ✔
683
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
6,006 ✔
684

685
  int longvalue = plong->value();
6,006 ✔
686
  if ( longvalue >= 0 && longvalue <= USHRT_MAX )
6,006 ✔
687
  {
688
    value = static_cast<unsigned short>( longvalue );
6,005 ✔
689
    return true;
6,005 ✔
690
  }
691
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
1 ✔
692
                                          param, longvalue, USHRT_MAX ) );
2 ✔
693
  return false;
1 ✔
694
}
695
bool Executor::getParam( unsigned param, unsigned& value )
164 ✔
696
{
697
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
164 ✔
698
  if ( !imp )
164 ✔
699
    return false;
5 ✔
700
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
159 ✔
701

702
  int longvalue = plong->value();
159 ✔
703
  if ( longvalue >= 0 )  // && longvalue <= (int)INT_MAX )
159 ✔
704
  {
705
    value = static_cast<unsigned>( longvalue );
159 ✔
706
    return true;
159 ✔
707
  }
708
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [0..{}]",
×
709
                                          param, longvalue, INT_MAX ) );
×
710
  return false;
×
711
}
712

713
bool Executor::getParam( unsigned param, short& value )
1,338 ✔
714
{
715
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
1,338 ✔
716
  if ( !imp )
1,338 ✔
717
    return false;
×
718
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
1,338 ✔
719

720
  int longvalue = plong->value();
1,338 ✔
721
  if ( longvalue >= (int)SHRT_MIN && longvalue <= (int)SHRT_MAX )
1,338 ✔
722
  {
723
    value = static_cast<short>( longvalue );
1,338 ✔
724
    return true;
1,338 ✔
725
  }
726
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
727
                                          param, longvalue, SHRT_MIN, SHRT_MAX ) );
×
728
  return false;
×
729
}
730

731
bool Executor::getParam( unsigned param, short& value, short maxval )
×
732
{
733
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
734
  if ( !imp )
×
735
    return false;
×
736
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
737

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

749
bool Executor::getParam( unsigned param, short& value, short minval, short maxval )
×
750
{
751
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
752
  if ( !imp )
×
753
    return false;
×
754
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
755

756
  int longvalue = plong->value();
×
757
  if ( longvalue >= minval && longvalue <= maxval )
×
758
  {
759
    value = static_cast<short>( longvalue );
×
760
    return true;
×
761
  }
762
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
763
                                          param, longvalue, minval, maxval ) );
×
764
  return false;
×
765
}
766

767
bool Executor::getParam( unsigned param, signed char& value )
1,421 ✔
768
{
769
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
1,421 ✔
770
  if ( !imp )
1,421 ✔
771
    return false;
×
772

773
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
1,421 ✔
774

775
  int longvalue = plong->value();
1,421 ✔
776
  if ( longvalue >= std::numeric_limits<s8>::min() && longvalue <= std::numeric_limits<s8>::max() )
1,421 ✔
777
  {
778
    value = static_cast<signed char>( longvalue );
1,421 ✔
779
    return true;
1,421 ✔
780
  }
781
  func_result_ = new BError( fmt::format( "Parameter {} value {} out of expected range of [{}..{}]",
×
782
                                          param, longvalue, std::numeric_limits<s8>::min(),
×
783
                                          std::numeric_limits<s8>::max() ) );
×
784
  return false;
×
785
}
786

787
bool Executor::getParam( unsigned param, bool& value )
50 ✔
788
{
789
  BObjectImp* imp = getParamImp( param );
50 ✔
790
  if ( auto* b = impptrIf<BBoolean>( imp ) )
50 ✔
791
  {
792
    value = b->value();
1 ✔
793
    return true;
1 ✔
794
  }
795
  if ( auto* l = impptrIf<BLong>( imp ) )
49 ✔
796
  {
797
    value = l->isTrue();
46 ✔
798
    return true;
46 ✔
799
  }
800
  DEBUGLOGLN(
3 ✔
801
      "Script Error in '{}' PC={}: \n"
802
      "\tCall to function {}:\n"
803
      "\tParameter {}: Expected Boolean or Integer, got datatype {}{}",
804
      scriptname(), PC, current_module_function->name.get(), param,
3 ✔
805
      BObjectImp::typestr( imp->type() ), script_stack_block() );
6 ✔
806

807
  return false;
3 ✔
808
}
809

810
bool Executor::getUnicodeStringParam( unsigned param, const String*& pstr )
62 ✔
811
{
812
  BObject* obj = getParam( param );
62 ✔
813
  if ( !obj )
62 ✔
814
    return false;
×
815
  if ( auto* s = obj->impptr_if<String>() )
62 ✔
816
  {
817
    pstr = s;
58 ✔
818
    return true;
58 ✔
819
  }
820
  if ( auto* a = obj->impptr_if<ObjArray>() )
4 ✔
821
  {
822
    String* str = String::fromUCArray( a );
1 ✔
823
    fparams[param].set( str );  // store raw pointer
1 ✔
824
    pstr = str;
1 ✔
825
    return true;
1 ✔
826
  }
827
  func_result_ = new BError( fmt::format(
3 ✔
828
      "Invalid parameter type.  Expected param {} as {} or {}, got {}", param,
829
      BObjectImp::typestr( BObjectImp::OTString ), BObjectImp::typestr( BObjectImp::OTArray ),
3 ✔
830
      BObjectImp::typestr( obj->impptr()->type() ) ) );
6 ✔
831
  return false;
3 ✔
832
}
833

834
BObjectRef& Executor::LocalVar( unsigned int varnum )
×
835
{
836
  passert( Locals2 );
×
837
  passert( varnum < Locals2->size() );
×
838

839
  return ( *Locals2 )[varnum];
×
840
}
841

842
BObjectRef& Executor::GlobalVar( unsigned int varnum )
×
843
{
844
  if ( varnum >= Globals2->size() )
×
845
  {
NEW
846
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={}){}", prog_->name, PC,
×
NEW
847
                    script_stack_block() );
×
848
    seterror( true );
×
849
    UninitObject::SharedInstanceRef.set( UninitObject::SharedInstance );
×
850
    return UninitObject::SharedInstanceRef;
×
851
  }
852
  return ( *Globals2 )[varnum];
×
853
}
854

855
int Executor::getToken( Token& token, unsigned position )
×
856
{
857
  if ( position >= nLines )
×
858
    return -1;
×
859
  token = prog_->instr[position].token;
×
860
  return 0;
×
861
}
862

863

864
bool Executor::setProgram( EScriptProgram* i_prog )
2,973 ✔
865
{
866
  prog_.set( i_prog );
2,973 ✔
867
  prog_ok_ = false;
2,973 ✔
868
  seterror( true );
2,973 ✔
869
  if ( !viewmode_ )
2,973 ✔
870
  {
871
    if ( !AttachFunctionalityModules() )
2,973 ✔
872
      return false;
×
873
  }
874

875
  nLines = static_cast<unsigned int>( prog_->instr.size() );
2,973 ✔
876

877
  Globals2->clear();
2,973 ✔
878
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
5,853 ✔
879
  {
880
    Globals2->emplace_back( UninitObject::create() );
2,880 ✔
881
  }
882

883
  prog_ok_ = true;
2,973 ✔
884
  seterror( false );
2,973 ✔
885
  ++prog_->invocations;
2,973 ✔
886
  return true;
2,973 ✔
887
}
888

889
BObjectRef Executor::getObjRef()
181,352 ✔
890
{
891
  if ( ValueStack.empty() )
181,352 ✔
892
  {
NEW
893
    POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={}){}", prog_->name, PC,
×
NEW
894
                    script_stack_block() );
×
895
    seterror( true );
×
896
    return BObjectRef( UninitObject::create() );
×
897
  }
898

899
  BObjectRef ref = ValueStack.back();
181,352 ✔
900
  ValueStack.pop_back();
181,352 ✔
901
  return ref;
181,352 ✔
902
}
181,352 ✔
903

904

905
void Executor::execFunc( const Token& token )
188,399,601 ✔
906
{
907
  FunctionalityModule* fm = prog_->modules[token.module];
188,399,601 ✔
908
  ModuleFunction* modfunc = fm->functions[token.lval];
188,399,601 ✔
909
  current_module_function = modfunc;
188,399,601 ✔
910
  if ( modfunc->funcidx == -1 )
188,399,601 ✔
911
  {
912
    DEBUGLOGLN(
×
913
        "Error in script '{}':\n"
914
        "\tModule Function {} was not found.",
915
        prog_->name.get(), modfunc->name.get() );
×
916

917
    throw std::runtime_error( "No implementation for function found." );
×
918
  }
919

920
  ExecutorModule* em = execmodules[token.module];
188,399,601 ✔
921

922
  func_result_ = nullptr;
188,399,601 ✔
923
  BObjectImp* resimp;
924
  {
925
    ESCRIPT_PROFILER( em, modfunc, fparams );
926
    resimp = em->execFunc( modfunc->funcidx );
188,399,601 ✔
927
  }
928
  if ( func_result_ )
188,399,601 ✔
929
  {
930
    if ( resimp )
111 ✔
931
    {
932
      BObject obj( resimp );
111 ✔
933
    }
111 ✔
934
    ValueStack.emplace_back( func_result_ );
111 ✔
935
    func_result_ = nullptr;
111 ✔
936
  }
937
  else if ( resimp )
188,399,490 ✔
938
  {
939
    ValueStack.emplace_back( resimp );
188,399,463 ✔
940
  }
941
  else
942
  {
943
    ValueStack.emplace_back( UninitObject::create() );
27 ✔
944
  }
945

946
  current_module_function = nullptr;
188,399,601 ✔
947
}
188,399,601 ✔
948

949
// RSV_LOCAL
950
void Executor::ins_makeLocal( const Instruction& /*ins*/ )
241,029 ✔
951
{
952
  passert( Locals2 != nullptr );
241,029 ✔
953

954
  Locals2->emplace_back( UninitObject::create() );
241,029 ✔
955

956
  ValueStack.emplace_back( Locals2->back().get() );
241,029 ✔
957
}
241,029 ✔
958

959
// RSV_DECLARE_ARRAY
960
void Executor::ins_declareArray( const Instruction& /*ins*/ )
72 ✔
961
{
962
  BObjectRef objref = getObjRef();
72 ✔
963

964
  if ( !objref->isa( BObjectImp::OTUninit ) )
72 ✔
965
  {
966
    // FIXME: weak error message
967
    ERROR_PRINTLN( "variable is already initialized.." );
×
968
    seterror( true );
×
969
    return;
×
970
  }
971
  objref->setimp( new ObjArray );
72 ✔
972

973
  ValueStack.emplace_back( objref );
72 ✔
974
}
72 ✔
975

976
void Executor::popParam( const Token& /*token*/ )
127,247 ✔
977
{
978
  BObjectRef objref = getObjRef();
127,247 ✔
979

980
  Locals2->emplace_back( objref->impptr()->copy() );
127,247 ✔
981
}
127,247 ✔
982

983
void Executor::popParamByRef( const Token& /*token*/ )
34,485 ✔
984
{
985
  BObjectRef objref = getObjRef();
34,485 ✔
986

987
  Locals2->emplace_back( objref );
34,485 ✔
988
}
34,485 ✔
989

990
void Executor::getArg( const Token& /*token*/ )
1,173 ✔
991
{
992
  if ( ValueStack.empty() )
1,173 ✔
993
  {
994
    Locals2->emplace_back( UninitObject::create() );
53 ✔
995
  }
996
  else
997
  {
998
    BObjectRef objref = getObjRef();
1,120 ✔
999
    Locals2->emplace_back( objref->impptr()->copy() );
1,120 ✔
1000
  }
1,120 ✔
1001
}
1,173 ✔
1002

1003

1004
BObjectRef Executor::addmember( BObject& left, const BObject& right )
4,758 ✔
1005
{
1006
  if ( !right.isa( BObjectImp::OTString ) )
4,758 ✔
1007
  {
1008
    return BObjectRef( left.clone() );
×
1009
  }
1010

1011
  const String& varname = right.impref<const String>();
4,758 ✔
1012

1013
  return left.impref().operDotPlus( varname.data() );
4,758 ✔
1014
}
1015

1016
BObjectRef Executor::removemember( BObject& left, const BObject& right )
94 ✔
1017
{
1018
  if ( !right.isa( BObjectImp::OTString ) )
94 ✔
1019
  {
1020
    return BObjectRef( left.clone() );
×
1021
  }
1022

1023
  const String& varname = right.impref<const String>();
94 ✔
1024

1025
  return left.impref().operDotMinus( varname.data() );
94 ✔
1026
}
1027

1028
BObjectRef Executor::checkmember( BObject& left, const BObject& right )
51,014 ✔
1029
{
1030
  if ( !right.isa( BObjectImp::OTString ) )
51,014 ✔
1031
  {
1032
    return BObjectRef( left.clone() );
×
1033
  }
1034

1035
  const String& varname = right.impref<const String>();
51,014 ✔
1036

1037
  return left.impref().operDotQMark( varname.data() );
51,014 ✔
1038
}
1039

1040

1041
/* Coming into initforeach, the expr to be iterated through is on the value stack.
1042
   Initforeach must create three local variables:
1043
   0. the iterator
1044
   1. the expression
1045
   2. the counter
1046
   and remove the expression from the value stack.
1047
   It then jumps to the STEPFOREACH instruction.
1048
   */
1049
void Executor::ins_initforeach( const Instruction& ins )
2,639 ✔
1050
{
1051
  Locals2->emplace_back( UninitObject::create() );  // the iterator
2,639 ✔
1052

1053
  auto pIterVal = new BObject( UninitObject::create() );
2,639 ✔
1054

1055
  // this is almost like popParam, only we don't want a copy.
1056
  BObjectRef objref = getObjRef();
2,639 ✔
1057
  ContIterator* pIter = objref->impptr()->createIterator( pIterVal );
2,639 ✔
1058
  Locals2->emplace_back( pIter );
2,639 ✔
1059

1060
  Locals2->emplace_back( pIterVal );
2,639 ✔
1061

1062
  // Jump to to the corresponding `stepforeach` instruction, advancing the iterator.
1063
  PC = ins.token.lval;
2,639 ✔
1064
}
2,639 ✔
1065

1066
void Executor::ins_stepforeach( const Instruction& ins )
34,292 ✔
1067
{
1068
  size_t locsize = Locals2->size();
34,292 ✔
1069
  ContIterator* pIter = ( *Locals2 )[locsize - 2]->impptr<ContIterator>();
34,292 ✔
1070

1071
  BObject* next = pIter->step();
34,292 ✔
1072
  // If iterator has a value, set it on the locals stack and jump to the
1073
  // corresponding instruction after `initforeach`.
1074
  if ( next != nullptr )
34,292 ✔
1075
  {
1076
    ( *Locals2 )[locsize - 3].set( next );
32,088 ✔
1077
    PC = ins.token.lval;
32,088 ✔
1078
  }
1079
}
34,292 ✔
1080

1081
/*
1082
    Coming into the INITFOR, there will be two values on the value stack:
1083
    START VALUE
1084
    END VALUE
1085

1086
    If START VALUE > END VALUE, we skip the whole for loop.
1087
    (the INITFOR's lval has the instr to jump to)
1088
    */
1089
void Executor::ins_initfor( const Instruction& ins )
119 ✔
1090
{
1091
  BObjectRef endref = getObjRef();
119 ✔
1092
  BObjectRef startref = getObjRef();
119 ✔
1093
  if ( *startref.get() > *endref.get() )
119 ✔
1094
  {
1095
    PC = ins.token.lval;
×
1096
    return;
×
1097
  }
1098

1099
  Locals2->emplace_back( startref->clone() );  // the iterator
119 ✔
1100
  Locals2->emplace_back( endref->clone() );
119 ✔
1101
}
119 ✔
1102

1103
void Executor::ins_nextfor( const Instruction& ins )
1,649 ✔
1104
{
1105
  size_t locsize = Locals2->size();
1,649 ✔
1106
  BObjectImp* itr = ( *Locals2 )[locsize - 2]->impptr();
1,649 ✔
1107
  BObjectImp* end = ( *Locals2 )[locsize - 1]->impptr();
1,649 ✔
1108

1109
  if ( auto* l = impptrIf<BLong>( itr ) )
1,649 ✔
1110
    l->increment();
1,649 ✔
1111
  else if ( auto* d = impptrIf<Double>( itr ) )
×
1112
    d->increment();
×
1113

1114
  if ( *end >= *itr )
1,649 ✔
1115
  {
1116
    PC = ins.token.lval;
1,544 ✔
1117
  }
1118
}
1,649 ✔
1119

1120
int Executor::ins_casejmp_findlong( const Token& token, BLong* blong )
148 ✔
1121
{
1122
  const unsigned char* dataptr = token.dataptr;
148 ✔
1123
  for ( ;; )
1124
  {
1125
    unsigned short offset;
1126
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
329 ✔
1127
    dataptr += 2;
329 ✔
1128
    unsigned char type = *dataptr;
329 ✔
1129
    dataptr += 1;
329 ✔
1130
    if ( type == CASE_TYPE_LONG )
329 ✔
1131
    {
1132
      int v = blong->value();
248 ✔
1133
      if ( std::memcmp( &v, dataptr, sizeof( int ) ) == 0 )
248 ✔
1134
        return offset;
115 ✔
1135
      dataptr += 4;
133 ✔
1136
    }
1137
    else if ( type == CASE_TYPE_DEFAULT )
81 ✔
1138
    {
1139
      return offset;
33 ✔
1140
    }
1141
    else if ( type == CASE_TYPE_UNINIT )
48 ✔
1142
    {
1143
      /* nothing */
1144
    }
1145
    else if ( type == CASE_TYPE_BOOL )
48 ✔
1146
    {
1147
      dataptr += 1;
×
1148
    }
1149
    else if ( type == CASE_TYPE_STRING )
48 ✔
1150
    {
1151
      unsigned char len = *dataptr;
48 ✔
1152
      dataptr += 1 + len;
48 ✔
1153
    }
1154
  }
181 ✔
1155
}
1156

1157
int Executor::ins_casejmp_findbool( const Token& token, BBoolean* bbool )
15 ✔
1158
{
1159
  const unsigned char* dataptr = token.dataptr;
15 ✔
1160
  for ( ;; )
1161
  {
1162
    unsigned short offset;
1163
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
45 ✔
1164
    dataptr += 2;
45 ✔
1165
    unsigned char type = *dataptr;
45 ✔
1166
    dataptr += 1;
45 ✔
1167
    if ( type == CASE_TYPE_LONG )
45 ✔
1168
    {
1169
      dataptr += 4;
24 ✔
1170
    }
1171
    else if ( type == CASE_TYPE_DEFAULT )
21 ✔
1172
    {
1173
      return offset;
15 ✔
1174
    }
1175
    else if ( type == CASE_TYPE_UNINIT )
21 ✔
1176
    {
1177
      /* nothing */
1178
    }
1179
    else if ( type == CASE_TYPE_BOOL )
21 ✔
1180
    {
1181
      bool value = static_cast<bool>( *dataptr );
21 ✔
1182
      dataptr += 1;
21 ✔
1183
      if ( value == bbool->value() )
21 ✔
1184
      {
1185
        return offset;
15 ✔
1186
      }
1187
    }
1188
    else if ( type == CASE_TYPE_STRING )
×
1189
    {
1190
      unsigned char len = *dataptr;
×
1191
      dataptr += 1 + len;
×
1192
    }
1193
  }
30 ✔
1194
}
1195

1196
int Executor::ins_casejmp_finduninit( const Token& token )
6 ✔
1197
{
1198
  const unsigned char* dataptr = token.dataptr;
6 ✔
1199
  for ( ;; )
1200
  {
1201
    unsigned short offset;
1202
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
30 ✔
1203
    dataptr += 2;
30 ✔
1204
    unsigned char type = *dataptr;
30 ✔
1205
    dataptr += 1;
30 ✔
1206
    if ( type == CASE_TYPE_LONG )
30 ✔
1207
    {
1208
      dataptr += 4;
12 ✔
1209
    }
1210
    else if ( type == CASE_TYPE_DEFAULT )
18 ✔
1211
    {
1212
      return offset;
6 ✔
1213
    }
1214
    else if ( type == CASE_TYPE_UNINIT )
18 ✔
1215
    {
1216
      return offset;
6 ✔
1217
    }
1218
    else if ( type == CASE_TYPE_BOOL )
12 ✔
1219
    {
1220
      dataptr += 1;
12 ✔
1221
    }
1222
    else if ( type == CASE_TYPE_STRING )
×
1223
    {
1224
      unsigned char len = *dataptr;
×
1225
      dataptr += 1 + len;
×
1226
    }
1227
  }
24 ✔
1228
}
1229

1230
int Executor::ins_casejmp_findstring( const Token& token, String* bstringimp )
390 ✔
1231
{
1232
  const std::string& bstring = bstringimp->value();
390 ✔
1233
  const unsigned char* dataptr = token.dataptr;
390 ✔
1234
  for ( ;; )
1235
  {
1236
    unsigned short offset;
1237
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
2,440 ✔
1238
    dataptr += 2;
2,440 ✔
1239
    unsigned char type = *dataptr;
2,440 ✔
1240
    dataptr += 1;
2,440 ✔
1241
    if ( type == CASE_TYPE_LONG )
2,440 ✔
1242
    {
1243
      dataptr += 4;
36 ✔
1244
    }
1245
    else if ( type == CASE_TYPE_DEFAULT )
2,404 ✔
1246
    {
1247
      return offset;
390 ✔
1248
    }
1249
    else if ( type == CASE_TYPE_BOOL )
2,382 ✔
1250
    {
1251
      dataptr += 1;
12 ✔
1252
    }
1253
    else if ( type == CASE_TYPE_UNINIT )
2,370 ✔
1254
    {
1255
      /* nothing */
1256
    }
1257
    else if ( type == CASE_TYPE_STRING )
2,364 ✔
1258
    {
1259
      unsigned char len = *dataptr;
2,364 ✔
1260
      dataptr += 1;
2,364 ✔
1261
      if ( bstring.size() == len && memcmp( bstring.data(), dataptr, len ) == 0 )
2,364 ✔
1262
      {
1263
        return offset;
368 ✔
1264
      }
1265
      dataptr += len;
1,996 ✔
1266
    }
1267
  }
2,050 ✔
1268
}
1269

1270
int Executor::ins_casejmp_finddefault( const Token& token )
23 ✔
1271
{
1272
  const unsigned char* dataptr = token.dataptr;
23 ✔
1273
  for ( ;; )
1274
  {
1275
    unsigned short offset;
1276
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
76 ✔
1277
    dataptr += 2;
76 ✔
1278
    unsigned char type = *dataptr;
76 ✔
1279
    dataptr += 1;
76 ✔
1280
    if ( type == CASE_TYPE_LONG )
76 ✔
1281
    {
1282
      dataptr += 4;
29 ✔
1283
    }
1284
    else if ( type == CASE_TYPE_DEFAULT )
47 ✔
1285
    {
1286
      return offset;
23 ✔
1287
    }
1288
    else if ( type == CASE_TYPE_UNINIT )
24 ✔
1289
    {
1290
      /* nothing */
1291
    }
1292
    else if ( type == CASE_TYPE_BOOL )
18 ✔
1293
    {
1294
      dataptr += 1;
12 ✔
1295
    }
1296
    else if ( type == CASE_TYPE_STRING )
6 ✔
1297
    {
1298
      unsigned char len = *dataptr;
6 ✔
1299
      dataptr += 1 + len;
6 ✔
1300
    }
1301
  }
53 ✔
1302
}
1303

1304
void Executor::ins_casejmp( const Instruction& ins )
582 ✔
1305
{
1306
  BObjectRef& objref = ValueStack.back();
582 ✔
1307
  BObjectImp* objimp = objref->impptr();
582 ✔
1308
  if ( auto* l = impptrIf<BLong>( objimp ) )
582 ✔
1309
    PC = ins_casejmp_findlong( ins.token, l );
148 ✔
1310
  else if ( auto* s = impptrIf<String>( objimp ) )
434 ✔
1311
    PC = ins_casejmp_findstring( ins.token, s );
390 ✔
1312
  else if ( auto* b = impptrIf<BBoolean>( objimp ) )
44 ✔
1313
    PC = ins_casejmp_findbool( ins.token, b );
15 ✔
1314
  else if ( impptrIf<UninitObject>( objimp ) )
29 ✔
1315
    PC = ins_casejmp_finduninit( ins.token );
6 ✔
1316
  else
1317
    PC = ins_casejmp_finddefault( ins.token );
23 ✔
1318
  ValueStack.pop_back();
582 ✔
1319
}
582 ✔
1320

1321

1322
void Executor::ins_jmpiftrue( const Instruction& ins )
31,525 ✔
1323
{
1324
  BObjectRef& objref = ValueStack.back();
31,525 ✔
1325

1326
  if ( objref->impptr()->isTrue() )
31,525 ✔
1327
    PC = (unsigned)ins.token.lval;
27,771 ✔
1328

1329
  ValueStack.pop_back();
31,525 ✔
1330
}
31,525 ✔
1331

1332
void Executor::ins_jmpiffalse( const Instruction& ins )
636,825 ✔
1333
{
1334
  BObjectRef& objref = ValueStack.back();
636,825 ✔
1335

1336
  if ( !objref->impptr()->isTrue() )
636,825 ✔
1337
    PC = (unsigned)ins.token.lval;
420,145 ✔
1338

1339
  ValueStack.pop_back();
636,825 ✔
1340
}
636,825 ✔
1341

1342
void Executor::ins_interpolate_string( const Instruction& ins )
15,426 ✔
1343
{
1344
  auto count = ins.token.lval;
15,426 ✔
1345
  if ( count == 0 )
15,426 ✔
1346
  {
1347
    ValueStack.emplace_back( new String( "" ) );
3 ✔
1348
  }
1349
  else
1350
  {
1351
    size_t length = 0;
15,423 ✔
1352

1353
    std::vector<std::string> contents;
15,423 ✔
1354
    contents.reserve( count );
15,423 ✔
1355

1356
    while ( count-- )
57,163 ✔
1357
    {
1358
      BObjectRef rightref = ValueStack.back();
41,740 ✔
1359
      ValueStack.pop_back();
41,740 ✔
1360
      auto str = rightref->impptr()->getStringRep();
41,740 ✔
1361
      length += str.length();
41,740 ✔
1362
      contents.push_back( std::move( str ) );
41,740 ✔
1363
    }
41,740 ✔
1364

1365
    std::string joined;
15,423 ✔
1366
    joined.reserve( length );
15,423 ✔
1367

1368
    while ( !contents.empty() )
57,163 ✔
1369
    {
1370
      joined += contents.back();
41,740 ✔
1371
      contents.pop_back();
41,740 ✔
1372
    }
1373

1374
    ValueStack.emplace_back( new String( joined ) );
15,423 ✔
1375
  }
15,423 ✔
1376
}
15,426 ✔
1377

1378
void Executor::ins_format_expression( const Instruction& )
2,414 ✔
1379
{
1380
  BObjectRef formatref = ValueStack.back();
2,414 ✔
1381
  ValueStack.pop_back();
2,414 ✔
1382
  BObjectRef& exprref = ValueStack.back();
2,414 ✔
1383
  BObject& expr = *exprref;
2,414 ✔
1384

1385
  auto format = formatref->impptr()->getFormattedStringRep();
2,414 ✔
1386
  auto formatted = Bscript::get_formatted( expr.impptr(), format );
2,414 ✔
1387

1388
  exprref.set( new String( formatted ) );
2,414 ✔
1389
}
2,414 ✔
1390

1391
void Executor::ins_skipiftrue_else_consume( const Instruction& ins )
2,121 ✔
1392
{
1393
  // This is for short-circuit evaluation of the elvis operator [expr_a] ?: [expr_b]
1394
  //
1395
  // Program instructions look like this:
1396
  //   [expr_a instructions] INS_SKIPIFTRUE_ELSE_CONSUME [expr_b instructions]
1397
  //
1398
  // The result value of expr_a is on the top of the value stack when this instruction executes.
1399
  //
1400
  // If [expr_a] evaluated to true, leave its result and skip over the expr_b instructions
1401
  // otherwise, consume the false value and continue so that expr_b can replace it.
1402
  //
1403
  BObjectRef& objref = ValueStack.back();
2,121 ✔
1404

1405
  if ( objref->impptr()->isTrue() )
2,121 ✔
1406
  {
1407
    PC = PC + (unsigned)( ins.token.lval );
893 ✔
1408
  }
1409
  else
1410
  {
1411
    ValueStack.pop_back();
1,228 ✔
1412
  }
1413
}
2,121 ✔
1414

1415

1416
// case TOK_LOCALVAR:
1417
void Executor::ins_localvar( const Instruction& ins )
1,597,554 ✔
1418
{
1419
  ValueStack.push_back( ( *Locals2 )[ins.token.lval] );
1,597,554 ✔
1420
}
1,597,554 ✔
1421

1422
// case RSV_GLOBAL:
1423
// case TOK_GLOBALVAR:
1424
void Executor::ins_globalvar( const Instruction& ins )
81,607 ✔
1425
{
1426
  if ( (unsigned)ins.token.lval >= Globals2->size() )
81,607 ✔
1427
  {
1428
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={}){}", prog_->name, PC,
1 ✔
1429
                    script_stack_block() );
2 ✔
1430
    seterror( true );
1 ✔
1431
    ValueStack.emplace_back( UninitObject::create() );
1 ✔
1432
    return;
1 ✔
1433
  }
1434
  ValueStack.push_back( ( *Globals2 )[ins.token.lval] );
81,606 ✔
1435
}
1436

1437
// case TOK_LONG:
1438
void Executor::ins_long( const Instruction& ins )
188,333,903 ✔
1439
{
1440
  ValueStack.emplace_back( new BLong( ins.token.lval ) );
188,333,903 ✔
1441
}
188,333,903 ✔
1442

1443
// case TOK_BOOL:
1444
void Executor::ins_bool( const Instruction& ins )
1,949 ✔
1445
{
1446
  ValueStack.emplace_back( new BBoolean( ins.token.lval ) );
1,949 ✔
1447
}
1,949 ✔
1448

1449
// case TOK_CONSUMER:
1450
void Executor::ins_consume( const Instruction& /*ins*/ )
188,346,087 ✔
1451
{
1452
  ValueStack.pop_back();
188,346,087 ✔
1453
}
188,346,087 ✔
1454

1455
void Executor::ins_set_member( const Instruction& ins )
3 ✔
1456
{
1457
  BObjectRef rightref = ValueStack.back();
3 ✔
1458
  ValueStack.pop_back();
3 ✔
1459
  BObjectRef& leftref = ValueStack.back();
3 ✔
1460

1461
  BObject& right = *rightref;
3 ✔
1462
  BObject& left = *leftref;
3 ✔
1463

1464
  BObjectImp& rightimpref = right.impref();
3 ✔
1465
  left.impref().set_member( ins.token.tokval(), &rightimpref,
6 ✔
1466
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
3 ✔
1467
}
3 ✔
1468

1469
void Executor::ins_set_member_id( const Instruction& ins )
3 ✔
1470
{
1471
  BObjectRef rightref = ValueStack.back();
3 ✔
1472
  ValueStack.pop_back();
3 ✔
1473
  BObjectRef& leftref = ValueStack.back();
3 ✔
1474

1475
  BObject& right = *rightref;
3 ✔
1476
  BObject& left = *leftref;
3 ✔
1477

1478
  BObjectImp& rightimpref = right.impref();
3 ✔
1479
  left.impref().set_member_id( ins.token.lval, &rightimpref,
6 ✔
1480
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
3 ✔
1481
}
3 ✔
1482

1483
void Executor::ins_set_member_consume( const Instruction& ins )
658 ✔
1484
{
1485
  BObjectRef rightref = ValueStack.back();
658 ✔
1486
  ValueStack.pop_back();
658 ✔
1487
  BObjectRef& leftref = ValueStack.back();
658 ✔
1488

1489
  BObject& right = *rightref;
658 ✔
1490
  BObject& left = *leftref;
658 ✔
1491

1492
  BObjectImp& rightimpref = right.impref();
658 ✔
1493
  left.impref().set_member( ins.token.tokval(), &rightimpref,
1,316 ✔
1494
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
658 ✔
1495
  ValueStack.pop_back();
658 ✔
1496
}
658 ✔
1497

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

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

1507
  BObjectImp& rightimpref = right.impref();
905 ✔
1508

1509
  left.impref().set_member_id( ins.token.lval, &rightimpref,
1,810 ✔
1510
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
905 ✔
1511
  ValueStack.pop_back();
905 ✔
1512
}
905 ✔
1513

1514
void Executor::ins_set_member_id_consume_plusequal( const Instruction& ins )
15 ✔
1515
{
1516
  BObjectRef rightref = ValueStack.back();
15 ✔
1517
  ValueStack.pop_back();
15 ✔
1518
  BObjectRef& leftref = ValueStack.back();
15 ✔
1519

1520
  BObject& right = *rightref;
15 ✔
1521
  BObject& left = *leftref;
15 ✔
1522

1523
  BObjectImp& leftimpref = left.impref();
15 ✔
1524

1525
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15 ✔
1526

1527
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
21 ✔
1528
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6 ✔
1529
  {
1530
    tmp->impref().operPlusEqual( *tmp, right.impref() );
6 ✔
1531
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6 ✔
1532
  }
1533
  ValueStack.pop_back();
15 ✔
1534
}
15 ✔
1535

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

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

1545
  BObjectImp& leftimpref = left.impref();
6 ✔
1546

1547
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6 ✔
1548

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

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

1564
  BObject& right = *rightref;
9 ✔
1565
  BObject& left = *leftref;
9 ✔
1566

1567
  BObjectImp& leftimpref = left.impref();
9 ✔
1568

1569
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
9 ✔
1570

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

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

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

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

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

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

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

1608
  BObject& right = *rightref;
6 ✔
1609
  BObject& left = *leftref;
6 ✔
1610

1611
  BObjectImp& leftimpref = left.impref();
6 ✔
1612

1613
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6 ✔
1614

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

1624
void Executor::ins_get_member( const Instruction& ins )
226,518 ✔
1625
{
1626
  BObjectRef& leftref = ValueStack.back();
226,518 ✔
1627
  BObject& left = *leftref;
226,518 ✔
1628
  {
1629
    ESCRIPT_PROFILER( ins, leftref, fparams );
1630
    leftref = left->get_member( ins.token.tokval() );
226,518 ✔
1631
  }
1632
}
226,518 ✔
1633

1634
void Executor::ins_get_member_id( const Instruction& ins )
200,240 ✔
1635
{
1636
  BObjectRef& leftref = ValueStack.back();
200,240 ✔
1637
  BObject& left = *leftref;
200,240 ✔
1638
  {
1639
    ESCRIPT_PROFILER( ins, leftref, fparams );
1640
    leftref = left->get_member_id( ins.token.lval );
200,240 ✔
1641
  }
1642
}
200,240 ✔
1643

1644
void Executor::ins_assign_localvar( const Instruction& ins )
22,997 ✔
1645
{
1646
  BObjectRef& lvar = ( *Locals2 )[ins.token.lval];
22,997 ✔
1647

1648
  BObjectRef& rightref = ValueStack.back();
22,997 ✔
1649

1650
  BObject& right = *rightref;
22,997 ✔
1651

1652
  BObjectImp& rightimpref = right.impref();
22,997 ✔
1653

1654
  if ( right.count() == 1 && rightimpref.count() == 1 )
22,997 ✔
1655
  {
1656
    lvar->setimp( &rightimpref );
22,074 ✔
1657
  }
1658
  else
1659
  {
1660
    lvar->setimp( rightimpref.copy() );
923 ✔
1661
  }
1662
  ValueStack.pop_back();
22,997 ✔
1663
}
22,997 ✔
1664
void Executor::ins_assign_globalvar( const Instruction& ins )
5,508 ✔
1665
{
1666
  if ( (unsigned)ins.token.lval >= Globals2->size() )
5,508 ✔
1667
  {
NEW
1668
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={}){}", prog_->name, PC,
×
NEW
1669
                    script_stack_block() );
×
1670
    seterror( true );
×
1671
    ValueStack.pop_back();
×
1672
    return;
×
1673
  }
1674
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
5,508 ✔
1675

1676
  BObjectRef& rightref = ValueStack.back();
5,508 ✔
1677

1678
  BObject& right = *rightref;
5,508 ✔
1679

1680
  BObjectImp& rightimpref = right.impref();
5,508 ✔
1681

1682
  if ( right.count() == 1 && rightimpref.count() == 1 )
5,508 ✔
1683
  {
1684
    gvar->setimp( &rightimpref );
5,435 ✔
1685
  }
1686
  else
1687
  {
1688
    gvar->setimp( rightimpref.copy() );
73 ✔
1689
  }
1690
  ValueStack.pop_back();
5,508 ✔
1691
}
1692

1693
// case INS_ASSIGN_CONSUME:
1694
void Executor::ins_assign_consume( const Instruction& /*ins*/ )
4,638 ✔
1695
{
1696
  BObjectRef rightref = ValueStack.back();
4,638 ✔
1697
  ValueStack.pop_back();
4,638 ✔
1698
  BObjectRef& leftref = ValueStack.back();
4,638 ✔
1699

1700
  BObject& right = *rightref;
4,638 ✔
1701
  BObject& left = *leftref;
4,638 ✔
1702

1703
  BObjectImp& rightimpref = right.impref();
4,638 ✔
1704

1705
  if ( right.count() == 1 && rightimpref.count() == 1 )
4,638 ✔
1706
  {
1707
    left.setimp( &rightimpref );
4,067 ✔
1708
  }
1709
  else
1710
  {
1711
    left.setimp( rightimpref.copy() );
571 ✔
1712
  }
1713
  ValueStack.pop_back();
4,638 ✔
1714
}
4,638 ✔
1715

1716
void Executor::ins_assign( const Instruction& /*ins*/ )
246,664 ✔
1717
{
1718
  /*
1719
      These each take two operands, and replace them with one.
1720
      We'll leave the second one on the value stack, and
1721
      just replace its object with the result
1722
      */
1723
  BObjectRef rightref = ValueStack.back();
246,664 ✔
1724
  ValueStack.pop_back();
246,664 ✔
1725
  BObjectRef& leftref = ValueStack.back();
246,664 ✔
1726

1727
  BObject& right = *rightref;
246,664 ✔
1728
  BObject& left = *leftref;
246,664 ✔
1729

1730
  BObjectImp& rightimpref = right.impref();
246,664 ✔
1731

1732
  if ( right.count() == 1 && rightimpref.count() == 1 )
246,664 ✔
1733
  {
1734
    left.setimp( &rightimpref );
241,839 ✔
1735
  }
1736
  else
1737
  {
1738
    left.setimp( rightimpref.copy() );
4,825 ✔
1739
  }
1740
}
246,664 ✔
1741

1742
void Executor::ins_array_assign( const Instruction& /*ins*/ )
9 ✔
1743
{
1744
  /*
1745
      on the value stack:
1746
      x[i] := y;
1747
      (top)
1748
      y
1749
      i
1750
      x
1751
      upon exit:
1752
      (x[i])
1753
      */
1754
  BObjectRef y_ref = ValueStack.back();
9 ✔
1755
  ValueStack.pop_back();
9 ✔
1756
  BObjectRef i_ref = ValueStack.back();
9 ✔
1757
  ValueStack.pop_back();
9 ✔
1758
  BObjectRef& x_ref = ValueStack.back();
9 ✔
1759

1760
  BObject& y = *y_ref;
9 ✔
1761
  BObject& i = *i_ref;
9 ✔
1762
  BObject& x = *x_ref;
9 ✔
1763

1764
  BObjectImp* result;
1765
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
9 ✔
1766

1767
  x_ref.set( result );
9 ✔
1768
}
9 ✔
1769
void Executor::ins_array_assign_consume( const Instruction& /*ins*/ )
1,145 ✔
1770
{
1771
  /*
1772
      on the value stack:
1773
      x[i] := y;
1774
      (top)
1775
      y
1776
      i
1777
      x
1778
      upon exit:
1779
      (x[i])
1780
      */
1781
  BObjectRef y_ref = ValueStack.back();
1,145 ✔
1782
  ValueStack.pop_back();
1,145 ✔
1783
  BObjectRef i_ref = ValueStack.back();
1,145 ✔
1784
  ValueStack.pop_back();
1,145 ✔
1785
  BObjectRef& x_ref = ValueStack.back();
1,145 ✔
1786

1787
  BObject& y = *y_ref;
1,145 ✔
1788
  BObject& i = *i_ref;
1,145 ✔
1789
  BObject& x = *x_ref;
1,145 ✔
1790

1791
  BObjectImp* result;
1792
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
1,145 ✔
1793

1794
  BObject obj( result );
1,145 ✔
1795
  ValueStack.pop_back();
1,145 ✔
1796
}
1,145 ✔
1797

1798
BObjectImp* Executor::operator_fallback( BTokenId token_id, BObjectImp& left, BObjectImp& right )
3,291 ✔
1799
{
1800
  std::string no_rule_message;
3,291 ✔
1801
  BObjectImp* result = apply_operator_fallback( token_id, left, right, &no_rule_message );
3,291 ✔
1802
  if ( !no_rule_message.empty() && !prog_->reported_operator_fallback.exchange( true ) )
3,291 ✔
1803
  {
1804
    // A PC names an instruction nobody can look up in a source file. Where the script was
1805
    // compiled with debug information the stack turns it into the line that wrote the
1806
    // expression, and names the callers with it: an operand that made the pair impossible
1807
    // usually arrived as an argument, so the frame above is as often the answer as the frame
1808
    // the operator is in.
1809
    POLLOG_ERRORLN( "{} ({},PC={}){}", no_rule_message, prog_->name, PC, script_stack_block() );
26 ✔
1810
  }
1811
  return result;
3,291 ✔
1812
}
3,291 ✔
1813

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

1826
  BObject& right = *rightref;
73,225 ✔
1827
  BObject& left = *leftref;
73,225 ✔
1828

1829
  BObjectImp* result = right.impref().selfPlusObjImp( left.impref() );
73,225 ✔
1830
  if ( !result )
73,225 ✔
1831
    result = operator_fallback( TOK_ADD, left.impref(), right.impref() );
171 ✔
1832
  leftref.set( result );
73,225 ✔
1833
}
73,225 ✔
1834

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

1847
  BObject& right = *rightref;
6,816 ✔
1848
  BObject& left = *leftref;
6,816 ✔
1849

1850
  BObjectImp* result = right.impref().selfMinusObjImp( left.impref() );
6,816 ✔
1851
  if ( !result )
6,816 ✔
1852
    result = operator_fallback( TOK_SUBTRACT, left.impref(), right.impref() );
162 ✔
1853
  leftref.set( result );
6,816 ✔
1854
}
6,816 ✔
1855

1856
// TOK_MULT:
1857
void Executor::ins_mult( const Instruction& /*ins*/ )
4,426 ✔
1858
{
1859
  /*
1860
      These each take two operands, and replace them with one.
1861
      We'll leave the second one on the value stack, and
1862
      just replace its object with the result
1863
      */
1864
  BObjectRef rightref = ValueStack.back();
4,426 ✔
1865
  ValueStack.pop_back();
4,426 ✔
1866
  BObjectRef& leftref = ValueStack.back();
4,426 ✔
1867

1868
  BObject& right = *rightref;
4,426 ✔
1869
  BObject& left = *leftref;
4,426 ✔
1870

1871
  BObjectImp* result = right.impref().selfTimesObjImp( left.impref() );
4,426 ✔
1872
  if ( !result )
4,426 ✔
1873
    result = operator_fallback( TOK_MULT, left.impref(), right.impref() );
351 ✔
1874
  leftref.set( result );
4,426 ✔
1875
}
4,426 ✔
1876
// TOK_DIV:
1877
void Executor::ins_div( const Instruction& /*ins*/ )
464 ✔
1878
{
1879
  /*
1880
      These each take two operands, and replace them with one.
1881
      We'll leave the second one on the value stack, and
1882
      just replace its object with the result
1883
      */
1884
  BObjectRef rightref = ValueStack.back();
464 ✔
1885
  ValueStack.pop_back();
464 ✔
1886
  BObjectRef& leftref = ValueStack.back();
464 ✔
1887

1888
  BObject& right = *rightref;
464 ✔
1889
  BObject& left = *leftref;
464 ✔
1890

1891
  BObjectImp* result = right.impref().selfDividedByObjImp( left.impref() );
464 ✔
1892
  if ( !result )
464 ✔
1893
    result = operator_fallback( TOK_DIV, left.impref(), right.impref() );
345 ✔
1894
  leftref.set( result );
464 ✔
1895
}
464 ✔
1896
// TOK_MODULUS:
1897
void Executor::ins_modulus( const Instruction& /*ins*/ )
591 ✔
1898
{
1899
  /*
1900
      These each take two operands, and replace them with one.
1901
      We'll leave the second one on the value stack, and
1902
      just replace its object with the result
1903
      */
1904
  BObjectRef rightref = ValueStack.back();
591 ✔
1905
  ValueStack.pop_back();
591 ✔
1906
  BObjectRef& leftref = ValueStack.back();
591 ✔
1907

1908
  BObject& right = *rightref;
591 ✔
1909
  BObject& left = *leftref;
591 ✔
1910

1911
  BObjectImp* result = right.impref().selfModulusObjImp( left.impref() );
591 ✔
1912
  if ( !result )
591 ✔
1913
    result = operator_fallback( TOK_MODULUS, left.impref(), right.impref() );
345 ✔
1914
  leftref.set( result );
591 ✔
1915
}
591 ✔
1916

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

1924
  BObject& right = *rightref;
100 ✔
1925
  BObject& left = *leftref;
100 ✔
1926

1927
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100 ✔
1928
}
100 ✔
1929

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

1942
  BObject& right = *rightref;
402 ✔
1943
  BObject& left = *leftref;
402 ✔
1944

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

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

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

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

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

2002
  BObject& right = *rightref;
393 ✔
2003
  BObject& left = *leftref;
393 ✔
2004

2005
  BObjectImp* result = right.impref().selfBitXorObjImp( left.impref() );
393 ✔
2006
  if ( !result )
393 ✔
2007
    result = operator_fallback( TOK_BITXOR, left.impref(), right.impref() );
381 ✔
2008
  leftref.set( result );
393 ✔
2009
}
393 ✔
2010
// TOK_BITOR:
2011
void Executor::ins_bitwise_or( const Instruction& /*ins*/ )
396 ✔
2012
{
2013
  /*
2014
      These each take two operands, and replace them with one.
2015
      We'll leave the second one on the value stack, and
2016
      just replace its object with the result
2017
      */
2018
  BObjectRef rightref = ValueStack.back();
396 ✔
2019
  ValueStack.pop_back();
396 ✔
2020
  BObjectRef& leftref = ValueStack.back();
396 ✔
2021

2022
  BObject& right = *rightref;
396 ✔
2023
  BObject& left = *leftref;
396 ✔
2024

2025
  BObjectImp* result = right.impref().selfBitOrObjImp( left.impref() );
396 ✔
2026
  if ( !result )
396 ✔
2027
    result = operator_fallback( TOK_BITOR, left.impref(), right.impref() );
384 ✔
2028
  leftref.set( result );
396 ✔
2029
}
396 ✔
2030

2031
void Executor::ins_logical_and( const Instruction& /*ins*/ )
334,063 ✔
2032
{
2033
  /*
2034
      These each take two operands, and replace them with one.
2035
      We'll leave the second one on the value stack, and
2036
      just replace its object with the result
2037
      */
2038
  BObjectRef rightref = ValueStack.back();
334,063 ✔
2039
  ValueStack.pop_back();
334,063 ✔
2040
  BObjectRef& leftref = ValueStack.back();
334,063 ✔
2041

2042
  BObject& right = *rightref;
334,063 ✔
2043
  BObject& left = *leftref;
334,063 ✔
2044

2045
  int _true = ( left.isTrue() && right.isTrue() );
334,063 ✔
2046
  leftref.set( new BLong( _true ) );
334,063 ✔
2047
}
334,063 ✔
2048
void Executor::ins_logical_or( const Instruction& /*ins*/ )
15,921 ✔
2049
{
2050
  /*
2051
      These each take two operands, and replace them with one.
2052
      We'll leave the second one on the value stack, and
2053
      just replace its object with the result
2054
      */
2055
  BObjectRef rightref = ValueStack.back();
15,921 ✔
2056
  ValueStack.pop_back();
15,921 ✔
2057
  BObjectRef& leftref = ValueStack.back();
15,921 ✔
2058

2059
  BObject& right = *rightref;
15,921 ✔
2060
  BObject& left = *leftref;
15,921 ✔
2061

2062
  int _true = ( left.isTrue() || right.isTrue() );
15,921 ✔
2063
  leftref.set( new BLong( _true ) );
15,921 ✔
2064
}
15,921 ✔
2065

2066
void Executor::ins_notequal( const Instruction& /*ins*/ )
54,784 ✔
2067
{
2068
  /*
2069
      These each take two operands, and replace them with one.
2070
      We'll leave the second one on the value stack, and
2071
      just replace its object with the result
2072
      */
2073
  BObjectRef rightref = ValueStack.back();
54,784 ✔
2074
  ValueStack.pop_back();
54,784 ✔
2075
  BObjectRef& leftref = ValueStack.back();
54,784 ✔
2076

2077
  BObject& right = *rightref;
54,784 ✔
2078
  BObject& left = *leftref;
54,784 ✔
2079

2080
  int _true = ( left != right );
54,784 ✔
2081
  leftref.set( new BLong( _true ) );
54,784 ✔
2082
}
54,784 ✔
2083

2084
void Executor::ins_equal( const Instruction& /*ins*/ )
190,656 ✔
2085
{
2086
  /*
2087
      These each take two operands, and replace them with one.
2088
      We'll leave the second one on the value stack, and
2089
      just replace its object with the result
2090
      */
2091
  BObjectRef rightref = ValueStack.back();
190,656 ✔
2092
  ValueStack.pop_back();
190,656 ✔
2093
  BObjectRef& leftref = ValueStack.back();
190,656 ✔
2094

2095
  BObject& right = *rightref;
190,656 ✔
2096
  BObject& left = *leftref;
190,656 ✔
2097

2098
  int _true = ( left == right );
190,656 ✔
2099
  leftref.set( new BLong( _true ) );
190,656 ✔
2100
}
190,656 ✔
2101

2102
void Executor::ins_lessthan( const Instruction& /*ins*/ )
184,107 ✔
2103
{
2104
  /*
2105
      These each take two operands, and replace them with one.
2106
      We'll leave the second one on the value stack, and
2107
      just replace its object with the result
2108
      */
2109
  BObjectRef rightref = ValueStack.back();
184,107 ✔
2110
  ValueStack.pop_back();
184,107 ✔
2111
  BObjectRef& leftref = ValueStack.back();
184,107 ✔
2112

2113
  BObject& right = *rightref;
184,107 ✔
2114
  BObject& left = *leftref;
184,107 ✔
2115

2116
  int _true = ( left < right );
184,107 ✔
2117
  leftref.set( new BLong( _true ) );
184,107 ✔
2118
}
184,107 ✔
2119

2120
void Executor::ins_lessequal( const Instruction& /*ins*/ )
12,217 ✔
2121
{
2122
  /*
2123
      These each take two operands, and replace them with one.
2124
      We'll leave the second one on the value stack, and
2125
      just replace its object with the result
2126
      */
2127
  BObjectRef rightref = ValueStack.back();
12,217 ✔
2128
  ValueStack.pop_back();
12,217 ✔
2129
  BObjectRef& leftref = ValueStack.back();
12,217 ✔
2130

2131
  BObject& right = *rightref;
12,217 ✔
2132
  BObject& left = *leftref;
12,217 ✔
2133
  int _true = ( left <= right );
12,217 ✔
2134
  leftref.set( new BLong( _true ) );
12,217 ✔
2135
}
12,217 ✔
2136
void Executor::ins_greaterthan( const Instruction& /*ins*/ )
22,832 ✔
2137
{
2138
  /*
2139
      These each take two operands, and replace them with one.
2140
      We'll leave the second one on the value stack, and
2141
      just replace its object with the result
2142
      */
2143
  BObjectRef rightref = ValueStack.back();
22,832 ✔
2144
  ValueStack.pop_back();
22,832 ✔
2145
  BObjectRef& leftref = ValueStack.back();
22,832 ✔
2146

2147
  BObject& right = *rightref;
22,832 ✔
2148
  BObject& left = *leftref;
22,832 ✔
2149

2150
  int _true = ( left > right );
22,832 ✔
2151
  leftref.set( new BLong( _true ) );
22,832 ✔
2152
}
22,832 ✔
2153
void Executor::ins_greaterequal( const Instruction& /*ins*/ )
150,692 ✔
2154
{
2155
  /*
2156
      These each take two operands, and replace them with one.
2157
      We'll leave the second one on the value stack, and
2158
      just replace its object with the result
2159
      */
2160
  BObjectRef rightref = ValueStack.back();
150,692 ✔
2161
  ValueStack.pop_back();
150,692 ✔
2162
  BObjectRef& leftref = ValueStack.back();
150,692 ✔
2163

2164
  BObject& right = *rightref;
150,692 ✔
2165
  BObject& left = *leftref;
150,692 ✔
2166

2167
  int _true = ( left >= right );
150,692 ✔
2168
  leftref.set( new BLong( _true ) );
150,692 ✔
2169
}
150,692 ✔
2170

2171
// case TOK_ARRAY_SUBSCRIPT:
2172
void Executor::ins_arraysubscript( const Instruction& /*ins*/ )
36,922 ✔
2173
{
2174
  /*
2175
      These each take two operands, and replace them with one.
2176
      We'll leave the second one on the value stack, and
2177
      just replace its object with the result
2178
      */
2179
  BObjectRef rightref = ValueStack.back();
36,922 ✔
2180
  ValueStack.pop_back();
36,922 ✔
2181
  BObjectRef& leftref = ValueStack.back();
36,922 ✔
2182

2183
  leftref = ( *leftref )->OperSubscript( *rightref );
36,922 ✔
2184
}
36,922 ✔
2185

2186
void Executor::ins_multisubscript( const Instruction& ins )
13,856 ✔
2187
{
2188
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2189
  std::stack<BObjectRef> indices;
13,856 ✔
2190
  for ( int i = 0; i < ins.token.lval; ++i )
41,571 ✔
2191
  {
2192
    indices.push( ValueStack.back() );
27,715 ✔
2193
    ValueStack.pop_back();
27,715 ✔
2194
  }
2195

2196
  BObjectRef& leftref = ValueStack.back();
13,856 ✔
2197
  leftref = ( *leftref )->OperMultiSubscript( indices );
13,856 ✔
2198
}
13,856 ✔
2199

2200
void Executor::ins_unpack_sequence( const Instruction& ins )
256 ✔
2201
{
2202
  bool rest = ins.token.lval >> 14;
256 ✔
2203
  auto count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
256 ✔
2204

2205
  BObjectRef refIter( new BObject( UninitObject::create() ) );
256 ✔
2206

2207
  BObjectRef rightref = ValueStack.back();
256 ✔
2208
  ValueStack.pop_back();
256 ✔
2209

2210
  // Reserve to keep the insert_at iterator valid
2211
  ValueStack.reserve( ValueStack.size() + count );
256 ✔
2212
  auto insert_at = ValueStack.begin() + ValueStack.size();
256 ✔
2213
  auto pIter = std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
256 ✔
2214

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

2234
  if ( rest )
250 ✔
2235
  {
2236
    auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
66 ✔
2237

2238
    // Add all elements up to the rest index
2239
    for ( u8 i = 0; i < rest_index; ++i )
147 ✔
2240
    {
2241
      if ( auto res = pIter->step() )
81 ✔
2242
        ValueStack.emplace( insert_at, res );
75 ✔
2243
      else
2244
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6 ✔
2245
    }
2246

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

2250
    while ( auto res = pIter->step() )
306 ✔
2251
      rest_array->addElement( res->impptr() );
240 ✔
2252

2253
    // Take the remaining elements from the rest array to fill the rest of the bindings.
2254
    auto left = count - rest_index - 1;
66 ✔
2255

2256
    for ( u8 i = 0; i < left; ++i )
138 ✔
2257
    {
2258
      if ( rest_array->ref_arr.empty() )
72 ✔
2259
        ValueStack.emplace( insert_at + i, new BError( "Index out of bounds" ) );
12 ✔
2260
      else
2261
      {
2262
        ValueStack.insert( insert_at + i, rest_array->ref_arr.back() );
60 ✔
2263

2264
        rest_array->ref_arr.pop_back();
60 ✔
2265
      }
2266
    }
2267
  }
2268
  else
2269
  {
2270
    for ( u8 i = 0; i < count; ++i )
626 ✔
2271
    {
2272
      if ( auto res = pIter->step() )
442 ✔
2273
        ValueStack.emplace( insert_at, res );
442 ✔
2274
      else
2275
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
×
2276
    }
2277
  }
2278
}
268 ✔
2279

2280
void Executor::ins_unpack_indices( const Instruction& ins )
140 ✔
2281
{
2282
  bool rest = ins.token.lval >> 14;
140 ✔
2283
  auto binding_count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
140 ✔
2284
  // If there is a rest binding, there will be one less index than the binding
2285
  // count, as the rest binding has no corresponding element index access.
2286
  auto index_count = rest ? Clib::clamp_convert<u8>( binding_count - 1 ) : binding_count;
140 ✔
2287

2288
  // Ensure there is a ValueStack entry for each index, + 1 for the unpacking object ('rightref').
2289
  passert_r( static_cast<int>( ValueStack.size() ) >= index_count + 1,
140 ✔
2290
             "Not enough values to unpack" );
2291

2292
  if ( rest )
140 ✔
2293
  {
2294
    // Use a multi_index because we need to (1) iterate over the indexes in
2295
    // order of the bindings in the script, and (2) keep track of which indexes
2296
    // have been used.
2297
    using namespace boost::multi_index;
2298
    using OrderedSet =
2299
        multi_index_container<BObject,
2300
                              indexed_by<sequenced<>,  // Maintains insertion order
2301
                                         ordered_unique<identity<BObject>>  // Ensures uniqueness
2302
                                         >>;
2303

2304
    OrderedSet indexes;
39 ✔
2305

2306
    for ( u8 i = 0; i < index_count; ++i )
120 ✔
2307
    {
2308
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
81 ✔
2309
      ValueStack.pop_back();
81 ✔
2310
    }
2311

2312
    BObjectRef rightref = ValueStack.back();
39 ✔
2313
    ValueStack.pop_back();
39 ✔
2314

2315
    // Reserve to keep the insert_at iterator valid
2316
    ValueStack.reserve( ValueStack.size() + binding_count );
39 ✔
2317
    auto insert_at = ValueStack.end();
39 ✔
2318

2319
    for ( const auto& index : indexes )
120 ✔
2320
    {
2321
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
81 ✔
2322
    }
2323

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

2328
    BObjectRef refIter( UninitObject::create() );
39 ✔
2329
    auto pIter =
2330
        std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
39 ✔
2331

2332
    // The default iterator is used for non-iterable objects, so we can't unpack them.
2333
    if ( pIter->is_default() )
39 ✔
2334
    {
2335
      ValueStack.emplace( insert_at, new BError( "Invalid type for rest binding" ) );
15 ✔
2336
      return;
15 ✔
2337
    }
2338
    rest_obj = std::make_unique<BDictionary>();
24 ✔
2339

2340
    auto& unique_index = indexes.get<1>();
24 ✔
2341

2342
    while ( auto res = pIter->step() )
120 ✔
2343
    {
2344
      auto itr = unique_index.find( *refIter.get() );
96 ✔
2345

2346
      if ( itr == unique_index.end() )
96 ✔
2347
        rest_obj->array_assign( refIter->impptr(), res->impptr(), true );
45 ✔
2348
    }
96 ✔
2349
    ValueStack.emplace( insert_at, rest_obj.release() );
24 ✔
2350
  }
99 ✔
2351
  else
2352
  {
2353
    // If not using a rest binding, only keep track of binding order.
2354
    std::list<BObject> indexes;
101 ✔
2355

2356
    for ( u8 i = 0; i < index_count; ++i )
323 ✔
2357
    {
2358
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
222 ✔
2359
      ValueStack.pop_back();
222 ✔
2360
    }
2361

2362
    BObjectRef rightref = ValueStack.back();
101 ✔
2363
    ValueStack.pop_back();
101 ✔
2364

2365
    // Reserve to keep the insert_at iterator valid
2366
    ValueStack.reserve( ValueStack.size() + binding_count );
101 ✔
2367
    auto insert_at = ValueStack.end();
101 ✔
2368

2369
    for ( const auto& index : indexes )
323 ✔
2370
    {
2371
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
222 ✔
2372
    }
2373
  }
101 ✔
2374
}
2375

2376
void Executor::ins_take_local( const Instruction& )
793 ✔
2377
{
2378
  passert( Locals2 != nullptr );
793 ✔
2379
  passert( !ValueStack.empty() );
793 ✔
2380

2381
  // There is no entry in the locals vector, so create a new one.
2382
  Locals2->emplace_back( UninitObject::create() );
793 ✔
2383
  BObjectRef& lvar = ( *Locals2 ).back();
793 ✔
2384

2385
  BObjectRef& rightref = ValueStack.back();
793 ✔
2386

2387
  BObject& right = *rightref;
793 ✔
2388

2389
  BObjectImp& rightimpref = right.impref();
793 ✔
2390

2391
  if ( right.count() == 1 && rightimpref.count() == 1 )
793 ✔
2392
  {
2393
    lvar->setimp( &rightimpref );
244 ✔
2394
  }
2395
  else
2396
  {
2397
    lvar->setimp( rightimpref.copy() );
549 ✔
2398
  }
2399
  ValueStack.pop_back();
793 ✔
2400
}
793 ✔
2401

2402
void Executor::ins_take_global( const Instruction& ins )
183 ✔
2403
{
2404
  passert( !ValueStack.empty() );
183 ✔
2405

2406
  if ( (unsigned)ins.token.lval >= Globals2->size() )
183 ✔
2407
  {
NEW
2408
    POLLOG_ERRORLN( "Fatal error: Globals access out of range! ({},PC={}){}", prog_->name, PC,
×
NEW
2409
                    script_stack_block() );
×
2410
    seterror( true );
×
2411
    ValueStack.pop_back();
×
2412
    return;
×
2413
  }
2414
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
183 ✔
2415

2416
  BObjectRef& rightref = ValueStack.back();
183 ✔
2417

2418
  BObject& right = *rightref;
183 ✔
2419

2420
  BObjectImp& rightimpref = right.impref();
183 ✔
2421

2422
  if ( right.count() == 1 && rightimpref.count() == 1 )
183 ✔
2423
  {
2424
    gvar->setimp( &rightimpref );
150 ✔
2425
  }
2426
  else
2427
  {
2428
    gvar->setimp( rightimpref.copy() );
33 ✔
2429
  }
2430
  ValueStack.pop_back();
183 ✔
2431
}
2432

2433
void Executor::ins_multisubscript_assign( const Instruction& ins )
30 ✔
2434
{
2435
  BObjectRef target_ref = ValueStack.back();
30 ✔
2436
  ValueStack.pop_back();
30 ✔
2437
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2438
  std::stack<BObjectRef> indices;
30 ✔
2439
  for ( int i = 0; i < ins.token.lval; ++i )
93 ✔
2440
  {
2441
    indices.push( ValueStack.back() );
63 ✔
2442
    ValueStack.pop_back();
63 ✔
2443
  }
2444

2445
  BObjectRef& leftref = ValueStack.back();
30 ✔
2446
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
30 ✔
2447
}
30 ✔
2448

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

2460
  BObject& right = *rightref;
4,758 ✔
2461
  BObject& left = *leftref;
4,758 ✔
2462

2463
  leftref = addmember( left, right );
4,758 ✔
2464
}
4,758 ✔
2465

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

2477
  BObject& right = *rightref;
94 ✔
2478
  BObject& left = *leftref;
94 ✔
2479

2480
  leftref = removemember( left, right );
94 ✔
2481
}
94 ✔
2482

2483
void Executor::ins_checkmember( const Instruction& /*ins*/ )
51,014 ✔
2484
{
2485
  /*
2486
      These each take two operands, and replace them with one.
2487
      We'll leave the second one on the value stack, and
2488
      just replace its object with the result
2489
      */
2490
  BObjectRef rightref = ValueStack.back();
51,014 ✔
2491
  ValueStack.pop_back();
51,014 ✔
2492
  BObjectRef& leftref = ValueStack.back();
51,014 ✔
2493

2494
  BObject& right = *rightref;
51,014 ✔
2495
  BObject& left = *leftref;
51,014 ✔
2496

2497
  leftref = checkmember( left, right );
51,014 ✔
2498
}
51,014 ✔
2499

2500
void Executor::ins_addmember2( const Instruction& ins )
45 ✔
2501
{
2502
  BObjectRef& obref = ValueStack.back();
45 ✔
2503

2504
  BObject& ob = *obref;
45 ✔
2505

2506
  ob.impref().operDotPlus( ins.token.tokval() );
45 ✔
2507
}
45 ✔
2508

2509
void Executor::ins_addmember_assign( const Instruction& ins )
10,863 ✔
2510
{
2511
  BObjectRef valref = ValueStack.back();
10,863 ✔
2512
  BObject& valob = *valref;
10,863 ✔
2513
  BObjectImp* valimp = valref->impptr();
10,863 ✔
2514

2515
  ValueStack.pop_back();
10,863 ✔
2516

2517
  BObjectRef& obref = ValueStack.back();
10,863 ✔
2518
  BObject& ob = *obref;
10,863 ✔
2519

2520
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
10,863 ✔
2521
  BObject& mem = *memref;
10,863 ✔
2522

2523
  if ( valob.count() == 1 && valimp->count() == 1 )
10,863 ✔
2524
  {
2525
    mem.setimp( valimp );
7,097 ✔
2526
  }
2527
  else
2528
  {
2529
    mem.setimp( valimp->copy() );
3,766 ✔
2530
  }
2531
  // the struct is at the top of the stack
2532
}
10,863 ✔
2533

2534
void Executor::ins_dictionary_addmember( const Instruction& /*ins*/ )
191 ✔
2535
{
2536
  /*
2537
      ENTRANCE: value stack:
2538
      dictionary
2539
      key
2540
      value
2541
      EXIT: value stack:
2542
      dictionary
2543
      FUNCTION:
2544
      adds the (key, value) pair to the dictionary
2545
      */
2546

2547
  BObjectRef valref = ValueStack.back();
191 ✔
2548
  ValueStack.pop_back();
191 ✔
2549
  BObject& valob = *valref;
191 ✔
2550
  BObjectImp* valimp = valob.impptr();
191 ✔
2551

2552
  BObjectRef keyref = ValueStack.back();
191 ✔
2553
  ValueStack.pop_back();
191 ✔
2554
  BObject& keyob = *keyref;
191 ✔
2555
  BObjectImp* keyimp = keyob.impptr();
191 ✔
2556

2557
  BObjectRef& dictref = ValueStack.back();
191 ✔
2558
  BObject& dictob = *dictref;
191 ✔
2559
  BDictionary* dict = dictob.impptr<BDictionary>();
191 ✔
2560

2561
  if ( keyob.count() != 1 || keyimp->count() != 1 )
191 ✔
2562
  {
2563
    keyimp = keyimp->copy();
6 ✔
2564
  }
2565
  if ( valob.count() != 1 || valimp->count() != 1 )
191 ✔
2566
  {
2567
    valimp = valimp->copy();
18 ✔
2568
  }
2569

2570
  dict->addMember( keyimp, valimp );
191 ✔
2571
  // the dictionary remains at the top of the stack.
2572
}
191 ✔
2573

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

2585
  BObject& right = *rightref;
5,298 ✔
2586
  BObject& left = *leftref;
5,298 ✔
2587

2588
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
5,298 ✔
2589
}
5,298 ✔
2590

2591
void Executor::ins_insert_into( const Instruction& /*ins*/ )
13,936 ✔
2592
{
2593
  BObjectRef rightref = ValueStack.back();
13,936 ✔
2594
  ValueStack.pop_back();
13,936 ✔
2595
  BObjectRef& leftref = ValueStack.back();
13,936 ✔
2596

2597
  BObject& right = *rightref;
13,936 ✔
2598
  BObject& left = *leftref;
13,936 ✔
2599

2600
  if ( auto* spread = right.impptr_if<BSpread>() )
13,936 ✔
2601
  {
2602
    BObjectRef refIter( UninitObject::create() );
1,473 ✔
2603

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

2607
    BObject* next = pIter->step();
1,473 ✔
2608
    while ( next != nullptr )
4,317 ✔
2609
    {
2610
      left.impref().operInsertInto( left, next->impref() );
2,844 ✔
2611
      next = pIter->step();
2,844 ✔
2612
    }
2613
  }
1,473 ✔
2614
  else
2615
  {
2616
    left.impref().operInsertInto( left, right.impref() );
12,463 ✔
2617
  }
2618
}
13,936 ✔
2619

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

2631
  BObject& right = *rightref;
10,860 ✔
2632
  BObject& left = *leftref;
10,860 ✔
2633

2634
  left.impref().operPlusEqual( left, right.impref() );
10,860 ✔
2635
}
10,860 ✔
2636

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

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

2651
  left.impref().operMinusEqual( left, right.impref() );
416 ✔
2652
}
416 ✔
2653

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

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

2668
  left.impref().operTimesEqual( left, right.impref() );
240 ✔
2669
}
240 ✔
2670

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

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

2685
  left.impref().operDivideEqual( left, right.impref() );
237 ✔
2686
}
237 ✔
2687

2688
void Executor::ins_modulusequal( const Instruction& /*ins*/ )
381 ✔
2689
{
2690
  /*
2691
      These each take two operands, and replace them with one.
2692
      We'll leave the second one on the value stack, and
2693
      just replace its object with the result
2694
      */
2695
  BObjectRef rightref = ValueStack.back();
381 ✔
2696
  ValueStack.pop_back();
381 ✔
2697
  BObjectRef& leftref = ValueStack.back();
381 ✔
2698

2699
  BObject& right = *rightref;
381 ✔
2700
  BObject& left = *leftref;
381 ✔
2701

2702
  left.impref().operModulusEqual( left, right.impref() );
381 ✔
2703
}
381 ✔
2704

2705
// case RSV_GOTO:
2706
void Executor::ins_goto( const Instruction& ins )
188,166,992 ✔
2707
{
2708
  PC = (unsigned)ins.token.lval;
188,166,992 ✔
2709
}
188,166,992 ✔
2710

2711
// TOK_FUNC:
2712
void Executor::ins_func( const Instruction& ins )
188,399,601 ✔
2713
{
2714
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
188,399,601 ✔
2715
  getParams( nparams );
188,399,601 ✔
2716
  execFunc( ins.token );
188,399,601 ✔
2717
  cleanParams();
188,399,601 ✔
2718
  return;
188,399,601 ✔
2719
}
2720

2721
void Executor::ins_call_method_id( const Instruction& ins )
265,446 ✔
2722
{
2723
  BContinuation* continuation = nullptr;
265,446 ✔
2724
  unsigned nparams = ins.token.type;
265,446 ✔
2725

2726
  do
2727
  {
2728
    getParams( nparams );
266,293 ✔
2729
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
266,293 ✔
2730
    {
2731
      Instruction jmp;
3,409 ✔
2732
      bool add_new_classinst = ins.token.lval == MTH_NEW;
3,409 ✔
2733

2734
      if ( add_new_classinst )
3,409 ✔
2735
      {
2736
        if ( funcr->constructor() )
108 ✔
2737
        {
2738
          fparams.insert( fparams.begin(),
81 ✔
2739
                          BObjectRef( new BConstObject( new BClassInstanceRef( new BClassInstance(
162 ✔
2740
                              prog_, funcr->class_index(), Globals2, pid() ) ) ) ) );
162 ✔
2741
        }
2742
      }
2743

2744
      if ( funcr->validCall( continuation ? MTH_CALL : ins.token.lval, *this, &jmp ) )
3,409 ✔
2745
      {
2746
        BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
3,304 ✔
2747
        call_function_reference( funcr, continuation, jmp );
3,304 ✔
2748
        return;
3,304 ✔
2749
      }
3,304 ✔
2750
    }
3,409 ✔
2751
    // If there _was_ a continuation to be handled (from previous loop
2752
    // iteration), there must have been a FuncRef on the stack. Otherwise,
2753
    // `continuation` may leak.
2754
    passert_always( continuation == nullptr );
262,989 ✔
2755

2756
    size_t stacksize = ValueStack.size();  // ValueStack can grow
262,989 ✔
2757
    BObjectImp* imp;
2758
    {
2759
      ESCRIPT_PROFILER( ins, ValueStack.back(), fparams );
2760
      imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
262,989 ✔
2761

2762
      if ( auto* cont = impptrIf<BContinuation>( imp ) )
262,989 ✔
2763
      {
2764
        continuation = cont;
847 ✔
2765
        // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2766
        // move.
2767
        nparams = static_cast<unsigned int>( continuation->args.size() );
847 ✔
2768

2769
        // Add function reference to stack
2770
        ValueStack.emplace_back( continuation->func() );
847 ✔
2771

2772
        // Move all arguments to the value stack
2773
        ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
847 ✔
2774
                           std::make_move_iterator( continuation->args.end() ) );
2775

2776
        continuation->args.clear();
847 ✔
2777

2778
        cleanParams();
847 ✔
2779

2780
        printStack( fmt::format(
1,694 ✔
2781
            "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2782
            "funcref.call()",
2783
            nparams ) );
2784

2785
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2786
        // handle it.
2787

2788
        // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2789
        // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2790
        // jumps.
2791

2792
        continue;
847 ✔
2793
      }
2794
    }
2795

2796
    if ( impptrIf<BSpecialUserFuncJump>( imp ) )
262,142 ✔
2797
    {
2798
      cleanParams();
3 ✔
2799
      return;
3 ✔
2800
    }
2801
    BObjectRef& objref = ValueStack[stacksize - 1];
262,139 ✔
2802
    if ( func_result_ )
262,139 ✔
2803
    {
2804
      if ( imp )
48 ✔
2805
      {
2806
        BObject obj( imp );
36 ✔
2807
      }
36 ✔
2808

2809
      objref.set( func_result_ );
48 ✔
2810
      func_result_ = nullptr;
48 ✔
2811
    }
2812
    else if ( imp )
262,091 ✔
2813
    {
2814
      objref.set( imp );
262,010 ✔
2815
    }
2816
    else
2817
    {
2818
      objref.set( UninitObject::create() );
81 ✔
2819
    }
2820

2821
    cleanParams();
262,139 ✔
2822
    return;
262,139 ✔
2823
  }
2824
  // This condition should only ever evaluate to `true` once. In the second loop
2825
  // iteration, handling the FuncRef will return out of this method.
2826
  while ( continuation != nullptr );
847 ✔
2827
}
2828

2829
void Executor::ins_call_method( const Instruction& ins )
3,868 ✔
2830
{
2831
  unsigned nparams = ins.token.lval;
3,868 ✔
2832
  auto method_name = ins.token.tokval();
3,868 ✔
2833

2834
  getParams( nparams );
3,868 ✔
2835
  BObjectImp* callee = ValueStack.back()->impptr();
3,868 ✔
2836

2837
  if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
3,868 ✔
2838
  {
2839
    Instruction jmp;
×
2840
    if ( funcr->validCall( method_name, *this, &jmp ) )
×
2841
    {
2842
      BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
×
2843
      call_function_reference( funcr, nullptr, jmp );
×
2844
      return;
×
2845
    }
×
2846
  }
×
2847

2848
  size_t stacksize = ValueStack.size();  // ValueStack can grow
3,868 ✔
2849
  BObjectImp* imp;
2850
  {
2851
    ESCRIPT_PROFILER( ins, callee, method_name, fparams );
2852
#ifdef BOBJECTIMP_DEBUG
2853
    if ( strcmp( method_name, "impptr" ) == 0 )
3,868 ✔
2854
      imp = new String( fmt::format( "{}", static_cast<void*>( callee ) ) );
×
2855
    else
2856
      imp = callee->call_method( method_name, *this );
3,868 ✔
2857
#else
2858
    imp = callee->call_method( method_name, *this );
2859
#endif
2860
  }
2861

2862
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
3,867 ✔
2863
  {
2864
    cleanParams();
2,527 ✔
2865
    return;
2,527 ✔
2866
  }
2867

2868
  BObjectRef& objref = ValueStack[stacksize - 1];
1,340 ✔
2869
  if ( func_result_ )
1,340 ✔
2870
  {
2871
    if ( imp )
1 ✔
2872
    {
2873
      BObject obj( imp );
×
2874
    }
×
2875

2876
    objref.set( func_result_ );
1 ✔
2877
    func_result_ = nullptr;
1 ✔
2878
  }
2879
  else if ( imp )
1,339 ✔
2880
  {
2881
    objref.set( imp );
261 ✔
2882
  }
2883
  else
2884
  {
2885
    objref.set( UninitObject::create() );
1,078 ✔
2886
  }
2887
  cleanParams();
1,340 ✔
2888
}
2889

2890
// CTRL_STATEMENTBEGIN:
2891
void Executor::ins_statementbegin( const Instruction& ins )
×
2892
{
2893
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
2894
    INFO_PRINTLN( ins.token.tokval() );
×
2895
}
×
2896

2897
// case CTRL_PROGEND:
2898
void Executor::ins_progend( const Instruction& /*ins*/ )
10,668 ✔
2899
{
2900
  done = 1;
10,668 ✔
2901
  run_ok_ = false;
10,668 ✔
2902
  PC = 0;
10,668 ✔
2903
}
10,668 ✔
2904

2905

2906
// case CTRL_MAKELOCAL:
2907
void Executor::ins_makelocal( const Instruction& /*ins*/ )
94,448 ✔
2908
{
2909
  if ( Locals2 )
94,448 ✔
2910
    upperLocals2.push_back( Locals2 );
94,448 ✔
2911
  Locals2 = new BObjectRefVec;
94,448 ✔
2912
}
94,448 ✔
2913

2914
void Executor::ins_check_mro( const Instruction& ins )
280 ✔
2915
{
2916
  auto classinst_offset = ins.token.lval;
280 ✔
2917

2918
  if ( classinst_offset > static_cast<int>( ValueStack.size() ) || ValueStack.empty() )
280 ✔
2919
  {
NEW
2920
    POLLOG_ERRORLN(
×
2921
        "Fatal error: Check MRO offset error! offset={}, ValueStack.size={} ({},PC={}){}",
NEW
2922
        classinst_offset, ValueStack.size(), prog_->name, PC, script_stack_block() );
×
2923
    seterror( true );
×
2924
    return;
×
2925
  }
2926

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

2929
  if ( nLines < PC + 1 )
280 ✔
2930
  {
NEW
2931
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction out of bounds! nLines={} ({},PC={}){}",
×
NEW
2932
                    nLines, prog_->name, PC, script_stack_block() );
×
2933
    seterror( true );
×
2934
    return;
×
2935
  }
2936

2937
  const Instruction& jsr_ins = prog_->instr.at( PC + 1 );
280 ✔
2938
  if ( jsr_ins.func != &Executor::ins_jsr_userfunc )
280 ✔
2939
  {
NEW
2940
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction not followed by JSR_USERFUNC! ({},PC={}){}",
×
NEW
2941
                    prog_->name, PC, script_stack_block() );
×
2942
    seterror( true );
×
2943
    return;
×
2944
  }
2945

2946
  auto ctor_addr = jsr_ins.token.lval;
280 ✔
2947

2948
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280 ✔
2949

2950
  if ( classinstref != nullptr && classinstref->instance()->constructors_called.find( ctor_addr ) ==
557 ✔
2951
                                      classinstref->instance()->constructors_called.end() )
557 ✔
2952
  {
2953
    classinstref->instance()->constructors_called.insert( ctor_addr );
224 ✔
2954
  }
2955
  else
2956
  {
2957
    // Constructor has been called, or `this` is not a class instance: clear
2958
    // arguments and skip jump instructions (makelocal, jsr_userfunc)
2959
    ValueStack.resize( ValueStack.size() - ins.token.lval );
56 ✔
2960
    PC += 2;
56 ✔
2961
  }
2962
}
2963

2964
// CTRL_JSR_USERFUNC:
2965
void Executor::ins_jsr_userfunc( const Instruction& ins )
51,964 ✔
2966
{
2967
  jump( ins.token.lval, nullptr, nullptr );
51,964 ✔
2968
}
51,964 ✔
2969

2970
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
94,448 ✔
2971
{
2972
  ReturnContext rc;
94,448 ✔
2973
  rc.PC = PC;
94,448 ✔
2974
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
94,448 ✔
2975
  if ( continuation )
94,448 ✔
2976
  {
2977
    rc.Continuation.set( continuation );
37,497 ✔
2978
  }
2979

2980
  // Only store our global context if the function is external to the current program.
2981
  if ( funcref != nullptr && funcref->pid() != pid() )
94,448 ✔
2982
  {
2983
    // Store external context for the return path.
2984
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
2,004 ✔
2985

2986
    if ( auto shared = funcref->globals.lock() )
4,008 ✔
2987
      Globals2 = shared;
2,002 ✔
2988
    else
2989
      Globals2 =
2990
          std::make_shared<BObjectRefVec>();  // empty but valid, access would stop the executor
2,004 ✔
2991

2992
    // Set the prog and globals to the external function's, updating nLines and
2993
    // execmodules.
2994
    prog_ = funcref->prog();
2,004 ✔
2995

2996

2997
    nLines = static_cast<unsigned int>( prog_->instr.size() );
2,004 ✔
2998

2999
    // Re-attach modules, as the external user function's module function call
3000
    // instructions refer to modules by index.
3001
    execmodules.clear();
2,004 ✔
3002

3003
    if ( !viewmode_ )
2,004 ✔
3004
    {
3005
      if ( !AttachFunctionalityModules() )
2,004 ✔
3006
      {
3007
        POLLOGLN( "Could not attach modules for external function call jump" );
×
3008
        seterror( true );
×
3009
      }
3010
    }
3011
  }
3012

3013
  ControlStack.push_back( rc );
94,448 ✔
3014

3015
  PC = target_PC;
94,448 ✔
3016
  if ( ControlStack.size() >= escript_config.max_call_depth )
94,448 ✔
3017
  {
3018
    std::string tmp = fmt::format(
3019
        "Script {} exceeded maximum call depth\n"
3020
        "Return path PCs: ",
3021
        scriptname() );
×
3022
    while ( !ControlStack.empty() )
×
3023
    {
3024
      rc = ControlStack.back();
×
3025
      ControlStack.pop_back();
×
3026
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
3027
    }
3028
    POLLOGLN( tmp );
×
3029
    seterror( true );
×
3030
  }
×
3031
}
94,448 ✔
3032

3033

3034
namespace
3035
{
3036
// Runs `handler` for every frame of `ex`'s call stack with the frame's PC
3037
// resolved against the program's debug symbols. Only meaningful once
3038
// read_dbg_file() has succeeded; callers pick the symbol-less walk otherwise.
3039
void walk_call_stack_with_symbols(
436 ✔
3040
    Executor& ex,
3041
    const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
3042
                              unsigned int /*line*/, const std::string& /*functionName*/ )>&
3043
        handler )
3044
{
3045
  ex.walkCallStack(
436 ✔
3046
      [&]( unsigned int pc )
436 ✔
3047
      {
3048
        auto filename = ex.prog()->dbg_filenames[ex.prog()->dbg_filenum[pc]];
749 ✔
3049
        auto line = ex.prog()->dbg_linenum[pc];
749 ✔
3050
        auto dbgFunction =
3051
            std::find_if( ex.prog()->dbg_functions.begin(), ex.prog()->dbg_functions.end(),
749 ✔
3052
                          [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
8,163 ✔
3053

3054
        std::string functionName =
3055
            dbgFunction != ex.prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
749 ✔
3056

3057
        handler( pc, filename, line, functionName );
749 ✔
3058
      } );
749 ✔
3059
}
436 ✔
3060
}  // namespace
3061

3062
BObjectImp* Executor::get_stacktrace( bool as_array )
84 ✔
3063
{
3064
  if ( !as_array )
84 ✔
3065
    return new String( stacktrace_string() );
80 ✔
3066

3067
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
4 ✔
3068

3069
  std::unique_ptr<ObjArray> result( new ObjArray );
4 ✔
3070

3071
  if ( has_symbols )
4 ✔
3072
  {
3073
    walk_call_stack_with_symbols( *this,
2 ✔
3074
                                  [&]( unsigned int pc, const std::string& filename,
4 ✔
3075
                                       unsigned int line, const std::string& functionName )
3076
                                  {
3077
                                    std::unique_ptr<BStruct> entry( new BStruct );
8 ✔
3078
                                    entry->addMember( "file", new String( filename ) );
8 ✔
3079
                                    entry->addMember( "line", new BLong( line ) );
8 ✔
3080
                                    entry->addMember( "name", new String( functionName ) );
8 ✔
3081
                                    entry->addMember( "pc", new BLong( pc ) );
8 ✔
3082
                                    result->addElement( entry.release() );
8 ✔
3083
                                  } );
8 ✔
3084
  }
3085
  else
3086
  {
3087
    walkCallStack(
2 ✔
3088
        [&]( unsigned int pc )
2 ✔
3089
        {
3090
          std::unique_ptr<BStruct> entry( new BStruct );
3 ✔
3091
          entry->addMember( "file", new String( scriptname() ) );
3 ✔
3092
          entry->addMember( "pc", new BLong( pc ) );
3 ✔
3093
          result->addElement( entry.release() );
3 ✔
3094
        } );
3 ✔
3095
  }
3096

3097
  return result.release();
4 ✔
3098
}
4 ✔
3099

3100
std::string Executor::stacktrace_string()
434 ✔
3101
{
3102
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
434 ✔
3103

3104
  std::string result;
434 ✔
3105

3106
  if ( has_symbols )
434 ✔
3107
  {
3108
    walk_call_stack_with_symbols(
434 ✔
3109
        *this,
3110
        [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
868 ✔
3111
             const std::string& functionName )
3112
        {
3113
          result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
1,482 ✔
3114
                                      filename, line ) );
3115
        } );
741 ✔
3116
  }
3117
  else
3118
  {
3119
    walkCallStack(
×
3120
        [&]( unsigned int pc )
×
3121
        {
3122
          result.append(
×
3123
              fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
3124
        } );
×
3125
  }
3126

3127
  return result;
434 ✔
3128
}
×
3129

3130
std::string Executor::script_stack_block()
353 ✔
3131
{
3132
  try
3133
  {
3134
    auto block = Clib::indent_stack_block( stacktrace_string(), MAX_REPORTED_ESCRIPT_FRAMES );
353 ✔
3135
    if ( block.empty() )
353 ✔
NEW
3136
      return {};
×
3137
    // indent_stack_block terminates its last line like every other; the caller's message ends
3138
    // where this block does.
3139
    block.pop_back();
353 ✔
3140
    return "\n" + block;
353 ✔
3141
  }
353 ✔
NEW
3142
  catch ( ... )
×
3143
  {
3144
    // Reading the debug file allocates and can fail. Losing the frames is acceptable; turning a
3145
    // diagnostic into a second failure is not.
NEW
3146
    return {};
×
NEW
3147
  }
×
3148
}
3149

3150
std::string Executor::execution_error_report( size_t onPC, const std::string& what )
1 ✔
3151
{
3152
  try
3153
  {
3154
    return format_execution_error( prog_->name.get(), pid(), onPC,
2 ✔
3155
                                   threadhelp::current_thread_name(), what, stacktrace_string(),
1 ✔
3156
                                   Clib::ExceptionParser::getTrace() );
3 ✔
3157
  }
3158
  catch ( ... )
×
3159
  {
3160
    // Collecting the stacks reads the debug file and symbolizes the native
3161
    // trace, either of which can fail. Losing the detail is acceptable; losing
3162
    // the message -- turning a script error into a second exception escaping an
3163
    // already-failing catch block -- is not.
3164
    return fmt::format( "Exception in {} PC {}\n  {}\n  (no stacks: reporting them failed)\n",
3165
                        prog_->name.get(), onPC, what );
×
3166
  }
×
3167
}
3168

3169
std::string format_execution_error( const std::string& script, unsigned int pid, size_t pc,
19 ✔
3170
                                    const std::string& thread_name, const std::string& what,
3171
                                    const std::string& escript_stack,
3172
                                    const std::string& native_stack )
3173
{
3174
  std::string result = fmt::format( "Exception in {}", script );
19 ✔
3175
  // A plain Executor has no pid; only scripts scheduled by the core do.
3176
  if ( pid )
19 ✔
3177
    fmt::format_to( std::back_inserter( result ), " (pid {})", pid );
34 ✔
3178
  fmt::format_to( std::back_inserter( result ), " PC {}", pc );
19 ✔
3179
  if ( !thread_name.empty() )
19 ✔
3180
    fmt::format_to( std::back_inserter( result ), ", thread {}", thread_name );
36 ✔
3181
  fmt::format_to( std::back_inserter( result ), "\n  {}\n", what );
19 ✔
3182

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

3189
  auto native = Clib::indent_stack_block( native_stack, Clib::MAX_REPORTED_NATIVE_FRAMES );
19 ✔
3190
  if ( !native.empty() )
19 ✔
3191
    result += "\nNative stack (throw site, innermost first):\n" + native;
11 ✔
3192

3193
  return result;
38 ✔
3194
}
19 ✔
3195

3196
void Executor::ins_pop_param( const Instruction& ins )
127,247 ✔
3197
{
3198
  popParam( ins.token );
127,247 ✔
3199
}
127,247 ✔
3200

3201
void Executor::ins_pop_param_byref( const Instruction& ins )
34,485 ✔
3202
{
3203
  popParamByRef( ins.token );
34,485 ✔
3204
}
34,485 ✔
3205

3206
void Executor::ins_get_arg( const Instruction& ins )
1,173 ✔
3207
{
3208
  getArg( ins.token );
1,173 ✔
3209
}
1,173 ✔
3210

3211
// CTRL_LEAVE_BLOCK:
3212
void Executor::ins_leave_block( const Instruction& ins )
193,392 ✔
3213
{
3214
  if ( Locals2 )
193,392 ✔
3215
  {
3216
    for ( int i = 0; i < ins.token.lval; i++ )
402,191 ✔
3217
      Locals2->pop_back();
208,799 ✔
3218
  }
3219
  else  // at global level.  ick.
3220
  {
3221
    for ( int i = 0; i < ins.token.lval; i++ )
×
3222
      Globals2->pop_back();
×
3223
  }
3224
}
193,392 ✔
3225

3226
void Executor::ins_gosub( const Instruction& ins )
×
3227
{
3228
  ReturnContext rc;
×
3229
  rc.PC = PC;
×
3230
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3231
  ControlStack.push_back( rc );
×
3232
  if ( Locals2 )
×
3233
    upperLocals2.push_back( Locals2 );
×
3234
  Locals2 = new BObjectRefVec;
×
3235

3236
  PC = (unsigned)ins.token.lval;
×
3237
}
×
3238

3239
// case RSV_RETURN
3240
void Executor::ins_return( const Instruction& /*ins*/ )
94,447 ✔
3241
{
3242
  if ( ControlStack.empty() )
94,447 ✔
3243
  {
3244
    // POLLOG, not ERROR_PRINTLN: the latter is stderr only, so a shard run as a service never
3245
    // saw this at all.
NEW
3246
    POLLOG_ERRORLN( "Return without GOSUB! (PC={}, {}){}", PC, scriptname(), script_stack_block() );
×
3247

3248
    seterror( true );
×
3249
    return;
×
3250
  }
3251
  BObjectRef continuation;
94,447 ✔
3252
  ReturnContext& rc = ControlStack.back();
94,447 ✔
3253
  PC = rc.PC;
94,447 ✔
3254

3255
  if ( rc.Continuation.get() != nullptr )
94,447 ✔
3256
  {
3257
    continuation = rc.Continuation;
37,497 ✔
3258
  }
3259

3260
  if ( Locals2 )
94,447 ✔
3261
  {
3262
    delete Locals2;
94,447 ✔
3263
    Locals2 = nullptr;
94,447 ✔
3264
  }
3265
  if ( !upperLocals2.empty() )
94,447 ✔
3266
  {
3267
    Locals2 = upperLocals2.back();
94,447 ✔
3268
    upperLocals2.pop_back();
94,447 ✔
3269
  }
3270

3271
  if ( rc.ExternalContext.has_value() )
94,447 ✔
3272
  {
3273
    prog_ = std::move( rc.ExternalContext->Program );
2,003 ✔
3274
    nLines = static_cast<unsigned int>( prog_->instr.size() );
2,003 ✔
3275
    execmodules = std::move( rc.ExternalContext->Modules );
2,003 ✔
3276
    Globals2 = std::move( rc.ExternalContext->Globals );
2,003 ✔
3277
  }
3278

3279
  // FIXME do something with rc.ValueStackDepth
3280
  ControlStack.pop_back();
94,447 ✔
3281

3282
  if ( continuation != nullptr )
94,447 ✔
3283
  {
3284
    auto result = ValueStack.back();
37,497 ✔
3285
    ValueStack.pop_back();
37,497 ✔
3286

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

3292
    // If the the continuation callback returned a continuation, handle the jump.
3293
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
37,497 ✔
3294
    {
3295
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3296
      // takes ownership.
3297

3298
      // Add function reference to stack
3299
      ValueStack.emplace_back( cont->func() );
36,650 ✔
3300

3301
      // Move all arguments to the fparams stack
3302
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
36,650 ✔
3303
                      std::make_move_iterator( cont->args.end() ) );
3304

3305
      cont->args.clear();
36,650 ✔
3306

3307
      printStack(
36,650 ✔
3308
          "continuation callback returned a continuation; continuation args added to fparams" );
3309

3310
      BObjectRef objref = ValueStack.back();
36,650 ✔
3311
      auto funcr = objref->impptr<BFunctionRef>();
36,650 ✔
3312
      Instruction jmp;
36,650 ✔
3313
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
36,650 ✔
3314
      {
3315
        call_function_reference( funcr, cont, jmp );
36,650 ✔
3316
      }
3317
      else
3318
      {
3319
        // Delete `imp` since a ReturnContext was not created.
3320
        BObject bobj( imp );
×
3321
      }
×
3322
    }
36,650 ✔
3323
    else
3324
    {
3325
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3326
      // `array{}.filter(...)`
3327
      ValueStack.pop_back();
847 ✔
3328

3329
      // Add the result to the stack.
3330
      ValueStack.emplace_back( imp );
847 ✔
3331
    }
3332
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
74,994 ✔
3333
  }
37,497 ✔
3334
}
94,447 ✔
3335

3336
void Executor::ins_exit( const Instruction& /*ins*/ )
3 ✔
3337
{
3338
  done = 1;
3 ✔
3339
  run_ok_ = false;
3 ✔
3340
}
3 ✔
3341

3342
void Executor::ins_double( const Instruction& ins )
5,295 ✔
3343
{
3344
  ValueStack.emplace_back( new Double( ins.token.dval ) );
5,295 ✔
3345
}
5,295 ✔
3346

3347
void Executor::ins_classinst( const Instruction& ins )
488 ✔
3348
{
3349
  ValueStack.emplace_back( new BConstObject(
488 ✔
3350
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2, pid() ) ) ) );
976 ✔
3351
}
488 ✔
3352

3353
void Executor::ins_string( const Instruction& ins )
428,787 ✔
3354
{
3355
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
428,787 ✔
3356
}
428,787 ✔
3357

3358
void Executor::ins_regexp( const Instruction& )
150 ✔
3359
{
3360
  BObjectRef flags = ValueStack.back();
150 ✔
3361
  ValueStack.pop_back();
150 ✔
3362
  BObjectRef& pattern = ValueStack.back();
150 ✔
3363

3364
  pattern.set(
150 ✔
3365
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300 ✔
3366
}
150 ✔
3367

3368
void Executor::ins_error( const Instruction& /*ins*/ )
5,153 ✔
3369
{
3370
  ValueStack.emplace_back( new BError() );
5,153 ✔
3371
}
5,153 ✔
3372
void Executor::ins_struct( const Instruction& /*ins*/ )
5,688 ✔
3373
{
3374
  ValueStack.emplace_back( new BStruct );
5,688 ✔
3375
}
5,688 ✔
3376
void Executor::ins_spread( const Instruction& ins )
2,126 ✔
3377
{
3378
  bool spread_into = ins.token.lval;
2,126 ✔
3379

3380
  // Pops TOP, and spreads it into (new) TOP.
3381
  if ( spread_into )
2,126 ✔
3382
  {
3383
    BObjectRef rightref = ValueStack.back();
24 ✔
3384
    ValueStack.pop_back();
24 ✔
3385
    BObjectRef& leftref = ValueStack.back();
24 ✔
3386

3387
    BObject& right = *rightref;
24 ✔
3388
    BObject& left = *leftref;
24 ✔
3389

3390
    BObjectRef refIter( UninitObject::create() );
24 ✔
3391

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

3394
    while ( auto next = pIter->step() )
72 ✔
3395
    {
3396
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48 ✔
3397
      BObject res_obj( result );
48 ✔
3398
    }
48 ✔
3399
  }
24 ✔
3400
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3401
  else
3402
  {
3403
    auto spread = new BSpread( ValueStack.back() );
2,102 ✔
3404
    ValueStack.pop_back();
2,102 ✔
3405
    ValueStack.emplace_back( spread );
2,102 ✔
3406
  }
3407
}
2,126 ✔
3408
void Executor::ins_array( const Instruction& /*ins*/ )
8,869 ✔
3409
{
3410
  ValueStack.emplace_back( new ObjArray );
8,869 ✔
3411
}
8,869 ✔
3412
void Executor::ins_dictionary( const Instruction& /*ins*/ )
211 ✔
3413
{
3414
  ValueStack.emplace_back( new BDictionary );
211 ✔
3415
}
211 ✔
3416
void Executor::ins_uninit( const Instruction& /*ins*/ )
139 ✔
3417
{
3418
  ValueStack.emplace_back( UninitObject::create() );
139 ✔
3419
}
139 ✔
3420
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3421
{
3422
  ValueStack.emplace_back( new BError( "Please recompile this script" ) );
×
3423
}
×
3424

3425
// case TOK_UNMINUS:
3426
void Executor::ins_unminus( const Instruction& /*ins*/ )
165 ✔
3427
{
3428
  BObjectRef ref = getObjRef();
165 ✔
3429
  BObjectImp* newobj;
3430
  newobj = ref->impref().inverse();
165 ✔
3431

3432
  ValueStack.emplace_back( newobj );
165 ✔
3433
}
165 ✔
3434

3435
// case TOK_UNPLUSPLUS:
3436
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
11,601 ✔
3437
{
3438
  BObjectRef& ref = ValueStack.back();
11,601 ✔
3439
  ref->impref().selfPlusPlus();
11,601 ✔
3440
}
11,601 ✔
3441

3442
// case TOK_UNMINUSMINUS:
3443
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
134 ✔
3444
{
3445
  BObjectRef& ref = ValueStack.back();
134 ✔
3446
  ref->impref().selfMinusMinus();
134 ✔
3447
}
134 ✔
3448

3449
// case TOK_UNPLUSPLUS_POST:
3450
void Executor::ins_unplusplus_post( const Instruction& /*ins*/ )
261 ✔
3451
{
3452
  BObjectRef& ref = ValueStack.back();
261 ✔
3453
  BObjectImp* imp = ref->impptr();
261 ✔
3454
  BObject* n = ref->clone();
261 ✔
3455
  imp->selfPlusPlus();
261 ✔
3456
  ref.set( n );
261 ✔
3457
}
261 ✔
3458

3459
// case TOK_UNMINUSMINUS_POST:
3460
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
100 ✔
3461
{
3462
  BObjectRef& ref = ValueStack.back();
100 ✔
3463
  BObjectImp* imp = ref->impptr();
100 ✔
3464
  BObject* n = ref->clone();
100 ✔
3465
  imp->selfMinusMinus();
100 ✔
3466
  ref.set( n );
100 ✔
3467
}
100 ✔
3468

3469
// case INS_SET_MEMBER_ID_UNPLUSPLUS:
3470
void Executor::ins_set_member_id_unplusplus( const Instruction& ins )
10 ✔
3471
{
3472
  BObjectRef& ref = ValueStack.back();
10 ✔
3473
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
10 ✔
3474
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
20 ✔
3475
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
10 ✔
3476
  {
3477
    tmp->impref().selfPlusPlus();
10 ✔
3478
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
10 ✔
3479
  }
3480
  ref.set( tmp.get() );
10 ✔
3481
}
10 ✔
3482

3483
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3484
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
3 ✔
3485
{
3486
  BObjectRef& ref = ValueStack.back();
3 ✔
3487
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3 ✔
3488
  BObject* res = tmp->clone();
3 ✔
3489
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6 ✔
3490
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3 ✔
3491
  {
3492
    tmp->impref().selfPlusPlus();
3 ✔
3493
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3 ✔
3494
  }
3495
  ref.set( res );
3 ✔
3496
}
3 ✔
3497

3498
// case INS_SET_MEMBER_ID_UNMINUSMINUS:
3499
void Executor::ins_set_member_id_unminusminus( const Instruction& ins )
13 ✔
3500
{
3501
  BObjectRef& ref = ValueStack.back();
13 ✔
3502
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
13 ✔
3503
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
26 ✔
3504
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
13 ✔
3505
  {
3506
    tmp->impref().selfMinusMinus();
13 ✔
3507
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
13 ✔
3508
  }
3509
  ref.set( tmp.get() );
13 ✔
3510
}
13 ✔
3511

3512
// case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3513
void Executor::ins_set_member_id_unminusminus_post( const Instruction& ins )
3 ✔
3514
{
3515
  BObjectRef& ref = ValueStack.back();
3 ✔
3516
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3 ✔
3517
  BObject* res = tmp->clone();
3 ✔
3518
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6 ✔
3519
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3 ✔
3520
  {
3521
    tmp->impref().selfMinusMinus();
3 ✔
3522
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3 ✔
3523
  }
3524
  ref.set( res );
3 ✔
3525
}
3 ✔
3526

3527
// case TOK_LOG_NOT:
3528
void Executor::ins_logical_not( const Instruction& /*ins*/ )
15,221 ✔
3529
{
3530
  BObjectRef ref = getObjRef();
15,221 ✔
3531
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
15,221 ✔
3532
}
15,221 ✔
3533

3534
// case TOK_BITWISE_NOT:
3535
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
165 ✔
3536
{
3537
  BObjectRef ref = getObjRef();
165 ✔
3538
  ValueStack.emplace_back( ref->impptr()->bitnot() );
165 ✔
3539
}
165 ✔
3540

3541
// case TOK_FUNCREF:
3542
void Executor::ins_funcref( const Instruction& ins )
2,392 ✔
3543
{
3544
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
2,392 ✔
3545
  {
3546
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3547
                    prog_->function_references.size() );
×
3548
    seterror( true );
×
3549
    return;
×
3550
  }
3551

3552
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
2,392 ✔
3553

3554
  ValueStack.emplace_back(
4,784 ✔
3555
      new BFunctionRef( prog_, pid(), funcref_index, Globals2, {} /* captures */ ) );
2,392 ✔
3556
}
3557

3558
void Executor::ins_functor( const Instruction& ins )
808 ✔
3559
{
3560
  auto funcref_index = static_cast<int>( ins.token.type );
808 ✔
3561

3562
  const auto& ep_funcref = prog_->function_references[funcref_index];
808 ✔
3563

3564
  int capture_count = ep_funcref.capture_count;
808 ✔
3565

3566
  auto captures = ValueStackCont();
808 ✔
3567
  while ( capture_count > 0 )
1,530 ✔
3568
  {
3569
    captures.push_back( ValueStack.back() );
722 ✔
3570
    ValueStack.pop_back();
722 ✔
3571
    capture_count--;
722 ✔
3572
  }
3573

3574
  auto func = new BFunctionRef( prog_, pid(), funcref_index, Globals2, std::move( captures ) );
808 ✔
3575

3576
  ValueStack.emplace_back( func );
808 ✔
3577

3578
  PC += ins.token.lval;
808 ✔
3579
}
808 ✔
3580

3581
void Executor::ins_logical_jump( const Instruction& ins )
685 ✔
3582
{
3583
  BObjectRef& objref = ValueStack.back();
685 ✔
3584
  // jmp if true for ||, jmp if false for &&
3585
  const bool obj_true = objref->impptr()->isTrue();
685 ✔
3586
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
685 ✔
3587

3588
  if ( jmp )
685 ✔
3589
    PC = (unsigned)ins.token.lval;
162 ✔
3590
  // keep the obj on the stack if it should jump (ShortCircuit)
3591
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3592
  // BLong (original &&/|| convert to BLong bool)
3593
  if ( !jmp )
685 ✔
3594
    ValueStack.pop_back();
523 ✔
3595
  else
3596
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
162 ✔
3597
}
685 ✔
3598

3599
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
212 ✔
3600
{
3601
  BObjectRef& objref = ValueStack.back();
212 ✔
3602
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
212 ✔
3603
}
212 ✔
3604

3605
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3606

3607
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
358,890 ✔
3608
{
3609
  switch ( token.id )
358,890 ✔
3610
  {
3611
  case INS_INITFOREACH:
702 ✔
3612
    return &Executor::ins_initforeach;
702 ✔
3613
  case INS_STEPFOREACH:
702 ✔
3614
    return &Executor::ins_stepforeach;
702 ✔
3615
  case INS_INITFOR:
84 ✔
3616
    return &Executor::ins_initfor;
84 ✔
3617
  case INS_NEXTFOR:
84 ✔
3618
    return &Executor::ins_nextfor;
84 ✔
3619
  case INS_CASEJMP:
97 ✔
3620
    return &Executor::ins_casejmp;
97 ✔
3621
  case RSV_JMPIFTRUE:
4,385 ✔
3622
    return &Executor::ins_jmpiftrue;
4,385 ✔
3623
  case RSV_JMPIFFALSE:
6,434 ✔
3624
    return &Executor::ins_jmpiffalse;
6,434 ✔
3625
  case RSV_LOCAL:
7,745 ✔
3626
    return &Executor::ins_makeLocal;
7,745 ✔
3627
  case RSV_GLOBAL:
16,820 ✔
3628
  case TOK_GLOBALVAR:
3629
    return &Executor::ins_globalvar;
16,820 ✔
3630
  case TOK_LOCALVAR:
43,880 ✔
3631
    return &Executor::ins_localvar;
43,880 ✔
3632
  case TOK_LONG:
30,768 ✔
3633
    return &Executor::ins_long;
30,768 ✔
3634
  case TOK_DOUBLE:
1,479 ✔
3635
    return &Executor::ins_double;
1,479 ✔
3636
  case TOK_STRING:
47,548 ✔
3637
    return &Executor::ins_string;
47,548 ✔
3638
  case TOK_REGEXP:
150 ✔
3639
    return &Executor::ins_regexp;
150 ✔
3640
  case TOK_ERROR:
626 ✔
3641
    return &Executor::ins_error;
626 ✔
3642
  case TOK_STRUCT:
1,300 ✔
3643
    return &Executor::ins_struct;
1,300 ✔
3644
  case TOK_SPREAD:
670 ✔
3645
    return &Executor::ins_spread;
670 ✔
3646
  case TOK_CLASSINST:
266 ✔
3647
    return &Executor::ins_classinst;
266 ✔
3648
  case TOK_ARRAY:
3,455 ✔
3649
    return &Executor::ins_array;
3,455 ✔
3650
  case TOK_DICTIONARY:
206 ✔
3651
    return &Executor::ins_dictionary;
206 ✔
3652
  case TOK_FUNCREF:
744 ✔
3653
    return &Executor::ins_funcref;
744 ✔
3654
  case TOK_FUNCTOR:
707 ✔
3655
    return &Executor::ins_functor;
707 ✔
3656
  case INS_UNINIT:
117 ✔
3657
    return &Executor::ins_uninit;
117 ✔
3658
  case TOK_IDENT:
×
3659
    return &Executor::ins_ident;
×
3660
  case INS_ASSIGN_GLOBALVAR:
3,348 ✔
3661
    return &Executor::ins_assign_globalvar;
3,348 ✔
3662
  case INS_ASSIGN_LOCALVAR:
2,494 ✔
3663
    return &Executor::ins_assign_localvar;
2,494 ✔
3664
  case INS_ASSIGN_CONSUME:
298 ✔
3665
    return &Executor::ins_assign_consume;
298 ✔
3666
  case TOK_CONSUMER:
31,579 ✔
3667
    return &Executor::ins_consume;
31,579 ✔
3668
  case TOK_ASSIGN:
9,924 ✔
3669
    return &Executor::ins_assign;
9,924 ✔
3670
  case INS_SUBSCRIPT_ASSIGN:
9 ✔
3671
    return &Executor::ins_array_assign;
9 ✔
3672
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
497 ✔
3673
    return &Executor::ins_array_assign_consume;
497 ✔
3674
  case INS_MULTISUBSCRIPT:
313 ✔
3675
    return &Executor::ins_multisubscript;
313 ✔
3676
  case INS_MULTISUBSCRIPT_ASSIGN:
30 ✔
3677
    return &Executor::ins_multisubscript_assign;
30 ✔
3678
  case INS_GET_MEMBER:
3,487 ✔
3679
    return &Executor::ins_get_member;
3,487 ✔
3680
  case INS_SET_MEMBER:
3 ✔
3681
    return &Executor::ins_set_member;
3 ✔
3682
  case INS_SET_MEMBER_CONSUME:
258 ✔
3683
    return &Executor::ins_set_member_consume;
258 ✔
3684

3685
  case INS_UNPACK_SEQUENCE:
146 ✔
3686
    return &Executor::ins_unpack_sequence;
146 ✔
3687
  case INS_UNPACK_INDICES:
89 ✔
3688
    return &Executor::ins_unpack_indices;
89 ✔
3689
  case INS_TAKE_GLOBAL:
183 ✔
3690
    return &Executor::ins_take_global;
183 ✔
3691
  case INS_TAKE_LOCAL:
398 ✔
3692
    return &Executor::ins_take_local;
398 ✔
3693

3694
  case INS_GET_MEMBER_ID:
5,509 ✔
3695
    return &Executor::ins_get_member_id;  // test id
5,509 ✔
3696
  case INS_SET_MEMBER_ID:
3 ✔
3697
    return &Executor::ins_set_member_id;  // test id
3 ✔
3698
  case INS_SET_MEMBER_ID_CONSUME:
462 ✔
3699
    return &Executor::ins_set_member_id_consume;  // test id
462 ✔
3700

3701
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15 ✔
3702
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15 ✔
3703
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6 ✔
3704
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6 ✔
3705
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9 ✔
3706
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9 ✔
3707
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6 ✔
3708
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6 ✔
3709
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6 ✔
3710
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6 ✔
3711

3712
  case TOK_ADD:
5,288 ✔
3713
    return &Executor::ins_add;
5,288 ✔
3714
  case TOK_SUBTRACT:
598 ✔
3715
    return &Executor::ins_subtract;
598 ✔
3716
  case TOK_DIV:
163 ✔
3717
    return &Executor::ins_div;
163 ✔
3718
  case TOK_MULT:
523 ✔
3719
    return &Executor::ins_mult;
523 ✔
3720
  case TOK_MODULUS:
116 ✔
3721
    return &Executor::ins_modulus;
116 ✔
3722

3723
  case TOK_INSERTINTO:
7,067 ✔
3724
    return &Executor::ins_insert_into;
7,067 ✔
3725

3726
  case TOK_PLUSEQUAL:
115 ✔
3727
    return &Executor::ins_plusequal;
115 ✔
3728
  case TOK_MINUSEQUAL:
36 ✔
3729
    return &Executor::ins_minusequal;
36 ✔
3730
  case TOK_TIMESEQUAL:
27 ✔
3731
    return &Executor::ins_timesequal;
27 ✔
3732
  case TOK_DIVIDEEQUAL:
24 ✔
3733
    return &Executor::ins_divideequal;
24 ✔
3734
  case TOK_MODULUSEQUAL:
24 ✔
3735
    return &Executor::ins_modulusequal;
24 ✔
3736

3737
  case TOK_LESSTHAN:
565 ✔
3738
    return &Executor::ins_lessthan;
565 ✔
3739
  case TOK_LESSEQ:
201 ✔
3740
    return &Executor::ins_lessequal;
201 ✔
3741
  case RSV_GOTO:
3,164 ✔
3742
    return &Executor::ins_goto;
3,164 ✔
3743
  case TOK_ARRAY_SUBSCRIPT:
2,171 ✔
3744
    return &Executor::ins_arraysubscript;
2,171 ✔
3745
  case TOK_EQUAL:
1,425 ✔
3746
    return &Executor::ins_equal;
1,425 ✔
3747
  case TOK_FUNC:
21,164 ✔
3748
    return &Executor::ins_func;
21,164 ✔
3749
  case INS_CALL_METHOD:
934 ✔
3750
    return &Executor::ins_call_method;
934 ✔
3751
  case INS_CALL_METHOD_ID:
7,375 ✔
3752
    return &Executor::ins_call_method_id;
7,375 ✔
3753
  case CTRL_STATEMENTBEGIN:
×
3754
    return &Executor::ins_statementbegin;
×
3755
  case CTRL_MAKELOCAL:
15,429 ✔
3756
    return &Executor::ins_makelocal;
15,429 ✔
3757
  case INS_CHECK_MRO:
225 ✔
3758
    return &Executor::ins_check_mro;
225 ✔
3759
  case CTRL_JSR_USERFUNC:
15,429 ✔
3760
    return &Executor::ins_jsr_userfunc;
15,429 ✔
3761
  case INS_POP_PARAM:
6,229 ✔
3762
    return &Executor::ins_pop_param;
6,229 ✔
3763
  case INS_POP_PARAM_BYREF:
1,355 ✔
3764
    return &Executor::ins_pop_param_byref;
1,355 ✔
3765
  case INS_GET_ARG:
216 ✔
3766
    return &Executor::ins_get_arg;
216 ✔
3767
  case CTRL_LEAVE_BLOCK:
2,678 ✔
3768
    return &Executor::ins_leave_block;
2,678 ✔
3769
  case RSV_GOSUB:
×
3770
    return &Executor::ins_gosub;
×
3771
  case RSV_RETURN:
13,014 ✔
3772
    return &Executor::ins_return;
13,014 ✔
3773
  case RSV_EXIT:
18 ✔
3774
    return &Executor::ins_exit;
18 ✔
3775
  case INS_DECLARE_ARRAY:
72 ✔
3776
    return &Executor::ins_declareArray;
72 ✔
3777
  case TOK_UNMINUS:
23 ✔
3778
    return &Executor::ins_unminus;
23 ✔
3779
  case TOK_UNPLUS:
×
3780
    return &Executor::ins_nop;
×
3781
  case TOK_LOG_NOT:
657 ✔
3782
    return &Executor::ins_logical_not;
657 ✔
3783
  case TOK_BITWISE_NOT:
21 ✔
3784
    return &Executor::ins_bitwise_not;
21 ✔
3785
  case TOK_BSRIGHT:
120 ✔
3786
    return &Executor::ins_bitshift_right;
120 ✔
3787
  case TOK_BSLEFT:
130 ✔
3788
    return &Executor::ins_bitshift_left;
130 ✔
3789
  case TOK_BITAND:
121 ✔
3790
    return &Executor::ins_bitwise_and;
121 ✔
3791
  case TOK_BITXOR:
111 ✔
3792
    return &Executor::ins_bitwise_xor;
111 ✔
3793
  case TOK_BITOR:
114 ✔
3794
    return &Executor::ins_bitwise_or;
114 ✔
3795

3796
  case TOK_NEQ:
3,699 ✔
3797
    return &Executor::ins_notequal;
3,699 ✔
3798
  case TOK_GRTHAN:
290 ✔
3799
    return &Executor::ins_greaterthan;
290 ✔
3800
  case TOK_GREQ:
182 ✔
3801
    return &Executor::ins_greaterequal;
182 ✔
3802
  case TOK_AND:
500 ✔
3803
    return &Executor::ins_logical_and;
500 ✔
3804
  case TOK_OR:
1,355 ✔
3805
    return &Executor::ins_logical_or;
1,355 ✔
3806

3807
  case TOK_ADDMEMBER:
349 ✔
3808
    return &Executor::ins_addmember;
349 ✔
3809
  case TOK_DELMEMBER:
25 ✔
3810
    return &Executor::ins_removemember;
25 ✔
3811
  case TOK_CHKMEMBER:
135 ✔
3812
    return &Executor::ins_checkmember;
135 ✔
3813
  case INS_DICTIONARY_ADDMEMBER:
185 ✔
3814
    return &Executor::ins_dictionary_addmember;
185 ✔
3815
  case TOK_IN:
126 ✔
3816
    return &Executor::ins_in;
126 ✔
3817
  case TOK_IS:
56 ✔
3818
    return &Executor::ins_is;
56 ✔
3819
  case INS_ADDMEMBER2:
45 ✔
3820
    return &Executor::ins_addmember2;
45 ✔
3821
  case INS_ADDMEMBER_ASSIGN:
2,843 ✔
3822
    return &Executor::ins_addmember_assign;
2,843 ✔
3823
  case CTRL_PROGEND:
4,611 ✔
3824
    return &Executor::ins_progend;
4,611 ✔
3825
  case TOK_UNPLUSPLUS:
119 ✔
3826
    return &Executor::ins_unplusplus;
119 ✔
3827
  case TOK_UNMINUSMINUS:
60 ✔
3828
    return &Executor::ins_unminusminus;
60 ✔
3829
  case TOK_UNPLUSPLUS_POST:
100 ✔
3830
    return &Executor::ins_unplusplus_post;
100 ✔
3831
  case TOK_UNMINUSMINUS_POST:
57 ✔
3832
    return &Executor::ins_unminusminus_post;
57 ✔
3833
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10 ✔
3834
    return &Executor::ins_set_member_id_unplusplus;  // test id
10 ✔
3835
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13 ✔
3836
    return &Executor::ins_set_member_id_unminusminus;  // test id
13 ✔
3837
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3 ✔
3838
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3 ✔
3839
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3 ✔
3840
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3 ✔
3841
  case INS_SKIPIFTRUE_ELSE_CONSUME:
212 ✔
3842
    return &Executor::ins_skipiftrue_else_consume;
212 ✔
3843
  case TOK_INTERPOLATE_STRING:
5,853 ✔
3844
    return &Executor::ins_interpolate_string;
5,853 ✔
3845
  case TOK_FORMAT_EXPRESSION:
589 ✔
3846
    return &Executor::ins_format_expression;
589 ✔
3847
  case TOK_BOOL:
2,038 ✔
3848
    return &Executor::ins_bool;
2,038 ✔
3849
  case INS_LOGICAL_JUMP:
300 ✔
3850
    return &Executor::ins_logical_jump;
300 ✔
3851
  case INS_LOGICAL_CONVERT:
149 ✔
3852
    return &Executor::ins_logical_convert;
149 ✔
3853
  default:
×
3854
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3855
  }
3856
}
3857

3858
void Executor::sethalt( bool halt )
17 ✔
3859
{
3860
  halt_ = halt;
17 ✔
3861

3862
  if ( halt && dbg_env_ )
17 ✔
3863
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18 ✔
3864
      listener->on_halt();
9 ✔
3865

3866
  calcrunnable();
17 ✔
3867
}
17 ✔
3868

3869
void Executor::execInstr()
759,132,780 ✔
3870
{
3871
  unsigned onPC = PC;
759,132,780 ✔
3872
  try
3873
  {  // this is really more of a class invariant.
3874
    passert( run_ok_ );
759,132,780 ✔
3875
    passert( PC < nLines );
759,132,780 ✔
3876
    passert( !error_ );
759,132,780 ✔
3877
    passert( !done );
759,132,780 ✔
3878

3879
#ifdef NDEBUG
3880
    const Instruction& ins = prog_->instr[PC];
759,132,780 ✔
3881
#else
3882
    const Instruction& ins = prog_->instr.at( PC );
3883
#endif
3884
    if ( debug_level >= INSTRUCTIONS )
759,132,780 ✔
3885
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3886

3887
    // If `on_instruction` returns false, do not execute this instruction.
3888
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
759,132,780 ✔
3889
    {
3890
      return;
9 ✔
3891
    }
3892

3893
    ++ins.cycles;
759,132,771 ✔
3894
    ++prog_->instr_cycles;
759,132,771 ✔
3895
    count_instr_cycle();
759,132,771 ✔
3896

3897
    ++PC;
759,132,771 ✔
3898

3899
    ( this->*( ins.func ) )( ins );
759,132,771 ✔
3900
  }
3901
  catch ( std::exception& ex )
1 ✔
3902
  {
3903
    std::string tmp = execution_error_report(
3904
        onPC, boost::core::demangle( typeid( ex ).name() ) + ": " + ex.what() );
1 ✔
3905
    if ( !run_ok_ )
1 ✔
3906
      tmp += "run_ok_ = false\n";
×
3907
    if ( PC < nLines )
1 ✔
3908
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
2 ✔
3909
    if ( error_ )
1 ✔
3910
      tmp += "error_ = true\n";
×
3911
    if ( done )
1 ✔
3912
      tmp += "done = true\n";
×
3913

3914
    seterror( true );
1 ✔
3915
    POLLOG_ERROR( tmp );
1 ✔
3916

3917
    show_context( onPC );
1 ✔
3918
  }
1 ✔
3919
#ifdef __unix__
3920
  catch ( ... )
×
3921
  {
3922
    // No exception object to describe, but the throw site is still recorded --
3923
    // boost's hook does not care what was thrown.
3924
    std::string tmp = execution_error_report( onPC, "unclassified" );
×
3925

3926
    seterror( true );
×
3927
    POLLOG_ERROR( tmp );
×
3928

3929
    show_context( onPC );
×
3930
  }
×
3931
#endif
3932
}
3933

3934
std::string Executor::dbg_get_instruction( size_t atPC ) const
7 ✔
3935
{
3936
  std::string out;
7 ✔
3937
  dbg_get_instruction( atPC, out );
7 ✔
3938
  return out;
7 ✔
3939
}
×
3940

3941
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
4,638 ✔
3942
{
3943
  bool has_breakpoint =
3944
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
4,638 ✔
3945
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
4,638 ✔
3946
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
4,638 ✔
3947
}
4,638 ✔
3948

3949
void Executor::show_context( unsigned atPC )
1 ✔
3950
{
3951
  unsigned start, end;
3952
  if ( atPC >= 5 )
1 ✔
3953
    start = atPC - 5;
×
3954
  else
3955
    start = 0;
1 ✔
3956

3957
  end = atPC + 5;
1 ✔
3958

3959
  if ( end >= nLines )
1 ✔
3960
    end = nLines - 1;
1 ✔
3961

3962
  for ( unsigned i = start; i <= end; ++i )
8 ✔
3963
  {
3964
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
7 ✔
3965
  }
3966
}
1 ✔
3967
void Executor::show_context( std::string& os, unsigned atPC )
421 ✔
3968
{
3969
  unsigned start, end;
3970
  if ( atPC >= 5 )
421 ✔
3971
    start = atPC - 5;
421 ✔
3972
  else
3973
    start = 0;
×
3974

3975
  end = atPC + 5;
421 ✔
3976

3977
  if ( end >= nLines )
421 ✔
3978
    end = nLines - 1;
×
3979

3980
  for ( unsigned i = start; i <= end; ++i )
5,052 ✔
3981
  {
3982
    dbg_get_instruction( i, os );
4,631 ✔
3983
    os += '\n';
4,631 ✔
3984
  }
3985
}
421 ✔
3986

3987
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
42,484 ✔
3988
                                        const Instruction& jmp )
3989
{
3990
  // params need to be on the stack, without current objectref
3991
  ValueStack.pop_back();
42,484 ✔
3992
  // Push captured parameters onto the stack prior to function parameters.
3993
  for ( auto& p : funcr->captures )
49,594 ✔
3994
    ValueStack.push_back( p );
7,110 ✔
3995

3996
  auto nparams = static_cast<int>( fparams.size() );
42,484 ✔
3997

3998
  // Handle variadic functions special. Construct an array{} corresponding to
3999
  // the rest parameter (the last parameter for the function). The logic for the
4000
  // condition:
4001
  // - if true, the last argument in the call may not be an array{}, so we need
4002
  //   to construct one (which is why the condition is `>=` and not `>`).
4003
  // - if false, then the address we're jumping to will be a "default argument
4004
  //   address" and _not_ the user function directly, which will create the
4005
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
4006
  if ( funcr->variadic() && nparams >= funcr->numParams() )
42,484 ✔
4007
  {
4008
    auto num_nonrest_args = funcr->numParams() - 1;
641 ✔
4009

4010
    auto rest_arg = std::make_unique<ObjArray>();
641 ✔
4011

4012
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,661 ✔
4013
    {
4014
      auto& p = fparams[i];
3,020 ✔
4015

4016
      if ( i < num_nonrest_args )
3,020 ✔
4017
      {
4018
        ValueStack.push_back( p );
617 ✔
4019
      }
4020
      else
4021
      {
4022
        rest_arg->ref_arr.push_back( p );
2,403 ✔
4023
      }
4024
    }
4025
    ValueStack.emplace_back( rest_arg.release() );
641 ✔
4026
  }
641 ✔
4027
  // The array{} will be created via the regular default-parameter handling by
4028
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
4029
  // prior to jumping to the user function.
4030
  else
4031
  {
4032
    for ( auto& p : fparams )
89,848 ✔
4033
      ValueStack.push_back( p );
48,005 ✔
4034
  }
4035

4036
  // jump to function
4037
  jump( jmp.token.lval, continuation, funcr );
42,484 ✔
4038
  fparams.clear();
42,484 ✔
4039
  // switch to new block
4040
  ins_makelocal( jmp );
42,484 ✔
4041
}
42,484 ✔
4042

4043
bool Executor::exec()
8,650 ✔
4044
{
4045
  passert( prog_ok_ );
8,650 ✔
4046
  passert( !error_ );
8,650 ✔
4047

4048
  Clib::script_status.set_script( scriptname() );
8,650 ✔
4049

4050
  set_running_to_completion( true );
8,650 ✔
4051
  while ( runnable() )
856,090 ✔
4052
  {
4053
    Clib::script_status.set_pc( PC );
847,440 ✔
4054
    execInstr();
847,440 ✔
4055
  }
4056

4057
  return !error_;
8,650 ✔
4058
}
4059

4060
void Executor::reinitExec()
×
4061
{
4062
  PC = 0;
×
4063
  done = 0;
×
4064
  seterror( false );
×
4065

4066
  ValueStack.clear();
×
4067
  delete Locals2;
×
4068
  Locals2 = new BObjectRefVec;
×
4069

4070
  if ( !prog_ok_ )
×
4071
  {
4072
    seterror( true );
×
4073
  }
4074
}
×
4075

4076
void Executor::initForFnCall( unsigned in_PC )
7,809 ✔
4077
{
4078
#ifdef MEMORYLEAK
4079
  bool data_shown = false;
4080
#endif
4081

4082
  PC = in_PC;
7,809 ✔
4083
  done = 0;
7,809 ✔
4084
  seterror( false );
7,809 ✔
4085

4086
#ifdef MEMORYLEAK
4087
  while ( !ValueStack.empty() )
4088
  {
4089
    if ( Clib::memoryleak_debug )
4090
    {
4091
      if ( !data_shown )
4092
      {
4093
        LEAKLOG( "ValueStack... " );
4094
        data_shown = true;
4095
      }
4096

4097
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
4098
               ValueStack.back()->impptr()->sizeEstimate() );
4099
    }
4100
    ValueStack.pop_back();
4101
  }
4102
  if ( Clib::memoryleak_debug )
4103
    if ( data_shown )
4104
      LEAKLOGLN( " ...deleted" );
4105
#endif
4106

4107
  ValueStack.clear();
7,809 ✔
4108
  Locals2->clear();
7,809 ✔
4109
}
7,809 ✔
4110

4111
void Executor::pushArg( BObjectImp* arg )
11,705 ✔
4112
{
4113
  passert_always( arg );
11,705 ✔
4114
  ValueStack.emplace_back( arg );
11,705 ✔
4115
}
11,705 ✔
4116

4117
void Executor::pushArg( const BObjectRef& arg )
4 ✔
4118
{
4119
  ValueStack.push_back( arg );
4 ✔
4120
}
4 ✔
4121

4122
void Executor::addModule( ExecutorModule* module )
34,937 ✔
4123
{
4124
  availmodules.push_back( module );
34,937 ✔
4125
}
34,937 ✔
4126

4127

4128
ExecutorModule* Executor::findModule( const std::string& name )
15,244 ✔
4129
{
4130
  unsigned idx;
4131
  for ( idx = 0; idx < availmodules.size(); idx++ )
157,829 ✔
4132
  {
4133
    ExecutorModule* module = availmodules[idx];
157,829 ✔
4134
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
157,829 ✔
4135
      return module;
15,244 ✔
4136
  }
4137
  return nullptr;
×
4138
}
4139

4140
bool Executor::attach_debugger( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching )
4 ✔
4141
{
4142
  // FIXME: a script can be in debugging state but have no debugger attached,
4143
  // eg. a script that called `os::Debugger()`. This needs to check if a
4144
  // debugger is attached. This works for `os::Debugger()` but not for poldbg cmd_attach.
4145
  if ( dbg_env_ )
4 ✔
4146
  {
4147
    if ( !listener.expired() )
2 ✔
4148
    {
4149
      auto& dbg_env_listener = dbg_env_->listener;
1 ✔
4150
      if ( !dbg_env_listener.expired() )
1 ✔
4151
      {
4152
        return false;
×
4153
      }
4154
      dbg_env_listener = listener;
1 ✔
4155
    }
4156
    if ( set_attaching )
2 ✔
4157
      dbg_env_->debug_state = ExecutorDebugState::ATTACHING;
2 ✔
4158
  }
4159
  else
4160
  {
4161
    dbg_env_ = std::make_unique<ExecutorDebugEnvironment>( listener, set_attaching );
2 ✔
4162
  }
4163

4164
  return true;
4 ✔
4165
}
4166

4167
void Executor::detach_debugger()
×
4168
{
4169
  dbg_env_.reset();
×
4170
  sethalt( false );
×
4171
}
×
4172

4173
void Executor::print_to_debugger( const std::string& message )
24,460 ✔
4174
{
4175
  if ( dbg_env_ )
24,460 ✔
4176
  {
4177
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2 ✔
4178
      listener->on_print( message );
1 ✔
4179
  }
4180
}
24,460 ✔
4181

4182
void Executor::dbg_ins_trace()
×
4183
{
4184
  if ( dbg_env_ )
×
4185
  {
4186
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4187
  }
4188
  sethalt( false );
×
4189
}
×
4190
void Executor::dbg_step_into()
1 ✔
4191
{
4192
  if ( dbg_env_ )
1 ✔
4193
  {
4194
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1 ✔
4195
  }
4196
  sethalt( false );
1 ✔
4197
}
1 ✔
4198
void Executor::dbg_step_over()
2 ✔
4199
{
4200
  if ( dbg_env_ )
2 ✔
4201
  {
4202
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2 ✔
4203
  }
4204
  sethalt( false );
2 ✔
4205
}
2 ✔
4206
void Executor::dbg_step_out()
1 ✔
4207
{
4208
  if ( dbg_env_ )
1 ✔
4209
  {
4210
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1 ✔
4211
  }
4212
  sethalt( false );
1 ✔
4213
}
1 ✔
4214
void Executor::dbg_run()
4 ✔
4215
{
4216
  if ( dbg_env_ )
4 ✔
4217
  {
4218
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4 ✔
4219
  }
4220
  sethalt( false );
4 ✔
4221
}
4 ✔
4222
void Executor::dbg_break()
1 ✔
4223
{
4224
  if ( dbg_env_ )
1 ✔
4225
  {
4226
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1 ✔
4227
  }
4228
}
1 ✔
4229

4230
void Executor::dbg_setbp( unsigned atPC )
2 ✔
4231
{
4232
  if ( dbg_env_ )
2 ✔
4233
  {
4234
    dbg_env_->breakpoints.insert( atPC );
2 ✔
4235
  }
4236
}
2 ✔
4237
void Executor::dbg_clrbp( unsigned atPC )
×
4238
{
4239
  if ( dbg_env_ )
×
4240
  {
4241
    dbg_env_->breakpoints.erase( atPC );
×
4242
  }
4243
}
×
4244

4245
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1 ✔
4246
{
4247
  if ( dbg_env_ )
1 ✔
4248
  {
4249
    std::set<unsigned> result;
1 ✔
4250
    auto& breakpoints = dbg_env_->breakpoints;
1 ✔
4251
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1 ✔
4252
                         std::inserter( result, result.end() ) );
4253
    breakpoints = result;
1 ✔
4254
  }
1 ✔
4255
}
1 ✔
4256

4257
void Executor::dbg_clrallbp()
×
4258
{
4259
  if ( dbg_env_ )
×
4260
  {
4261
    dbg_env_->breakpoints.clear();
×
4262
  }
4263
}
×
4264

4265
size_t Executor::sizeEstimate() const
2,160 ✔
4266
{
4267
  size_t size = sizeof( *this );
2,160 ✔
4268
  size += Clib::memsize( upperLocals2 );
2,160 ✔
4269
  for ( const auto& bobjectrefvec : upperLocals2 )
2,161 ✔
4270
  {
4271
    size += Clib::memsize( *bobjectrefvec );
1 ✔
4272
    for ( const auto& bojectref : *bobjectrefvec )
1 ✔
4273
    {
4274
      if ( bojectref != nullptr )
×
4275
        size += bojectref->sizeEstimate();
×
4276
    }
4277
  }
4278
  size += Clib::memsize( ControlStack );
2,160 ✔
4279

4280
  size += Clib::memsize( *Locals2 );
2,160 ✔
4281
  for ( const auto& bojectref : *Locals2 )
2,271 ✔
4282
  {
4283
    if ( bojectref != nullptr )
111 ✔
4284
      size += bojectref->sizeEstimate();
111 ✔
4285
  }
4286
  size += Clib::memsize( *Globals2 );
2,160 ✔
4287
  for ( const auto& bojectref : *Globals2 )
4,691 ✔
4288
  {
4289
    if ( bojectref != nullptr )
2,531 ✔
4290
      size += bojectref->sizeEstimate();
2,531 ✔
4291
  }
4292
  size += Clib::memsize( ValueStack );
2,160 ✔
4293
  for ( const auto& bojectref : ValueStack )
2,199 ✔
4294
  {
4295
    if ( bojectref != nullptr )
39 ✔
4296
      size += bojectref->sizeEstimate();
39 ✔
4297
  }
4298
  size += Clib::memsize( fparams );
2,160 ✔
4299
  for ( const auto& bojectref : fparams )
2,160 ✔
4300
  {
4301
    if ( bojectref != nullptr )
×
4302
      size += bojectref->sizeEstimate();
×
4303
  }
4304
  for ( const auto& module : availmodules )
17,597 ✔
4305
  {
4306
    if ( module != nullptr )
15,437 ✔
4307
      size += module->sizeEstimate();
15,437 ✔
4308
  }
4309
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
2,160 ✔
4310
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
2,160 ✔
4311
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
2,160 ✔
4312
  size += Clib::memsize( class_methods );
2,160 ✔
4313
  return size;
2,160 ✔
4314
}
4315

4316
bool Executor::builtinMethodForced( const char*& methodname )
30 ✔
4317
{
4318
  if ( methodname[0] == '_' )
30 ✔
4319
  {
4320
    ++methodname;
×
4321
    return true;
×
4322
  }
4323
  return false;
30 ✔
4324
}
4325

4326
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
36,650 ✔
4327
{
4328
  auto* func = continuation->func();
36,650 ✔
4329

4330
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4331
  // there are too many
4332
  while ( func->numParams() > static_cast<int>( args.size() ) )
36,656 ✔
4333
  {
4334
    args.emplace_back( UninitObject::create() );
6 ✔
4335
  }
4336

4337
  // Resize args only for non-varadic functions
4338
  if ( !func->variadic() )
36,650 ✔
4339
    args.resize( func->numParams() );
36,626 ✔
4340

4341
  continuation->args = std::move( args );
36,650 ✔
4342

4343
  return continuation;
36,650 ✔
4344
}
4345

4346
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
5,760 ✔
4347
{
4348
  // Compare the program pointers
4349
  if ( prog < other.prog )
5,760 ✔
4350
    return true;
×
4351
  if ( prog > other.prog )
5,760 ✔
4352
    return false;
×
4353

4354
  // Compare the indices
4355
  if ( index < other.index )
5,760 ✔
4356
    return true;
174 ✔
4357
  if ( index > other.index )
5,586 ✔
4358
    return false;
69 ✔
4359

4360
  // Perform a case-insensitive comparison for method_name using stricmp
4361
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
5,517 ✔
4362
}
4363

4364
#ifdef ESCRIPT_PROFILE
4365
std::map<std::string, EscriptProfiler::profile_instr> EscriptProfiler::escript_profile_map_{};
4366
EscriptProfiler::EscriptProfiler( ExecutorModule* em, const ModuleFunction* modfunc,
4367
                                  const std::vector<BObjectRef>& fparams )
4368
{
4369
  name_ = em->functionName( modfunc->funcidx );
4370
  if ( !fparams.empty() )
4371
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4372
}
4373
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectRef& leftref,
4374
                                  const std::vector<BObjectRef>& fparams )
4375
{
4376
  switch ( ins.token.id )
4377
  {
4378
  case INS_GET_MEMBER:
4379
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.tokval() );
4380
    break;
4381
  case INS_GET_MEMBER_ID:
4382
    name_ = fmt::format( "MBR_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4383
    break;
4384
  case INS_CALL_METHOD_ID:
4385
    name_ = fmt::format( "MTHID_{} .{}", leftref->impptr()->typeOf(), ins.token.lval );
4386
  default:
4387
    break;
4388
  }
4389
  if ( !fparams.empty() )
4390
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4391
}
4392
EscriptProfiler::EscriptProfiler( const Instruction& ins, const BObjectImp* callee,
4393
                                  const char* method_name, const std::vector<BObjectRef>& fparams )
4394
{
4395
  switch ( ins.token.id )
4396
  {
4397
  case INS_CALL_METHOD:
4398
    name_ = fmt::format( "MTH_{} .{}", callee->typeOf(), method_name );
4399
    break;
4400
  default:
4401
    break;
4402
  }
4403
  if ( !fparams.empty() )
4404
    name_ += fmt::format( " [{}]", fparams[0].get()->impptr()->typeOf() );
4405
}
4406

4407
EscriptProfiler::~EscriptProfiler()
4408
{
4409
  auto profile_end = timer_.ellapsed().count();
4410
  auto itr = escript_profile_map_.find( name_ );
4411
  if ( itr != escript_profile_map_.end() )
4412
  {
4413
    itr->second.count++;
4414
    itr->second.sum += profile_end;
4415
    if ( itr->second.max < profile_end )
4416
      itr->second.max = profile_end;
4417
    else if ( itr->second.min > profile_end )
4418
      itr->second.min = profile_end;
4419
  }
4420
  else
4421
  {
4422
    escript_profile_map_[name_] = {
4423
        .sum = profile_end, .max = profile_end, .min = profile_end, .count = 1 };
4424
  }
4425
}
4426
std::string EscriptProfiler::result()
4427
{
4428
  std::string buffer = "FuncName,Count,Min,Max,Sum,Avarage\n";
4429
  for ( const auto& [name, profile] : escript_profile_map_ )
4430
  {
4431
    fmt::format_to( std::back_inserter( buffer ), "{},{},{},{},{},{:.2f}\n", name, profile.count,
4432
                    profile.min, profile.max, profile.sum, profile.sum / ( 1.0 * profile.count ) );
4433
  }
4434
  return buffer;
4435
}
4436
#endif
4437
}  // 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