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

polserver / polserver / 16578783200

28 Jul 2025 07:48PM UTC coverage: 59.79% (+0.2%) from 59.58%
16578783200

push

github

web-flow
Short Circuit Optimizer (#797)

* first working short curcuit for && and or
creates jumps after each expressions to skip the following

* fixed valuestack when short circuit jmp does not jump

* use specialized instructions for short circuit && and ||

1: lhs
2: logical jump if false/true goto 4 <- if jmp do logical convert
3: rhs
4: logical convert
5: rest

logical convert is needed since "normal" && || operations convert isTrue
to BLong

added BObjectRef BObjectImp set specialization to remove noise

* fixed converted objimp when jump on false values

* first version of short circuit warning
should be moved to analyzer
added whitelist of module functions which have no sideeffect to reduce
the number of warnings

* moved warning visitor to analyzer and added it as extra compile step
fixed sourceline print and cache the content

* missing include, unused member

* included the correct header

* ecompile.cfg to activate and warn
ecompile cmdline arg to activate it
run all tests also with it active
fixed that only the most right side statement was checked

* ecompile cmdline

* increase ecompile version
cleanup

* compilation error

* missing header

* allow -S- to deactivate shortcircuit like the other params do

* extended whitelist functions

* revert fileformat version increase
fixed escript test cmake

* use the correct arg

* escript testoutput can now be different if shortcircuit is active

* docs

* additional test

* addressed comments

210 of 239 new or added lines in 16 files covered. (87.87%)

1 existing line in 1 file now uncovered.

43653 of 73010 relevant lines covered (59.79%)

466031.96 hits per line

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

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

15

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

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

44
#include <boost/multi_index/ordered_index.hpp>
45
#include <boost/multi_index/sequenced_index.hpp>
46
#include <boost/multi_index_container.hpp>
47

48
#include <cstdlib>
49
#include <cstring>
50
#include <exception>
51
#include <numeric>
52

53
#ifdef ESCRIPT_PROFILE
54
#ifdef _WIN32
55
#define WIN32_LEAN_AND_MEAN
56
#include <windows.h>
57
#else
58
#include <sys/time.h>
59
#endif
60
#endif
61

62
namespace Pol
63
{
64
namespace Bscript
65
{
66
std::set<Executor*> executor_instances;
67

68
#ifdef ESCRIPT_PROFILE
69
escript_profile_map EscriptProfileMap;
70
#endif
71

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

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

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

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

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

190
  return true;
296✔
191
}
192

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

216
  if ( !UninitObject::SharedInstance )
2,131✔
217
  {
218
    UninitObject::SharedInstance = new UninitObject;
1,854✔
219
    UninitObject::SharedInstanceOwner.set( UninitObject::SharedInstance );
1,854✔
220
  }
221
}
2,131✔
222

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

240
  delete Locals2;
2,411✔
241
  Locals2 = nullptr;
2,411✔
242

243
  while ( !upperLocals2.empty() )
2,411✔
244
  {
245
    delete upperLocals2.back();
×
246
    upperLocals2.pop_back();
×
247
  }
248

249
  execmodules.clear();
2,411✔
250
  Clib::delete_all( availmodules );
2,411✔
251
}
2,411✔
252

253
bool Executor::AttachFunctionalityModules()
2,220✔
254
{
255
  for ( auto& fm : prog_->modules )
5,352✔
256
  {
257
    // if no function in the module is actually called, don't go searching for it.
258
    if ( fm->functions.empty() )
3,132✔
259
    {
260
      execmodules.push_back( nullptr );
×
261
      continue;
×
262
    }
263

264
    ExecutorModule* em = findModule( fm->modulename );
3,132✔
265
    execmodules.push_back( em );
3,132✔
266
    if ( em == nullptr )
3,132✔
267
    {
268
      ERROR_PRINTLN( "WARNING: {}: Unable to find module {}", scriptname(), fm->modulename.get() );
×
269
      return false;
×
270
    }
271

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

299
int Executor::getParams( unsigned howMany )
19,193,369✔
300
{
301
  if ( howMany )
19,193,369✔
302
  {
303
    fparams.resize( howMany );
19,180,976✔
304
    for ( int i = howMany - 1; i >= 0; --i )
38,432,950✔
305
    {
306
      if ( ValueStack.empty() )
19,251,974✔
307
      {
308
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
309
        seterror( true );
×
310
        return -1;
×
311
      }
312
      fparams[i] = ValueStack.back();
19,251,974✔
313
      ValueStack.pop_back();
19,251,974✔
314
    }
315
  }
316
  expandParams();
19,193,369✔
317
  return 0;
19,193,369✔
318
}
319

320
void Executor::expandParams()
19,193,369✔
321
{
322
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
38,446,570✔
323
  {
324
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
19,253,201✔
325
    {
326
      // defer destruction
327
      BObjectRef obj( spread );
628✔
328

329
      // Remove the spread
330
      fparams.erase( fparams.begin() + i );
628✔
331

332
      BObjectRef refIter( new BObject( UninitObject::create() ) );
628✔
333

334
      auto pIter = std::unique_ptr<ContIterator>(
335
          spread->object->impptr()->createIterator( refIter.get() ) );
628✔
336

337
      BObject* next = pIter->step();
628✔
338

339
      int added = 0;
628✔
340
      while ( next != nullptr )
1,855✔
341
      {
342
        fparams.insert( fparams.begin() + i + added, BObjectRef( next ) );
1,227✔
343
        next = pIter->step();
1,227✔
344
        added++;
1,227✔
345
      }
346
      i += added;
628✔
347
    }
628✔
348
  }
349
}
19,193,369✔
350

351
void Executor::cleanParams()
19,190,773✔
352
{
353
  fparams.clear();
19,190,773✔
354
}
19,190,773✔
355

356
int Executor::makeString( unsigned param )
16,395✔
357
{
358
  BObject* obj = getParam( param );
16,395✔
359
  if ( !obj )
16,395✔
360
    return -1;
×
361
  if ( obj->isa( BObjectImp::OTString ) )
16,395✔
362
    return 0;
16,395✔
363

NEW
364
  fparams[param].set( new String( obj->impref() ) );
×
365

366
  return 0;
×
367
}
368

369
const char* Executor::paramAsString( unsigned param )
12,323✔
370
{
371
  makeString( param );
12,323✔
372
  BObjectImp* objimp = fparams[param]->impptr();
12,323✔
373

374
  String* str = (String*)objimp;
12,323✔
375
  return str ? str->data() : "";
12,323✔
376
}
377

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

390
  return 0;
×
391
}
392

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

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

417
  return fparams[param].get();
19,127,191✔
418
}
419

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

427
  return fparams[param].get()->impptr();
55,219✔
428
}
429

430
BObject* Executor::getParamObj( unsigned param )
923✔
431
{
432
  if ( fparams.size() > param )
923✔
433
  {
434
    return fparams[param].get();
923✔
435
  }
436
  else
437
  {
438
    return nullptr;
×
439
  }
440
}
441

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

449
  BObjectImp* imp = fparams[param].get()->impptr();
40,757✔
450

451
  passert( imp != nullptr );
40,757✔
452

453
  if ( imp->isa( type ) )
40,757✔
454
  {
455
    return imp;
40,724✔
456
  }
457
  else
458
  {
459
    if ( !IS_DEBUGLOG_DISABLED )
33✔
460
    {
461
      std::string tmp = fmt::format( "Script Error in '{}' PC={}:\n", scriptname(), PC );
33✔
462
      if ( current_module_function )
33✔
463
        fmt::format_to( std::back_inserter( tmp ), "\tCall to function {}:\n",
34✔
464
                        current_module_function->name.get() );
17✔
465
      else
466
        tmp += "\tCall to an object method.\n";
16✔
467
      fmt::format_to( std::back_inserter( tmp ),
33✔
468
                      "\tParameter {}: Expected datatype {}, got datatype {}", param,
469
                      BObjectImp::typestr( type ), BObjectImp::typestr( imp->type() ) );
33✔
470
      DEBUGLOGLN( tmp );
33✔
471
    }
33✔
472
    return nullptr;
33✔
473
  }
474
}
475

476
BObjectImp* Executor::getParamImp2( unsigned param, BObjectImp::BObjectType type )
26,213✔
477
{
478
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
26,213✔
479
                                         ": Less Parameter than expected. " +
480
                                         "You should use *.em-files shipped with this Core and "
481
                                         "recompile ALL of your Scripts _now_! RTFM" );
482

483
  BObjectImp* imp = fparams[param].get()->impptr();
26,213✔
484

485
  passert( imp != nullptr );
26,213✔
486

487
  if ( imp->isa( type ) )
26,213✔
488
  {
489
    return imp;
26,213✔
490
  }
491
  else
492
  {
493
    std::string report = "Invalid parameter type.  Expected param " + Clib::tostring( param ) +
×
494
                         " as " + BObjectImp::typestr( type ) + ", got " +
×
495
                         BObjectImp::typestr( imp->type() );
×
496
    func_result_ = new BError( report );
×
497
    return nullptr;
×
498
  }
×
499
}
500

501

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

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

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

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

525
  value = plong->value();
6,395✔
526
  return true;
6,395✔
527
}
528

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

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

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

544
  size_t i = 0;
×
545
  for ( auto riter = fparams.rbegin(); riter != fparams.rend(); ++riter )
×
546
  {
547
    auto* ptr = riter->get()->impptr();
×
548
    INFO_PRINTLN( "fparam[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
549
    i++;
×
550
  }
551

552
  i = 0;
×
553
  for ( auto riter = ValueStack.rbegin(); riter != ValueStack.rend(); ++riter )
×
554
  {
555
    auto* ptr = riter->get()->impptr();
×
556
    INFO_PRINTLN( "vstack[{} @ {}] {}", static_cast<void*>( ptr ), i, ptr->getStringRep() );
×
557
    i++;
×
558
  }
559

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

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

570
    value = plong->value();
8✔
571
    if ( value >= 0 && value <= maxval )
8✔
572
    {
573
      return true;
8✔
574
    }
575
    else
576
    {
577
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
578
                           Clib::tostring( value ) + " out of expected range of [0.." +
×
579
                           Clib::tostring( maxval ) + "]";
×
580
      func_result_ = new BError( report );
×
581
      return false;
×
582
    }
×
583
  }
584
  else
585
  {
586
    return false;
×
587
  }
588
}
589

590
bool Executor::getParam( unsigned param, int& value, int minval, int maxval )
482✔
591
{
592
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
482✔
593
  if ( imp )
482✔
594
  {
595
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
482✔
596

597
    value = plong->value();
482✔
598
    if ( value >= minval && value <= maxval )
482✔
599
    {
600
      return true;
467✔
601
    }
602
    else
603
    {
604
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
30✔
605
                           Clib::tostring( value ) + " out of expected range of [" +
60✔
606
                           Clib::tostring( minval ) + ".." + Clib::tostring( maxval ) + "]";
45✔
607
      func_result_ = new BError( report );
15✔
608
      return false;
15✔
609
    }
15✔
610
  }
611
  else
612
  {
613
    return false;
×
614
  }
615
}
616

617
bool Executor::getRealParam( unsigned param, double& value )
159✔
618
{
619
  BObjectImp* imp = getParamImp( param );
159✔
620
  if ( auto* d = impptrIf<Double>( imp ) )
159✔
621
  {
622
    value = d->value();
39✔
623
    return true;
39✔
624
  }
625
  else if ( auto* l = impptrIf<BLong>( imp ) )
120✔
626
  {
627
    value = l->value();
69✔
628
    return true;
69✔
629
  }
630
  else
631
  {
632
    DEBUGLOGLN(
51✔
633
        "Script Error in '{}' PC={}: \n"
634
        "\tCall to function {}:\n"
635
        "\tParameter {}: Expected Integer or Real, got datatype {}",
636
        scriptname(), PC, current_module_function->name.get(), param,
51✔
637
        BObjectImp::typestr( imp->type() ) );
102✔
638

639
    return false;
51✔
640
  }
641
}
642

643
bool Executor::getObjArrayParam( unsigned param, ObjArray*& pobjarr )
413✔
644
{
645
  pobjarr =
413✔
646
      Clib::explicit_cast<ObjArray*, BObjectImp*>( getParamImp( param, BObjectImp::OTArray ) );
413✔
647
  return ( pobjarr != nullptr );
413✔
648
}
649

650
void* Executor::getApplicPtrParam( unsigned param, const BApplicObjType* pointer_type )
×
651
{
652
  auto ap = static_cast<BApplicPtr*>( getParamImp( param, BObjectImp::OTApplicPtr ) );
×
653
  if ( ap == nullptr )
×
654
    return nullptr;
×
655

656
  if ( ap->pointer_type() == pointer_type )
×
657
  {
658
    return ap->ptr();
×
659
  }
660
  else
661
  {
662
    DEBUGLOGLN(
×
663
        "Script Error in '{}' PC={}: \n"
664
        "\tCall to function {}:\n"
665
        "\tParameter {}: Expected datatype, got datatype {}",
666
        scriptname(), PC, current_module_function->name.get(), param,
×
667
        BObjectImp::typestr( ap->type() ) );
×
668

669
    return nullptr;
×
670
  }
671
}
672

673
BApplicObjBase* Executor::getApplicObjParam( unsigned param, const BApplicObjType* object_type )
161✔
674
{
675
  auto aob = static_cast<BApplicObjBase*>( getParamImp( param, BObjectImp::OTApplicObj ) );
161✔
676
  if ( aob == nullptr )
161✔
677
    return nullptr;
×
678

679
  if ( aob->object_type() == object_type )
161✔
680
  {
681
    return aob;
161✔
682
  }
683
  else
684
  {
685
    DEBUGLOGLN(
×
686
        "Script Error in '{}' PC={}: \n"
687
        "\tCall to function {}:\n"
688
        "\tParameter {}: Expected datatype, got datatype {}",
689
        scriptname(), PC, current_module_function->name.get(), param, aob->getStringRep() );
×
690

691
    return nullptr;
×
692
  }
693
}
694

695
bool Executor::getParam( unsigned param, unsigned short& value, unsigned short maxval )
×
696
{
697
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
698
  if ( imp )
×
699
  {
700
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
701

702
    int longvalue = plong->value();
×
703
    if ( longvalue >= 0 && longvalue <= maxval )
×
704
    {
705
      value = static_cast<unsigned short>( longvalue );
×
706
      return true;
×
707
    }
708
    else
709
    {
710
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
711
                           Clib::tostring( longvalue ) + " out of expected range of [0.." +
×
712
                           Clib::tostring( maxval ) + "]";
×
713
      func_result_ = new BError( report );
×
714
      return false;
×
715
    }
×
716
  }
717
  else
718
  {
719
    return false;
×
720
  }
721
}
722

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

731
    int longvalue = plong->value();
6,116✔
732
    if ( longvalue >= minval && longvalue <= maxval )
6,116✔
733
    {
734
      value = static_cast<unsigned short>( longvalue );
6,116✔
735
      return true;
6,116✔
736
    }
737
    else
738
    {
739
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
740
                           Clib::tostring( longvalue ) + " out of expected range of [" +
×
741
                           Clib::tostring( minval ) + ".." + Clib::tostring( maxval ) + "]";
×
742
      func_result_ = new BError( report );
×
743
      return false;
×
744
    }
×
745
  }
746
  else
747
  {
748
    return false;
×
749
  }
750
}
751
bool Executor::getParam( unsigned param, unsigned short& value )
12,964✔
752
{
753
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
12,964✔
754
  if ( imp )
12,964✔
755
  {
756
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
12,964✔
757

758
    int longvalue = plong->value();
12,964✔
759
    if ( longvalue >= 0 && longvalue <= USHRT_MAX )
12,964✔
760
    {
761
      value = static_cast<unsigned short>( longvalue );
12,964✔
762
      return true;
12,964✔
763
    }
764
    else
765
    {
766
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
767
                           Clib::tostring( longvalue ) + " out of expected range of [0.." +
×
768
                           Clib::tostring( USHRT_MAX ) + "]";
×
769
      func_result_ = new BError( report );
×
770
      return false;
×
771
    }
×
772
  }
773
  else
774
  {
775
    return false;
×
776
  }
777
}
778
bool Executor::getParam( unsigned param, unsigned& value )
15✔
779
{
780
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
15✔
781
  if ( imp )
15✔
782
  {
783
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
15✔
784

785
    int longvalue = plong->value();
15✔
786
    if ( longvalue >= 0 )  // && longvalue <= (int)INT_MAX )
15✔
787
    {
788
      value = static_cast<unsigned>( longvalue );
15✔
789
      return true;
15✔
790
    }
791
    else
792
    {
793
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
794
                           Clib::tostring( longvalue ) + " out of expected range of [0.." +
×
795
                           Clib::tostring( INT_MAX ) + "]";
×
796
      func_result_ = new BError( report );
×
797
      return false;
×
798
    }
×
799
  }
800
  else
801
  {
802
    return false;
×
803
  }
804
}
805

806
bool Executor::getParam( unsigned param, short& value )
277✔
807
{
808
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
277✔
809
  if ( imp )
277✔
810
  {
811
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
277✔
812

813
    int longvalue = plong->value();
277✔
814
    if ( longvalue >= (int)SHRT_MIN && longvalue <= (int)SHRT_MAX )
277✔
815
    {
816
      value = static_cast<short>( longvalue );
277✔
817
      return true;
277✔
818
    }
819
    else
820
    {
821
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
822
                           Clib::tostring( longvalue ) + " out of expected range of [" +
×
823
                           Clib::tostring( SHRT_MIN ) + ".." + Clib::tostring( SHRT_MAX ) + "]";
×
824
      func_result_ = new BError( report );
×
825
      return false;
×
826
    }
×
827
  }
828
  else
829
  {
830
    return false;
×
831
  }
832
}
833

834
bool Executor::getParam( unsigned param, short& value, short maxval )
×
835
{
836
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
837
  if ( imp )
×
838
  {
839
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
840

841
    int longvalue = plong->value();
×
842
    if ( longvalue >= (int)SHRT_MIN && longvalue <= maxval )
×
843
    {
844
      value = static_cast<short>( longvalue );
×
845
      return true;
×
846
    }
847
    else
848
    {
849
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
850
                           Clib::tostring( longvalue ) + " out of expected range of [" +
×
851
                           Clib::tostring( SHRT_MIN ) + ".." + Clib::tostring( maxval ) + "]";
×
852
      func_result_ = new BError( report );
×
853
      return false;
×
854
    }
×
855
  }
856
  else
857
  {
858
    return false;
×
859
  }
860
}
861

862
bool Executor::getParam( unsigned param, short& value, short minval, short maxval )
×
863
{
864
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
×
865
  if ( imp )
×
866
  {
867
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
×
868

869
    int longvalue = plong->value();
×
870
    if ( longvalue >= minval && longvalue <= maxval )
×
871
    {
872
      value = static_cast<short>( longvalue );
×
873
      return true;
×
874
    }
875
    else
876
    {
877
      std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
878
                           Clib::tostring( longvalue ) + " out of expected range of [" +
×
879
                           Clib::tostring( minval ) + ".." + Clib::tostring( maxval ) + "]";
×
880
      func_result_ = new BError( report );
×
881
      return false;
×
882
    }
×
883
  }
884
  else
885
  {
886
    return false;
×
887
  }
888
}
889

890
bool Executor::getParam( unsigned param, signed char& value )
6,351✔
891
{
892
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,351✔
893
  if ( !imp )
6,351✔
894
    return false;
×
895

896
  BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
6,351✔
897

898
  int longvalue = plong->value();
6,351✔
899
  if ( longvalue >= std::numeric_limits<s8>::min() && longvalue <= std::numeric_limits<s8>::max() )
6,351✔
900
  {
901
    value = static_cast<signed char>( longvalue );
6,351✔
902
    return true;
6,351✔
903
  }
904
  else
905
  {
906
    std::string report = "Parameter " + Clib::tostring( param ) + " value " +
×
907
                         Clib::tostring( longvalue ) + " out of expected range of [" +
×
908
                         Clib::tostring( std::numeric_limits<s8>::max() ) + ".." +
×
909
                         Clib::tostring( std::numeric_limits<s8>::max() ) + "]";
×
910
    func_result_ = new BError( report );
×
911
    return false;
×
912
  }
×
913
}
914

915
bool Executor::getParam( unsigned param, bool& value )
15✔
916
{
917
  BObjectImp* imp = getParamImp( param );
15✔
918
  if ( auto* b = impptrIf<BBoolean>( imp ) )
15✔
919
  {
920
    value = b->value();
1✔
921
    return true;
1✔
922
  }
923
  else if ( auto* l = impptrIf<BLong>( imp ) )
14✔
924
  {
925
    value = l->isTrue();
14✔
926
    return true;
14✔
927
  }
928
  else
929
  {
930
    DEBUGLOGLN(
×
931
        "Script Error in '{}' PC={}: \n"
932
        "\tCall to function {}:\n"
933
        "\tParameter {}: Expected Boolean or Integer, got datatype {}",
934
        scriptname(), PC, current_module_function->name.get(), param,
×
935
        BObjectImp::typestr( imp->type() ) );
×
936

937
    return false;
×
938
  }
939
}
940

941
bool Executor::getUnicodeStringParam( unsigned param, const String*& pstr )
2✔
942
{
943
  BObject* obj = getParam( param );
2✔
944
  if ( !obj )
2✔
945
    return false;
×
946
  if ( auto* s = obj->impptr_if<String>() )
2✔
947
  {
948
    pstr = s;
2✔
949
    return true;
2✔
950
  }
951
  else if ( auto* a = obj->impptr_if<ObjArray>() )
×
952
  {
953
    String* str = String::fromUCArray( a );
×
NEW
954
    fparams[param].set( str );  // store raw pointer
×
955
    pstr = str;
×
956
    return true;
×
957
  }
958
  std::string report = "Invalid parameter type.  Expected param " + Clib::tostring( param ) +
×
959
                       " as " + BObjectImp::typestr( BObjectImp::OTString ) + " or " +
×
960
                       BObjectImp::typestr( BObjectImp::OTArray ) + ", got " +
×
961
                       BObjectImp::typestr( obj->impptr()->type() );
×
962
  func_result_ = new BError( report );
×
963
  return false;
×
964
}
×
965

966
BObjectRef& Executor::LocalVar( unsigned int varnum )
×
967
{
968
  passert( Locals2 );
×
969
  passert( varnum < Locals2->size() );
×
970

971
  return ( *Locals2 )[varnum];
×
972
}
973

974
BObjectRef& Executor::GlobalVar( unsigned int varnum )
×
975
{
976
  passert( varnum < Globals2->size() );
×
977
  return ( *Globals2 )[varnum];
×
978
}
979

980
int Executor::getToken( Token& token, unsigned position )
×
981
{
982
  if ( position >= nLines )
×
983
    return -1;
×
984
  token = prog_->instr[position].token;
×
985
  return 0;
×
986
}
987

988

989
bool Executor::setProgram( EScriptProgram* i_prog )
2,131✔
990
{
991
  prog_.set( i_prog );
2,131✔
992
  prog_ok_ = false;
2,131✔
993
  seterror( true );
2,131✔
994
  if ( !viewmode_ )
2,131✔
995
  {
996
    if ( !AttachFunctionalityModules() )
2,131✔
997
      return false;
×
998
  }
999

1000
  nLines = static_cast<unsigned int>( prog_->instr.size() );
2,131✔
1001

1002
  Globals2->clear();
2,131✔
1003
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
4,334✔
1004
  {
1005
    Globals2->push_back( BObjectRef() );
2,203✔
1006
    Globals2->back().set( UninitObject::create() );
2,203✔
1007
  }
1008

1009
  prog_ok_ = true;
2,131✔
1010
  seterror( false );
2,131✔
1011
  ++prog_->invocations;
2,131✔
1012
  return true;
2,131✔
1013
}
1014

1015
BObjectRef Executor::getObjRef( void )
28,174✔
1016
{
1017
  if ( ValueStack.empty() )
28,174✔
1018
  {
1019
    POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={})", prog_->name, PC );
×
1020
    seterror( true );
×
1021
    return BObjectRef( UninitObject::create() );
×
1022
  }
1023

1024
  BObjectRef ref = ValueStack.back();
28,174✔
1025
  ValueStack.pop_back();
28,174✔
1026
  return ref;
28,174✔
1027
}
28,174✔
1028

1029

1030
void Executor::execFunc( const Token& token )
19,154,637✔
1031
{
1032
  FunctionalityModule* fm = prog_->modules[token.module];
19,154,637✔
1033
  ModuleFunction* modfunc = fm->functions[token.lval];
19,154,637✔
1034
  current_module_function = modfunc;
19,154,637✔
1035
  if ( modfunc->funcidx == -1 )
19,154,637✔
1036
  {
1037
    DEBUGLOGLN(
×
1038
        "Error in script '{}':\n"
1039
        "\tModule Function {} was not found.",
1040
        prog_->name.get(), modfunc->name.get() );
×
1041

1042
    throw std::runtime_error( "No implementation for function found." );
×
1043
  }
1044

1045
  ExecutorModule* em = execmodules[token.module];
19,154,637✔
1046

1047
  func_result_ = nullptr;
19,154,637✔
1048
#ifdef ESCRIPT_PROFILE
1049
  std::stringstream strm;
1050
  strm << em->functionName( modfunc->funcidx );
1051
  if ( !fparams.empty() )
1052
    strm << " [" << fparams[0].get()->impptr()->typeOf() << "]";
1053
  std::string name( strm.str() );
1054
  unsigned long profile_start = GetTimeUs();
1055
#endif
1056
  BObjectImp* resimp = em->execFunc( modfunc->funcidx );
19,154,637✔
1057
#ifdef ESCRIPT_PROFILE
1058
  profile_escript( name, profile_start );
1059
#endif
1060

1061
  if ( func_result_ )
19,154,637✔
1062
  {
1063
    if ( resimp )
8✔
1064
    {
1065
      BObject obj( resimp );
8✔
1066
    }
8✔
1067
    ValueStack.push_back( BObjectRef( new BObject( func_result_ ) ) );
8✔
1068
    func_result_ = nullptr;
8✔
1069
  }
1070
  else if ( resimp )
19,154,629✔
1071
  {
1072
    ValueStack.push_back( BObjectRef( new BObject( resimp ) ) );
19,154,628✔
1073
  }
1074
  else
1075
  {
1076
    ValueStack.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
1✔
1077
  }
1078

1079
  current_module_function = nullptr;
19,154,637✔
1080
  return;
19,154,637✔
1081
}
1082

1083
// RSV_LOCAL
1084
void Executor::ins_makeLocal( const Instruction& /*ins*/ )
17,712✔
1085
{
1086
  passert( Locals2 != nullptr );
17,712✔
1087

1088
  Locals2->push_back( BObjectRef() );
17,712✔
1089
  Locals2->back().set( UninitObject::create() );
17,712✔
1090

1091
  ValueStack.push_back( BObjectRef( Locals2->back().get() ) );
17,712✔
1092
}
17,712✔
1093

1094
// RSV_DECLARE_ARRAY
1095
void Executor::ins_declareArray( const Instruction& /*ins*/ )
69✔
1096
{
1097
  BObjectRef objref = getObjRef();
69✔
1098

1099
  if ( !objref->isa( BObjectImp::OTUninit ) )
69✔
1100
  {
1101
    // FIXME: weak error message
1102
    ERROR_PRINTLN( "variable is already initialized.." );
×
1103
    seterror( true );
×
1104
    return;
×
1105
  }
1106
  auto arr = new ObjArray;
69✔
1107

1108
  objref->setimp( arr );
69✔
1109

1110
  ValueStack.push_back( BObjectRef( objref ) );
69✔
1111
}
69✔
1112

1113
void Executor::popParam( const Token& /*token*/ )
20,411✔
1114
{
1115
  BObjectRef objref = getObjRef();
20,411✔
1116

1117
  Locals2->push_back( BObjectRef() );
20,411✔
1118
  Locals2->back().set( objref->impptr()->copy() );
20,411✔
1119
}
20,411✔
1120

1121
void Executor::popParamByRef( const Token& /*token*/ )
2,670✔
1122
{
1123
  BObjectRef objref = getObjRef();
2,670✔
1124

1125
  Locals2->push_back( BObjectRef( objref ) );
2,670✔
1126
}
2,670✔
1127

1128
void Executor::getArg( const Token& /*token*/ )
433✔
1129
{
1130
  if ( ValueStack.empty() )
433✔
1131
  {
1132
    Locals2->push_back( BObjectRef() );
47✔
1133
    Locals2->back().set( UninitObject::create() );
47✔
1134
  }
1135
  else
1136
  {
1137
    BObjectRef objref = getObjRef();
386✔
1138
    Locals2->push_back( BObjectRef() );
386✔
1139
    Locals2->back().set( objref->impptr()->copy() );
386✔
1140
  }
386✔
1141
}
433✔
1142

1143

1144
BObjectRef Executor::addmember( BObject& left, const BObject& right )
168✔
1145
{
1146
  if ( !right.isa( BObjectImp::OTString ) )
168✔
1147
  {
1148
    return BObjectRef( left.clone() );
×
1149
  }
1150

1151
  const String& varname = right.impref<const String>();
168✔
1152

1153
  return left.impref().operDotPlus( varname.data() );
168✔
1154
}
1155

1156
BObjectRef Executor::removemember( BObject& left, const BObject& right )
78✔
1157
{
1158
  if ( !right.isa( BObjectImp::OTString ) )
78✔
1159
  {
1160
    return BObjectRef( left.clone() );
×
1161
  }
1162

1163
  const String& varname = right.impref<const String>();
78✔
1164

1165
  return left.impref().operDotMinus( varname.data() );
78✔
1166
}
1167

1168
BObjectRef Executor::checkmember( BObject& left, const BObject& right )
1,518✔
1169
{
1170
  if ( !right.isa( BObjectImp::OTString ) )
1,518✔
1171
  {
1172
    return BObjectRef( left.clone() );
×
1173
  }
1174

1175
  const String& varname = right.impref<const String>();
1,518✔
1176

1177
  return left.impref().operDotQMark( varname.data() );
1,518✔
1178
}
1179

1180

1181
ContIterator::ContIterator() : BObjectImp( BObjectImp::OTUnknown ) {}
2,931✔
1182
BObject* ContIterator::step()
18✔
1183
{
1184
  return nullptr;
18✔
1185
}
1186
BObjectImp* ContIterator::copy( void ) const
×
1187
{
1188
  return nullptr;
×
1189
}
1190
size_t ContIterator::sizeEstimate() const
12✔
1191
{
1192
  return sizeof( ContIterator );
12✔
1193
}
1194
std::string ContIterator::getStringRep() const
×
1195
{
1196
  return "<iterator>";
×
1197
}
1198

1199
class ArrayIterator final : public ContIterator
1200
{
1201
public:
1202
  ArrayIterator( ObjArray* pArr, BObject* pIterVal );
1203
  virtual BObject* step() override;
1204

1205
private:
1206
  size_t m_Index;
1207
  BObject m_Array;
1208
  ObjArray* m_pArray;
1209
  BObjectRef m_IterVal;
1210
  BLong* m_pIterVal;
1211
};
1212
ArrayIterator::ArrayIterator( ObjArray* pArr, BObject* pIterVal )
2,717✔
1213
    : ContIterator(),
1214
      m_Index( 0 ),
2,717✔
1215
      m_Array( pArr ),
2,717✔
1216
      m_pArray( pArr ),
2,717✔
1217
      m_IterVal( pIterVal ),
2,717✔
1218
      m_pIterVal( new BLong( 0 ) )
5,434✔
1219
{
1220
  m_IterVal.get()->setimp( m_pIterVal );
2,717✔
1221
}
2,717✔
1222
BObject* ArrayIterator::step()
25,883✔
1223
{
1224
  m_pIterVal->increment();
25,883✔
1225
  if ( ++m_Index > m_pArray->ref_arr.size() )
25,883✔
1226
    return nullptr;
2,619✔
1227

1228
  BObjectRef& objref = m_pArray->ref_arr[m_Index - 1];
23,264✔
1229
  BObject* elem = objref.get();
23,264✔
1230
  if ( elem == nullptr )
23,264✔
1231
  {
1232
    elem = new BObject( UninitObject::create() );
18✔
1233
    objref.set( elem );
18✔
1234
  }
1235
  return elem;
23,264✔
1236
}
1237

1238
ContIterator* BObjectImp::createIterator( BObject* /*pIterVal*/ )
24✔
1239
{
1240
  return new ContIterator();
24✔
1241
}
1242
ContIterator* ObjArray::createIterator( BObject* pIterVal )
2,717✔
1243
{
1244
  auto pItr = new ArrayIterator( this, pIterVal );
2,717✔
1245
  return pItr;
2,717✔
1246
}
1247

1248
/* Coming into initforeach, the expr to be iterated through is on the value stack.
1249
   Initforeach must create three local variables:
1250
   0. the iterator
1251
   1. the expression
1252
   2. the counter
1253
   and remove the expression from the value stack.
1254
   It then jumps to the STEPFOREACH instruction.
1255
   */
1256
void Executor::ins_initforeach( const Instruction& ins )
664✔
1257
{
1258
  Locals2->push_back( BObjectRef( UninitObject::create() ) );  // the iterator
664✔
1259

1260
  auto pIterVal = new BObject( UninitObject::create() );
664✔
1261

1262
  // this is almost like popParam, only we don't want a copy.
1263
  BObjectRef objref = getObjRef();
664✔
1264
  Locals2->push_back( BObjectRef() );
664✔
1265
  ContIterator* pIter = objref->impptr()->createIterator( pIterVal );
664✔
1266
  Locals2->back().set( pIter );
664✔
1267

1268
  Locals2->push_back( BObjectRef() );
664✔
1269
  Locals2->back().set( pIterVal );
664✔
1270

1271
  // Jump to to the corresponding `stepforeach` instruction, advancing the iterator.
1272
  PC = ins.token.lval;
664✔
1273
}
664✔
1274

1275
void Executor::ins_stepforeach( const Instruction& ins )
19,788✔
1276
{
1277
  size_t locsize = Locals2->size();
19,788✔
1278
  ContIterator* pIter = ( *Locals2 )[locsize - 2]->impptr<ContIterator>();
19,788✔
1279

1280
  BObject* next = pIter->step();
19,788✔
1281
  // If iterator has a value, set it on the locals stack and jump to the
1282
  // corresponding instruction after `initforeach`.
1283
  if ( next != nullptr )
19,788✔
1284
  {
1285
    ( *Locals2 )[locsize - 3].set( next );
19,196✔
1286
    PC = ins.token.lval;
19,196✔
1287
  }
1288
}
19,788✔
1289

1290
/*
1291
    Coming into the INITFOR, there will be two values on the value stack:
1292
    START VALUE
1293
    END VALUE
1294

1295
    If START VALUE > END VALUE, we skip the whole for loop.
1296
    (the INITFOR's lval has the instr to jump to)
1297
    */
1298

1299
void Executor::ins_initfor( const Instruction& ins )
59✔
1300
{
1301
  BObjectRef endref = getObjRef();
59✔
1302
  BObjectRef startref = getObjRef();
59✔
1303
  if ( *startref.get() > *endref.get() )
59✔
1304
  {
1305
    PC = ins.token.lval;
×
1306
    return;
×
1307
  }
1308

1309
  Locals2->push_back( BObjectRef( startref->clone() ) );  // the iterator
59✔
1310
  Locals2->push_back( BObjectRef( endref->clone() ) );
59✔
1311
}
59✔
1312

1313
void Executor::ins_nextfor( const Instruction& ins )
6,225✔
1314
{
1315
  size_t locsize = Locals2->size();
6,225✔
1316
  BObjectImp* itr = ( *Locals2 )[locsize - 2]->impptr();
6,225✔
1317
  BObjectImp* end = ( *Locals2 )[locsize - 1]->impptr();
6,225✔
1318

1319
  if ( auto* l = impptrIf<BLong>( itr ) )
6,225✔
1320
    l->increment();
6,225✔
1321
  else if ( auto* d = impptrIf<Double>( itr ) )
×
1322
    d->increment();
×
1323

1324
  if ( *end >= *itr )
6,225✔
1325
  {
1326
    PC = ins.token.lval;
6,176✔
1327
  }
1328
}
6,225✔
1329

1330

1331
int Executor::ins_casejmp_findlong( const Token& token, BLong* blong )
139✔
1332
{
1333
  const unsigned char* dataptr = token.dataptr;
139✔
1334
  for ( ;; )
1335
  {
1336
    unsigned short offset;
1337
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
311✔
1338
    dataptr += 2;
311✔
1339
    unsigned char type = *dataptr;
311✔
1340
    dataptr += 1;
311✔
1341
    if ( type == CASE_TYPE_LONG )
311✔
1342
    {
1343
      int v = blong->value();
230✔
1344
      if ( std::memcmp( &v, dataptr, sizeof( int ) ) == 0 )
230✔
1345
        return offset;
106✔
1346
      dataptr += 4;
124✔
1347
    }
1348
    else if ( type == CASE_TYPE_DEFAULT )
81✔
1349
    {
1350
      return offset;
33✔
1351
    }
1352
    else if ( type == CASE_TYPE_UNINIT )
48✔
1353
    {
1354
      /* nothing */
1355
    }
1356
    else if ( type == CASE_TYPE_BOOL )
48✔
1357
    {
1358
      dataptr += 1;
×
1359
    }
1360
    else if ( type == CASE_TYPE_STRING )
48✔
1361
    {
1362
      unsigned char len = *dataptr;
48✔
1363
      dataptr += 1 + len;
48✔
1364
    }
1365
  }
172✔
1366
}
1367

1368
int Executor::ins_casejmp_findbool( const Token& token, BBoolean* bbool )
15✔
1369
{
1370
  const unsigned char* dataptr = token.dataptr;
15✔
1371
  for ( ;; )
1372
  {
1373
    unsigned short offset;
1374
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
45✔
1375
    dataptr += 2;
45✔
1376
    unsigned char type = *dataptr;
45✔
1377
    dataptr += 1;
45✔
1378
    if ( type == CASE_TYPE_LONG )
45✔
1379
    {
1380
      dataptr += 4;
24✔
1381
    }
1382
    else if ( type == CASE_TYPE_DEFAULT )
21✔
1383
    {
1384
      return offset;
15✔
1385
    }
1386
    else if ( type == CASE_TYPE_UNINIT )
21✔
1387
    {
1388
      /* nothing */
1389
    }
1390
    else if ( type == CASE_TYPE_BOOL )
21✔
1391
    {
1392
      bool value = static_cast<bool>( *dataptr );
21✔
1393
      dataptr += 1;
21✔
1394
      if ( value == bbool->value() )
21✔
1395
      {
1396
        return offset;
15✔
1397
      }
1398
    }
1399
    else if ( type == CASE_TYPE_STRING )
×
1400
    {
1401
      unsigned char len = *dataptr;
×
1402
      dataptr += 1 + len;
×
1403
    }
1404
  }
30✔
1405
}
1406

1407
int Executor::ins_casejmp_finduninit( const Token& token )
6✔
1408
{
1409
  const unsigned char* dataptr = token.dataptr;
6✔
1410
  for ( ;; )
1411
  {
1412
    unsigned short offset;
1413
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
30✔
1414
    dataptr += 2;
30✔
1415
    unsigned char type = *dataptr;
30✔
1416
    dataptr += 1;
30✔
1417
    if ( type == CASE_TYPE_LONG )
30✔
1418
    {
1419
      dataptr += 4;
12✔
1420
    }
1421
    else if ( type == CASE_TYPE_DEFAULT )
18✔
1422
    {
1423
      return offset;
6✔
1424
    }
1425
    else if ( type == CASE_TYPE_UNINIT )
18✔
1426
    {
1427
      return offset;
6✔
1428
    }
1429
    else if ( type == CASE_TYPE_BOOL )
12✔
1430
    {
1431
      dataptr += 1;
12✔
1432
    }
1433
    else if ( type == CASE_TYPE_STRING )
×
1434
    {
1435
      unsigned char len = *dataptr;
×
1436
      dataptr += 1 + len;
×
1437
    }
1438
  }
24✔
1439
}
1440

1441
int Executor::ins_casejmp_findstring( const Token& token, String* bstringimp )
141✔
1442
{
1443
  const std::string& bstring = bstringimp->value();
141✔
1444
  const unsigned char* dataptr = token.dataptr;
141✔
1445
  for ( ;; )
1446
  {
1447
    unsigned short offset;
1448
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
333✔
1449
    dataptr += 2;
333✔
1450
    unsigned char type = *dataptr;
333✔
1451
    dataptr += 1;
333✔
1452
    if ( type == CASE_TYPE_LONG )
333✔
1453
    {
1454
      dataptr += 4;
36✔
1455
    }
1456
    else if ( type == CASE_TYPE_DEFAULT )
297✔
1457
    {
1458
      return offset;
141✔
1459
    }
1460
    else if ( type == CASE_TYPE_BOOL )
275✔
1461
    {
1462
      dataptr += 1;
12✔
1463
    }
1464
    else if ( type == CASE_TYPE_UNINIT )
263✔
1465
    {
1466
      /* nothing */
1467
    }
1468
    else if ( type == CASE_TYPE_STRING )
257✔
1469
    {
1470
      unsigned char len = *dataptr;
257✔
1471
      dataptr += 1;
257✔
1472
      if ( bstring.size() == len && memcmp( bstring.data(), dataptr, len ) == 0 )
257✔
1473
      {
1474
        return offset;
119✔
1475
      }
1476
      dataptr += len;
138✔
1477
    }
1478
  }
192✔
1479
}
1480

1481
int Executor::ins_casejmp_finddefault( const Token& token )
10✔
1482
{
1483
  const unsigned char* dataptr = token.dataptr;
10✔
1484
  for ( ;; )
1485
  {
1486
    unsigned short offset;
1487
    std::memcpy( &offset, dataptr, sizeof( unsigned short ) );
50✔
1488
    dataptr += 2;
50✔
1489
    unsigned char type = *dataptr;
50✔
1490
    dataptr += 1;
50✔
1491
    if ( type == CASE_TYPE_LONG )
50✔
1492
    {
1493
      dataptr += 4;
16✔
1494
    }
1495
    else if ( type == CASE_TYPE_DEFAULT )
34✔
1496
    {
1497
      return offset;
10✔
1498
    }
1499
    else if ( type == CASE_TYPE_UNINIT )
24✔
1500
    {
1501
      /* nothing */
1502
    }
1503
    else if ( type == CASE_TYPE_BOOL )
18✔
1504
    {
1505
      dataptr += 1;
12✔
1506
    }
1507
    else if ( type == CASE_TYPE_STRING )
6✔
1508
    {
1509
      unsigned char len = *dataptr;
6✔
1510
      dataptr += 1 + len;
6✔
1511
    }
1512
  }
40✔
1513
}
1514

1515
void Executor::ins_casejmp( const Instruction& ins )
311✔
1516
{
1517
  BObjectRef& objref = ValueStack.back();
311✔
1518
  BObjectImp* objimp = objref->impptr();
311✔
1519
  if ( auto* l = impptrIf<BLong>( objimp ) )
311✔
1520
    PC = ins_casejmp_findlong( ins.token, l );
139✔
1521
  else if ( auto* s = impptrIf<String>( objimp ) )
172✔
1522
    PC = ins_casejmp_findstring( ins.token, s );
141✔
1523
  else if ( auto* b = impptrIf<BBoolean>( objimp ) )
31✔
1524
    PC = ins_casejmp_findbool( ins.token, b );
15✔
1525
  else if ( impptrIf<UninitObject>( objimp ) )
16✔
1526
    PC = ins_casejmp_finduninit( ins.token );
6✔
1527
  else
1528
    PC = ins_casejmp_finddefault( ins.token );
10✔
1529
  ValueStack.pop_back();
311✔
1530
}
311✔
1531

1532

1533
void Executor::ins_jmpiftrue( const Instruction& ins )
2,880✔
1534
{
1535
  BObjectRef& objref = ValueStack.back();
2,880✔
1536

1537
  if ( objref->impptr()->isTrue() )
2,880✔
1538
    PC = (unsigned)ins.token.lval;
2,768✔
1539

1540
  ValueStack.pop_back();
2,880✔
1541
}
2,880✔
1542

1543
void Executor::ins_jmpiffalse( const Instruction& ins )
34,349✔
1544
{
1545
  BObjectRef& objref = ValueStack.back();
34,349✔
1546

1547
  if ( !objref->impptr()->isTrue() )
34,349✔
1548
    PC = (unsigned)ins.token.lval;
24,980✔
1549

1550
  ValueStack.pop_back();
34,349✔
1551
}
34,349✔
1552

1553
void Executor::ins_interpolate_string( const Instruction& ins )
14,753✔
1554
{
1555
  auto count = ins.token.lval;
14,753✔
1556
  if ( count == 0 )
14,753✔
1557
  {
1558
    ValueStack.push_back( BObjectRef( new BObject( new String( "" ) ) ) );
3✔
1559
  }
1560
  else
1561
  {
1562
    size_t length = 0;
14,750✔
1563

1564
    std::vector<std::string> contents;
14,750✔
1565
    contents.reserve( count );
14,750✔
1566

1567
    while ( count-- )
50,139✔
1568
    {
1569
      BObjectRef rightref = ValueStack.back();
35,389✔
1570
      ValueStack.pop_back();
35,389✔
1571
      auto str = rightref->impptr()->getStringRep();
35,389✔
1572
      length += str.length();
35,389✔
1573
      contents.push_back( std::move( str ) );
35,389✔
1574
    }
35,389✔
1575

1576
    std::string joined;
14,750✔
1577
    joined.reserve( length );
14,750✔
1578

1579
    while ( !contents.empty() )
50,139✔
1580
    {
1581
      joined += contents.back();
35,389✔
1582
      contents.pop_back();
35,389✔
1583
    }
1584

1585
    ValueStack.push_back( BObjectRef( new BObject( new String( joined ) ) ) );
14,750✔
1586
  }
14,750✔
1587
}
14,753✔
1588

1589
void Executor::ins_format_expression( const Instruction& )
143✔
1590
{
1591
  BObjectRef formatref = ValueStack.back();
143✔
1592
  ValueStack.pop_back();
143✔
1593
  BObjectRef& exprref = ValueStack.back();
143✔
1594
  BObject& expr = *exprref;
143✔
1595

1596
  auto format = formatref->impptr()->getFormattedStringRep();
143✔
1597
  auto formatted = Bscript::get_formatted( expr.impptr(), format );
143✔
1598

1599
  exprref.set( new String( formatted ) );
143✔
1600
}
143✔
1601

1602
void Executor::ins_skipiftrue_else_consume( const Instruction& ins )
1,629✔
1603
{
1604
  // This is for short-circuit evaluation of the elvis operator [expr_a] ?: [expr_b]
1605
  //
1606
  // Program instructions look like this:
1607
  //   [expr_a instructions] INS_SKIPIFTRUE_ELSE_CONSUME [expr_b instructions]
1608
  //
1609
  // The result value of expr_a is on the top of the value stack when this instruction executes.
1610
  //
1611
  // If [expr_a] evaluated to true, leave its result and skip over the expr_b instructions
1612
  // otherwise, consume the false value and continue so that expr_b can replace it.
1613
  //
1614
  BObjectRef& objref = ValueStack.back();
1,629✔
1615

1616
  if ( objref->impptr()->isTrue() )
1,629✔
1617
  {
1618
    PC = PC + (unsigned)( ins.token.lval );
787✔
1619
  }
1620
  else
1621
  {
1622
    ValueStack.pop_back();
842✔
1623
  }
1624
}
1,629✔
1625

1626

1627
// case TOK_LOCALVAR:
1628
void Executor::ins_localvar( const Instruction& ins )
153,015✔
1629
{
1630
  ValueStack.push_back( ( *Locals2 )[ins.token.lval] );
153,015✔
1631
}
153,015✔
1632

1633
// case RSV_GLOBAL:
1634
// case TOK_GLOBALVAR:
1635
void Executor::ins_globalvar( const Instruction& ins )
35,446✔
1636
{
1637
  ValueStack.push_back( ( *Globals2 )[ins.token.lval] );
35,446✔
1638
}
35,446✔
1639

1640
// case TOK_LONG:
1641
void Executor::ins_long( const Instruction& ins )
19,192,931✔
1642
{
1643
  ValueStack.push_back( BObjectRef( new BObject( new BLong( ins.token.lval ) ) ) );
19,192,931✔
1644
}
19,192,931✔
1645

1646
// case TOK_BOOL:
1647
void Executor::ins_bool( const Instruction& ins )
290✔
1648
{
1649
  ValueStack.push_back( BObjectRef( new BObject( new BBoolean( ins.token.lval ) ) ) );
290✔
1650
}
290✔
1651

1652
// case TOK_CONSUMER:
1653
void Executor::ins_consume( const Instruction& /*ins*/ )
19,186,359✔
1654
{
1655
  ValueStack.pop_back();
19,186,359✔
1656
}
19,186,359✔
1657

1658
void Executor::ins_set_member( const Instruction& ins )
3✔
1659
{
1660
  BObjectRef rightref = ValueStack.back();
3✔
1661
  ValueStack.pop_back();
3✔
1662
  BObjectRef& leftref = ValueStack.back();
3✔
1663

1664
  BObject& right = *rightref;
3✔
1665
  BObject& left = *leftref;
3✔
1666

1667
  BObjectImp& rightimpref = right.impref();
3✔
1668
  left.impref().set_member( ins.token.tokval(), &rightimpref,
6✔
1669
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
3✔
1670
}
3✔
1671

1672
void Executor::ins_set_member_id( const Instruction& ins )
3✔
1673
{
1674
  BObjectRef rightref = ValueStack.back();
3✔
1675
  ValueStack.pop_back();
3✔
1676
  BObjectRef& leftref = ValueStack.back();
3✔
1677

1678
  BObject& right = *rightref;
3✔
1679
  BObject& left = *leftref;
3✔
1680

1681
  BObjectImp& rightimpref = right.impref();
3✔
1682
  left.impref().set_member_id( ins.token.lval, &rightimpref,
6✔
1683
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
3✔
1684
}
3✔
1685

1686
void Executor::ins_set_member_consume( const Instruction& ins )
438✔
1687
{
1688
  BObjectRef rightref = ValueStack.back();
438✔
1689
  ValueStack.pop_back();
438✔
1690
  BObjectRef& leftref = ValueStack.back();
438✔
1691

1692
  BObject& right = *rightref;
438✔
1693
  BObject& left = *leftref;
438✔
1694

1695
  BObjectImp& rightimpref = right.impref();
438✔
1696
  left.impref().set_member( ins.token.tokval(), &rightimpref,
876✔
1697
                            !( right.count() == 1 && rightimpref.count() == 1 ) );
438✔
1698
  ValueStack.pop_back();
438✔
1699
}
438✔
1700

1701
void Executor::ins_set_member_id_consume( const Instruction& ins )
216✔
1702
{
1703
  BObjectRef rightref = ValueStack.back();
216✔
1704
  ValueStack.pop_back();
216✔
1705
  BObjectRef& leftref = ValueStack.back();
216✔
1706

1707
  BObject& right = *rightref;
216✔
1708
  BObject& left = *leftref;
216✔
1709

1710
  BObjectImp& rightimpref = right.impref();
216✔
1711

1712
  left.impref().set_member_id( ins.token.lval, &rightimpref,
432✔
1713
                               !( right.count() == 1 && rightimpref.count() == 1 ) );
216✔
1714
  ValueStack.pop_back();
216✔
1715
}
216✔
1716

1717
void Executor::ins_set_member_id_consume_plusequal( const Instruction& ins )
15✔
1718
{
1719
  BObjectRef rightref = ValueStack.back();
15✔
1720
  ValueStack.pop_back();
15✔
1721
  BObjectRef& leftref = ValueStack.back();
15✔
1722

1723
  BObject& right = *rightref;
15✔
1724
  BObject& left = *leftref;
15✔
1725

1726
  BObjectImp& leftimpref = left.impref();
15✔
1727

1728
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
15✔
1729

1730
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
21✔
1731
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1732
  {
1733
    tmp->impref().operPlusEqual( *tmp, right.impref() );
6✔
1734
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1735
  }
1736
  ValueStack.pop_back();
15✔
1737
}
15✔
1738

1739
void Executor::ins_set_member_id_consume_minusequal( const Instruction& ins )
6✔
1740
{
1741
  BObjectRef rightref = ValueStack.back();
6✔
1742
  ValueStack.pop_back();
6✔
1743
  BObjectRef& leftref = ValueStack.back();
6✔
1744

1745
  BObject& right = *rightref;
6✔
1746
  BObject& left = *leftref;
6✔
1747

1748
  BObjectImp& leftimpref = left.impref();
6✔
1749

1750
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1751

1752
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1753
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1754
  {
1755
    tmp->impref().operMinusEqual( *tmp, right.impref() );
6✔
1756
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1757
  }
1758
  ValueStack.pop_back();
6✔
1759
}
6✔
1760

1761
void Executor::ins_set_member_id_consume_timesequal( const Instruction& ins )
6✔
1762
{
1763
  BObjectRef rightref = ValueStack.back();
6✔
1764
  ValueStack.pop_back();
6✔
1765
  BObjectRef& leftref = ValueStack.back();
6✔
1766

1767
  BObject& right = *rightref;
6✔
1768
  BObject& left = *leftref;
6✔
1769

1770
  BObjectImp& leftimpref = left.impref();
6✔
1771

1772
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1773

1774
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1775
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1776
  {
1777
    tmp->impref().operTimesEqual( *tmp, right.impref() );
6✔
1778
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1779
  }
1780
  ValueStack.pop_back();
6✔
1781
}
6✔
1782

1783
void Executor::ins_set_member_id_consume_divideequal( const Instruction& ins )
6✔
1784
{
1785
  BObjectRef rightref = ValueStack.back();
6✔
1786
  ValueStack.pop_back();
6✔
1787
  BObjectRef& leftref = ValueStack.back();
6✔
1788

1789
  BObject& right = *rightref;
6✔
1790
  BObject& left = *leftref;
6✔
1791

1792
  BObjectImp& leftimpref = left.impref();
6✔
1793

1794
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1795

1796
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1797
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1798
  {
1799
    tmp->impref().operDivideEqual( *tmp, right.impref() );
6✔
1800
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1801
  }
1802
  ValueStack.pop_back();
6✔
1803
}
6✔
1804

1805
void Executor::ins_set_member_id_consume_modulusequal( const Instruction& ins )
6✔
1806
{
1807
  BObjectRef rightref = ValueStack.back();
6✔
1808
  ValueStack.pop_back();
6✔
1809
  BObjectRef& leftref = ValueStack.back();
6✔
1810

1811
  BObject& right = *rightref;
6✔
1812
  BObject& left = *leftref;
6✔
1813

1814
  BObjectImp& leftimpref = left.impref();
6✔
1815

1816
  BObjectRef tmp = leftimpref.get_member_id( ins.token.lval );
6✔
1817

1818
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
12✔
1819
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
6✔
1820
  {
1821
    tmp->impref().operModulusEqual( *tmp, right.impref() );
6✔
1822
    leftimpref.set_member_id( ins.token.lval, &tmp->impref(), false );
6✔
1823
  }
1824
  ValueStack.pop_back();
6✔
1825
}
6✔
1826

1827
void Executor::ins_get_member( const Instruction& ins )
3,999✔
1828
{
1829
  BObjectRef& leftref = ValueStack.back();
3,999✔
1830

1831
  BObject& left = *leftref;
3,999✔
1832

1833
#ifdef ESCRIPT_PROFILE
1834
  std::stringstream strm;
1835
  strm << "MBR_" << leftref->impptr()->typeOf() << " ." << ins.token.tokval();
1836
  if ( !fparams.empty() )
1837
    strm << " [" << fparams[0].get()->impptr()->typeOf() << "]";
1838
  std::string name( strm.str() );
1839
  unsigned long profile_start = GetTimeUs();
1840
#endif
1841
  leftref = left->get_member( ins.token.tokval() );
3,999✔
1842
#ifdef ESCRIPT_PROFILE
1843
  profile_escript( name, profile_start );
1844
#endif
1845
}
3,999✔
1846

1847
void Executor::ins_get_member_id( const Instruction& ins )
15,152✔
1848
{
1849
  BObjectRef& leftref = ValueStack.back();
15,152✔
1850

1851
  BObject& left = *leftref;
15,152✔
1852

1853
#ifdef ESCRIPT_PROFILE
1854
  std::stringstream strm;
1855
  strm << "MBR_" << leftref->impptr()->typeOf() << " ." << ins.token.lval;
1856
  if ( !fparams.empty() )
1857
    strm << " [" << fparams[0].get()->impptr()->typeOf() << "]";
1858
  std::string name( strm.str() );
1859
  unsigned long profile_start = GetTimeUs();
1860
#endif
1861
  leftref = left->get_member_id( ins.token.lval );
15,152✔
1862
#ifdef ESCRIPT_PROFILE
1863
  profile_escript( name, profile_start );
1864
#endif
1865
}
15,152✔
1866

1867
void Executor::ins_assign_localvar( const Instruction& ins )
6,175✔
1868
{
1869
  BObjectRef& lvar = ( *Locals2 )[ins.token.lval];
6,175✔
1870

1871
  BObjectRef& rightref = ValueStack.back();
6,175✔
1872

1873
  BObject& right = *rightref;
6,175✔
1874

1875
  BObjectImp& rightimpref = right.impref();
6,175✔
1876

1877
  if ( right.count() == 1 && rightimpref.count() == 1 )
6,175✔
1878
  {
1879
    lvar->setimp( &rightimpref );
5,426✔
1880
  }
1881
  else
1882
  {
1883
    lvar->setimp( rightimpref.copy() );
749✔
1884
  }
1885
  ValueStack.pop_back();
6,175✔
1886
}
6,175✔
1887
void Executor::ins_assign_globalvar( const Instruction& ins )
1,084✔
1888
{
1889
  BObjectRef& gvar = ( *Globals2 )[ins.token.lval];
1,084✔
1890

1891
  BObjectRef& rightref = ValueStack.back();
1,084✔
1892

1893
  BObject& right = *rightref;
1,084✔
1894

1895
  BObjectImp& rightimpref = right.impref();
1,084✔
1896

1897
  if ( right.count() == 1 && rightimpref.count() == 1 )
1,084✔
1898
  {
1899
    gvar->setimp( &rightimpref );
1,010✔
1900
  }
1901
  else
1902
  {
1903
    gvar->setimp( rightimpref.copy() );
74✔
1904
  }
1905
  ValueStack.pop_back();
1,084✔
1906
}
1,084✔
1907

1908
// case INS_ASSIGN_CONSUME:
1909
void Executor::ins_assign_consume( const Instruction& /*ins*/ )
66✔
1910
{
1911
  BObjectRef rightref = ValueStack.back();
66✔
1912
  ValueStack.pop_back();
66✔
1913
  BObjectRef& leftref = ValueStack.back();
66✔
1914

1915
  BObject& right = *rightref;
66✔
1916
  BObject& left = *leftref;
66✔
1917

1918
  BObjectImp& rightimpref = right.impref();
66✔
1919

1920
  if ( right.count() == 1 && rightimpref.count() == 1 )
66✔
1921
  {
1922
    left.setimp( &rightimpref );
63✔
1923
  }
1924
  else
1925
  {
1926
    left.setimp( rightimpref.copy() );
3✔
1927
  }
1928
  ValueStack.pop_back();
66✔
1929
}
66✔
1930

1931
void Executor::ins_assign( const Instruction& /*ins*/ )
22,411✔
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();
22,411✔
1939
  ValueStack.pop_back();
22,411✔
1940
  BObjectRef& leftref = ValueStack.back();
22,411✔
1941

1942
  BObject& right = *rightref;
22,411✔
1943
  BObject& left = *leftref;
22,411✔
1944

1945
  BObjectImp& rightimpref = right.impref();
22,411✔
1946

1947
  if ( right.count() == 1 && rightimpref.count() == 1 )
22,411✔
1948
  {
1949
    left.setimp( &rightimpref );
20,917✔
1950
  }
1951
  else
1952
  {
1953
    left.setimp( rightimpref.copy() );
1,494✔
1954
  }
1955
}
22,411✔
1956

1957
void Executor::ins_array_assign( const Instruction& /*ins*/ )
3✔
1958
{
1959
  /*
1960
      on the value stack:
1961
      x[i] := y;
1962
      (top)
1963
      y
1964
      i
1965
      x
1966
      upon exit:
1967
      (x[i])
1968
      */
1969
  BObjectRef y_ref = ValueStack.back();
3✔
1970
  ValueStack.pop_back();
3✔
1971
  BObjectRef i_ref = ValueStack.back();
3✔
1972
  ValueStack.pop_back();
3✔
1973
  BObjectRef& x_ref = ValueStack.back();
3✔
1974

1975
  BObject& y = *y_ref;
3✔
1976
  BObject& i = *i_ref;
3✔
1977
  BObject& x = *x_ref;
3✔
1978

1979
  BObjectImp* result;
1980
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
3✔
1981

1982
  x_ref.set( result );
3✔
1983
}
3✔
1984
void Executor::ins_array_assign_consume( const Instruction& /*ins*/ )
669✔
1985
{
1986
  /*
1987
      on the value stack:
1988
      x[i] := y;
1989
      (top)
1990
      y
1991
      i
1992
      x
1993
      upon exit:
1994
      (x[i])
1995
      */
1996
  BObjectRef y_ref = ValueStack.back();
669✔
1997
  ValueStack.pop_back();
669✔
1998
  BObjectRef i_ref = ValueStack.back();
669✔
1999
  ValueStack.pop_back();
669✔
2000
  BObjectRef& x_ref = ValueStack.back();
669✔
2001

2002
  BObject& y = *y_ref;
669✔
2003
  BObject& i = *i_ref;
669✔
2004
  BObject& x = *x_ref;
669✔
2005

2006
  BObjectImp* result;
2007
  result = x->array_assign( i.impptr(), y.impptr(), ( y.count() != 1 ) );
669✔
2008

2009
  BObject obj( result );
669✔
2010
  ValueStack.pop_back();
669✔
2011
}
669✔
2012

2013
// TOK_ADD:
2014
void Executor::ins_add( const Instruction& /*ins*/ )
21,813✔
2015
{
2016
  /*
2017
      These each take two operands, and replace them with one.
2018
      We'll leave the second one on the value stack, and
2019
      just replace its object with the result
2020
      */
2021
  BObjectRef rightref = ValueStack.back();
21,813✔
2022
  ValueStack.pop_back();
21,813✔
2023
  BObjectRef& leftref = ValueStack.back();
21,813✔
2024

2025
  BObject& right = *rightref;
21,813✔
2026
  BObject& left = *leftref;
21,813✔
2027

2028
  leftref.set( right.impref().selfPlusObjImp( left.impref() ) );
21,813✔
2029
}
21,813✔
2030

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

2043
  BObject& right = *rightref;
3,375✔
2044
  BObject& left = *leftref;
3,375✔
2045

2046
  leftref.set( right.impref().selfMinusObjImp( left.impref() ) );
3,375✔
2047
}
3,375✔
2048

2049
// TOK_MULT:
2050
void Executor::ins_mult( const Instruction& /*ins*/ )
1,490✔
2051
{
2052
  /*
2053
      These each take two operands, and replace them with one.
2054
      We'll leave the second one on the value stack, and
2055
      just replace its object with the result
2056
      */
2057
  BObjectRef rightref = ValueStack.back();
1,490✔
2058
  ValueStack.pop_back();
1,490✔
2059
  BObjectRef& leftref = ValueStack.back();
1,490✔
2060

2061
  BObject& right = *rightref;
1,490✔
2062
  BObject& left = *leftref;
1,490✔
2063

2064
  leftref.set( right.impref().selfTimesObjImp( left.impref() ) );
1,490✔
2065
}
1,490✔
2066
// TOK_DIV:
2067
void Executor::ins_div( const Instruction& /*ins*/ )
326✔
2068
{
2069
  /*
2070
      These each take two operands, and replace them with one.
2071
      We'll leave the second one on the value stack, and
2072
      just replace its object with the result
2073
      */
2074
  BObjectRef rightref = ValueStack.back();
326✔
2075
  ValueStack.pop_back();
326✔
2076
  BObjectRef& leftref = ValueStack.back();
326✔
2077

2078
  BObject& right = *rightref;
326✔
2079
  BObject& left = *leftref;
326✔
2080

2081
  leftref.set( right.impref().selfDividedByObjImp( left.impref() ) );
326✔
2082
}
326✔
2083
// TOK_MODULUS:
2084
void Executor::ins_modulus( const Instruction& /*ins*/ )
495✔
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();
495✔
2092
  ValueStack.pop_back();
495✔
2093
  BObjectRef& leftref = ValueStack.back();
495✔
2094

2095
  BObject& right = *rightref;
495✔
2096
  BObject& left = *leftref;
495✔
2097

2098
  leftref.set( right.impref().selfModulusObjImp( left.impref() ) );
495✔
2099
}
495✔
2100

2101
// TOK_IS:
2102
void Executor::ins_is( const Instruction& /*ins*/ )
100✔
2103
{
2104
  BObjectRef rightref = ValueStack.back();
100✔
2105
  ValueStack.pop_back();
100✔
2106
  BObjectRef& leftref = ValueStack.back();
100✔
2107

2108
  BObject& right = *rightref;
100✔
2109
  BObject& left = *leftref;
100✔
2110

2111
  leftref.set( right.impref().selfIsObjImp( left.impref() ) );
100✔
2112
}
100✔
2113

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

2126
  BObject& right = *rightref;
297✔
2127
  BObject& left = *leftref;
297✔
2128

2129
  leftref.set( right.impref().selfBitShiftRightObjImp( left.impref() ) );
297✔
2130
}
297✔
2131
// TOK_BSLEFT:
2132
void Executor::ins_bitshift_left( const Instruction& /*ins*/ )
297✔
2133
{
2134
  /*
2135
      These each take two operands, and replace them with one.
2136
      We'll leave the second one on the value stack, and
2137
      just replace its object with the result
2138
      */
2139
  BObjectRef rightref = ValueStack.back();
297✔
2140
  ValueStack.pop_back();
297✔
2141
  BObjectRef& leftref = ValueStack.back();
297✔
2142

2143
  BObject& right = *rightref;
297✔
2144
  BObject& left = *leftref;
297✔
2145

2146
  leftref.set( right.impref().selfBitShiftLeftObjImp( left.impref() ) );
297✔
2147
}
297✔
2148
// TOK_BITAND:
2149
void Executor::ins_bitwise_and( const Instruction& /*ins*/ )
300✔
2150
{
2151
  /*
2152
      These each take two operands, and replace them with one.
2153
      We'll leave the second one on the value stack, and
2154
      just replace its object with the result
2155
      */
2156
  BObjectRef rightref = ValueStack.back();
300✔
2157
  ValueStack.pop_back();
300✔
2158
  BObjectRef& leftref = ValueStack.back();
300✔
2159

2160
  BObject& right = *rightref;
300✔
2161
  BObject& left = *leftref;
300✔
2162

2163
  leftref.set( right.impref().selfBitAndObjImp( left.impref() ) );
300✔
2164
}
300✔
2165
// TOK_BITXOR:
2166
void Executor::ins_bitwise_xor( const Instruction& /*ins*/ )
297✔
2167
{
2168
  /*
2169
      These each take two operands, and replace them with one.
2170
      We'll leave the second one on the value stack, and
2171
      just replace its object with the result
2172
      */
2173
  BObjectRef rightref = ValueStack.back();
297✔
2174
  ValueStack.pop_back();
297✔
2175
  BObjectRef& leftref = ValueStack.back();
297✔
2176

2177
  BObject& right = *rightref;
297✔
2178
  BObject& left = *leftref;
297✔
2179

2180
  leftref.set( right.impref().selfBitXorObjImp( left.impref() ) );
297✔
2181
}
297✔
2182
// TOK_BITOR:
2183
void Executor::ins_bitwise_or( const Instruction& /*ins*/ )
297✔
2184
{
2185
  /*
2186
      These each take two operands, and replace them with one.
2187
      We'll leave the second one on the value stack, and
2188
      just replace its object with the result
2189
      */
2190
  BObjectRef rightref = ValueStack.back();
297✔
2191
  ValueStack.pop_back();
297✔
2192
  BObjectRef& leftref = ValueStack.back();
297✔
2193

2194
  BObject& right = *rightref;
297✔
2195
  BObject& left = *leftref;
297✔
2196

2197
  leftref.set( right.impref().selfBitOrObjImp( left.impref() ) );
297✔
2198
}
297✔
2199

2200
void Executor::ins_logical_and( const Instruction& /*ins*/ )
5,680✔
2201
{
2202
  /*
2203
      These each take two operands, and replace them with one.
2204
      We'll leave the second one on the value stack, and
2205
      just replace its object with the result
2206
      */
2207
  BObjectRef rightref = ValueStack.back();
5,680✔
2208
  ValueStack.pop_back();
5,680✔
2209
  BObjectRef& leftref = ValueStack.back();
5,680✔
2210

2211
  BObject& right = *rightref;
5,680✔
2212
  BObject& left = *leftref;
5,680✔
2213

2214
  int _true = ( left.isTrue() && right.isTrue() );
5,680✔
2215
  leftref.set( new BLong( _true ) );
5,680✔
2216
}
5,680✔
2217
void Executor::ins_logical_or( const Instruction& /*ins*/ )
1,046✔
2218
{
2219
  /*
2220
      These each take two operands, and replace them with one.
2221
      We'll leave the second one on the value stack, and
2222
      just replace its object with the result
2223
      */
2224
  BObjectRef rightref = ValueStack.back();
1,046✔
2225
  ValueStack.pop_back();
1,046✔
2226
  BObjectRef& leftref = ValueStack.back();
1,046✔
2227

2228
  BObject& right = *rightref;
1,046✔
2229
  BObject& left = *leftref;
1,046✔
2230

2231
  int _true = ( left.isTrue() || right.isTrue() );
1,046✔
2232
  leftref.set( new BLong( _true ) );
1,046✔
2233
}
1,046✔
2234

2235
void Executor::ins_notequal( const Instruction& /*ins*/ )
7,468✔
2236
{
2237
  /*
2238
      These each take two operands, and replace them with one.
2239
      We'll leave the second one on the value stack, and
2240
      just replace its object with the result
2241
      */
2242
  BObjectRef rightref = ValueStack.back();
7,468✔
2243
  ValueStack.pop_back();
7,468✔
2244
  BObjectRef& leftref = ValueStack.back();
7,468✔
2245

2246
  BObject& right = *rightref;
7,468✔
2247
  BObject& left = *leftref;
7,468✔
2248

2249
  int _true = ( left != right );
7,468✔
2250
  leftref.set( new BLong( _true ) );
7,468✔
2251
}
7,468✔
2252

2253
void Executor::ins_equal( const Instruction& /*ins*/ )
7,061✔
2254
{
2255
  /*
2256
      These each take two operands, and replace them with one.
2257
      We'll leave the second one on the value stack, and
2258
      just replace its object with the result
2259
      */
2260
  BObjectRef rightref = ValueStack.back();
7,061✔
2261
  ValueStack.pop_back();
7,061✔
2262
  BObjectRef& leftref = ValueStack.back();
7,061✔
2263

2264
  BObject& right = *rightref;
7,061✔
2265
  BObject& left = *leftref;
7,061✔
2266

2267
  int _true = ( left == right );
7,061✔
2268
  leftref.set( new BLong( _true ) );
7,061✔
2269
}
7,061✔
2270

2271
void Executor::ins_lessthan( const Instruction& /*ins*/ )
3,840✔
2272
{
2273
  /*
2274
      These each take two operands, and replace them with one.
2275
      We'll leave the second one on the value stack, and
2276
      just replace its object with the result
2277
      */
2278
  BObjectRef rightref = ValueStack.back();
3,840✔
2279
  ValueStack.pop_back();
3,840✔
2280
  BObjectRef& leftref = ValueStack.back();
3,840✔
2281

2282
  BObject& right = *rightref;
3,840✔
2283
  BObject& left = *leftref;
3,840✔
2284

2285
  int _true = ( left < right );
3,840✔
2286
  leftref.set( new BLong( _true ) );
3,840✔
2287
}
3,840✔
2288

2289
void Executor::ins_lessequal( const Instruction& /*ins*/ )
779✔
2290
{
2291
  /*
2292
      These each take two operands, and replace them with one.
2293
      We'll leave the second one on the value stack, and
2294
      just replace its object with the result
2295
      */
2296
  BObjectRef rightref = ValueStack.back();
779✔
2297
  ValueStack.pop_back();
779✔
2298
  BObjectRef& leftref = ValueStack.back();
779✔
2299

2300
  BObject& right = *rightref;
779✔
2301
  BObject& left = *leftref;
779✔
2302
  int _true = ( left <= right );
779✔
2303
  leftref.set( new BLong( _true ) );
779✔
2304
}
779✔
2305
void Executor::ins_greaterthan( const Instruction& /*ins*/ )
5,626✔
2306
{
2307
  /*
2308
      These each take two operands, and replace them with one.
2309
      We'll leave the second one on the value stack, and
2310
      just replace its object with the result
2311
      */
2312
  BObjectRef rightref = ValueStack.back();
5,626✔
2313
  ValueStack.pop_back();
5,626✔
2314
  BObjectRef& leftref = ValueStack.back();
5,626✔
2315

2316
  BObject& right = *rightref;
5,626✔
2317
  BObject& left = *leftref;
5,626✔
2318

2319
  int _true = ( left > right );
5,626✔
2320
  leftref.set( new BLong( _true ) );
5,626✔
2321
}
5,626✔
2322
void Executor::ins_greaterequal( const Instruction& /*ins*/ )
418✔
2323
{
2324
  /*
2325
      These each take two operands, and replace them with one.
2326
      We'll leave the second one on the value stack, and
2327
      just replace its object with the result
2328
      */
2329
  BObjectRef rightref = ValueStack.back();
418✔
2330
  ValueStack.pop_back();
418✔
2331
  BObjectRef& leftref = ValueStack.back();
418✔
2332

2333
  BObject& right = *rightref;
418✔
2334
  BObject& left = *leftref;
418✔
2335

2336
  int _true = ( left >= right );
418✔
2337
  leftref.set( new BLong( _true ) );
418✔
2338
}
418✔
2339

2340
// case TOK_ARRAY_SUBSCRIPT:
2341
void Executor::ins_arraysubscript( const Instruction& /*ins*/ )
8,971✔
2342
{
2343
  /*
2344
      These each take two operands, and replace them with one.
2345
      We'll leave the second one on the value stack, and
2346
      just replace its object with the result
2347
      */
2348
  BObjectRef rightref = ValueStack.back();
8,971✔
2349
  ValueStack.pop_back();
8,971✔
2350
  BObjectRef& leftref = ValueStack.back();
8,971✔
2351

2352
  leftref = ( *leftref )->OperSubscript( *rightref );
8,971✔
2353
}
8,971✔
2354

2355
void Executor::ins_multisubscript( const Instruction& ins )
7,214✔
2356
{
2357
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2358
  std::stack<BObjectRef> indices;
7,214✔
2359
  for ( int i = 0; i < ins.token.lval; ++i )
21,642✔
2360
  {
2361
    indices.push( ValueStack.back() );
14,428✔
2362
    ValueStack.pop_back();
14,428✔
2363
  }
2364

2365
  BObjectRef& leftref = ValueStack.back();
7,214✔
2366
  leftref = ( *leftref )->OperMultiSubscript( indices );
7,214✔
2367
}
7,214✔
2368

2369
void Executor::ins_unpack_sequence( const Instruction& ins )
103✔
2370
{
2371
  bool rest = ins.token.lval >> 14;
103✔
2372
  auto count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
103✔
2373

2374
  BObjectRef refIter( new BObject( UninitObject::create() ) );
103✔
2375

2376
  BObjectRef rightref = ValueStack.back();
103✔
2377
  ValueStack.pop_back();
103✔
2378

2379
  // Reserve to keep the insert_at iterator valid
2380
  ValueStack.reserve( ValueStack.size() + count );
103✔
2381
  auto insert_at = ValueStack.begin() + ValueStack.size();
103✔
2382
  auto pIter = std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
103✔
2383

2384
  // The default iterator is used for non-iterable objects, so we can't unpack
2385
  // them. Simply add errors for each value to unpack.
2386
  if ( pIter->is_default() )
103✔
2387
  {
2388
    if ( rest )
6✔
2389
    {
2390
      auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
3✔
2391
      for ( u8 i = 0; i < count; ++i )
12✔
2392
        ValueStack.emplace( insert_at, new BError( i == rest_index ? "Invalid type for rest binding"
9✔
2393
                                                                   : "Index out of bounds" ) );
9✔
2394
    }
2395
    else
2396
    {
2397
      for ( u8 i = 0; i < count; ++i )
9✔
2398
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6✔
2399
    }
2400
    return;
6✔
2401
  }
2402

2403
  if ( rest )
97✔
2404
  {
2405
    auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
66✔
2406

2407
    // Add all elements up to the rest index
2408
    for ( u8 i = 0; i < rest_index; ++i )
147✔
2409
    {
2410
      if ( auto res = pIter->step() )
81✔
2411
        ValueStack.emplace( insert_at, res );
75✔
2412
      else
2413
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
6✔
2414
    }
2415

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

2419
    while ( auto res = pIter->step() )
306✔
2420
      rest_array->addElement( res->impptr() );
240✔
2421

2422
    // Take the remaining elements from the rest array to fill the rest of the bindings.
2423
    auto left = count - rest_index - 1;
66✔
2424

2425
    for ( u8 i = 0; i < left; ++i )
138✔
2426
    {
2427
      if ( rest_array->ref_arr.empty() )
72✔
2428
        ValueStack.emplace( insert_at + i, new BError( "Index out of bounds" ) );
12✔
2429
      else
2430
      {
2431
        ValueStack.insert( insert_at + i, rest_array->ref_arr.back() );
60✔
2432

2433
        rest_array->ref_arr.pop_back();
60✔
2434
      }
2435
    }
2436
  }
2437
  else
2438
  {
2439
    for ( u8 i = 0; i < count; ++i )
113✔
2440
    {
2441
      if ( auto res = pIter->step() )
82✔
2442
        ValueStack.emplace( insert_at, res );
82✔
2443
      else
2444
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
×
2445
    }
2446
  }
2447
}
115✔
2448

2449
void Executor::ins_unpack_indices( const Instruction& ins )
106✔
2450
{
2451
  bool rest = ins.token.lval >> 14;
106✔
2452
  auto binding_count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
106✔
2453
  // If there is a rest binding, there will be one less index than the binding
2454
  // count, as the rest binding has no corresponding element index access.
2455
  auto index_count = rest ? Clib::clamp_convert<u8>( binding_count - 1 ) : binding_count;
106✔
2456

2457
  // Ensure there is a ValueStack entry for each index, + 1 for the unpacking object ('rightref').
2458
  passert_r( static_cast<int>( ValueStack.size() ) >= index_count + 1,
106✔
2459
             "Not enough values to unpack" );
2460

2461
  if ( rest )
106✔
2462
  {
2463
    // Use a multi_index because we need to (1) iterate over the indexes in
2464
    // order of the bindings in the script, and (2) keep track of which indexes
2465
    // have been used.
2466
    using namespace boost::multi_index;
2467
    using OrderedSet =
2468
        multi_index_container<BObject,
2469
                              indexed_by<sequenced<>,  // Maintains insertion order
2470
                                         ordered_unique<identity<BObject>>  // Ensures uniqueness
2471
                                         >>;
2472

2473
    OrderedSet indexes;
39✔
2474

2475
    for ( u8 i = 0; i < index_count; ++i )
120✔
2476
    {
2477
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
81✔
2478
      ValueStack.pop_back();
81✔
2479
    }
2480

2481
    BObjectRef rightref = ValueStack.back();
39✔
2482
    ValueStack.pop_back();
39✔
2483

2484
    // Reserve to keep the insert_at iterator valid
2485
    ValueStack.reserve( ValueStack.size() + binding_count );
39✔
2486
    auto insert_at = ValueStack.end();
39✔
2487

2488
    for ( const auto& index : indexes )
120✔
2489
    {
2490
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
81✔
2491
    }
2492

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

2497
    BObjectRef refIter( new BObject( UninitObject::create() ) );
39✔
2498
    auto pIter =
2499
        std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
39✔
2500

2501
    // The default iterator is used for non-iterable objects, so we can't unpack them.
2502
    if ( pIter->is_default() )
39✔
2503
    {
2504
      ValueStack.emplace( insert_at, new BError( "Invalid type for rest binding" ) );
15✔
2505
      return;
15✔
2506
    }
2507
    else
2508
      rest_obj = std::make_unique<BDictionary>();
24✔
2509

2510
    auto& unique_index = indexes.get<1>();
24✔
2511

2512
    while ( auto res = pIter->step() )
120✔
2513
    {
2514
      auto itr = unique_index.find( *refIter.get() );
96✔
2515

2516
      if ( itr == unique_index.end() )
96✔
2517
        rest_obj->array_assign( refIter->impptr(), res->impptr(), true );
45✔
2518
    }
96✔
2519
    ValueStack.emplace( insert_at, rest_obj.release() );
24✔
2520
  }
99✔
2521
  else
2522
  {
2523
    // If not using a rest binding, only keep track of binding order.
2524
    std::list<BObject> indexes;
67✔
2525

2526
    for ( u8 i = 0; i < index_count; ++i )
212✔
2527
    {
2528
      indexes.insert( indexes.begin(), BObject( *ValueStack.back().get() ) );
145✔
2529
      ValueStack.pop_back();
145✔
2530
    }
2531

2532
    BObjectRef rightref = ValueStack.back();
67✔
2533
    ValueStack.pop_back();
67✔
2534

2535
    // Reserve to keep the insert_at iterator valid
2536
    ValueStack.reserve( ValueStack.size() + binding_count );
67✔
2537
    auto insert_at = ValueStack.end();
67✔
2538

2539
    for ( const auto& index : indexes )
212✔
2540
    {
2541
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
145✔
2542
    }
2543
  }
67✔
2544
}
2545

2546
void Executor::ins_take_local( const Instruction& )
356✔
2547
{
2548
  passert( Locals2 != nullptr );
356✔
2549
  passert( !ValueStack.empty() );
356✔
2550

2551
  // There is no entry in the locals vector, so create a new one.
2552
  Locals2->push_back( BObjectRef( UninitObject::create() ) );
356✔
2553
  BObjectRef& lvar = ( *Locals2 ).back();
356✔
2554

2555
  BObjectRef& rightref = ValueStack.back();
356✔
2556

2557
  BObject& right = *rightref;
356✔
2558

2559
  BObjectImp& rightimpref = right.impref();
356✔
2560

2561
  if ( right.count() == 1 && rightimpref.count() == 1 )
356✔
2562
  {
2563
    lvar->setimp( &rightimpref );
244✔
2564
  }
2565
  else
2566
  {
2567
    lvar->setimp( rightimpref.copy() );
112✔
2568
  }
2569
  ValueStack.pop_back();
356✔
2570
}
356✔
2571

2572
void Executor::ins_take_global( const Instruction& ins )
183✔
2573
{
2574
  passert( !ValueStack.empty() );
183✔
2575

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

2579
  BObjectRef& rightref = ValueStack.back();
183✔
2580

2581
  BObject& right = *rightref;
183✔
2582

2583
  BObjectImp& rightimpref = right.impref();
183✔
2584

2585
  if ( right.count() == 1 && rightimpref.count() == 1 )
183✔
2586
  {
2587
    gvar->setimp( &rightimpref );
150✔
2588
  }
2589
  else
2590
  {
2591
    gvar->setimp( rightimpref.copy() );
33✔
2592
  }
2593
  ValueStack.pop_back();
183✔
2594
}
183✔
2595

2596
void Executor::ins_multisubscript_assign( const Instruction& ins )
6✔
2597
{
2598
  BObjectRef target_ref = ValueStack.back();
6✔
2599
  ValueStack.pop_back();
6✔
2600
  // the subscripts are on the value stack in right-to-left order, followed by the array itself
2601
  std::stack<BObjectRef> indices;
6✔
2602
  for ( int i = 0; i < ins.token.lval; ++i )
18✔
2603
  {
2604
    indices.push( ValueStack.back() );
12✔
2605
    ValueStack.pop_back();
12✔
2606
  }
2607

2608
  BObjectRef& leftref = ValueStack.back();
6✔
2609
  leftref = ( *leftref )->OperMultiSubscriptAssign( indices, target_ref->impptr() );
6✔
2610
}
6✔
2611

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

2623
  BObject& right = *rightref;
168✔
2624
  BObject& left = *leftref;
168✔
2625

2626
  leftref = addmember( left, right );
168✔
2627
}
168✔
2628

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

2640
  BObject& right = *rightref;
78✔
2641
  BObject& left = *leftref;
78✔
2642

2643
  leftref = removemember( left, right );
78✔
2644
}
78✔
2645

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

2657
  BObject& right = *rightref;
1,518✔
2658
  BObject& left = *leftref;
1,518✔
2659

2660
  leftref = checkmember( left, right );
1,518✔
2661
}
1,518✔
2662

2663
void Executor::ins_addmember2( const Instruction& ins )
45✔
2664
{
2665
  BObjectRef obref = ValueStack.back();
45✔
2666

2667
  BObject& ob = *obref;
45✔
2668

2669
  ob.impref().operDotPlus( ins.token.tokval() );
45✔
2670
}
45✔
2671

2672
void Executor::ins_addmember_assign( const Instruction& ins )
1,815✔
2673
{
2674
  BObjectRef valref = ValueStack.back();
1,815✔
2675
  BObject& valob = *valref;
1,815✔
2676
  BObjectImp* valimp = valref->impptr();
1,815✔
2677

2678
  ValueStack.pop_back();
1,815✔
2679

2680
  BObjectRef obref = ValueStack.back();
1,815✔
2681
  BObject& ob = *obref;
1,815✔
2682

2683
  BObjectRef memref = ob.impref().operDotPlus( ins.token.tokval() );
1,815✔
2684
  BObject& mem = *memref;
1,815✔
2685

2686
  if ( valob.count() == 1 && valimp->count() == 1 )
1,815✔
2687
  {
2688
    mem.setimp( valimp );
1,314✔
2689
  }
2690
  else
2691
  {
2692
    mem.setimp( valimp->copy() );
501✔
2693
  }
2694
  // the struct is at the top of the stack
2695
}
1,815✔
2696

2697
void Executor::ins_dictionary_addmember( const Instruction& /*ins*/ )
138✔
2698
{
2699
  /*
2700
      ENTRANCE: value stack:
2701
      dictionary
2702
      key
2703
      value
2704
      EXIT: value stack:
2705
      dictionary
2706
      FUNCTION:
2707
      adds the (key, value) pair to the dictionary
2708
      */
2709

2710
  BObjectRef valref = ValueStack.back();
138✔
2711
  ValueStack.pop_back();
138✔
2712
  BObject& valob = *valref;
138✔
2713
  BObjectImp* valimp = valob.impptr();
138✔
2714

2715
  BObjectRef keyref = ValueStack.back();
138✔
2716
  ValueStack.pop_back();
138✔
2717
  BObject& keyob = *keyref;
138✔
2718
  BObjectImp* keyimp = keyob.impptr();
138✔
2719

2720
  BObjectRef dictref = ValueStack.back();
138✔
2721
  BObject& dictob = *dictref;
138✔
2722
  BDictionary* dict = dictob.impptr<BDictionary>();
138✔
2723

2724
  if ( keyob.count() != 1 || keyimp->count() != 1 )
138✔
2725
  {
2726
    keyimp = keyimp->copy();
6✔
2727
  }
2728
  if ( valob.count() != 1 || valimp->count() != 1 )
138✔
2729
  {
2730
    valimp = valimp->copy();
18✔
2731
  }
2732

2733
  dict->addMember( keyimp, valimp );
138✔
2734
  // the dictionary remains at the top of the stack.
2735
}
138✔
2736

2737
void Executor::ins_in( const Instruction& /*ins*/ )
721✔
2738
{
2739
  /*
2740
      These each take two operands, and replace them with one.
2741
      We'll leave the second one on the value stack, and
2742
      just replace its object with the result
2743
      */
2744
  BObjectRef rightref = ValueStack.back();
721✔
2745
  ValueStack.pop_back();
721✔
2746
  BObjectRef& leftref = ValueStack.back();
721✔
2747

2748
  BObject& right = *rightref;
721✔
2749
  BObject& left = *leftref;
721✔
2750

2751
  leftref.set( new BLong( right.impref().contains( left.impref() ) ) );
721✔
2752
}
721✔
2753

2754
void Executor::ins_insert_into( const Instruction& /*ins*/ )
7,548✔
2755
{
2756
  BObjectRef rightref = ValueStack.back();
7,548✔
2757
  ValueStack.pop_back();
7,548✔
2758
  BObjectRef& leftref = ValueStack.back();
7,548✔
2759

2760
  BObject& right = *rightref;
7,548✔
2761
  BObject& left = *leftref;
7,548✔
2762

2763
  if ( auto* spread = right.impptr_if<BSpread>() )
7,548✔
2764
  {
2765
    BObjectRef refIter( new BObject( UninitObject::create() ) );
1,473✔
2766

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

2770
    BObject* next = pIter->step();
1,473✔
2771
    while ( next != nullptr )
4,317✔
2772
    {
2773
      left.impref().operInsertInto( left, next->impref() );
2,844✔
2774
      next = pIter->step();
2,844✔
2775
    }
2776
  }
1,473✔
2777
  else
2778
  {
2779
    left.impref().operInsertInto( left, right.impref() );
6,075✔
2780
  }
2781
}
7,548✔
2782

2783
void Executor::ins_plusequal( const Instruction& /*ins*/ )
819✔
2784
{
2785
  /*
2786
      These each take two operands, and replace them with one.
2787
      We'll leave the second one on the value stack, and
2788
      just replace its object with the result
2789
      */
2790
  BObjectRef rightref = ValueStack.back();
819✔
2791
  ValueStack.pop_back();
819✔
2792
  BObjectRef& leftref = ValueStack.back();
819✔
2793

2794
  BObject& right = *rightref;
819✔
2795
  BObject& left = *leftref;
819✔
2796

2797
  left.impref().operPlusEqual( left, right.impref() );
819✔
2798
}
819✔
2799

2800
void Executor::ins_minusequal( const Instruction& /*ins*/ )
240✔
2801
{
2802
  /*
2803
      These each take two operands, and replace them with one.
2804
      We'll leave the second one on the value stack, and
2805
      just replace its object with the result
2806
      */
2807
  BObjectRef rightref = ValueStack.back();
240✔
2808
  ValueStack.pop_back();
240✔
2809
  BObjectRef& leftref = ValueStack.back();
240✔
2810

2811
  BObject& right = *rightref;
240✔
2812
  BObject& left = *leftref;
240✔
2813

2814
  left.impref().operMinusEqual( left, right.impref() );
240✔
2815
}
240✔
2816

2817
void Executor::ins_timesequal( const Instruction& /*ins*/ )
231✔
2818
{
2819
  /*
2820
      These each take two operands, and replace them with one.
2821
      We'll leave the second one on the value stack, and
2822
      just replace its object with the result
2823
      */
2824
  BObjectRef rightref = ValueStack.back();
231✔
2825
  ValueStack.pop_back();
231✔
2826
  BObjectRef& leftref = ValueStack.back();
231✔
2827

2828
  BObject& right = *rightref;
231✔
2829
  BObject& left = *leftref;
231✔
2830

2831
  left.impref().operTimesEqual( left, right.impref() );
231✔
2832
}
231✔
2833

2834
void Executor::ins_divideequal( const Instruction& /*ins*/ )
228✔
2835
{
2836
  /*
2837
      These each take two operands, and replace them with one.
2838
      We'll leave the second one on the value stack, and
2839
      just replace its object with the result
2840
      */
2841
  BObjectRef rightref = ValueStack.back();
228✔
2842
  ValueStack.pop_back();
228✔
2843
  BObjectRef& leftref = ValueStack.back();
228✔
2844

2845
  BObject& right = *rightref;
228✔
2846
  BObject& left = *leftref;
228✔
2847

2848
  left.impref().operDivideEqual( left, right.impref() );
228✔
2849
}
228✔
2850

2851
void Executor::ins_modulusequal( const Instruction& /*ins*/ )
375✔
2852
{
2853
  /*
2854
      These each take two operands, and replace them with one.
2855
      We'll leave the second one on the value stack, and
2856
      just replace its object with the result
2857
      */
2858
  BObjectRef rightref = ValueStack.back();
375✔
2859
  ValueStack.pop_back();
375✔
2860
  BObjectRef& leftref = ValueStack.back();
375✔
2861

2862
  BObject& right = *rightref;
375✔
2863
  BObject& left = *leftref;
375✔
2864

2865
  left.impref().operModulusEqual( left, right.impref() );
375✔
2866
}
375✔
2867

2868
// case RSV_GOTO:
2869
void Executor::ins_goto( const Instruction& ins )
19,118,370✔
2870
{
2871
  PC = (unsigned)ins.token.lval;
19,118,370✔
2872
}
19,118,370✔
2873

2874
// TOK_FUNC:
2875
void Executor::ins_func( const Instruction& ins )
19,154,637✔
2876
{
2877
  unsigned nparams = prog_->modules[ins.token.module]->functions[ins.token.lval]->nargs;
19,154,637✔
2878
  getParams( nparams );
19,154,637✔
2879
  execFunc( ins.token );
19,154,637✔
2880
  cleanParams();
19,154,637✔
2881
  return;
19,154,637✔
2882
}
2883

2884
void Executor::ins_call_method_id( const Instruction& ins )
37,395✔
2885
{
2886
  BContinuation* continuation = nullptr;
37,395✔
2887
  unsigned nparams = ins.token.type;
37,395✔
2888

2889
  do
2890
  {
2891
    getParams( nparams );
38,005✔
2892
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
38,005✔
2893
    {
2894
      Instruction jmp;
2,204✔
2895
      bool add_new_classinst = ins.token.lval == MTH_NEW;
2,204✔
2896

2897
      if ( add_new_classinst )
2,204✔
2898
      {
2899
        if ( funcr->constructor() )
102✔
2900
        {
2901
          fparams.insert( fparams.begin(),
75✔
2902
                          BObjectRef( new BConstObject( new BClassInstanceRef(
150✔
2903
                              new BClassInstance( prog_, funcr->class_index(), Globals2 ) ) ) ) );
150✔
2904
        }
2905
      }
2906

2907
      if ( funcr->validCall( continuation ? MTH_CALL : ins.token.lval, *this, &jmp ) )
2,204✔
2908
      {
2909
        BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
2,099✔
2910
        call_function_reference( funcr, continuation, jmp );
2,099✔
2911
        return;
2,099✔
2912
      }
2,099✔
2913
    }
2,204✔
2914
    // If there _was_ a continuation to be handled (from previous loop
2915
    // iteration), there must have been a FuncRef on the stack. Otherwise,
2916
    // `continuation` may leak.
2917
    passert_always( continuation == nullptr );
35,906✔
2918

2919
    size_t stacksize = ValueStack.size();  // ValueStack can grow
35,906✔
2920
#ifdef ESCRIPT_PROFILE
2921
    std::stringstream strm;
2922
    strm << "MTHID_" << ValueStack.back()->impptr()->typeOf() << " ." << ins.token.lval;
2923
    if ( !fparams.empty() )
2924
      strm << " [" << fparams[0].get()->impptr()->typeOf() << "]";
2925
    std::string name( strm.str() );
2926
    unsigned long profile_start = GetTimeUs();
2927
#endif
2928
    BObjectImp* imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
35,906✔
2929

2930
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
35,906✔
2931
    {
2932
      continuation = cont;
610✔
2933
      // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2934
      // move.
2935
      nparams = static_cast<unsigned int>( continuation->args.size() );
610✔
2936

2937
      // Add function reference to stack
2938
      ValueStack.push_back( BObjectRef( continuation->func() ) );
610✔
2939

2940
      // Move all arguments to the value stack
2941
      ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
610✔
2942
                         std::make_move_iterator( continuation->args.end() ) );
2943

2944
      continuation->args.clear();
610✔
2945

2946
      cleanParams();
610✔
2947

2948
      printStack( fmt::format(
610✔
2949
          "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2950
          "funcref.call()",
2951
          nparams ) );
2952

2953
      // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and handle
2954
      // it.
2955

2956
      // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2957
      // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2958
      // jumps.
2959

2960
      continue;
610✔
2961
    }
2962

2963
#ifdef ESCRIPT_PROFILE
2964
    profile_escript( name, profile_start );
2965
#endif
2966
    BObjectRef& objref = ValueStack[stacksize - 1];
35,296✔
2967
    if ( func_result_ )
35,296✔
2968
    {
2969
      if ( imp )
16✔
2970
      {
2971
        BObject obj( imp );
7✔
2972
      }
7✔
2973

2974
      objref.set( func_result_ );
16✔
2975
      func_result_ = nullptr;
16✔
2976
    }
2977
    else if ( imp )
35,280✔
2978
    {
2979
      objref.set( imp );
35,226✔
2980
    }
2981
    else
2982
    {
2983
      objref.set( UninitObject::create() );
54✔
2984
    }
2985

2986
    cleanParams();
35,296✔
2987
    return;
35,296✔
2988
  }
2989
  // This condition should only ever evaluate to `true` once. In the second loop
2990
  // iteration, handling the FuncRef will return out of this method.
2991
  while ( continuation != nullptr );
610✔
2992
}
2993

2994
void Executor::ins_call_method( const Instruction& ins )
727✔
2995
{
2996
  unsigned nparams = ins.token.lval;
727✔
2997
  auto method_name = ins.token.tokval();
727✔
2998

2999
  getParams( nparams );
727✔
3000
  BObjectImp* callee = ValueStack.back()->impptr();
727✔
3001

3002
  if ( auto* classinstref = ValueStack.back()->impptr_if<BClassInstanceRef>() )
727✔
3003
  {
3004
    BFunctionRef* funcr = nullptr;
524✔
3005
    auto classinst = classinstref->instance();
524✔
3006

3007
    // Prefer members over class methods by checking contents first.
3008
    auto member_itr = classinst->contents().find( method_name );
524✔
3009

3010
    if ( member_itr != classinst->contents().end() )
524✔
3011
    {
3012
      // If the member exists and is NOT a function reference, we will still try
3013
      // to "call" it. This is _intentional_, and will result in a runtime
3014
      // BError. This is similar to `var foo := 3; print(foo.bar());`, resulting
3015
      // in a "Method 'bar' not found" error.
3016
      callee = member_itr->second.get()->impptr();
9✔
3017

3018
      funcr = member_itr->second.get()->impptr_if<BFunctionRef>();
9✔
3019
    }
3020
    else
3021
    {
3022
      // Have we already looked up this method?
3023
      ClassMethodKey key{ prog_, classinst->index(), method_name };
515✔
3024
      auto cache_itr = class_methods.find( key );
515✔
3025
      if ( cache_itr != class_methods.end() )
515✔
3026
      {
3027
        // Switch the callee to the function reference: if the
3028
        // funcr->validCall fails, we will go into the funcref
3029
        // ins_call_method, giving the error about invalid parameter counts.
3030
        funcr = cache_itr->second->impptr_if<BFunctionRef>();
395✔
3031
        callee = funcr;
395✔
3032
        method_name = getObjMethod( MTH_CALL_METHOD )->code;
395✔
3033
      }
3034
      else
3035
      {
3036
        // Does the class define this method?
3037
        funcr = classinst->makeMethod( method_name );
120✔
3038

3039
        if ( funcr != nullptr )
120✔
3040
        {
3041
          // Cache the method for future lookups
3042
          class_methods[key] = BObjectRef( funcr );
117✔
3043

3044
          // Switch the callee to the function reference.
3045
          callee = funcr;
117✔
3046
          method_name = getObjMethod( MTH_CALL_METHOD )->code;
117✔
3047
        }
3048
      }
3049
    }
515✔
3050

3051
    if ( funcr != nullptr )
524✔
3052
    {
3053
      Instruction jmp;
518✔
3054
      int id;
3055

3056
      // Add `this` to the front of the argument list only for class methods,
3057
      // skipping eg. an instance member function reference set via
3058
      // `this.foo := @(){};`.
3059
      if ( funcr->class_method() )
518✔
3060
      {
3061
        id = MTH_CALL_METHOD;
512✔
3062
        fparams.insert( fparams.begin(), ValueStack.back() );
512✔
3063
      }
3064
      else
3065
      {
3066
        id = MTH_CALL;
6✔
3067
      }
3068

3069
      if ( funcr->validCall( id, *this, &jmp ) )
518✔
3070
      {
3071
        BObjectRef funcobj( funcr );  // valuestack gets modified, protect BFunctionRef
497✔
3072
        call_function_reference( funcr, nullptr, jmp );
497✔
3073
        return;
497✔
3074
      }
497✔
3075
    }
518✔
3076
  }
3077
  else if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
203✔
3078
  {
3079
    Instruction jmp;
×
3080
    if ( funcr->validCall( method_name, *this, &jmp ) )
×
3081
    {
3082
      BObjectRef funcobj( ValueStack.back() );  // valuestack gets modified, protect BFunctionRef
×
3083
      call_function_reference( funcr, nullptr, jmp );
×
3084
      return;
×
3085
    }
×
3086
  }
×
3087

3088
  size_t stacksize = ValueStack.size();  // ValueStack can grow
230✔
3089
#ifdef ESCRIPT_PROFILE
3090
  std::stringstream strm;
3091
  strm << "MTH_" << callee->typeOf() << " ." << method_name;
3092
  if ( !fparams.empty() )
3093
    strm << " [" << fparams[0].get()->impptr()->typeOf() << "]";
3094
  std::string name( strm.str() );
3095
  unsigned long profile_start = GetTimeUs();
3096
#endif
3097
#ifdef BOBJECTIMP_DEBUG
3098
  BObjectImp* imp;
3099

3100
  if ( strcmp( method_name, "impptr" ) == 0 )
230✔
3101
    imp = new String( fmt::format( "{}", static_cast<void*>( callee ) ) );
×
3102
  else
3103
    imp = callee->call_method( method_name, *this );
230✔
3104
#else
3105
  BObjectImp* imp = callee->call_method( method_name, *this );
3106
#endif
3107
#ifdef ESCRIPT_PROFILE
3108
  profile_escript( name, profile_start );
3109
#endif
3110

3111
  BObjectRef& objref = ValueStack[stacksize - 1];
230✔
3112
  if ( func_result_ )
230✔
3113
  {
3114
    if ( imp )
×
3115
    {
3116
      BObject obj( imp );
×
3117
    }
×
3118

NEW
3119
    objref.set( func_result_ );
×
3120
    func_result_ = nullptr;
×
3121
  }
3122
  else if ( imp )
230✔
3123
  {
3124
    objref.set( imp );
60✔
3125
  }
3126
  else
3127
  {
3128
    objref.set( UninitObject::create() );
170✔
3129
  }
3130

3131
  cleanParams();
230✔
3132
  return;
230✔
3133
}
3134

3135
// CTRL_STATEMENTBEGIN:
3136
void Executor::ins_statementbegin( const Instruction& ins )
×
3137
{
3138
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
3139
    INFO_PRINTLN( ins.token.tokval() );
×
3140
}
×
3141

3142
// case CTRL_PROGEND:
3143
void Executor::ins_progend( const Instruction& /*ins*/ )
2,889✔
3144
{
3145
  done = 1;
2,889✔
3146
  run_ok_ = false;
2,889✔
3147
  PC = 0;
2,889✔
3148
}
2,889✔
3149

3150

3151
// case CTRL_MAKELOCAL:
3152
void Executor::ins_makelocal( const Instruction& /*ins*/ )
16,369✔
3153
{
3154
  if ( Locals2 )
16,369✔
3155
    upperLocals2.push_back( Locals2 );
16,369✔
3156
  Locals2 = new BObjectRefVec;
16,369✔
3157
}
16,369✔
3158

3159
void Executor::ins_check_mro( const Instruction& ins )
268✔
3160
{
3161
  auto classinst_offset = ins.token.lval;
268✔
3162

3163
  if ( classinst_offset > static_cast<int>( ValueStack.size() ) || ValueStack.empty() )
268✔
3164
  {
3165
    POLLOG_ERRORLN( "Fatal error: Check MRO offset error! offset={}, ValueStack.size={} ({},PC={})",
×
3166
                    classinst_offset, ValueStack.size(), prog_->name, PC );
×
3167
    seterror( true );
×
3168
    return;
×
3169
  }
3170

3171
  const auto& classinst_ref = ValueStack.at( ValueStack.size() - classinst_offset - 1 );
268✔
3172

3173
  if ( nLines < PC + 1 )
268✔
3174
  {
3175
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction out of bounds! nLines={} ({},PC={})",
×
3176
                    nLines, prog_->name, PC );
×
3177
    seterror( true );
×
3178
    return;
×
3179
  }
3180

3181
  const Instruction& jsr_ins = prog_->instr.at( PC + 1 );
268✔
3182
  if ( jsr_ins.func != &Executor::ins_jsr_userfunc )
268✔
3183
  {
3184
    POLLOG_ERRORLN( "Fatal error: Check MRO instruction not followed by JSR_USERFUNC! ({},PC={})",
×
3185
                    prog_->name, PC );
×
3186
    seterror( true );
×
3187
    return;
×
3188
  }
3189

3190
  auto ctor_addr = jsr_ins.token.lval;
268✔
3191

3192
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
268✔
3193

3194
  if ( classinstref != nullptr && classinstref->instance()->constructors_called.find( ctor_addr ) ==
533✔
3195
                                      classinstref->instance()->constructors_called.end() )
533✔
3196
  {
3197
    classinstref->instance()->constructors_called.insert( ctor_addr );
212✔
3198
  }
3199
  else
3200
  {
3201
    // Constructor has been called, or `this` is not a class instance: clear
3202
    // arguments and skip jump instructions (makelocal, jsr_userfunc)
3203
    ValueStack.resize( ValueStack.size() - ins.token.lval );
56✔
3204
    PC += 2;
56✔
3205
  }
3206
}
3207

3208
// CTRL_JSR_USERFUNC:
3209
void Executor::ins_jsr_userfunc( const Instruction& ins )
5,944✔
3210
{
3211
  jump( ins.token.lval, nullptr, nullptr );
5,944✔
3212
}
5,944✔
3213

3214
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
16,369✔
3215
{
3216
  ReturnContext rc;
16,369✔
3217
  rc.PC = PC;
16,369✔
3218
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
16,369✔
3219
  if ( continuation )
16,369✔
3220
  {
3221
    rc.Continuation.set( continuation );
8,439✔
3222
  }
3223

3224
  // Only store our global context if the function is external to the current program.
3225
  if ( funcref != nullptr && funcref->prog() != prog_ )
16,369✔
3226
  {
3227
    // Store external context for the return path.
3228
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
89✔
3229

3230
    // Set the prog and globals to the external function's, updating nLines and
3231
    // execmodules.
3232
    prog_ = funcref->prog();
89✔
3233

3234
    Globals2 = funcref->globals;
89✔
3235

3236
    nLines = static_cast<unsigned int>( prog_->instr.size() );
89✔
3237

3238
    // Re-attach modules, as the external user function's module function call
3239
    // instructions refer to modules by index.
3240
    execmodules.clear();
89✔
3241

3242
    if ( !viewmode_ )
89✔
3243
    {
3244
      if ( !AttachFunctionalityModules() )
89✔
3245
      {
3246
        POLLOGLN( "Could not attach modules for external function call jump" );
×
3247
        seterror( true );
×
3248
      }
3249
    }
3250
  }
3251

3252
  ControlStack.push_back( rc );
16,369✔
3253

3254
  PC = target_PC;
16,369✔
3255
  if ( ControlStack.size() >= escript_config.max_call_depth )
16,369✔
3256
  {
3257
    std::string tmp = fmt::format(
3258
        "Script {} exceeded maximum call depth\n"
3259
        "Return path PCs: ",
3260
        scriptname() );
×
3261
    while ( !ControlStack.empty() )
×
3262
    {
3263
      rc = ControlStack.back();
×
3264
      ControlStack.pop_back();
×
3265
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
3266
    }
3267
    POLLOGLN( tmp );
×
3268
    seterror( true );
×
3269
  }
×
3270
}
16,369✔
3271

3272

3273
BObjectImp* Executor::get_stacktrace( bool as_array )
42✔
3274
{
3275
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
42✔
3276

3277
  auto with_dbginfo =
3278
      [&]( const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
40✔
3279
                                     unsigned int /*line*/, const std::string& /*functionName*/ )>&
3280
               handler )
3281
  {
3282
    walkCallStack(
40✔
3283
        [&]( unsigned int pc )
155✔
3284
        {
3285
          auto filename = prog()->dbg_filenames[prog()->dbg_filenum[pc]];
195✔
3286
          auto line = prog()->dbg_linenum[pc];
155✔
3287
          auto dbgFunction =
3288
              std::find_if( prog()->dbg_functions.begin(), prog()->dbg_functions.end(),
155✔
3289
                            [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
2,900✔
3290

3291
          std::string functionName =
3292
              dbgFunction != prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
155✔
3293

3294
          handler( pc, filename, line, functionName );
155✔
3295
        } );
155✔
3296
  };
82✔
3297

3298
  if ( as_array )
42✔
3299
  {
3300
    std::unique_ptr<ObjArray> result( new ObjArray );
4✔
3301

3302
    if ( has_symbols )
4✔
3303
    {
3304
      with_dbginfo(
2✔
3305
          [&]( unsigned int pc, const std::string& filename, unsigned int line,
4✔
3306
               const std::string& functionName )
3307
          {
3308
            std::unique_ptr<BStruct> entry( new BStruct );
8✔
3309
            entry->addMember( "file", new String( filename ) );
8✔
3310
            entry->addMember( "line", new BLong( line ) );
8✔
3311
            entry->addMember( "name", new String( functionName ) );
8✔
3312
            entry->addMember( "pc", new BLong( pc ) );
8✔
3313
            result->addElement( entry.release() );
8✔
3314
          } );
8✔
3315
    }
3316
    else
3317
    {
3318
      walkCallStack(
2✔
3319
          [&]( unsigned int pc )
3✔
3320
          {
3321
            std::unique_ptr<BStruct> entry( new BStruct );
3✔
3322
            entry->addMember( "file", new String( scriptname() ) );
3✔
3323
            entry->addMember( "pc", new BLong( pc ) );
3✔
3324
            result->addElement( entry.release() );
3✔
3325
          } );
3✔
3326
    }
3327

3328
    return result.release();
4✔
3329
  }
4✔
3330
  else  // as string
3331
  {
3332
    std::string result;
38✔
3333

3334
    if ( has_symbols )
38✔
3335
    {
3336
      with_dbginfo(
38✔
3337
          [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
76✔
3338
               const std::string& functionName )
3339
          {
3340
            result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
294✔
3341
                                        filename, line ) );
3342
          } );
147✔
3343
    }
3344
    else
3345
    {
3346
      walkCallStack(
×
3347
          [&]( unsigned int pc ) {
×
3348
            result.append(
×
3349
                fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
3350
          } );
×
3351
    }
3352

3353
    return new String( std::move( result ) );
38✔
3354
  }
38✔
3355
}
3356

3357
void Executor::ins_pop_param( const Instruction& ins )
20,411✔
3358
{
3359
  popParam( ins.token );
20,411✔
3360
}
20,411✔
3361

3362
void Executor::ins_pop_param_byref( const Instruction& ins )
2,670✔
3363
{
3364
  popParamByRef( ins.token );
2,670✔
3365
}
2,670✔
3366

3367
void Executor::ins_get_arg( const Instruction& ins )
433✔
3368
{
3369
  getArg( ins.token );
433✔
3370
}
433✔
3371

3372
// CTRL_LEAVE_BLOCK:
3373
void Executor::ins_leave_block( const Instruction& ins )
8,981✔
3374
{
3375
  if ( Locals2 )
8,981✔
3376
  {
3377
    for ( int i = 0; i < ins.token.lval; i++ )
24,103✔
3378
      Locals2->pop_back();
15,122✔
3379
  }
3380
  else  // at global level.  ick.
3381
  {
3382
    for ( int i = 0; i < ins.token.lval; i++ )
×
3383
      Globals2->pop_back();
×
3384
  }
3385
}
8,981✔
3386

3387
void Executor::ins_gosub( const Instruction& ins )
×
3388
{
3389
  ReturnContext rc;
×
3390
  rc.PC = PC;
×
3391
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
3392
  ControlStack.push_back( rc );
×
3393
  if ( Locals2 )
×
3394
    upperLocals2.push_back( Locals2 );
×
3395
  Locals2 = new BObjectRefVec;
×
3396

3397
  PC = (unsigned)ins.token.lval;
×
3398
}
×
3399

3400
// case RSV_RETURN
3401
void Executor::ins_return( const Instruction& /*ins*/ )
16,369✔
3402
{
3403
  if ( ControlStack.empty() )
16,369✔
3404
  {
3405
    ERROR_PRINTLN( "Return without GOSUB! (PC={}, {})", PC, scriptname() );
×
3406

3407
    seterror( true );
×
3408
    return;
×
3409
  }
3410
  BObjectRef continuation;
16,369✔
3411
  ReturnContext& rc = ControlStack.back();
16,369✔
3412
  PC = rc.PC;
16,369✔
3413

3414
  if ( rc.Continuation.get() != nullptr )
16,369✔
3415
  {
3416
    continuation = rc.Continuation;
8,439✔
3417
  }
3418

3419
  if ( Locals2 )
16,369✔
3420
  {
3421
    delete Locals2;
16,369✔
3422
    Locals2 = nullptr;
16,369✔
3423
  }
3424
  if ( !upperLocals2.empty() )
16,369✔
3425
  {
3426
    Locals2 = upperLocals2.back();
16,369✔
3427
    upperLocals2.pop_back();
16,369✔
3428
  }
3429

3430
  if ( rc.ExternalContext.has_value() )
16,369✔
3431
  {
3432
    prog_ = std::move( rc.ExternalContext->Program );
89✔
3433
    nLines = static_cast<unsigned int>( prog_->instr.size() );
89✔
3434
    execmodules = std::move( rc.ExternalContext->Modules );
89✔
3435
    Globals2 = std::move( rc.ExternalContext->Globals );
89✔
3436
  }
3437

3438
  // FIXME do something with rc.ValueStackDepth
3439
  ControlStack.pop_back();
16,369✔
3440

3441
  if ( continuation != nullptr )
16,369✔
3442
  {
3443
    auto result = ValueStack.back();
8,439✔
3444
    ValueStack.pop_back();
8,439✔
3445

3446
    // Do not move the `result` object, as the continuation callback may return
3447
    // the result's BObjectImp*. If we move `result`, the BObjectImp* will be
3448
    // deleted when the callback ends.
3449
    auto* imp = continuation->impptr<BContinuation>()->continueWith( *this, result );
8,439✔
3450

3451
    // If the the continuation callback returned a continuation, handle the jump.
3452
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
8,439✔
3453
    {
3454
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3455
      // takes ownership.
3456

3457
      // Add function reference to stack
3458
      ValueStack.push_back( BObjectRef( new BObject( cont->func() ) ) );
7,829✔
3459

3460
      // Move all arguments to the fparams stack
3461
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
7,829✔
3462
                      std::make_move_iterator( cont->args.end() ) );
3463

3464
      cont->args.clear();
7,829✔
3465

3466
      printStack(
7,829✔
3467
          "continuation callback returned a continuation; continuation args added to fparams" );
3468

3469
      BObjectRef objref = ValueStack.back();
7,829✔
3470
      auto funcr = objref->impptr<BFunctionRef>();
7,829✔
3471
      Instruction jmp;
7,829✔
3472
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
7,829✔
3473
      {
3474
        call_function_reference( funcr, cont, jmp );
7,829✔
3475
      }
3476
      else
3477
      {
3478
        // Delete `imp` since a ReturnContext was not created.
3479
        BObject bobj( imp );
×
3480
      }
×
3481
    }
7,829✔
3482
    else
3483
    {
3484
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3485
      // `array{}.filter(...)`
3486
      ValueStack.pop_back();
610✔
3487

3488
      // Add the result to the stack.
3489
      ValueStack.push_back( BObjectRef( new BObject( imp ) ) );
610✔
3490
    }
3491
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
16,878✔
3492
  }
8,439✔
3493
}
16,369✔
3494

3495
void Executor::ins_exit( const Instruction& /*ins*/ )
3✔
3496
{
3497
  done = 1;
3✔
3498
  run_ok_ = false;
3✔
3499
}
3✔
3500

3501
void Executor::ins_double( const Instruction& ins )
415✔
3502
{
3503
  ValueStack.push_back( BObjectRef( new BObject( new Double( ins.token.dval ) ) ) );
415✔
3504
}
415✔
3505

3506
void Executor::ins_classinst( const Instruction& ins )
254✔
3507
{
3508
  ValueStack.push_back( BObjectRef( new BConstObject(
254✔
3509
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2 ) ) ) ) );
508✔
3510
}
254✔
3511

3512
void Executor::ins_string( const Instruction& ins )
84,374✔
3513
{
3514
  ValueStack.push_back( BObjectRef( new BObject( new String( ins.token.tokval() ) ) ) );
84,374✔
3515
}
84,374✔
3516
void Executor::ins_error( const Instruction& /*ins*/ )
4,163✔
3517
{
3518
  ValueStack.push_back( BObjectRef( new BObject( new BError() ) ) );
4,163✔
3519
}
4,163✔
3520
void Executor::ins_struct( const Instruction& /*ins*/ )
803✔
3521
{
3522
  ValueStack.push_back( BObjectRef( new BObject( new BStruct ) ) );
803✔
3523
}
803✔
3524
void Executor::ins_spread( const Instruction& ins )
2,125✔
3525
{
3526
  bool spread_into = ins.token.lval;
2,125✔
3527

3528
  // Pops TOP, and spreads it into (new) TOP.
3529
  if ( spread_into )
2,125✔
3530
  {
3531
    BObjectRef rightref = ValueStack.back();
24✔
3532
    ValueStack.pop_back();
24✔
3533
    BObjectRef& leftref = ValueStack.back();
24✔
3534

3535
    BObject& right = *rightref;
24✔
3536
    BObject& left = *leftref;
24✔
3537

3538
    BObjectRef refIter( new BObject( UninitObject::create() ) );
24✔
3539

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

3542
    while ( auto next = pIter->step() )
72✔
3543
    {
3544
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48✔
3545
      BObject res_obj( result );
48✔
3546
    }
48✔
3547
  }
24✔
3548
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3549
  else
3550
  {
3551
    auto spread = new BSpread( ValueStack.back() );
2,101✔
3552
    ValueStack.pop_back();
2,101✔
3553
    ValueStack.push_back( BObjectRef( new BObject( spread ) ) );
2,101✔
3554
  }
3555
}
2,125✔
3556
void Executor::ins_array( const Instruction& /*ins*/ )
3,649✔
3557
{
3558
  ValueStack.push_back( BObjectRef( new BObject( new ObjArray ) ) );
3,649✔
3559
}
3,649✔
3560
void Executor::ins_dictionary( const Instruction& /*ins*/ )
161✔
3561
{
3562
  ValueStack.push_back( BObjectRef( new BObject( new BDictionary ) ) );
161✔
3563
}
161✔
3564
void Executor::ins_uninit( const Instruction& /*ins*/ )
113✔
3565
{
3566
  ValueStack.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
113✔
3567
}
113✔
3568
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3569
{
3570
  ValueStack.push_back( BObjectRef( new BObject( new BError( "Please recompile this script" ) ) ) );
×
3571
}
×
3572

3573
// case TOK_UNMINUS:
3574
void Executor::ins_unminus( const Instruction& /*ins*/ )
147✔
3575
{
3576
  BObjectRef ref = getObjRef();
147✔
3577
  BObjectImp* newobj;
3578
  newobj = ref->impref().inverse();
147✔
3579

3580
  ValueStack.push_back( BObjectRef( new BObject( newobj ) ) );
147✔
3581
}
147✔
3582

3583
// case TOK_UNPLUSPLUS:
3584
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
450✔
3585
{
3586
  BObjectRef ref = ValueStack.back();
450✔
3587
  ref->impref().selfPlusPlus();
450✔
3588
}
450✔
3589

3590
// case TOK_UNMINUSMINUS:
3591
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
101✔
3592
{
3593
  BObjectRef ref = ValueStack.back();
101✔
3594
  ref->impref().selfMinusMinus();
101✔
3595
}
101✔
3596

3597
// case TOK_UNPLUSPLUS_POST:
3598
void Executor::ins_unplusplus_post( const Instruction& /*ins*/ )
261✔
3599
{
3600
  BObjectRef ref = ValueStack.back();
261✔
3601
  BObjectImp* imp = ref->impptr();
261✔
3602
  BObject* n = ref->clone();
261✔
3603
  imp->selfPlusPlus();
261✔
3604
  ValueStack.back().set( n );
261✔
3605
}
261✔
3606

3607
// case TOK_UNMINUSMINUS_POST:
3608
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
95✔
3609
{
3610
  BObjectRef ref = ValueStack.back();
95✔
3611
  BObjectImp* imp = ref->impptr();
95✔
3612
  BObject* n = ref->clone();
95✔
3613
  imp->selfMinusMinus();
95✔
3614
  ValueStack.back().set( n );
95✔
3615
}
95✔
3616

3617
// case INS_SET_MEMBER_ID_UNPLUSPLUS:
3618
void Executor::ins_set_member_id_unplusplus( const Instruction& ins )
10✔
3619
{
3620
  BObjectRef ref = ValueStack.back();
10✔
3621
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
10✔
3622
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
20✔
3623
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
10✔
3624
  {
3625
    tmp->impref().selfPlusPlus();
10✔
3626
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
10✔
3627
  }
3628
  ValueStack.back().set( tmp.get() );
10✔
3629
}
10✔
3630

3631
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3632
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
3✔
3633
{
3634
  BObjectRef ref = ValueStack.back();
3✔
3635
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3636
  BObject* res = tmp->clone();
3✔
3637
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3638
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3639
  {
3640
    tmp->impref().selfPlusPlus();
3✔
3641
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3642
  }
3643
  ValueStack.back().set( res );
3✔
3644
}
3✔
3645

3646
// case INS_SET_MEMBER_ID_UNMINUSMINUS:
3647
void Executor::ins_set_member_id_unminusminus( const Instruction& ins )
13✔
3648
{
3649
  BObjectRef ref = ValueStack.back();
13✔
3650
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
13✔
3651
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
26✔
3652
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
13✔
3653
  {
3654
    tmp->impref().selfMinusMinus();
13✔
3655
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
13✔
3656
  }
3657
  ValueStack.back().set( tmp.get() );
13✔
3658
}
13✔
3659

3660
// case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3661
void Executor::ins_set_member_id_unminusminus_post( const Instruction& ins )
3✔
3662
{
3663
  BObjectRef ref = ValueStack.back();
3✔
3664
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3✔
3665
  BObject* res = tmp->clone();
3✔
3666
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6✔
3667
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3✔
3668
  {
3669
    tmp->impref().selfMinusMinus();
3✔
3670
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3✔
3671
  }
3672
  ValueStack.back().set( res );
3✔
3673
}
3✔
3674

3675
// case TOK_LOG_NOT:
3676
void Executor::ins_logical_not( const Instruction& /*ins*/ )
3,553✔
3677
{
3678
  BObjectRef ref = getObjRef();
3,553✔
3679
  ValueStack.push_back( BObjectRef( new BObject( new BLong( (int)!ref->impptr()->isTrue() ) ) ) );
3,553✔
3680
}
3,553✔
3681

3682
// case TOK_BITWISE_NOT:
3683
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
156✔
3684
{
3685
  BObjectRef ref = getObjRef();
156✔
3686
  ValueStack.push_back( BObjectRef( new BObject( ref->impptr()->bitnot() ) ) );
156✔
3687
}
156✔
3688

3689
// case TOK_FUNCREF:
3690
void Executor::ins_funcref( const Instruction& ins )
1,432✔
3691
{
3692
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
1,432✔
3693
  {
3694
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3695
                    prog_->function_references.size() );
×
3696
    seterror( true );
×
3697
    return;
×
3698
  }
3699

3700
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
1,432✔
3701

3702
  ValueStack.push_back(
1,432✔
3703
      BObjectRef( new BFunctionRef( prog_, funcref_index, Globals2, {} /* captures */ ) ) );
2,864✔
3704
}
3705

3706
void Executor::ins_functor( const Instruction& ins )
545✔
3707
{
3708
  auto funcref_index = static_cast<int>( ins.token.type );
545✔
3709

3710
  const auto& ep_funcref = prog_->function_references[funcref_index];
545✔
3711

3712
  int capture_count = ep_funcref.capture_count;
545✔
3713

3714
  auto captures = ValueStackCont();
545✔
3715
  while ( capture_count > 0 )
1,150✔
3716
  {
3717
    captures.push_back( ValueStack.back() );
605✔
3718
    ValueStack.pop_back();
605✔
3719
    capture_count--;
605✔
3720
  }
3721

3722
  auto func = new BFunctionRef( prog_, funcref_index, Globals2, std::move( captures ) );
545✔
3723

3724
  ValueStack.push_back( BObjectRef( func ) );
545✔
3725

3726
  PC += ins.token.lval;
545✔
3727
}
545✔
3728

3729
void Executor::ins_logical_jump( const Instruction& ins )
39✔
3730
{
3731
  BObjectRef& objref = ValueStack.back();
39✔
3732
  // jmp if true for ||, jmp if false for &&
3733
  const bool obj_true = objref->impptr()->isTrue();
39✔
3734
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
39✔
3735

3736
  if ( jmp )
39✔
3737
    PC = (unsigned)ins.token.lval;
12✔
3738
  // keep the obj on the stack if it should jump (ShortCircuit)
3739
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3740
  // BLong (original &&/|| convert to BLong bool)
3741
  if ( !jmp )
39✔
3742
    ValueStack.pop_back();
27✔
3743
  else
3744
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
12✔
3745
}
39✔
3746

3747
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
27✔
3748
{
3749
  BObjectRef& objref = ValueStack.back();
27✔
3750
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
27✔
3751
}
27✔
3752

UNCOV
3753
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3754

3755
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
148,739✔
3756
{
3757
  switch ( token.id )
148,739✔
3758
  {
3759
  case INS_INITFOREACH:
391✔
3760
    return &Executor::ins_initforeach;
391✔
3761
  case INS_STEPFOREACH:
391✔
3762
    return &Executor::ins_stepforeach;
391✔
3763
  case INS_INITFOR:
51✔
3764
    return &Executor::ins_initfor;
51✔
3765
  case INS_NEXTFOR:
51✔
3766
    return &Executor::ins_nextfor;
51✔
3767
  case INS_CASEJMP:
92✔
3768
    return &Executor::ins_casejmp;
92✔
3769
  case RSV_JMPIFTRUE:
965✔
3770
    return &Executor::ins_jmpiftrue;
965✔
3771
  case RSV_JMPIFFALSE:
1,777✔
3772
    return &Executor::ins_jmpiffalse;
1,777✔
3773
  case RSV_LOCAL:
2,403✔
3774
    return &Executor::ins_makeLocal;
2,403✔
3775
  case RSV_GLOBAL:
9,009✔
3776
  case TOK_GLOBALVAR:
3777
    return &Executor::ins_globalvar;
9,009✔
3778
  case TOK_LOCALVAR:
15,177✔
3779
    return &Executor::ins_localvar;
15,177✔
3780
  case TOK_LONG:
12,972✔
3781
    return &Executor::ins_long;
12,972✔
3782
  case TOK_DOUBLE:
415✔
3783
    return &Executor::ins_double;
415✔
3784
  case TOK_STRING:
22,073✔
3785
    return &Executor::ins_string;
22,073✔
3786
  case TOK_ERROR:
186✔
3787
    return &Executor::ins_error;
186✔
3788
  case TOK_STRUCT:
578✔
3789
    return &Executor::ins_struct;
578✔
3790
  case TOK_SPREAD:
670✔
3791
    return &Executor::ins_spread;
670✔
3792
  case TOK_CLASSINST:
176✔
3793
    return &Executor::ins_classinst;
176✔
3794
  case TOK_ARRAY:
2,263✔
3795
    return &Executor::ins_array;
2,263✔
3796
  case TOK_DICTIONARY:
156✔
3797
    return &Executor::ins_dictionary;
156✔
3798
  case TOK_FUNCREF:
707✔
3799
    return &Executor::ins_funcref;
707✔
3800
  case TOK_FUNCTOR:
499✔
3801
    return &Executor::ins_functor;
499✔
3802
  case INS_UNINIT:
106✔
3803
    return &Executor::ins_uninit;
106✔
3804
  case TOK_IDENT:
×
3805
    return &Executor::ins_ident;
×
3806
  case INS_ASSIGN_GLOBALVAR:
713✔
3807
    return &Executor::ins_assign_globalvar;
713✔
3808
  case INS_ASSIGN_LOCALVAR:
723✔
3809
    return &Executor::ins_assign_localvar;
723✔
3810
  case INS_ASSIGN_CONSUME:
63✔
3811
    return &Executor::ins_assign_consume;
63✔
3812
  case TOK_CONSUMER:
16,803✔
3813
    return &Executor::ins_consume;
16,803✔
3814
  case TOK_ASSIGN:
3,965✔
3815
    return &Executor::ins_assign;
3,965✔
3816
  case INS_SUBSCRIPT_ASSIGN:
3✔
3817
    return &Executor::ins_array_assign;
3✔
3818
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
425✔
3819
    return &Executor::ins_array_assign_consume;
425✔
3820
  case INS_MULTISUBSCRIPT:
177✔
3821
    return &Executor::ins_multisubscript;
177✔
3822
  case INS_MULTISUBSCRIPT_ASSIGN:
6✔
3823
    return &Executor::ins_multisubscript_assign;
6✔
3824
  case INS_GET_MEMBER:
931✔
3825
    return &Executor::ins_get_member;
931✔
3826
  case INS_SET_MEMBER:
3✔
3827
    return &Executor::ins_set_member;
3✔
3828
  case INS_SET_MEMBER_CONSUME:
210✔
3829
    return &Executor::ins_set_member_consume;
210✔
3830

3831
  case INS_UNPACK_SEQUENCE:
106✔
3832
    return &Executor::ins_unpack_sequence;
106✔
3833
  case INS_UNPACK_INDICES:
81✔
3834
    return &Executor::ins_unpack_indices;
81✔
3835
  case INS_TAKE_GLOBAL:
183✔
3836
    return &Executor::ins_take_global;
183✔
3837
  case INS_TAKE_LOCAL:
292✔
3838
    return &Executor::ins_take_local;
292✔
3839

3840
  case INS_GET_MEMBER_ID:
1,479✔
3841
    return &Executor::ins_get_member_id;  // test id
1,479✔
3842
  case INS_SET_MEMBER_ID:
3✔
3843
    return &Executor::ins_set_member_id;  // test id
3✔
3844
  case INS_SET_MEMBER_ID_CONSUME:
190✔
3845
    return &Executor::ins_set_member_id_consume;  // test id
190✔
3846

3847
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15✔
3848
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15✔
3849
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6✔
3850
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6✔
3851
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
6✔
3852
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
6✔
3853
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6✔
3854
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6✔
3855
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6✔
3856
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6✔
3857

3858
  case TOK_ADD:
2,351✔
3859
    return &Executor::ins_add;
2,351✔
3860
  case TOK_SUBTRACT:
377✔
3861
    return &Executor::ins_subtract;
377✔
3862
  case TOK_DIV:
53✔
3863
    return &Executor::ins_div;
53✔
3864
  case TOK_MULT:
213✔
3865
    return &Executor::ins_mult;
213✔
3866
  case TOK_MODULUS:
30✔
3867
    return &Executor::ins_modulus;
30✔
3868

3869
  case TOK_INSERTINTO:
5,058✔
3870
    return &Executor::ins_insert_into;
5,058✔
3871

3872
  case TOK_PLUSEQUAL:
80✔
3873
    return &Executor::ins_plusequal;
80✔
3874
  case TOK_MINUSEQUAL:
27✔
3875
    return &Executor::ins_minusequal;
27✔
3876
  case TOK_TIMESEQUAL:
18✔
3877
    return &Executor::ins_timesequal;
18✔
3878
  case TOK_DIVIDEEQUAL:
15✔
3879
    return &Executor::ins_divideequal;
15✔
3880
  case TOK_MODULUSEQUAL:
18✔
3881
    return &Executor::ins_modulusequal;
18✔
3882

3883
  case TOK_LESSTHAN:
173✔
3884
    return &Executor::ins_lessthan;
173✔
3885
  case TOK_LESSEQ:
72✔
3886
    return &Executor::ins_lessequal;
72✔
3887
  case RSV_GOTO:
1,656✔
3888
    return &Executor::ins_goto;
1,656✔
3889
  case TOK_ARRAY_SUBSCRIPT:
1,109✔
3890
    return &Executor::ins_arraysubscript;
1,109✔
3891
  case TOK_EQUAL:
920✔
3892
    return &Executor::ins_equal;
920✔
3893
  case TOK_FUNC:
11,069✔
3894
    return &Executor::ins_func;
11,069✔
3895
  case INS_CALL_METHOD:
283✔
3896
    return &Executor::ins_call_method;
283✔
3897
  case INS_CALL_METHOD_ID:
2,763✔
3898
    return &Executor::ins_call_method_id;
2,763✔
3899
  case CTRL_STATEMENTBEGIN:
×
3900
    return &Executor::ins_statementbegin;
×
3901
  case CTRL_MAKELOCAL:
3,657✔
3902
    return &Executor::ins_makelocal;
3,657✔
3903
  case INS_CHECK_MRO:
213✔
3904
    return &Executor::ins_check_mro;
213✔
3905
  case CTRL_JSR_USERFUNC:
3,657✔
3906
    return &Executor::ins_jsr_userfunc;
3,657✔
3907
  case INS_POP_PARAM:
2,575✔
3908
    return &Executor::ins_pop_param;
2,575✔
3909
  case INS_POP_PARAM_BYREF:
1,116✔
3910
    return &Executor::ins_pop_param_byref;
1,116✔
3911
  case INS_GET_ARG:
139✔
3912
    return &Executor::ins_get_arg;
139✔
3913
  case CTRL_LEAVE_BLOCK:
1,476✔
3914
    return &Executor::ins_leave_block;
1,476✔
3915
  case RSV_GOSUB:
×
3916
    return &Executor::ins_gosub;
×
3917
  case RSV_RETURN:
4,005✔
3918
    return &Executor::ins_return;
4,005✔
3919
  case RSV_EXIT:
18✔
3920
    return &Executor::ins_exit;
18✔
3921
  case INS_DECLARE_ARRAY:
69✔
3922
    return &Executor::ins_declareArray;
69✔
3923
  case TOK_UNMINUS:
3✔
3924
    return &Executor::ins_unminus;
3✔
3925
  case TOK_UNPLUS:
×
3926
    return &Executor::ins_nop;
×
3927
  case TOK_LOG_NOT:
159✔
3928
    return &Executor::ins_logical_not;
159✔
3929
  case TOK_BITWISE_NOT:
12✔
3930
    return &Executor::ins_bitwise_not;
12✔
3931
  case TOK_BSRIGHT:
15✔
3932
    return &Executor::ins_bitshift_right;
15✔
3933
  case TOK_BSLEFT:
15✔
3934
    return &Executor::ins_bitshift_left;
15✔
3935
  case TOK_BITAND:
17✔
3936
    return &Executor::ins_bitwise_and;
17✔
3937
  case TOK_BITXOR:
15✔
3938
    return &Executor::ins_bitwise_xor;
15✔
3939
  case TOK_BITOR:
15✔
3940
    return &Executor::ins_bitwise_or;
15✔
3941

3942
  case TOK_NEQ:
639✔
3943
    return &Executor::ins_notequal;
639✔
3944
  case TOK_GRTHAN:
146✔
3945
    return &Executor::ins_greaterthan;
146✔
3946
  case TOK_GREQ:
84✔
3947
    return &Executor::ins_greaterequal;
84✔
3948
  case TOK_AND:
49✔
3949
    return &Executor::ins_logical_and;
49✔
3950
  case TOK_OR:
260✔
3951
    return &Executor::ins_logical_or;
260✔
3952

3953
  case TOK_ADDMEMBER:
96✔
3954
    return &Executor::ins_addmember;
96✔
3955
  case TOK_DELMEMBER:
9✔
3956
    return &Executor::ins_removemember;
9✔
3957
  case TOK_CHKMEMBER:
78✔
3958
    return &Executor::ins_checkmember;
78✔
3959
  case INS_DICTIONARY_ADDMEMBER:
132✔
3960
    return &Executor::ins_dictionary_addmember;
132✔
3961
  case TOK_IN:
47✔
3962
    return &Executor::ins_in;
47✔
3963
  case TOK_IS:
56✔
3964
    return &Executor::ins_is;
56✔
3965
  case INS_ADDMEMBER2:
45✔
3966
    return &Executor::ins_addmember2;
45✔
3967
  case INS_ADDMEMBER_ASSIGN:
1,224✔
3968
    return &Executor::ins_addmember_assign;
1,224✔
3969
  case CTRL_PROGEND:
2,515✔
3970
    return &Executor::ins_progend;
2,515✔
3971
  case TOK_UNPLUSPLUS:
91✔
3972
    return &Executor::ins_unplusplus;
91✔
3973
  case TOK_UNMINUSMINUS:
56✔
3974
    return &Executor::ins_unminusminus;
56✔
3975
  case TOK_UNPLUSPLUS_POST:
100✔
3976
    return &Executor::ins_unplusplus_post;
100✔
3977
  case TOK_UNMINUSMINUS_POST:
50✔
3978
    return &Executor::ins_unminusminus_post;
50✔
3979
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10✔
3980
    return &Executor::ins_set_member_id_unplusplus;  // test id
10✔
3981
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13✔
3982
    return &Executor::ins_set_member_id_unminusminus;  // test id
13✔
3983
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3✔
3984
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3✔
3985
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3✔
3986
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3✔
3987
  case INS_SKIPIFTRUE_ELSE_CONSUME:
190✔
3988
    return &Executor::ins_skipiftrue_else_consume;
190✔
3989
  case TOK_INTERPOLATE_STRING:
1,568✔
3990
    return &Executor::ins_interpolate_string;
1,568✔
3991
  case TOK_FORMAT_EXPRESSION:
89✔
3992
    return &Executor::ins_format_expression;
89✔
3993
  case TOK_BOOL:
146✔
3994
    return &Executor::ins_bool;
146✔
3995
  case INS_LOGICAL_JUMP:
38✔
3996
    return &Executor::ins_logical_jump;
38✔
3997
  case INS_LOGICAL_CONVERT:
38✔
3998
    return &Executor::ins_logical_convert;
38✔
3999
  default:
×
4000
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
4001
  }
4002
}
4003

4004
void Executor::sethalt( bool halt )
17✔
4005
{
4006
  halt_ = halt;
17✔
4007

4008
  if ( halt && dbg_env_ )
17✔
4009
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
9✔
4010
      listener->on_halt();
9✔
4011

4012
  calcrunnable();
17✔
4013
}
17✔
4014

4015
void Executor::execInstr()
77,286,691✔
4016
{
4017
  unsigned onPC = PC;
77,286,691✔
4018
  try
4019
  {  // this is really more of a class invariant.
4020
    passert( run_ok_ );
77,286,691✔
4021
    passert( PC < nLines );
77,286,691✔
4022
    passert( !error_ );
77,286,691✔
4023
    passert( !done );
77,286,691✔
4024

4025
#ifdef NDEBUG
4026
    const Instruction& ins = prog_->instr[PC];
77,286,691✔
4027
#else
4028
    const Instruction& ins = prog_->instr.at( PC );
4029
#endif
4030
    if ( debug_level >= INSTRUCTIONS )
77,286,691✔
4031
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
4032

4033
    // If `on_instruction` returns false, do not execute this instruction.
4034
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
77,286,691✔
4035
    {
4036
      return;
9✔
4037
    }
4038

4039
    ++ins.cycles;
77,286,682✔
4040
    ++prog_->instr_cycles;
77,286,682✔
4041
    ++escript_instr_cycles;
77,286,682✔
4042

4043
    ++PC;
77,286,682✔
4044

4045
    ( this->*( ins.func ) )( ins );
77,286,682✔
4046
  }
4047
  catch ( std::exception& ex )
3✔
4048
  {
4049
    std::string tmp =
4050
        fmt::format( "Exception in: {} PC={}: {}\n", prog_->name.get(), onPC, ex.what() );
3✔
4051
    if ( !run_ok_ )
3✔
4052
      tmp += "run_ok_ = false\n";
×
4053
    if ( PC < nLines )
3✔
4054
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
9✔
4055
    if ( error_ )
3✔
4056
      tmp += "error_ = true\n";
×
4057
    if ( done )
3✔
4058
      tmp += "done = true\n";
×
4059

4060
    seterror( true );
3✔
4061
    POLLOG_ERROR( tmp );
3✔
4062

4063
    show_context( onPC );
3✔
4064
  }
3✔
4065
#ifdef __unix__
4066
  catch ( ... )
×
4067
  {
4068
    seterror( true );
×
4069
    POLLOG_ERRORLN( "Exception in {}, PC={}: unclassified", prog_->name.get(), onPC );
×
4070

4071
    show_context( onPC );
×
4072
  }
×
4073
#endif
4074
}
4075

4076
std::string Executor::dbg_get_instruction( size_t atPC ) const
33✔
4077
{
4078
  std::string out;
33✔
4079
  dbg_get_instruction( atPC, out );
33✔
4080
  return out;
33✔
4081
}
×
4082

4083
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
297✔
4084
{
4085
  bool has_breakpoint =
4086
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
297✔
4087
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
594✔
4088
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
297✔
4089
}
297✔
4090

4091
void Executor::show_context( unsigned atPC )
3✔
4092
{
4093
  unsigned start, end;
4094
  if ( atPC >= 5 )
3✔
4095
    start = atPC - 5;
3✔
4096
  else
4097
    start = 0;
×
4098

4099
  end = atPC + 5;
3✔
4100

4101
  if ( end >= nLines )
3✔
4102
    end = nLines - 1;
×
4103

4104
  for ( unsigned i = start; i <= end; ++i )
36✔
4105
  {
4106
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
33✔
4107
  }
4108
}
3✔
4109
void Executor::show_context( std::string& os, unsigned atPC )
24✔
4110
{
4111
  unsigned start, end;
4112
  if ( atPC >= 5 )
24✔
4113
    start = atPC - 5;
24✔
4114
  else
4115
    start = 0;
×
4116

4117
  end = atPC + 5;
24✔
4118

4119
  if ( end >= nLines )
24✔
4120
    end = nLines - 1;
×
4121

4122
  for ( unsigned i = start; i <= end; ++i )
288✔
4123
  {
4124
    dbg_get_instruction( i, os );
264✔
4125
    os += '\n';
264✔
4126
  }
4127
}
24✔
4128

4129
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
10,425✔
4130
                                        const Instruction& jmp )
4131
{
4132
  // params need to be on the stack, without current objectref
4133
  ValueStack.pop_back();
10,425✔
4134

4135
  // Push captured parameters onto the stack prior to function parameters.
4136
  for ( auto& p : funcr->captures )
10,871✔
4137
    ValueStack.push_back( p );
446✔
4138

4139
  auto nparams = static_cast<int>( fparams.size() );
10,425✔
4140

4141
  // Handle variadic functions special. Construct an array{} corresponding to
4142
  // the rest parameter (the last parameter for the function). The logic for the
4143
  // condition:
4144
  // - if true, the last argument in the call may not be an array{}, so we need
4145
  //   to construct one (which is why the condition is `>=` and not `>`).
4146
  // - if false, then the address we're jumping to will be a "default argument
4147
  //   address" and _not_ the user function directly, which will create the
4148
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
4149
  if ( funcr->variadic() && nparams >= funcr->numParams() )
10,425✔
4150
  {
4151
    auto num_nonrest_args = funcr->numParams() - 1;
628✔
4152

4153
    auto rest_arg = std::make_unique<ObjArray>();
628✔
4154

4155
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,601✔
4156
    {
4157
      auto& p = fparams[i];
2,973✔
4158

4159
      if ( i < num_nonrest_args )
2,973✔
4160
      {
4161
        ValueStack.push_back( p );
591✔
4162
      }
4163
      else
4164
      {
4165
        rest_arg->ref_arr.push_back( p );
2,382✔
4166
      }
4167
    }
4168
    ValueStack.push_back( BObjectRef( rest_arg.release() ) );
628✔
4169
  }
628✔
4170
  // The array{} will be created via the regular default-parameter handling by
4171
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
4172
  // prior to jumping to the user function.
4173
  else
4174
  {
4175
    for ( auto& p : fparams )
21,744✔
4176
      ValueStack.push_back( p );
11,947✔
4177
  }
4178

4179
  // jump to function
4180
  jump( jmp.token.lval, continuation, funcr );
10,425✔
4181
  fparams.clear();
10,425✔
4182
  // switch to new block
4183
  ins_makelocal( jmp );
10,425✔
4184
}
10,425✔
4185

4186
bool Executor::exec()
2,361✔
4187
{
4188
  passert( prog_ok_ );
2,361✔
4189
  passert( !error_ );
2,361✔
4190

4191
  Clib::scripts_thread_script = scriptname();
2,361✔
4192

4193
  set_running_to_completion( true );
2,361✔
4194
  while ( runnable() )
442,139✔
4195
  {
4196
    Clib::scripts_thread_scriptPC = PC;
439,778✔
4197
    execInstr();
439,778✔
4198
  }
4199

4200
  return !error_;
2,361✔
4201
}
4202

4203
void Executor::reinitExec()
×
4204
{
4205
  PC = 0;
×
4206
  done = 0;
×
4207
  seterror( false );
×
4208

4209
  ValueStack.clear();
×
4210
  delete Locals2;
×
4211
  Locals2 = new BObjectRefVec;
×
4212

4213
  if ( !prog_ok_ )
×
4214
  {
4215
    seterror( true );
×
4216
  }
4217
}
×
4218

4219
void Executor::initForFnCall( unsigned in_PC )
786✔
4220
{
4221
#ifdef MEMORYLEAK
4222
  bool data_shown = false;
4223
#endif
4224

4225
  PC = in_PC;
786✔
4226
  done = 0;
786✔
4227
  seterror( false );
786✔
4228

4229
#ifdef MEMORYLEAK
4230
  while ( !ValueStack.empty() )
4231
  {
4232
    if ( Clib::memoryleak_debug )
4233
    {
4234
      if ( !data_shown )
4235
      {
4236
        LEAKLOG( "ValueStack... " );
4237
        data_shown = true;
4238
      }
4239

4240
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
4241
               ValueStack.back()->impptr()->sizeEstimate() );
4242
    }
4243
    ValueStack.pop_back();
4244
  }
4245
  if ( Clib::memoryleak_debug )
4246
    if ( data_shown )
4247
      LEAKLOGLN( " ...deleted" );
4248
#endif
4249

4250
  ValueStack.clear();
786✔
4251
  Locals2->clear();
786✔
4252
}
786✔
4253

4254
void Executor::pushArg( BObjectImp* arg )
1,181✔
4255
{
4256
  passert_always( arg );
1,181✔
4257
  ValueStack.push_back( BObjectRef( arg ) );
1,181✔
4258
}
1,181✔
4259

4260
void Executor::pushArg( const BObjectRef& arg )
×
4261
{
4262
  ValueStack.push_back( arg );
×
4263
}
×
4264

4265
void Executor::addModule( ExecutorModule* module )
19,818✔
4266
{
4267
  availmodules.push_back( module );
19,818✔
4268
}
19,818✔
4269

4270

4271
ExecutorModule* Executor::findModule( const std::string& name )
3,203✔
4272
{
4273
  unsigned idx;
4274
  for ( idx = 0; idx < availmodules.size(); idx++ )
16,856✔
4275
  {
4276
    ExecutorModule* module = availmodules[idx];
16,856✔
4277
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
16,856✔
4278
      return module;
3,203✔
4279
  }
4280
  return nullptr;
×
4281
}
4282

4283
bool Executor::attach_debugger( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching )
4✔
4284
{
4285
  // FIXME: a script can be in debugging state but have no debugger attached,
4286
  // eg. a script that called `os::Debugger()`. This needs to check if a
4287
  // debugger is attached. This works for `os::Debugger()` but not for poldbg cmd_attach.
4288
  if ( dbg_env_ )
4✔
4289
  {
4290
    if ( !listener.expired() )
2✔
4291
    {
4292
      auto& dbg_env_listener = dbg_env_->listener;
1✔
4293
      if ( !dbg_env_listener.expired() )
1✔
4294
      {
4295
        return false;
×
4296
      }
4297
      dbg_env_listener = listener;
1✔
4298
    }
4299
    if ( set_attaching )
2✔
4300
      dbg_env_->debug_state = ExecutorDebugState::ATTACHING;
2✔
4301
  }
4302
  else
4303
  {
4304
    dbg_env_ = std::make_unique<ExecutorDebugEnvironment>( listener, set_attaching );
2✔
4305
  }
4306

4307
  return true;
4✔
4308
}
4309

4310
void Executor::detach_debugger()
×
4311
{
4312
  dbg_env_.reset();
×
4313
  sethalt( false );
×
4314
}
×
4315

4316
void Executor::print_to_debugger( const std::string& message )
21,876✔
4317
{
4318
  if ( dbg_env_ )
21,876✔
4319
  {
4320
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
1✔
4321
      listener->on_print( message );
1✔
4322
  }
4323
}
21,876✔
4324

4325
void Executor::dbg_ins_trace()
×
4326
{
4327
  if ( dbg_env_ )
×
4328
  {
4329
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4330
  }
4331
  sethalt( false );
×
4332
}
×
4333
void Executor::dbg_step_into()
1✔
4334
{
4335
  if ( dbg_env_ )
1✔
4336
  {
4337
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1✔
4338
  }
4339
  sethalt( false );
1✔
4340
}
1✔
4341
void Executor::dbg_step_over()
2✔
4342
{
4343
  if ( dbg_env_ )
2✔
4344
  {
4345
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2✔
4346
  }
4347
  sethalt( false );
2✔
4348
}
2✔
4349
void Executor::dbg_step_out()
1✔
4350
{
4351
  if ( dbg_env_ )
1✔
4352
  {
4353
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1✔
4354
  }
4355
  sethalt( false );
1✔
4356
}
1✔
4357
void Executor::dbg_run()
4✔
4358
{
4359
  if ( dbg_env_ )
4✔
4360
  {
4361
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4✔
4362
  }
4363
  sethalt( false );
4✔
4364
}
4✔
4365
void Executor::dbg_break()
1✔
4366
{
4367
  if ( dbg_env_ )
1✔
4368
  {
4369
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1✔
4370
  }
4371
}
1✔
4372

4373
void Executor::dbg_setbp( unsigned atPC )
2✔
4374
{
4375
  if ( dbg_env_ )
2✔
4376
  {
4377
    dbg_env_->breakpoints.insert( atPC );
2✔
4378
  }
4379
}
2✔
4380
void Executor::dbg_clrbp( unsigned atPC )
×
4381
{
4382
  if ( dbg_env_ )
×
4383
  {
4384
    dbg_env_->breakpoints.erase( atPC );
×
4385
  }
4386
}
×
4387

4388
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1✔
4389
{
4390
  if ( dbg_env_ )
1✔
4391
  {
4392
    std::set<unsigned> result;
1✔
4393
    auto& breakpoints = dbg_env_->breakpoints;
1✔
4394
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1✔
4395
                         std::inserter( result, result.end() ) );
4396
    breakpoints = result;
1✔
4397
  }
1✔
4398
}
1✔
4399

4400
void Executor::dbg_clrallbp()
×
4401
{
4402
  if ( dbg_env_ )
×
4403
  {
4404
    dbg_env_->breakpoints.clear();
×
4405
  }
4406
}
×
4407

4408
size_t Executor::sizeEstimate() const
1,865✔
4409
{
4410
  size_t size = sizeof( *this );
1,865✔
4411
  size += Clib::memsize( upperLocals2 );
1,865✔
4412
  for ( const auto& bobjectrefvec : upperLocals2 )
1,865✔
4413
  {
4414
    size += Clib::memsize( *bobjectrefvec );
×
4415
    for ( const auto& bojectref : *bobjectrefvec )
×
4416
    {
4417
      if ( bojectref != nullptr )
×
4418
        size += bojectref->sizeEstimate();
×
4419
    }
4420
  }
4421
  size += Clib::memsize( ControlStack );
1,865✔
4422

4423
  size += Clib::memsize( *Locals2 );
1,865✔
4424
  for ( const auto& bojectref : *Locals2 )
1,982✔
4425
  {
4426
    if ( bojectref != nullptr )
117✔
4427
      size += bojectref->sizeEstimate();
117✔
4428
  }
4429
  size += Clib::memsize( *Globals2 );
1,865✔
4430
  for ( const auto& bojectref : *Globals2 )
3,919✔
4431
  {
4432
    if ( bojectref != nullptr )
2,054✔
4433
      size += bojectref->sizeEstimate();
2,054✔
4434
  }
4435
  size += Clib::memsize( ValueStack );
1,865✔
4436
  for ( const auto& bojectref : ValueStack )
1,903✔
4437
  {
4438
    if ( bojectref != nullptr )
38✔
4439
      size += bojectref->sizeEstimate();
38✔
4440
  }
4441
  size += Clib::memsize( fparams );
1,865✔
4442
  for ( const auto& bojectref : fparams )
1,865✔
4443
  {
4444
    if ( bojectref != nullptr )
×
4445
      size += bojectref->sizeEstimate();
×
4446
  }
4447
  for ( const auto& module : availmodules )
15,088✔
4448
  {
4449
    if ( module != nullptr )
13,223✔
4450
      size += module->sizeEstimate();
13,223✔
4451
  }
4452
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
1,865✔
4453
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
1,865✔
4454
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
1,865✔
4455
  size += Clib::memsize( class_methods );
1,865✔
4456
  return size;
1,865✔
4457
}
4458

4459
bool Executor::builtinMethodForced( const char*& methodname )
7✔
4460
{
4461
  if ( methodname[0] == '_' )
7✔
4462
  {
4463
    ++methodname;
×
4464
    return true;
×
4465
  }
4466
  return false;
7✔
4467
}
4468

4469
#ifdef ESCRIPT_PROFILE
4470
void Executor::profile_escript( std::string name, unsigned long profile_start )
4471
{
4472
  unsigned long profile_end = GetTimeUs() - profile_start;
4473
  escript_profile_map::iterator itr = EscriptProfileMap.find( name );
4474
  if ( itr != EscriptProfileMap.end() )
4475
  {
4476
    itr->second.count++;
4477
    itr->second.sum += profile_end;
4478
    if ( itr->second.max < profile_end )
4479
      itr->second.max = profile_end;
4480
    else if ( itr->second.min > profile_end )
4481
      itr->second.min = profile_end;
4482
  }
4483
  else
4484
  {
4485
    profile_instr profInstr;
4486
    profInstr.count = 1;
4487
    profInstr.max = profile_end;
4488
    profInstr.min = profile_end;
4489
    profInstr.sum = profile_end;
4490
    EscriptProfileMap[name] = profInstr;
4491
  }
4492
}
4493
#ifdef _WIN32
4494

4495
unsigned long Executor::GetTimeUs()
4496
{
4497
  static bool bInitialized = false;
4498
  static LARGE_INTEGER lFreq, lStart;
4499
  static LARGE_INTEGER lDivisor;
4500
  if ( !bInitialized )
4501
  {
4502
    bInitialized = true;
4503
    QueryPerformanceFrequency( &lFreq );
4504
    QueryPerformanceCounter( &lStart );
4505
    lDivisor.QuadPart = lFreq.QuadPart / 1000000;
4506
  }
4507

4508
  LARGE_INTEGER lEnd;
4509
  QueryPerformanceCounter( &lEnd );
4510
  double duration = double( lEnd.QuadPart - lStart.QuadPart ) / lFreq.QuadPart;
4511
  duration *= 1000000;
4512
  LONGLONG llDuration = static_cast<LONGLONG>( duration );
4513
  return llDuration & 0xffffffff;
4514
}
4515
#else
4516
unsigned long Executor::GetTimeUs()
4517
{
4518
  static bool bInitialized = false;
4519
  static timeval t1;
4520
  if ( !bInitialized )
4521
  {
4522
    bInitialized = true;
4523
    gettimeofday( &t1, nullptr );
4524
  }
4525

4526
  timeval t2;
4527
  gettimeofday( &t2, nullptr );
4528

4529
  double elapsedTime;
4530
  elapsedTime = ( t2.tv_sec - t1.tv_sec ) * 1000000.0;
4531
  elapsedTime += ( t2.tv_usec - t1.tv_usec );
4532

4533
  long long llDuration = static_cast<long long>( elapsedTime );
4534
  return llDuration & 0xffffffff;
4535
}
4536
#endif
4537
#endif
4538
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
7,829✔
4539
{
4540
  auto* func = continuation->func();
7,829✔
4541

4542
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4543
  // there are too many
4544
  while ( func->numParams() > static_cast<int>( args.size() ) )
7,835✔
4545
  {
4546
    args.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
6✔
4547
  }
4548

4549
  // Resize args only for non-varadic functions
4550
  if ( !func->variadic() )
7,829✔
4551
    args.resize( func->numParams() );
7,805✔
4552

4553
  continuation->args = std::move( args );
7,829✔
4554

4555
  return continuation;
7,829✔
4556
}
4557

4558
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
1,375✔
4559
{
4560
  // Compare the program pointers
4561
  if ( prog < other.prog )
1,375✔
4562
    return true;
×
4563
  if ( prog > other.prog )
1,375✔
4564
    return false;
×
4565

4566
  // Compare the indices
4567
  if ( index < other.index )
1,375✔
4568
    return true;
141✔
4569
  if ( index > other.index )
1,234✔
4570
    return false;
60✔
4571

4572
  // Perform a case-insensitive comparison for method_name using stricmp
4573
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
1,174✔
4574
}
4575
}  // namespace Bscript
4576
}  // namespace Pol
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc