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

polserver / polserver / 13378462461

17 Feb 2025 08:59PM UTC coverage: 58.754% (+0.3%) from 58.475%
13378462461

push

github

web-flow
Add support for sequence and index bindings (#760)

* update grammar

* add ast nodes; ast building

* Rename to unpacking

* semantic analysis

* executor work part 1, unpacking indices

* renamings; implement index binding

* small cleanup

* use multi_index; more tests

* use multi_index only for rest, otherwise list

* initial formatting

* add missing token decoding

* address self-review comments

* formatting tweaks

* add test for var binding in classes

* add StringIterator

* fix spread tests

* add cfgfile iterator; add cfgelem opersubscript; tests

* add iterator for SQLResultSet and SQLRow; tests

* Copy value in take global/local

* Allow any iterable can index rest unpacking
Always use dictionary as rest object in index unpacking

* add docs, core-changes, doc tests

* formatting changes...

* address self-review comments

* update formatter, format all binding test srcs

* address review comments
- unset var scope

* add cfgfile/cfgelem docs

* reformat objref svg

501 of 550 new or added lines in 19 files covered. (91.09%)

14 existing lines in 3 files now uncovered.

42235 of 71885 relevant lines covered (58.75%)

377989.47 hits per line

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

83.02
/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()
1,406✔
196
    : done( 0 ),
1,406✔
197
      error_( false ),
1,406✔
198
      halt_( false ),
1,406✔
199
      run_ok_( false ),
1,406✔
200
      debug_level( NONE ),
1,406✔
201
      PC( 0 ),
1,406✔
202
      Globals2( std::make_shared<BObjectRefVec>() ),
1,406✔
203
      Locals2( new BObjectRefVec ),
1,406✔
204
      nLines( 0 ),
1,406✔
205
      current_module_function( nullptr ),
1,406✔
206
      prog_ok_( false ),
1,406✔
207
      viewmode_( false ),
1,406✔
208
      runs_to_completion_( false ),
1,406✔
209
      dbg_env_( nullptr ),
1,406✔
210
      func_result_( nullptr )
4,218✔
211
{
212
  Clib::SpinLockGuard lock( _executor_lock );
1,406✔
213
  ++executor_count;
1,406✔
214
  executor_instances.insert( this );
1,406✔
215

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

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

240
  delete Locals2;
1,626✔
241
  Locals2 = nullptr;
1,626✔
242

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

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

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

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

272
    if ( !fm->have_indexes )
1,898✔
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++ )
3,952✔
280
      {
281
        ModuleFunction* func = fm->functions[fidx];
2,291✔
282
        // FIXME: should check number of params, blah.
283
        if ( !func->name.get().empty() )
2,291✔
284
        {
285
          func->funcidx = em->functionIndex( func->name.get() );
2,291✔
286
          if ( func->funcidx == -1 )
2,291✔
287
          {
288
            ERROR_PRINTLN( "Unable to find {}::{}", fm->modulename.get(), func->name.get() );
×
289
            return false;
×
290
          }
291
        }
292
      }
293
      fm->have_indexes = true;
1,661✔
294
    }
295
  }
296
  return true;
1,434✔
297
}
298

299
int Executor::getParams( unsigned howMany )
9,742,027✔
300
{
301
  if ( howMany )
9,742,027✔
302
  {
303
    fparams.resize( howMany );
9,735,450✔
304
    for ( int i = howMany - 1; i >= 0; --i )
19,532,554✔
305
    {
306
      if ( ValueStack.empty() )
9,797,104✔
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();
9,797,104✔
313
      ValueStack.pop_back();
9,797,104✔
314
    }
315
  }
316
  expandParams();
9,742,027✔
317
  return 0;
9,742,027✔
318
}
319

320
void Executor::expandParams()
9,742,027✔
321
{
322
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
19,539,950✔
323
  {
324
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
9,797,923✔
325
    {
326
      // defer destruction
327
      BObjectRef obj( spread );
419✔
328

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

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

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

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

339
      int added = 0;
419✔
340
      while ( next != nullptr )
1,238✔
341
      {
342
        fparams.insert( fparams.begin() + i + added, BObjectRef( next ) );
819✔
343
        next = pIter->step();
819✔
344
        added++;
819✔
345
      }
346
      i += added;
419✔
347
    }
419✔
348
  }
349
}
9,742,027✔
350

351
void Executor::cleanParams()
9,740,429✔
352
{
353
  fparams.clear();
9,740,429✔
354
}
9,740,429✔
355

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

364
  fparams[param].set( new BObject( new String( obj->impref() ) ) );
32✔
365

366
  return 0;
32✔
367
}
368

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

374
  String* str = (String*)objimp;
4,442✔
375
  return str ? str->data() : "";
4,442✔
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>() )
×
386
    fparams[param].set( new BObject( new Double( v->value() ) ) );
×
387
  else
388
    fparams[param].set( new BObject( 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 )
9,690,631✔
402
{
403
  BObjectImp* objimp = getParam( param )->impptr();
9,690,631✔
404
  if ( auto* l = impptrIf<BLong>( objimp ) )
9,690,631✔
405
    return l->value();
9,690,631✔
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 )
9,699,039✔
411
{
412
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
9,699,039✔
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();
9,699,039✔
418
}
419

420
BObjectImp* Executor::getParamImp( unsigned param )
40,217✔
421
{
422
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
40,217✔
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();
40,217✔
428
}
429

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

442
BObjectImp* Executor::getParamImp( unsigned param, BObjectImp::BObjectType type )
32,160✔
443
{
444
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
32,160✔
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();
32,160✔
450

451
  passert( imp != nullptr );
32,160✔
452

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

476
BObjectImp* Executor::getParamImp2( unsigned param, BObjectImp::BObjectType type )
25,713✔
477
{
478
  passert_r( param < fparams.size(), "Script Error in '" + scriptname() +
25,713✔
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();
25,713✔
484

485
  passert( imp != nullptr );
25,713✔
486

487
  if ( imp->isa( type ) )
25,713✔
488
  {
489
    return imp;
25,713✔
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 )
25,403✔
503
{
504
  return Clib::explicit_cast<String*, BObjectImp*>( getParamImp( param, BObjectImp::OTString ) );
25,403✔
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 )
25,381✔
513
{
514
  pstr = getStringParam( param );
25,381✔
515
  return ( pstr != nullptr );
25,381✔
516
}
517

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

525
  value = plong->value();
5,854✔
526
  return true;
5,854✔
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 = "" )
1,378✔
535
{
536
  if ( debug_level < INSTRUCTIONS )
1,378✔
537
    return;
1,378✔
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 )
316✔
591
{
592
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
316✔
593
  if ( imp )
316✔
594
  {
595
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
316✔
596

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

617
bool Executor::getRealParam( unsigned param, double& value )
106✔
618
{
619
  BObjectImp* imp = getParamImp( param );
106✔
620
  if ( auto* d = impptrIf<Double>( imp ) )
106✔
621
  {
622
    value = d->value();
26✔
623
    return true;
26✔
624
  }
625
  else if ( auto* l = impptrIf<BLong>( imp ) )
80✔
626
  {
627
    value = l->value();
46✔
628
    return true;
46✔
629
  }
630
  else
631
  {
632
    DEBUGLOGLN(
34✔
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,
34✔
637
        BObjectImp::typestr( imp->type() ) );
68✔
638

639
    return false;
34✔
640
  }
641
}
642

643
bool Executor::getObjArrayParam( unsigned param, ObjArray*& pobjarr )
345✔
644
{
645
  pobjarr =
345✔
646
      Clib::explicit_cast<ObjArray*, BObjectImp*>( getParamImp( param, BObjectImp::OTArray ) );
345✔
647
  return ( pobjarr != nullptr );
345✔
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 )
108✔
674
{
675
  auto aob = static_cast<BApplicObjBase*>( getParamImp( param, BObjectImp::OTApplicObj ) );
108✔
676
  if ( aob == nullptr )
108✔
677
    return nullptr;
×
678

679
  if ( aob->object_type() == object_type )
108✔
680
  {
681
    return aob;
108✔
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,094✔
724
                         unsigned short maxval )
725
{
726
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,094✔
727
  if ( imp )
6,094✔
728
  {
729
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
6,094✔
730

731
    int longvalue = plong->value();
6,094✔
732
    if ( longvalue >= minval && longvalue <= maxval )
6,094✔
733
    {
734
      value = static_cast<unsigned short>( longvalue );
6,094✔
735
      return true;
6,094✔
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,762✔
752
{
753
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
12,762✔
754
  if ( imp )
12,762✔
755
  {
756
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
12,762✔
757

758
    int longvalue = plong->value();
12,762✔
759
    if ( longvalue >= 0 && longvalue <= USHRT_MAX )
12,762✔
760
    {
761
      value = static_cast<unsigned short>( longvalue );
12,762✔
762
      return true;
12,762✔
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 )
228✔
807
{
808
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
228✔
809
  if ( imp )
228✔
810
  {
811
    BLong* plong = Clib::explicit_cast<BLong*, BObjectImp*>( imp );
228✔
812

813
    int longvalue = plong->value();
228✔
814
    if ( longvalue >= (int)SHRT_MIN && longvalue <= (int)SHRT_MAX )
228✔
815
    {
816
      value = static_cast<short>( longvalue );
228✔
817
      return true;
228✔
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,290✔
891
{
892
  BObjectImp* imp = getParamImp2( param, BObjectImp::OTLong );
6,290✔
893
  if ( !imp )
6,290✔
894
    return false;
×
895

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

898
  int longvalue = plong->value();
6,290✔
899
  if ( longvalue >= std::numeric_limits<s8>::min() && longvalue <= std::numeric_limits<s8>::max() )
6,290✔
900
  {
901
    value = static_cast<signed char>( longvalue );
6,290✔
902
    return true;
6,290✔
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 )
11✔
916
{
917
  BObjectImp* imp = getParamImp( param );
11✔
918
  if ( auto* b = impptrIf<BBoolean>( imp ) )
11✔
919
  {
920
    value = b->value();
1✔
921
    return true;
1✔
922
  }
923
  else if ( auto* l = impptrIf<BLong>( imp ) )
10✔
924
  {
925
    value = l->isTrue();
10✔
926
    return true;
10✔
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 );
×
954
    fparams[param].set( new BObject( 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 )
1,406✔
990
{
991
  prog_.set( i_prog );
1,406✔
992
  prog_ok_ = false;
1,406✔
993
  seterror( true );
1,406✔
994
  if ( !viewmode_ )
1,406✔
995
  {
996
    if ( !AttachFunctionalityModules() )
1,406✔
997
      return false;
×
998
  }
999

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

1002
  Globals2->clear();
1,406✔
1003
  for ( unsigned i = 0; i < prog_->nglobals; ++i )
2,875✔
1004
  {
1005
    Globals2->push_back( BObjectRef() );
1,469✔
1006
    Globals2->back().set( new BObject( UninitObject::create() ) );
1,469✔
1007
  }
1008

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

1015
BObjectRef Executor::getObjRef( void )
12,072✔
1016
{
1017
  if ( ValueStack.empty() )
12,072✔
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();
12,072✔
1025
  ValueStack.pop_back();
12,072✔
1026
  return ref;
12,072✔
1027
}
12,072✔
1028

1029

1030
void Executor::execFunc( const Token& token )
9,724,608✔
1031
{
1032
  FunctionalityModule* fm = prog_->modules[token.module];
9,724,608✔
1033
  ModuleFunction* modfunc = fm->functions[token.lval];
9,724,608✔
1034
  current_module_function = modfunc;
9,724,608✔
1035
  if ( modfunc->funcidx == -1 )
9,724,608✔
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];
9,724,608✔
1046

1047
  func_result_ = nullptr;
9,724,608✔
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 );
9,724,608✔
1057
#ifdef ESCRIPT_PROFILE
1058
  profile_escript( name, profile_start );
1059
#endif
1060

1061
  if ( func_result_ )
9,724,608✔
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 )
9,724,600✔
1071
  {
1072
    ValueStack.push_back( BObjectRef( new BObject( resimp ) ) );
9,724,599✔
1073
  }
1074
  else
1075
  {
1076
    ValueStack.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
1✔
1077
  }
1078

1079
  current_module_function = nullptr;
9,724,608✔
1080
  return;
9,724,608✔
1081
}
1082

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

1088
  Locals2->push_back( BObjectRef() );
10,893✔
1089
  Locals2->back().set( new BObject( UninitObject::create() ) );
10,893✔
1090

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

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

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

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

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

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

1117
  Locals2->push_back( BObjectRef() );
8,688✔
1118
  Locals2->back().set( new BObject( objref->impptr()->copy() ) );
8,688✔
1119
}
8,688✔
1120

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

1125
  Locals2->push_back( BObjectRef( objref ) );
1,836✔
1126
}
1,836✔
1127

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

1143

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

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

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

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

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

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

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

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

1177
  return left.impref().operDotQMark( varname.data() );
879✔
1178
}
1179

1180

1181
ContIterator::ContIterator() : BObjectImp( BObjectImp::OTUnknown ) {}
1,971✔
1182
BObject* ContIterator::step()
12✔
1183
{
1184
  return nullptr;
12✔
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 )
1,851✔
1213
    : ContIterator(),
1214
      m_Index( 0 ),
1,851✔
1215
      m_Array( pArr ),
1,851✔
1216
      m_pArray( pArr ),
1,851✔
1217
      m_IterVal( pIterVal ),
1,851✔
1218
      m_pIterVal( new BLong( 0 ) )
3,702✔
1219
{
1220
  m_IterVal.get()->setimp( m_pIterVal );
1,851✔
1221
}
1,851✔
1222
BObject* ArrayIterator::step()
16,905✔
1223
{
1224
  m_pIterVal->increment();
16,905✔
1225
  if ( ++m_Index > m_pArray->ref_arr.size() )
16,905✔
1226
    return nullptr;
1,785✔
1227

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

1238
ContIterator* BObjectImp::createIterator( BObject* /*pIterVal*/ )
16✔
1239
{
1240
  return new ContIterator();
16✔
1241
}
1242
ContIterator* ObjArray::createIterator( BObject* pIterVal )
1,851✔
1243
{
1244
  auto pItr = new ArrayIterator( this, pIterVal );
1,851✔
1245
  return pItr;
1,851✔
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 )
476✔
1257
{
1258
  Locals2->push_back( BObjectRef( UninitObject::create() ) );  // the iterator
476✔
1259

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

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

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

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

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

1280
  BObject* next = pIter->step();
12,831✔
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 )
12,831✔
1284
  {
1285
    ( *Locals2 )[locsize - 3].set( next );
12,405✔
1286
    PC = ins.token.lval;
12,405✔
1287
  }
1288
}
12,831✔
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 )
45✔
1300
{
1301
  BObjectRef endref = getObjRef();
45✔
1302
  BObjectRef startref = getObjRef();
45✔
1303
  if ( *startref.get() > *endref.get() )
45✔
1304
  {
1305
    PC = ins.token.lval;
×
1306
    return;
×
1307
  }
1308

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

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

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

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

1330

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

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

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

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

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

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

1532

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

1537
  if ( objref->impptr()->isTrue() )
5,854✔
1538
    PC = (unsigned)ins.token.lval;
5,771✔
1539

1540
  ValueStack.pop_back();
5,854✔
1541
}
5,854✔
1542

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

1547
  if ( !objref->impptr()->isTrue() )
9,707,530✔
1548
    PC = (unsigned)ins.token.lval;
16,040✔
1549

1550
  ValueStack.pop_back();
9,707,530✔
1551
}
9,707,530✔
1552

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

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

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

1576
    std::string joined;
7,547✔
1577
    joined.reserve( length );
7,547✔
1578

1579
    while ( !contents.empty() )
26,150✔
1580
    {
1581
      joined += contents.back();
18,603✔
1582
      contents.pop_back();
18,603✔
1583
    }
1584

1585
    ValueStack.push_back( BObjectRef( new BObject( new String( joined ) ) ) );
7,547✔
1586
  }
7,547✔
1587
}
7,549✔
1588

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

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

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

1602
void Executor::ins_skipiftrue_else_consume( const Instruction& ins )
219✔
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();
219✔
1615

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

1626

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1831
  BObject& left = *leftref;
2,776✔
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() );
2,776✔
1842
#ifdef ESCRIPT_PROFILE
1843
  profile_escript( name, profile_start );
1844
#endif
1845
}
2,776✔
1846

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

1851
  BObject& left = *leftref;
14,338✔
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 );
14,338✔
1862
#ifdef ESCRIPT_PROFILE
1863
  profile_escript( name, profile_start );
1864
#endif
1865
}
14,338✔
1866

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

1871
  BObjectRef& rightref = ValueStack.back();
5,505✔
1872

1873
  BObject& right = *rightref;
5,505✔
1874

1875
  BObjectImp& rightimpref = right.impref();
5,505✔
1876

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

1891
  BObjectRef& rightref = ValueStack.back();
737✔
1892

1893
  BObject& right = *rightref;
737✔
1894

1895
  BObjectImp& rightimpref = right.impref();
737✔
1896

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

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

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

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

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

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

1942
  BObject& right = *rightref;
14,031✔
1943
  BObject& left = *leftref;
14,031✔
1944

1945
  BObjectImp& rightimpref = right.impref();
14,031✔
1946

1947
  if ( right.count() == 1 && rightimpref.count() == 1 )
14,031✔
1948
  {
1949
    left.setimp( &rightimpref );
12,666✔
1950
  }
1951
  else
1952
  {
1953
    left.setimp( rightimpref.copy() );
1,365✔
1954
  }
1955
}
14,031✔
1956

1957
void Executor::ins_array_assign( const Instruction& /*ins*/ )
×
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();
×
1970
  ValueStack.pop_back();
×
1971
  BObjectRef i_ref = ValueStack.back();
×
1972
  ValueStack.pop_back();
×
1973
  BObjectRef& x_ref = ValueStack.back();
×
1974

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

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

1982
  x_ref.set( new BObject( result ) );
×
1983
}
×
1984
void Executor::ins_array_assign_consume( const Instruction& /*ins*/ )
465✔
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();
465✔
1997
  ValueStack.pop_back();
465✔
1998
  BObjectRef i_ref = ValueStack.back();
465✔
1999
  ValueStack.pop_back();
465✔
2000
  BObjectRef& x_ref = ValueStack.back();
465✔
2001

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

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

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

2013
// TOK_ADD:
2014
void Executor::ins_add( const Instruction& /*ins*/ )
13,076✔
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();
13,076✔
2022
  ValueStack.pop_back();
13,076✔
2023
  BObjectRef& leftref = ValueStack.back();
13,076✔
2024

2025
  BObject& right = *rightref;
13,076✔
2026
  BObject& left = *leftref;
13,076✔
2027

2028
  leftref.set( new BObject( right.impref().selfPlusObjImp( left.impref() ) ) );
13,076✔
2029
}
13,076✔
2030

2031
// TOK_SUBTRACT
2032
void Executor::ins_subtract( const Instruction& /*ins*/ )
1,283✔
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();
1,283✔
2040
  ValueStack.pop_back();
1,283✔
2041
  BObjectRef& leftref = ValueStack.back();
1,283✔
2042

2043
  BObject& right = *rightref;
1,283✔
2044
  BObject& left = *leftref;
1,283✔
2045

2046
  leftref.set( new BObject( right.impref().selfMinusObjImp( left.impref() ) ) );
1,283✔
2047
}
1,283✔
2048

2049
// TOK_MULT:
2050
void Executor::ins_mult( const Instruction& /*ins*/ )
1,131✔
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,131✔
2058
  ValueStack.pop_back();
1,131✔
2059
  BObjectRef& leftref = ValueStack.back();
1,131✔
2060

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

2064
  leftref.set( new BObject( right.impref().selfTimesObjImp( left.impref() ) ) );
1,131✔
2065
}
1,131✔
2066
// TOK_DIV:
2067
void Executor::ins_div( const Instruction& /*ins*/ )
316✔
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();
316✔
2075
  ValueStack.pop_back();
316✔
2076
  BObjectRef& leftref = ValueStack.back();
316✔
2077

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

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

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

2098
  leftref.set( new BObject( right.impref().selfModulusObjImp( left.impref() ) ) );
338✔
2099
}
338✔
2100

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

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

2111
  leftref.set( new BObject( right.impref().selfIsObjImp( left.impref() ) ) );
75✔
2112
}
75✔
2113

2114
// TOK_BSRIGHT:
2115
void Executor::ins_bitshift_right( const Instruction& /*ins*/ )
296✔
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();
296✔
2123
  ValueStack.pop_back();
296✔
2124
  BObjectRef& leftref = ValueStack.back();
296✔
2125

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

2129
  leftref.set( new BObject( right.impref().selfBitShiftRightObjImp( left.impref() ) ) );
296✔
2130
}
296✔
2131
// TOK_BSLEFT:
2132
void Executor::ins_bitshift_left( const Instruction& /*ins*/ )
296✔
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();
296✔
2140
  ValueStack.pop_back();
296✔
2141
  BObjectRef& leftref = ValueStack.back();
296✔
2142

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

2146
  leftref.set( new BObject( right.impref().selfBitShiftLeftObjImp( left.impref() ) ) );
296✔
2147
}
296✔
2148
// TOK_BITAND:
2149
void Executor::ins_bitwise_and( const Instruction& /*ins*/ )
299✔
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();
299✔
2157
  ValueStack.pop_back();
299✔
2158
  BObjectRef& leftref = ValueStack.back();
299✔
2159

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

2163
  leftref.set( new BObject( right.impref().selfBitAndObjImp( left.impref() ) ) );
299✔
2164
}
299✔
2165
// TOK_BITXOR:
2166
void Executor::ins_bitwise_xor( const Instruction& /*ins*/ )
296✔
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();
296✔
2174
  ValueStack.pop_back();
296✔
2175
  BObjectRef& leftref = ValueStack.back();
296✔
2176

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

2180
  leftref.set( new BObject( right.impref().selfBitXorObjImp( left.impref() ) ) );
296✔
2181
}
296✔
2182
// TOK_BITOR:
2183
void Executor::ins_bitwise_or( const Instruction& /*ins*/ )
296✔
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();
296✔
2191
  ValueStack.pop_back();
296✔
2192
  BObjectRef& leftref = ValueStack.back();
296✔
2193

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

2197
  leftref.set( new BObject( right.impref().selfBitOrObjImp( left.impref() ) ) );
296✔
2198
}
296✔
2199

2200
void Executor::ins_logical_and( const Instruction& /*ins*/ )
234✔
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();
234✔
2208
  ValueStack.pop_back();
234✔
2209
  BObjectRef& leftref = ValueStack.back();
234✔
2210

2211
  BObject& right = *rightref;
234✔
2212
  BObject& left = *leftref;
234✔
2213

2214
  int _true = ( left.isTrue() && right.isTrue() );
234✔
2215
  leftref.set( new BObject( new BLong( _true ) ) );
234✔
2216
}
234✔
2217
void Executor::ins_logical_or( const Instruction& /*ins*/ )
686✔
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();
686✔
2225
  ValueStack.pop_back();
686✔
2226
  BObjectRef& leftref = ValueStack.back();
686✔
2227

2228
  BObject& right = *rightref;
686✔
2229
  BObject& left = *leftref;
686✔
2230

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

2235
void Executor::ins_notequal( const Instruction& /*ins*/ )
1,913✔
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();
1,913✔
2243
  ValueStack.pop_back();
1,913✔
2244
  BObjectRef& leftref = ValueStack.back();
1,913✔
2245

2246
  BObject& right = *rightref;
1,913✔
2247
  BObject& left = *leftref;
1,913✔
2248

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

2253
void Executor::ins_equal( const Instruction& /*ins*/ )
6,367✔
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();
6,367✔
2261
  ValueStack.pop_back();
6,367✔
2262
  BObjectRef& leftref = ValueStack.back();
6,367✔
2263

2264
  BObject& right = *rightref;
6,367✔
2265
  BObject& left = *leftref;
6,367✔
2266

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

2271
void Executor::ins_lessthan( const Instruction& /*ins*/ )
2,603✔
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();
2,603✔
2279
  ValueStack.pop_back();
2,603✔
2280
  BObjectRef& leftref = ValueStack.back();
2,603✔
2281

2282
  BObject& right = *rightref;
2,603✔
2283
  BObject& left = *leftref;
2,603✔
2284

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

2289
void Executor::ins_lessequal( const Instruction& /*ins*/ )
541✔
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();
541✔
2297
  ValueStack.pop_back();
541✔
2298
  BObjectRef& leftref = ValueStack.back();
541✔
2299

2300
  BObject& right = *rightref;
541✔
2301
  BObject& left = *leftref;
541✔
2302
  int _true = ( left <= right );
541✔
2303
  leftref.set( new BObject( new BLong( _true ) ) );
541✔
2304
}
541✔
2305
void Executor::ins_greaterthan( const Instruction& /*ins*/ )
393✔
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();
393✔
2313
  ValueStack.pop_back();
393✔
2314
  BObjectRef& leftref = ValueStack.back();
393✔
2315

2316
  BObject& right = *rightref;
393✔
2317
  BObject& left = *leftref;
393✔
2318

2319
  int _true = ( left > right );
393✔
2320
  leftref.set( new BObject( new BLong( _true ) ) );
393✔
2321
}
393✔
2322
void Executor::ins_greaterequal( const Instruction& /*ins*/ )
307✔
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();
307✔
2330
  ValueStack.pop_back();
307✔
2331
  BObjectRef& leftref = ValueStack.back();
307✔
2332

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

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

2340
// case TOK_ARRAY_SUBSCRIPT:
2341
void Executor::ins_arraysubscript( const Instruction& /*ins*/ )
6,523✔
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();
6,523✔
2349
  ValueStack.pop_back();
6,523✔
2350
  BObjectRef& leftref = ValueStack.back();
6,523✔
2351

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

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

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

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

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

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

2379
  // Reserve to keep the insert_at iterator valid
2380
  ValueStack.reserve( ValueStack.size() + count );
68✔
2381
  auto insert_at = ValueStack.begin() + ValueStack.size();
68✔
2382
  auto pIter = std::unique_ptr<ContIterator>( rightref->impptr()->createIterator( refIter.get() ) );
68✔
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() )
68✔
2387
  {
2388
    if ( rest )
4✔
2389
    {
2390
      auto rest_index = Clib::clamp_convert<u8>( ( ins.token.lval & 0x3FFF ) >> 7 );
2✔
2391
      for ( u8 i = 0; i < count; ++i )
8✔
2392
        ValueStack.emplace( insert_at, new BError( i == rest_index ? "Invalid type for rest binding"
6✔
2393
                                                                   : "Index out of bounds" ) );
6✔
2394
    }
2395
    else
2396
    {
2397
      for ( u8 i = 0; i < count; ++i )
6✔
2398
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
4✔
2399
    }
2400
    return;
4✔
2401
  }
2402

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

2407
    // Add all elements up to the rest index
2408
    for ( u8 i = 0; i < rest_index; ++i )
98✔
2409
    {
2410
      if ( auto res = pIter->step() )
54✔
2411
        ValueStack.emplace( insert_at, res );
50✔
2412
      else
2413
        ValueStack.emplace( insert_at, new BError( "Index out of bounds" ) );
4✔
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>();
44✔
2418

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

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

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

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

2449
void Executor::ins_unpack_indices( const Instruction& ins )
58✔
2450
{
2451
  bool rest = ins.token.lval >> 14;
58✔
2452
  auto binding_count = Clib::clamp_convert<u8>( ins.token.lval & 0x7F );
58✔
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;
58✔
2456

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

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

2472
    OrderedSet indexes;
26✔
2473

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

2480
    BObjectRef rightref = ValueStack.back();
26✔
2481
    ValueStack.pop_back();
26✔
2482

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

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

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

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

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

2509
    auto& unique_index = indexes.get<1>();
16✔
2510

2511
    while ( auto res = pIter->step() )
80✔
2512
    {
2513
      auto itr = unique_index.find( *refIter.get() );
64✔
2514

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

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

2531
    BObjectRef rightref = ValueStack.back();
32✔
2532
    ValueStack.pop_back();
32✔
2533

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

2538
    for ( const auto& index : indexes )
96✔
2539
    {
2540
      ValueStack.insert( insert_at, rightref->impptr()->OperSubscript( index ) );
64✔
2541
    }
2542
  }
32✔
2543
}
2544

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

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

2554
  BObjectRef& rightref = ValueStack.back();
202✔
2555

2556
  BObject& right = *rightref;
202✔
2557

2558
  BObjectImp& rightimpref = right.impref();
202✔
2559

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

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

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

2578
  BObjectRef& rightref = ValueStack.back();
122✔
2579

2580
  BObject& right = *rightref;
122✔
2581

2582
  BObjectImp& rightimpref = right.impref();
122✔
2583

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

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

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

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

2622
  BObject& right = *rightref;
110✔
2623
  BObject& left = *leftref;
110✔
2624

2625
  leftref = addmember( left, right );
110✔
2626
}
110✔
2627

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

2639
  BObject& right = *rightref;
50✔
2640
  BObject& left = *leftref;
50✔
2641

2642
  leftref = removemember( left, right );
50✔
2643
}
50✔
2644

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

2656
  BObject& right = *rightref;
879✔
2657
  BObject& left = *leftref;
879✔
2658

2659
  leftref = checkmember( left, right );
879✔
2660
}
879✔
2661

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

2666
  BObject& ob = *obref;
30✔
2667

2668
  ob.impref().operDotPlus( ins.token.tokval() );
30✔
2669
}
30✔
2670

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

2677
  ValueStack.pop_back();
1,370✔
2678

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

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

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

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

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

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

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

2723
  if ( keyob.count() != 1 || keyimp->count() != 1 )
84✔
2724
  {
2725
    keyimp = keyimp->copy();
4✔
2726
  }
2727
  if ( valob.count() != 1 || valimp->count() != 1 )
84✔
2728
  {
2729
    valimp = valimp->copy();
12✔
2730
  }
2731

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

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

2747
  BObject& right = *rightref;
426✔
2748
  BObject& left = *leftref;
426✔
2749

2750
  leftref.set( new BObject( new BLong( right.impref().contains( left.impref() ) ) ) );
426✔
2751
}
426✔
2752

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

2759
  BObject& right = *rightref;
5,722✔
2760
  BObject& left = *leftref;
5,722✔
2761

2762
  if ( auto* spread = right.impptr_if<BSpread>() )
5,722✔
2763
  {
2764
    BObjectRef refIter( new BObject( UninitObject::create() ) );
982✔
2765

2766
    auto pIter =
2767
        std::unique_ptr<ContIterator>( spread->object->impptr()->createIterator( refIter.get() ) );
982✔
2768

2769
    BObject* next = pIter->step();
982✔
2770
    while ( next != nullptr )
2,878✔
2771
    {
2772
      left.impref().operInsertInto( left, next->impref() );
1,896✔
2773
      next = pIter->step();
1,896✔
2774
    }
2775
  }
982✔
2776
  else
2777
  {
2778
    left.impref().operInsertInto( left, right.impref() );
4,740✔
2779
  }
2780
}
5,722✔
2781

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

2793
  BObject& right = *rightref;
665✔
2794
  BObject& left = *leftref;
665✔
2795

2796
  left.impref().operPlusEqual( left, right.impref() );
665✔
2797
}
665✔
2798

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

2810
  BObject& right = *rightref;
258✔
2811
  BObject& left = *leftref;
258✔
2812

2813
  left.impref().operMinusEqual( left, right.impref() );
258✔
2814
}
258✔
2815

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

2827
  BObject& right = *rightref;
250✔
2828
  BObject& left = *leftref;
250✔
2829

2830
  left.impref().operTimesEqual( left, right.impref() );
250✔
2831
}
250✔
2832

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

2844
  BObject& right = *rightref;
246✔
2845
  BObject& left = *leftref;
246✔
2846

2847
  left.impref().operDivideEqual( left, right.impref() );
246✔
2848
}
246✔
2849

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

2861
  BObject& right = *rightref;
250✔
2862
  BObject& left = *leftref;
250✔
2863

2864
  left.impref().operModulusEqual( left, right.impref() );
250✔
2865
}
250✔
2866

2867
// case RSV_GOTO:
2868
void Executor::ins_goto( const Instruction& ins )
9,690,229✔
2869
{
2870
  PC = (unsigned)ins.token.lval;
9,690,229✔
2871
}
9,690,229✔
2872

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

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

2888
  do
2889
  {
2890
    getParams( nparams );
16,896✔
2891
    if ( auto* funcr = ValueStack.back()->impptr_if<BFunctionRef>() )
16,896✔
2892
    {
2893
      Instruction jmp;
1,319✔
2894
      bool add_new_classinst = ins.token.lval == MTH_NEW;
1,319✔
2895

2896
      if ( add_new_classinst )
1,319✔
2897
      {
2898
        if ( funcr->constructor() )
68✔
2899
        {
2900
          fparams.insert( fparams.begin(),
50✔
2901
                          BObjectRef( new BConstObject( new BClassInstanceRef(
100✔
2902
                              new BClassInstance( prog_, funcr->class_index(), Globals2 ) ) ) ) );
100✔
2903
        }
2904
      }
2905

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

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

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

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

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

2943
      continuation->args.clear();
284✔
2944

2945
      cleanParams();
284✔
2946

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

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

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

2959
      continue;
284✔
2960
    }
2961

2962
#ifdef ESCRIPT_PROFILE
2963
    profile_escript( name, profile_start );
2964
#endif
2965
    BObjectRef& objref = ValueStack[stacksize - 1];
15,363✔
2966
    if ( func_result_ )
15,363✔
2967
    {
2968
      if ( imp )
11✔
2969
      {
2970
        BObject obj( imp );
5✔
2971
      }
5✔
2972

2973
      objref.set( new BObject( func_result_ ) );
11✔
2974
      func_result_ = nullptr;
11✔
2975
    }
2976
    else if ( imp )
15,352✔
2977
    {
2978
      objref.set( new BObject( imp ) );
15,315✔
2979
    }
2980
    else
2981
    {
2982
      objref.set( new BObject( UninitObject::create() ) );
37✔
2983
    }
2984

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

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

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

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

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

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

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

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

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

3050
    if ( funcr != nullptr )
367✔
3051
    {
3052
      Instruction jmp;
363✔
3053
      int id;
3054

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

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

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

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

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

3118
    objref.set( new BObject( func_result_ ) );
×
3119
    func_result_ = nullptr;
×
3120
  }
3121
  else if ( imp )
174✔
3122
  {
3123
    objref.set( new BObject( imp ) );
39✔
3124
  }
3125
  else
3126
  {
3127
    objref.set( new BObject( UninitObject::create() ) );
135✔
3128
  }
3129

3130
  cleanParams();
174✔
3131
  return;
174✔
3132
}
3133

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

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

3149

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

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

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

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

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

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

3189
  auto ctor_addr = jsr_ins.token.lval;
182✔
3190

3191
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
182✔
3192

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

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

3213
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
6,599✔
3214
{
3215
  ReturnContext rc;
6,599✔
3216
  rc.PC = PC;
6,599✔
3217
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
6,599✔
3218
  if ( continuation )
6,599✔
3219
  {
3220
    rc.Continuation.set( new BObject( continuation ) );
689✔
3221
  }
3222

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

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

3233
    Globals2 = funcref->globals;
28✔
3234

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

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

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

3251
  ControlStack.push_back( rc );
6,599✔
3252

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

3271

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

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

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

3293
          handler( pc, filename, line, functionName );
49✔
3294
        } );
49✔
3295
  };
30✔
3296

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

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

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

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

3352
    return new String( std::move( result ) );
12✔
3353
  }
12✔
3354
}
3355

3356
void Executor::ins_pop_param( const Instruction& ins )
8,688✔
3357
{
3358
  popParam( ins.token );
8,688✔
3359
}
8,688✔
3360

3361
void Executor::ins_pop_param_byref( const Instruction& ins )
1,836✔
3362
{
3363
  popParamByRef( ins.token );
1,836✔
3364
}
1,836✔
3365

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

3371
// CTRL_LEAVE_BLOCK:
3372
void Executor::ins_leave_block( const Instruction& ins )
5,236✔
3373
{
3374
  if ( Locals2 )
5,236✔
3375
  {
3376
    for ( int i = 0; i < ins.token.lval; i++ )
14,817✔
3377
      Locals2->pop_back();
9,581✔
3378
  }
3379
  else  // at global level.  ick.
3380
  {
3381
    for ( int i = 0; i < ins.token.lval; i++ )
×
3382
      Globals2->pop_back();
×
3383
  }
3384
}
5,236✔
3385

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

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

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

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

3413
  if ( rc.Continuation.get() != nullptr )
6,599✔
3414
  {
3415
    continuation = rc.Continuation;
689✔
3416
  }
3417

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

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

3437
  // FIXME do something with rc.ValueStackDepth
3438
  ControlStack.pop_back();
6,599✔
3439

3440
  if ( continuation != nullptr )
6,599✔
3441
  {
3442
    auto result = ValueStack.back();
689✔
3443
    ValueStack.pop_back();
689✔
3444

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

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

3456
      // Add function reference to stack
3457
      ValueStack.push_back( BObjectRef( new BObject( cont->func() ) ) );
405✔
3458

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

3463
      cont->args.clear();
405✔
3464

3465
      printStack(
405✔
3466
          "continuation callback returned a continuation; continuation args added to fparams" );
3467

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

3487
      // Add the result to the stack.
3488
      ValueStack.push_back( BObjectRef( new BObject( imp ) ) );
284✔
3489
    }
3490
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
1,378✔
3491
  }
689✔
3492
}
6,599✔
3493

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

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

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

3511
void Executor::ins_string( const Instruction& ins )
47,235✔
3512
{
3513
  ValueStack.push_back( BObjectRef( new BObject( new String( ins.token.tokval() ) ) ) );
47,235✔
3514
}
47,235✔
3515
void Executor::ins_error( const Instruction& /*ins*/ )
4,114✔
3516
{
3517
  ValueStack.push_back( BObjectRef( new BObject( new BError() ) ) );
4,114✔
3518
}
4,114✔
3519
void Executor::ins_struct( const Instruction& /*ins*/ )
598✔
3520
{
3521
  ValueStack.push_back( BObjectRef( new BObject( new BStruct ) ) );
598✔
3522
}
598✔
3523
void Executor::ins_spread( const Instruction& /*ins*/ )
1,401✔
3524
{
3525
  auto spread = new BSpread( ValueStack.back() );
1,401✔
3526
  ValueStack.pop_back();
1,401✔
3527
  ValueStack.push_back( BObjectRef( new BObject( spread ) ) );
1,401✔
3528
}
1,401✔
3529
void Executor::ins_array( const Instruction& /*ins*/ )
2,772✔
3530
{
3531
  ValueStack.push_back( BObjectRef( new BObject( new ObjArray ) ) );
2,772✔
3532
}
2,772✔
3533
void Executor::ins_dictionary( const Instruction& /*ins*/ )
96✔
3534
{
3535
  ValueStack.push_back( BObjectRef( new BObject( new BDictionary ) ) );
96✔
3536
}
96✔
3537
void Executor::ins_uninit( const Instruction& /*ins*/ )
78✔
3538
{
3539
  ValueStack.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
78✔
3540
}
78✔
3541
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3542
{
3543
  ValueStack.push_back( BObjectRef( new BObject( new BError( "Please recompile this script" ) ) ) );
×
3544
}
×
3545

3546
// case TOK_UNMINUS:
3547
void Executor::ins_unminus( const Instruction& /*ins*/ )
98✔
3548
{
3549
  BObjectRef ref = getObjRef();
98✔
3550
  BObjectImp* newobj;
3551
  newobj = ref->impref().inverse();
98✔
3552

3553
  ValueStack.push_back( BObjectRef( new BObject( newobj ) ) );
98✔
3554
}
98✔
3555

3556
// case TOK_UNPLUSPLUS:
3557
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
309✔
3558
{
3559
  BObjectRef ref = ValueStack.back();
309✔
3560
  ref->impref().selfPlusPlus();
309✔
3561
}
309✔
3562

3563
// case TOK_UNMINUSMINUS:
3564
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
83✔
3565
{
3566
  BObjectRef ref = ValueStack.back();
83✔
3567
  ref->impref().selfMinusMinus();
83✔
3568
}
83✔
3569

3570
// case TOK_UNPLUSPLUS_POST:
3571
void Executor::ins_unplusplus_post( const Instruction& /*ins*/ )
184✔
3572
{
3573
  BObjectRef ref = ValueStack.back();
184✔
3574
  BObjectImp* imp = ref->impptr();
184✔
3575
  BObject* n = ref->clone();
184✔
3576
  imp->selfPlusPlus();
184✔
3577
  ValueStack.back().set( n );
184✔
3578
}
184✔
3579

3580
// case TOK_UNMINUSMINUS_POST:
3581
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
49✔
3582
{
3583
  BObjectRef ref = ValueStack.back();
49✔
3584
  BObjectImp* imp = ref->impptr();
49✔
3585
  BObject* n = ref->clone();
49✔
3586
  imp->selfMinusMinus();
49✔
3587
  ValueStack.back().set( n );
49✔
3588
}
49✔
3589

3590
// case INS_SET_MEMBER_ID_UNPLUSPLUS:
3591
void Executor::ins_set_member_id_unplusplus( const Instruction& ins )
7✔
3592
{
3593
  BObjectRef ref = ValueStack.back();
7✔
3594
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
7✔
3595
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
14✔
3596
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
7✔
3597
  {
3598
    tmp->impref().selfPlusPlus();
7✔
3599
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
7✔
3600
  }
3601
  ValueStack.back().set( tmp.get() );
7✔
3602
}
7✔
3603

3604
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3605
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
2✔
3606
{
3607
  BObjectRef ref = ValueStack.back();
2✔
3608
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
2✔
3609
  BObject* res = tmp->clone();
2✔
3610
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
4✔
3611
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
2✔
3612
  {
3613
    tmp->impref().selfPlusPlus();
2✔
3614
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
2✔
3615
  }
3616
  ValueStack.back().set( res );
2✔
3617
}
2✔
3618

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

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

3648
// case TOK_LOG_NOT:
3649
void Executor::ins_logical_not( const Instruction& /*ins*/ )
551✔
3650
{
3651
  BObjectRef ref = getObjRef();
551✔
3652
  ValueStack.push_back( BObjectRef( new BObject( new BLong( (int)!ref->impptr()->isTrue() ) ) ) );
551✔
3653
}
551✔
3654

3655
// case TOK_BITWISE_NOT:
3656
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
104✔
3657
{
3658
  BObjectRef ref = getObjRef();
104✔
3659
  ValueStack.push_back( BObjectRef( new BObject( ref->impptr()->bitnot() ) ) );
104✔
3660
}
104✔
3661

3662
// case TOK_FUNCREF:
3663
void Executor::ins_funcref( const Instruction& ins )
932✔
3664
{
3665
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
932✔
3666
  {
3667
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
3668
                    prog_->function_references.size() );
×
3669
    seterror( true );
×
3670
    return;
×
3671
  }
3672

3673
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
932✔
3674

3675
  ValueStack.push_back(
932✔
3676
      BObjectRef( new BFunctionRef( prog_, funcref_index, Globals2, {} /* captures */ ) ) );
1,864✔
3677
}
3678

3679
void Executor::ins_functor( const Instruction& ins )
226✔
3680
{
3681
  auto funcref_index = static_cast<int>( ins.token.type );
226✔
3682

3683
  const auto& ep_funcref = prog_->function_references[funcref_index];
226✔
3684

3685
  int capture_count = ep_funcref.capture_count;
226✔
3686

3687
  auto captures = ValueStackCont();
226✔
3688
  while ( capture_count > 0 )
603✔
3689
  {
3690
    captures.push_back( ValueStack.back() );
377✔
3691
    ValueStack.pop_back();
377✔
3692
    capture_count--;
377✔
3693
  }
3694

3695
  auto func = new BFunctionRef( prog_, funcref_index, Globals2, std::move( captures ) );
226✔
3696

3697
  ValueStack.push_back( BObjectRef( func ) );
226✔
3698

3699
  PC += ins.token.lval;
226✔
3700
}
226✔
3701

3702
void Executor::ins_nop( const Instruction& /*ins*/ ) {}
×
3703

3704
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
103,729✔
3705
{
3706
  switch ( token.id )
103,729✔
3707
  {
3708
  case INS_INITFOREACH:
285✔
3709
    return &Executor::ins_initforeach;
285✔
3710
  case INS_STEPFOREACH:
285✔
3711
    return &Executor::ins_stepforeach;
285✔
3712
  case INS_INITFOR:
38✔
3713
    return &Executor::ins_initfor;
38✔
3714
  case INS_NEXTFOR:
38✔
3715
    return &Executor::ins_nextfor;
38✔
3716
  case INS_CASEJMP:
63✔
3717
    return &Executor::ins_casejmp;
63✔
3718
  case RSV_JMPIFTRUE:
764✔
3719
    return &Executor::ins_jmpiftrue;
764✔
3720
  case RSV_JMPIFFALSE:
1,412✔
3721
    return &Executor::ins_jmpiffalse;
1,412✔
3722
  case RSV_LOCAL:
1,761✔
3723
    return &Executor::ins_makeLocal;
1,761✔
3724
  case RSV_GLOBAL:
5,960✔
3725
  case TOK_GLOBALVAR:
3726
    return &Executor::ins_globalvar;
5,960✔
3727
  case TOK_LOCALVAR:
10,708✔
3728
    return &Executor::ins_localvar;
10,708✔
3729
  case TOK_LONG:
9,525✔
3730
    return &Executor::ins_long;
9,525✔
3731
  case TOK_DOUBLE:
233✔
3732
    return &Executor::ins_double;
233✔
3733
  case TOK_STRING:
14,836✔
3734
    return &Executor::ins_string;
14,836✔
3735
  case TOK_ERROR:
156✔
3736
    return &Executor::ins_error;
156✔
3737
  case TOK_STRUCT:
428✔
3738
    return &Executor::ins_struct;
428✔
3739
  case TOK_SPREAD:
431✔
3740
    return &Executor::ins_spread;
431✔
3741
  case TOK_CLASSINST:
120✔
3742
    return &Executor::ins_classinst;
120✔
3743
  case TOK_ARRAY:
1,764✔
3744
    return &Executor::ins_array;
1,764✔
3745
  case TOK_DICTIONARY:
91✔
3746
    return &Executor::ins_dictionary;
91✔
3747
  case TOK_FUNCREF:
463✔
3748
    return &Executor::ins_funcref;
463✔
3749
  case TOK_FUNCTOR:
213✔
3750
    return &Executor::ins_functor;
213✔
3751
  case INS_UNINIT:
73✔
3752
    return &Executor::ins_uninit;
73✔
3753
  case TOK_IDENT:
×
3754
    return &Executor::ins_ident;
×
3755
  case INS_ASSIGN_GLOBALVAR:
469✔
3756
    return &Executor::ins_assign_globalvar;
469✔
3757
  case INS_ASSIGN_LOCALVAR:
499✔
3758
    return &Executor::ins_assign_localvar;
499✔
3759
  case INS_ASSIGN_CONSUME:
42✔
3760
    return &Executor::ins_assign_consume;
42✔
3761
  case TOK_CONSUMER:
10,786✔
3762
    return &Executor::ins_consume;
10,786✔
3763
  case TOK_ASSIGN:
2,783✔
3764
    return &Executor::ins_assign;
2,783✔
3765
  case INS_SUBSCRIPT_ASSIGN:
×
3766
    return &Executor::ins_array_assign;
×
3767
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
282✔
3768
    return &Executor::ins_array_assign_consume;
282✔
3769
  case INS_MULTISUBSCRIPT:
84✔
3770
    return &Executor::ins_multisubscript;
84✔
3771
  case INS_MULTISUBSCRIPT_ASSIGN:
4✔
3772
    return &Executor::ins_multisubscript_assign;
4✔
3773
  case INS_GET_MEMBER:
752✔
3774
    return &Executor::ins_get_member;
752✔
3775
  case INS_SET_MEMBER:
×
3776
    return &Executor::ins_set_member;
×
3777
  case INS_SET_MEMBER_CONSUME:
160✔
3778
    return &Executor::ins_set_member_consume;
160✔
3779

3780
  case INS_UNPACK_SEQUENCE:
67✔
3781
    return &Executor::ins_unpack_sequence;
67✔
3782
  case INS_UNPACK_INDICES:
50✔
3783
    return &Executor::ins_unpack_indices;
50✔
3784
  case INS_TAKE_GLOBAL:
122✔
3785
    return &Executor::ins_take_global;
122✔
3786
  case INS_TAKE_LOCAL:
176✔
3787
    return &Executor::ins_take_local;
176✔
3788

3789
  case INS_GET_MEMBER_ID:
1,253✔
3790
    return &Executor::ins_get_member_id;  // test id
1,253✔
3791
  case INS_SET_MEMBER_ID:
2✔
3792
    return &Executor::ins_set_member_id;  // test id
2✔
3793
  case INS_SET_MEMBER_ID_CONSUME:
163✔
3794
    return &Executor::ins_set_member_id_consume;  // test id
163✔
3795

3796
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
10✔
3797
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
10✔
3798
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
4✔
3799
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
4✔
3800
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
4✔
3801
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
4✔
3802
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
×
3803
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
×
3804
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
4✔
3805
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
4✔
3806

3807
  case TOK_ADD:
1,591✔
3808
    return &Executor::ins_add;
1,591✔
3809
  case TOK_SUBTRACT:
208✔
3810
    return &Executor::ins_subtract;
208✔
3811
  case TOK_DIV:
38✔
3812
    return &Executor::ins_div;
38✔
3813
  case TOK_MULT:
151✔
3814
    return &Executor::ins_mult;
151✔
3815
  case TOK_MODULUS:
28✔
3816
    return &Executor::ins_modulus;
28✔
3817

3818
  case TOK_INSERTINTO:
4,007✔
3819
    return &Executor::ins_insert_into;
4,007✔
3820

3821
  case TOK_PLUSEQUAL:
55✔
3822
    return &Executor::ins_plusequal;
55✔
3823
  case TOK_MINUSEQUAL:
18✔
3824
    return &Executor::ins_minusequal;
18✔
3825
  case TOK_TIMESEQUAL:
10✔
3826
    return &Executor::ins_timesequal;
10✔
3827
  case TOK_DIVIDEEQUAL:
6✔
3828
    return &Executor::ins_divideequal;
6✔
3829
  case TOK_MODULUSEQUAL:
10✔
3830
    return &Executor::ins_modulusequal;
10✔
3831

3832
  case TOK_LESSTHAN:
118✔
3833
    return &Executor::ins_lessthan;
118✔
3834
  case TOK_LESSEQ:
50✔
3835
    return &Executor::ins_lessequal;
50✔
3836
  case RSV_GOTO:
1,190✔
3837
    return &Executor::ins_goto;
1,190✔
3838
  case TOK_ARRAY_SUBSCRIPT:
894✔
3839
    return &Executor::ins_arraysubscript;
894✔
3840
  case TOK_EQUAL:
658✔
3841
    return &Executor::ins_equal;
658✔
3842
  case TOK_FUNC:
7,543✔
3843
    return &Executor::ins_func;
7,543✔
3844
  case INS_CALL_METHOD:
168✔
3845
    return &Executor::ins_call_method;
168✔
3846
  case INS_CALL_METHOD_ID:
1,735✔
3847
    return &Executor::ins_call_method_id;
1,735✔
3848
  case CTRL_STATEMENTBEGIN:
×
3849
    return &Executor::ins_statementbegin;
×
3850
  case CTRL_MAKELOCAL:
2,826✔
3851
    return &Executor::ins_makelocal;
2,826✔
3852
  case INS_CHECK_MRO:
146✔
3853
    return &Executor::ins_check_mro;
146✔
3854
  case CTRL_JSR_USERFUNC:
2,826✔
3855
    return &Executor::ins_jsr_userfunc;
2,826✔
3856
  case INS_POP_PARAM:
1,562✔
3857
    return &Executor::ins_pop_param;
1,562✔
3858
  case INS_POP_PARAM_BYREF:
736✔
3859
    return &Executor::ins_pop_param_byref;
736✔
3860
  case INS_GET_ARG:
91✔
3861
    return &Executor::ins_get_arg;
91✔
3862
  case CTRL_LEAVE_BLOCK:
1,127✔
3863
    return &Executor::ins_leave_block;
1,127✔
3864
  case RSV_GOSUB:
×
3865
    return &Executor::ins_gosub;
×
3866
  case RSV_RETURN:
2,815✔
3867
    return &Executor::ins_return;
2,815✔
3868
  case RSV_EXIT:
12✔
3869
    return &Executor::ins_exit;
12✔
3870
  case INS_DECLARE_ARRAY:
46✔
3871
    return &Executor::ins_declareArray;
46✔
3872
  case TOK_UNMINUS:
2✔
3873
    return &Executor::ins_unminus;
2✔
3874
  case TOK_UNPLUS:
×
3875
    return &Executor::ins_nop;
×
3876
  case TOK_LOG_NOT:
95✔
3877
    return &Executor::ins_logical_not;
95✔
3878
  case TOK_BITWISE_NOT:
8✔
3879
    return &Executor::ins_bitwise_not;
8✔
3880
  case TOK_BSRIGHT:
12✔
3881
    return &Executor::ins_bitshift_right;
12✔
3882
  case TOK_BSLEFT:
12✔
3883
    return &Executor::ins_bitshift_left;
12✔
3884
  case TOK_BITAND:
14✔
3885
    return &Executor::ins_bitwise_and;
14✔
3886
  case TOK_BITXOR:
12✔
3887
    return &Executor::ins_bitwise_xor;
12✔
3888
  case TOK_BITOR:
12✔
3889
    return &Executor::ins_bitwise_or;
12✔
3890

3891
  case TOK_NEQ:
577✔
3892
    return &Executor::ins_notequal;
577✔
3893
  case TOK_GRTHAN:
96✔
3894
    return &Executor::ins_greaterthan;
96✔
3895
  case TOK_GREQ:
58✔
3896
    return &Executor::ins_greaterequal;
58✔
3897
  case TOK_AND:
31✔
3898
    return &Executor::ins_logical_and;
31✔
3899
  case TOK_OR:
195✔
3900
    return &Executor::ins_logical_or;
195✔
3901

3902
  case TOK_ADDMEMBER:
62✔
3903
    return &Executor::ins_addmember;
62✔
3904
  case TOK_DELMEMBER:
4✔
3905
    return &Executor::ins_removemember;
4✔
3906
  case TOK_CHKMEMBER:
52✔
3907
    return &Executor::ins_checkmember;
52✔
3908
  case INS_DICTIONARY_ADDMEMBER:
78✔
3909
    return &Executor::ins_dictionary_addmember;
78✔
3910
  case TOK_IN:
38✔
3911
    return &Executor::ins_in;
38✔
3912
  case TOK_IS:
39✔
3913
    return &Executor::ins_is;
39✔
3914
  case INS_ADDMEMBER2:
30✔
3915
    return &Executor::ins_addmember2;
30✔
3916
  case INS_ADDMEMBER_ASSIGN:
946✔
3917
    return &Executor::ins_addmember_assign;
946✔
3918
  case CTRL_PROGEND:
1,773✔
3919
    return &Executor::ins_progend;
1,773✔
3920
  case TOK_UNPLUSPLUS:
67✔
3921
    return &Executor::ins_unplusplus;
67✔
3922
  case TOK_UNMINUSMINUS:
42✔
3923
    return &Executor::ins_unminusminus;
42✔
3924
  case TOK_UNPLUSPLUS_POST:
54✔
3925
    return &Executor::ins_unplusplus_post;
54✔
3926
  case TOK_UNMINUSMINUS_POST:
27✔
3927
    return &Executor::ins_unminusminus_post;
27✔
3928
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
7✔
3929
    return &Executor::ins_set_member_id_unplusplus;  // test id
7✔
3930
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
9✔
3931
    return &Executor::ins_set_member_id_unminusminus;  // test id
9✔
3932
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
2✔
3933
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
2✔
3934
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
2✔
3935
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
2✔
3936
  case INS_SKIPIFTRUE_ELSE_CONSUME:
102✔
3937
    return &Executor::ins_skipiftrue_else_consume;
102✔
3938
  case TOK_INTERPOLATE_STRING:
1,084✔
3939
    return &Executor::ins_interpolate_string;
1,084✔
3940
  case TOK_FORMAT_EXPRESSION:
80✔
3941
    return &Executor::ins_format_expression;
80✔
3942
  case TOK_BOOL:
79✔
3943
    return &Executor::ins_bool;
79✔
3944
  default:
×
3945
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3946
  }
3947
}
3948

3949
void Executor::sethalt( bool halt )
17✔
3950
{
3951
  halt_ = halt;
17✔
3952

3953
  if ( halt && dbg_env_ )
17✔
3954
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
9✔
3955
      listener->on_halt();
9✔
3956

3957
  calcrunnable();
17✔
3958
}
17✔
3959

3960
void Executor::execInstr()
58,659,585✔
3961
{
3962
  unsigned onPC = PC;
58,659,585✔
3963
  try
3964
  {  // this is really more of a class invariant.
3965
    passert( run_ok_ );
58,659,585✔
3966
    passert( PC < nLines );
58,659,585✔
3967
    passert( !error_ );
58,659,585✔
3968
    passert( !done );
58,659,585✔
3969

3970
#ifdef NDEBUG
3971
    const Instruction& ins = prog_->instr[PC];
58,659,585✔
3972
#else
3973
    const Instruction& ins = prog_->instr.at( PC );
3974
#endif
3975
    if ( debug_level >= INSTRUCTIONS )
58,659,585✔
3976
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3977

3978
    // If `on_instruction` returns false, do not execute this instruction.
3979
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
58,659,585✔
3980
    {
3981
      return;
9✔
3982
    }
3983

3984
    ++ins.cycles;
58,659,576✔
3985
    ++prog_->instr_cycles;
58,659,576✔
3986
    ++escript_instr_cycles;
58,659,576✔
3987

3988
    ++PC;
58,659,576✔
3989

3990
    ( this->*( ins.func ) )( ins );
58,659,576✔
3991
  }
3992
  catch ( std::exception& ex )
2✔
3993
  {
3994
    std::string tmp =
3995
        fmt::format( "Exception in: {} PC={}: {}\n", prog_->name.get(), onPC, ex.what() );
2✔
3996
    if ( !run_ok_ )
2✔
3997
      tmp += "run_ok_ = false\n";
×
3998
    if ( PC < nLines )
2✔
3999
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
6✔
4000
    if ( error_ )
2✔
4001
      tmp += "error_ = true\n";
×
4002
    if ( done )
2✔
4003
      tmp += "done = true\n";
×
4004

4005
    seterror( true );
2✔
4006
    POLLOG_ERROR( tmp );
2✔
4007

4008
    show_context( onPC );
2✔
4009
  }
2✔
4010
#ifdef __unix__
4011
  catch ( ... )
×
4012
  {
4013
    seterror( true );
×
4014
    POLLOG_ERRORLN( "Exception in {}, PC={}: unclassified", prog_->name.get(), onPC );
×
4015

4016
    show_context( onPC );
×
4017
  }
×
4018
#endif
4019
}
4020

4021
std::string Executor::dbg_get_instruction( size_t atPC ) const
22✔
4022
{
4023
  std::string out;
22✔
4024
  dbg_get_instruction( atPC, out );
22✔
4025
  return out;
22✔
4026
}
×
4027

4028
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
187✔
4029
{
4030
  bool has_breakpoint =
4031
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
187✔
4032
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
374✔
4033
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
187✔
4034
}
187✔
4035

4036
void Executor::show_context( unsigned atPC )
2✔
4037
{
4038
  unsigned start, end;
4039
  if ( atPC >= 5 )
2✔
4040
    start = atPC - 5;
2✔
4041
  else
4042
    start = 0;
×
4043

4044
  end = atPC + 5;
2✔
4045

4046
  if ( end >= nLines )
2✔
4047
    end = nLines - 1;
×
4048

4049
  for ( unsigned i = start; i <= end; ++i )
24✔
4050
  {
4051
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
22✔
4052
  }
4053
}
2✔
4054
void Executor::show_context( std::string& os, unsigned atPC )
15✔
4055
{
4056
  unsigned start, end;
4057
  if ( atPC >= 5 )
15✔
4058
    start = atPC - 5;
15✔
4059
  else
4060
    start = 0;
×
4061

4062
  end = atPC + 5;
15✔
4063

4064
  if ( end >= nLines )
15✔
4065
    end = nLines - 1;
×
4066

4067
  for ( unsigned i = start; i <= end; ++i )
180✔
4068
  {
4069
    dbg_get_instruction( i, os );
165✔
4070
    os += '\n';
165✔
4071
  }
4072
}
15✔
4073

4074
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
2,003✔
4075
                                        const Instruction& jmp )
4076
{
4077
  // params need to be on the stack, without current objectref
4078
  ValueStack.pop_back();
2,003✔
4079

4080
  // Push captured parameters onto the stack prior to function parameters.
4081
  for ( auto& p : funcr->captures )
2,286✔
4082
    ValueStack.push_back( p );
283✔
4083

4084
  auto nparams = static_cast<int>( fparams.size() );
2,003✔
4085

4086
  // Handle variadic functions special. Construct an array{} corresponding to
4087
  // the rest parameter (the last parameter for the function). The logic for the
4088
  // condition:
4089
  // - if true, the last argument in the call may not be an array{}, so we need
4090
  //   to construct one (which is why the condition is `>=` and not `>`).
4091
  // - if false, then the address we're jumping to will be a "default argument
4092
  //   address" and _not_ the user function directly, which will create the
4093
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
4094
  if ( funcr->variadic() && nparams >= funcr->numParams() )
2,003✔
4095
  {
4096
    auto num_nonrest_args = funcr->numParams() - 1;
419✔
4097

4098
    auto rest_arg = std::make_unique<ObjArray>();
419✔
4099

4100
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
2,402✔
4101
    {
4102
      auto& p = fparams[i];
1,983✔
4103

4104
      if ( i < num_nonrest_args )
1,983✔
4105
      {
4106
        ValueStack.push_back( p );
394✔
4107
      }
4108
      else
4109
      {
4110
        rest_arg->ref_arr.push_back( p );
1,589✔
4111
      }
4112
    }
4113
    ValueStack.push_back( BObjectRef( rest_arg.release() ) );
419✔
4114
  }
419✔
4115
  // The array{} will be created via the regular default-parameter handling by
4116
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
4117
  // prior to jumping to the user function.
4118
  else
4119
  {
4120
    for ( auto& p : fparams )
3,620✔
4121
      ValueStack.push_back( p );
2,036✔
4122
  }
4123

4124
  // jump to function
4125
  jump( jmp.token.lval, continuation, funcr );
2,003✔
4126
  fparams.clear();
2,003✔
4127
  // switch to new block
4128
  ins_makelocal( jmp );
2,003✔
4129
}
2,003✔
4130

4131
bool Executor::exec()
1,600✔
4132
{
4133
  passert( prog_ok_ );
1,600✔
4134
  passert( !error_ );
1,600✔
4135

4136
  Clib::scripts_thread_script = scriptname();
1,600✔
4137

4138
  set_running_to_completion( true );
1,600✔
4139
  while ( runnable() )
258,010✔
4140
  {
4141
    Clib::scripts_thread_scriptPC = PC;
256,410✔
4142
    execInstr();
256,410✔
4143
  }
4144

4145
  return !error_;
1,600✔
4146
}
4147

4148
void Executor::reinitExec()
×
4149
{
4150
  PC = 0;
×
4151
  done = 0;
×
4152
  seterror( false );
×
4153

4154
  ValueStack.clear();
×
4155
  delete Locals2;
×
4156
  Locals2 = new BObjectRefVec;
×
4157

4158
  if ( !prog_ok_ )
×
4159
  {
4160
    seterror( true );
×
4161
  }
4162
}
×
4163

4164
void Executor::initForFnCall( unsigned in_PC )
654✔
4165
{
4166
#ifdef MEMORYLEAK
4167
  bool data_shown = false;
4168
#endif
4169

4170
  PC = in_PC;
654✔
4171
  done = 0;
654✔
4172
  seterror( false );
654✔
4173

4174
#ifdef MEMORYLEAK
4175
  while ( !ValueStack.empty() )
4176
  {
4177
    if ( Clib::memoryleak_debug )
4178
    {
4179
      if ( !data_shown )
4180
      {
4181
        LEAKLOG( "ValueStack... " );
4182
        data_shown = true;
4183
      }
4184

4185
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
4186
               ValueStack.back()->impptr()->sizeEstimate() );
4187
    }
4188
    ValueStack.pop_back();
4189
  }
4190
  if ( Clib::memoryleak_debug )
4191
    if ( data_shown )
4192
      LEAKLOGLN( " ...deleted" );
4193
#endif
4194

4195
  ValueStack.clear();
654✔
4196
  Locals2->clear();
654✔
4197
}
654✔
4198

4199
void Executor::pushArg( BObjectImp* arg )
845✔
4200
{
4201
  passert_always( arg );
845✔
4202
  ValueStack.push_back( BObjectRef( arg ) );
845✔
4203
}
845✔
4204

4205
void Executor::pushArg( const BObjectRef& arg )
×
4206
{
4207
  ValueStack.push_back( arg );
×
4208
}
×
4209

4210
void Executor::addModule( ExecutorModule* module )
13,955✔
4211
{
4212
  availmodules.push_back( module );
13,955✔
4213
}
13,955✔
4214

4215

4216
ExecutorModule* Executor::findModule( const std::string& name )
1,961✔
4217
{
4218
  unsigned idx;
4219
  for ( idx = 0; idx < availmodules.size(); idx++ )
9,500✔
4220
  {
4221
    ExecutorModule* module = availmodules[idx];
9,500✔
4222
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
9,500✔
4223
      return module;
1,961✔
4224
  }
4225
  return nullptr;
×
4226
}
4227

4228
bool Executor::attach_debugger( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching )
4✔
4229
{
4230
  // FIXME: a script can be in debugging state but have no debugger attached,
4231
  // eg. a script that called `os::Debugger()`. This needs to check if a
4232
  // debugger is attached. This works for `os::Debugger()` but not for poldbg cmd_attach.
4233
  if ( dbg_env_ )
4✔
4234
  {
4235
    if ( !listener.expired() )
2✔
4236
    {
4237
      auto& dbg_env_listener = dbg_env_->listener;
1✔
4238
      if ( !dbg_env_listener.expired() )
1✔
4239
      {
4240
        return false;
×
4241
      }
4242
      dbg_env_listener = listener;
1✔
4243
    }
4244
    if ( set_attaching )
2✔
4245
      dbg_env_->debug_state = ExecutorDebugState::ATTACHING;
2✔
4246
  }
4247
  else
4248
  {
4249
    dbg_env_ = std::make_unique<ExecutorDebugEnvironment>( listener, set_attaching );
2✔
4250
  }
4251

4252
  return true;
4✔
4253
}
4254

4255
void Executor::detach_debugger()
×
4256
{
4257
  dbg_env_.reset();
×
4258
  sethalt( false );
×
4259
}
×
4260

4261
void Executor::print_to_debugger( const std::string& message )
15,525✔
4262
{
4263
  if ( dbg_env_ )
15,525✔
4264
  {
4265
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
1✔
4266
      listener->on_print( message );
1✔
4267
  }
4268
}
15,525✔
4269

4270
void Executor::dbg_ins_trace()
×
4271
{
4272
  if ( dbg_env_ )
×
4273
  {
4274
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4275
  }
4276
  sethalt( false );
×
4277
}
×
4278
void Executor::dbg_step_into()
1✔
4279
{
4280
  if ( dbg_env_ )
1✔
4281
  {
4282
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1✔
4283
  }
4284
  sethalt( false );
1✔
4285
}
1✔
4286
void Executor::dbg_step_over()
2✔
4287
{
4288
  if ( dbg_env_ )
2✔
4289
  {
4290
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2✔
4291
  }
4292
  sethalt( false );
2✔
4293
}
2✔
4294
void Executor::dbg_step_out()
1✔
4295
{
4296
  if ( dbg_env_ )
1✔
4297
  {
4298
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1✔
4299
  }
4300
  sethalt( false );
1✔
4301
}
1✔
4302
void Executor::dbg_run()
4✔
4303
{
4304
  if ( dbg_env_ )
4✔
4305
  {
4306
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4✔
4307
  }
4308
  sethalt( false );
4✔
4309
}
4✔
4310
void Executor::dbg_break()
1✔
4311
{
4312
  if ( dbg_env_ )
1✔
4313
  {
4314
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1✔
4315
  }
4316
}
1✔
4317

4318
void Executor::dbg_setbp( unsigned atPC )
2✔
4319
{
4320
  if ( dbg_env_ )
2✔
4321
  {
4322
    dbg_env_->breakpoints.insert( atPC );
2✔
4323
  }
4324
}
2✔
4325
void Executor::dbg_clrbp( unsigned atPC )
×
4326
{
4327
  if ( dbg_env_ )
×
4328
  {
4329
    dbg_env_->breakpoints.erase( atPC );
×
4330
  }
4331
}
×
4332

4333
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1✔
4334
{
4335
  if ( dbg_env_ )
1✔
4336
  {
4337
    std::set<unsigned> result;
1✔
4338
    auto& breakpoints = dbg_env_->breakpoints;
1✔
4339
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1✔
4340
                         std::inserter( result, result.end() ) );
4341
    breakpoints = result;
1✔
4342
  }
1✔
4343
}
1✔
4344

4345
void Executor::dbg_clrallbp()
×
4346
{
4347
  if ( dbg_env_ )
×
4348
  {
4349
    dbg_env_->breakpoints.clear();
×
4350
  }
4351
}
×
4352

4353
size_t Executor::sizeEstimate() const
1,206✔
4354
{
4355
  size_t size = sizeof( *this );
1,206✔
4356
  size += Clib::memsize( upperLocals2 );
1,206✔
4357
  for ( const auto& bobjectrefvec : upperLocals2 )
1,206✔
4358
  {
4359
    size += Clib::memsize( *bobjectrefvec );
×
4360
    for ( const auto& bojectref : *bobjectrefvec )
×
4361
    {
4362
      if ( bojectref != nullptr )
×
4363
        size += bojectref->sizeEstimate();
×
4364
    }
4365
  }
4366
  size += Clib::memsize( ControlStack );
1,206✔
4367

4368
  size += Clib::memsize( *Locals2 );
1,206✔
4369
  for ( const auto& bojectref : *Locals2 )
1,323✔
4370
  {
4371
    if ( bojectref != nullptr )
117✔
4372
      size += bojectref->sizeEstimate();
117✔
4373
  }
4374
  size += Clib::memsize( *Globals2 );
1,206✔
4375
  for ( const auto& bojectref : *Globals2 )
2,536✔
4376
  {
4377
    if ( bojectref != nullptr )
1,330✔
4378
      size += bojectref->sizeEstimate();
1,330✔
4379
  }
4380
  size += Clib::memsize( ValueStack );
1,206✔
4381
  for ( const auto& bojectref : ValueStack )
1,255✔
4382
  {
4383
    if ( bojectref != nullptr )
49✔
4384
      size += bojectref->sizeEstimate();
49✔
4385
  }
4386
  size += Clib::memsize( fparams );
1,206✔
4387
  for ( const auto& bojectref : fparams )
1,206✔
4388
  {
4389
    if ( bojectref != nullptr )
×
4390
      size += bojectref->sizeEstimate();
×
4391
  }
4392
  for ( const auto& module : availmodules )
9,888✔
4393
  {
4394
    if ( module != nullptr )
8,682✔
4395
      size += module->sizeEstimate();
8,682✔
4396
  }
4397
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
1,206✔
4398
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
1,206✔
4399
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
1,206✔
4400
  size += Clib::memsize( class_methods );
1,206✔
4401
  return size;
1,206✔
4402
}
4403

4404
bool Executor::builtinMethodForced( const char*& methodname )
7✔
4405
{
4406
  if ( methodname[0] == '_' )
7✔
4407
  {
4408
    ++methodname;
×
4409
    return true;
×
4410
  }
4411
  return false;
7✔
4412
}
4413

4414
#ifdef ESCRIPT_PROFILE
4415
void Executor::profile_escript( std::string name, unsigned long profile_start )
4416
{
4417
  unsigned long profile_end = GetTimeUs() - profile_start;
4418
  escript_profile_map::iterator itr = EscriptProfileMap.find( name );
4419
  if ( itr != EscriptProfileMap.end() )
4420
  {
4421
    itr->second.count++;
4422
    itr->second.sum += profile_end;
4423
    if ( itr->second.max < profile_end )
4424
      itr->second.max = profile_end;
4425
    else if ( itr->second.min > profile_end )
4426
      itr->second.min = profile_end;
4427
  }
4428
  else
4429
  {
4430
    profile_instr profInstr;
4431
    profInstr.count = 1;
4432
    profInstr.max = profile_end;
4433
    profInstr.min = profile_end;
4434
    profInstr.sum = profile_end;
4435
    EscriptProfileMap[name] = profInstr;
4436
  }
4437
}
4438
#ifdef _WIN32
4439

4440
unsigned long Executor::GetTimeUs()
4441
{
4442
  static bool bInitialized = false;
4443
  static LARGE_INTEGER lFreq, lStart;
4444
  static LARGE_INTEGER lDivisor;
4445
  if ( !bInitialized )
4446
  {
4447
    bInitialized = true;
4448
    QueryPerformanceFrequency( &lFreq );
4449
    QueryPerformanceCounter( &lStart );
4450
    lDivisor.QuadPart = lFreq.QuadPart / 1000000;
4451
  }
4452

4453
  LARGE_INTEGER lEnd;
4454
  QueryPerformanceCounter( &lEnd );
4455
  double duration = double( lEnd.QuadPart - lStart.QuadPart ) / lFreq.QuadPart;
4456
  duration *= 1000000;
4457
  LONGLONG llDuration = static_cast<LONGLONG>( duration );
4458
  return llDuration & 0xffffffff;
4459
}
4460
#else
4461
unsigned long Executor::GetTimeUs()
4462
{
4463
  static bool bInitialized = false;
4464
  static timeval t1;
4465
  if ( !bInitialized )
4466
  {
4467
    bInitialized = true;
4468
    gettimeofday( &t1, nullptr );
4469
  }
4470

4471
  timeval t2;
4472
  gettimeofday( &t2, nullptr );
4473

4474
  double elapsedTime;
4475
  elapsedTime = ( t2.tv_sec - t1.tv_sec ) * 1000000.0;
4476
  elapsedTime += ( t2.tv_usec - t1.tv_usec );
4477

4478
  long long llDuration = static_cast<long long>( elapsedTime );
4479
  return llDuration & 0xffffffff;
4480
}
4481
#endif
4482
#endif
4483
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
405✔
4484
{
4485
  auto* func = continuation->func();
405✔
4486

4487
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4488
  // there are too many
4489
  while ( func->numParams() > static_cast<int>( args.size() ) )
409✔
4490
  {
4491
    args.push_back( BObjectRef( new BObject( UninitObject::create() ) ) );
4✔
4492
  }
4493

4494
  // Resize args only for non-varadic functions
4495
  if ( !func->variadic() )
405✔
4496
    args.resize( func->numParams() );
389✔
4497

4498
  continuation->args = std::move( args );
405✔
4499

4500
  return continuation;
405✔
4501
}
4502

4503
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
886✔
4504
{
4505
  // Compare the program pointers
4506
  if ( prog < other.prog )
886✔
4507
    return true;
×
4508
  if ( prog > other.prog )
886✔
4509
    return false;
×
4510

4511
  // Compare the indices
4512
  if ( index < other.index )
886✔
4513
    return true;
94✔
4514
  if ( index > other.index )
792✔
4515
    return false;
40✔
4516

4517
  // Perform a case-insensitive comparison for method_name using stricmp
4518
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
752✔
4519
}
4520
}  // namespace Bscript
4521
}  // 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