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

polserver / polserver / 35525772954

20 Sep 2026 05:24PM UTC coverage: 81.76% (+0.004%) from 81.756%
35525772954

push

github

web-flow
Refuse a gump reply whose int count wraps the length check (#975)

gumpbutton_handler summed the reply's size as a size_t and stored it in a
32-bit unsigned. Four bytes an int times a count of 0x40000000 is 2^32, so
the stored size came out as the fixed 23 bytes, passed the check against
the packet's length, and the handler then read the string count from four
gigabytes past the packet. Any player with a script gump open could crash
the server with one 23-byte reply.

Both counts are now checked by dividing the room the packet has left,
which cannot wrap, and the running offset is a size_t. The string count
had the same shape, though on a 64-bit build its product did not wrap.

Co-authored-by: murderinc <3738856+murderinc@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

4 of 4 new or added lines in 1 file covered. (100.0%)

131 existing lines in 3 files now uncovered.

60042 of 73437 relevant lines covered (81.76%)

2313353.43 hits per line

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

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

15

16
#include "bscript/executor.h"
17
#include "bscript/bobject.h"
18
#include "bscript/exectype.h"
19
#include "bscript/executor.inl.h"
20

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

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

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

70

71
namespace Pol::Bscript
72
{
73
namespace
74
{
75
// Script-controlled recursion is unbounded, so this block is capped tighter than
76
// the native one.
77
constexpr size_t MAX_REPORTED_ESCRIPT_FRAMES = 30;
78
}  // namespace
79

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

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

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

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

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

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

200
  return true;
296 ✔
201
}
202

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

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

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

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

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

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

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

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

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

309
int Executor::getParams( unsigned howMany )
229,405,093 ✔
310
{
311
  if ( howMany )
229,405,093 ✔
312
  {
313
    fparams.resize( howMany );
229,022,430 ✔
314
    for ( int i = howMany - 1; i >= 0; --i )
458,140,325 ✔
315
    {
316
      if ( ValueStack.empty() )
229,117,895 ✔
317
      {
318
        POLLOG_ERRORLN( "Fatal error: Value Stack Empty! ({},PC={}){}", prog_->name, PC,
×
319
                        script_stack_block() );
×
320
        seterror( true );
×
321
        return -1;
×
322
      }
323
      fparams[i] = ValueStack.back();
229,117,895 ✔
324
      ValueStack.pop_back();
229,117,895 ✔
325
    }
326
  }
327
  expandParams();
229,405,093 ✔
328
  return 0;
229,405,093 ✔
329
}
330

331
void Executor::expandParams()
229,405,093 ✔
332
{
333
  for ( auto i = static_cast<int>( fparams.size() ) - 1; i >= 0; --i )
458,524,218 ✔
334
  {
335
    if ( auto* spread = fparams[i]->impptr_if<BSpread>() )
229,119,125 ✔
336
    {
337
      // defer destruction
338
      BObjectRef obj( spread );
629 ✔
339

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

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

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

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

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

362
void Executor::cleanParams()
229,401,770 ✔
363
{
364
  fparams.clear();
229,401,770 ✔
365
}
229,401,770 ✔
366

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

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

377
  return 0;
×
378
}
379

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

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

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

401
  return 0;
×
402
}
403

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

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

429
  return fparams[param].get();
228,659,375 ✔
430
}
431

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

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

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

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

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

460
  passert( imp != nullptr );
93,897 ✔
461

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

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

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

491
  passert( imp != nullptr );
12,064 ✔
492

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

502

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

614
  return false;
54 ✔
615
}
616

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

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

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

639
  return nullptr;
1 ✔
640
}
641

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

807
  return false;
3 ✔
808
}
809

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

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

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

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

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

863

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

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

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

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

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

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

904

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

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

920
  ExecutorModule* em = execmodules[token.module];
229,117,298 ✔
921

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

946
  current_module_function = nullptr;
229,117,298 ✔
947
}
229,117,298 ✔
948

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

954
  Locals2->emplace_back( UninitObject::create() );
257,848 ✔
955

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

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

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

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

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

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

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

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

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

1003

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

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

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

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

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

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

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

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

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

1040

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1321

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

1326
  if ( objref->impptr()->isTrue() )
32,335 ✔
1327
    PC = (unsigned)ins.token.lval;
28,521 ✔
1328

1329
  ValueStack.pop_back();
32,335 ✔
1330
}
32,335 ✔
1331

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

1336
  if ( !objref->impptr()->isTrue() )
686,194 ✔
1337
    PC = (unsigned)ins.token.lval;
453,090 ✔
1338

1339
  ValueStack.pop_back();
686,194 ✔
1340
}
686,194 ✔
1341

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

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

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

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

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

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

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

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

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

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

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

1415

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1648
  BObjectRef& rightref = ValueStack.back();
23,189 ✔
1649

1650
  BObject& right = *rightref;
23,189 ✔
1651

1652
  BObjectImp& rightimpref = right.impref();
23,189 ✔
1653

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

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

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

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

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

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

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

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

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

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

1727
  BObject& right = *rightref;
263,512 ✔
1728
  BObject& left = *leftref;
263,512 ✔
1729

1730
  BObjectImp& rightimpref = right.impref();
263,512 ✔
1731

1732
  if ( right.count() == 1 && rightimpref.count() == 1 )
263,512 ✔
1733
  {
1734
    left.setimp( &rightimpref );
258,643 ✔
1735
  }
1736
  else
1737
  {
1738
    left.setimp( rightimpref.copy() );
4,869 ✔
1739
  }
1740
}
263,512 ✔
1741

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2042
  BObject& right = *rightref;
365,679 ✔
2043
  BObject& left = *leftref;
365,679 ✔
2044

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

2059
  BObject& right = *rightref;
16,200 ✔
2060
  BObject& left = *leftref;
16,200 ✔
2061

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

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

2077
  BObject& right = *rightref;
55,653 ✔
2078
  BObject& left = *leftref;
55,653 ✔
2079

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

2084
void Executor::ins_equal( const Instruction& /*ins*/ )
206,420 ✔
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();
206,420 ✔
2092
  ValueStack.pop_back();
206,420 ✔
2093
  BObjectRef& leftref = ValueStack.back();
206,420 ✔
2094

2095
  BObject& right = *rightref;
206,420 ✔
2096
  BObject& left = *leftref;
206,420 ✔
2097

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

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

2113
  BObject& right = *rightref;
200,092 ✔
2114
  BObject& left = *leftref;
200,092 ✔
2115

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

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

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

2147
  BObject& right = *rightref;
23,507 ✔
2148
  BObject& left = *leftref;
23,507 ✔
2149

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

2164
  BObject& right = *rightref;
166,244 ✔
2165
  BObject& left = *leftref;
166,244 ✔
2166

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2304
    OrderedSet indexes;
39 ✔
2305

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2387
  BObject& right = *rightref;
795 ✔
2388

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

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

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

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

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

2418
  BObject& right = *rightref;
183 ✔
2419

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2504
  BObject& ob = *obref;
45 ✔
2505

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

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

2515
  ValueStack.pop_back();
11,048 ✔
2516

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

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

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

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

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

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

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

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

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

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

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

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

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

2597
  BObject& right = *rightref;
14,080 ✔
2598
  BObject& left = *leftref;
14,080 ✔
2599

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2734
      if ( add_new_classinst )
3,427 ✔
2735
      {
2736
        if ( funcr->constructor() )
109 ✔
2737
        {
2738
          // The class index addresses the function reference's program, so the instance has to
2739
          // be built from that program and its creator's globals, not from whatever this
2740
          // executor currently runs.
2741
          fparams.insert( fparams.begin(),
82 ✔
2742
                          BObjectRef( new BConstObject( new BClassInstanceRef( new BClassInstance(
164 ✔
2743
                              funcr->prog(), funcr->class_index(), funcr->globals,
164 ✔
2744
                              funcr->pid() ) ) ) ) );
246 ✔
2745
        }
2746
      }
2747

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

2760
    size_t stacksize = ValueStack.size();  // ValueStack can grow
280,527 ✔
2761
    BObjectImp* imp;
2762
    {
2763
      ESCRIPT_PROFILER( ins, ValueStack.back(), fparams );
2764
      imp = ValueStack.back()->impptr()->call_method_id( ins.token.lval, *this );
280,527 ✔
2765

2766
      if ( auto* cont = impptrIf<BContinuation>( imp ) )
280,527 ✔
2767
      {
2768
        continuation = cont;
847 ✔
2769
        // Set nparams, so the next loop iteration's `getParams` will know how many arguments to
2770
        // move.
2771
        nparams = static_cast<unsigned int>( continuation->args.size() );
847 ✔
2772

2773
        // Add function reference to stack
2774
        ValueStack.emplace_back( continuation->func() );
847 ✔
2775

2776
        // Move all arguments to the value stack
2777
        ValueStack.insert( ValueStack.end(), std::make_move_iterator( continuation->args.begin() ),
847 ✔
2778
                           std::make_move_iterator( continuation->args.end() ) );
2779

2780
        continuation->args.clear();
847 ✔
2781

2782
        cleanParams();
847 ✔
2783

2784
        printStack( fmt::format(
1,694 ✔
2785
            "call_method_id continuation arguments added to ValueStack, prior to getParams({}) and "
2786
            "funcref.call()",
2787
            nparams ) );
2788

2789
        // Next on the stack is a `FuncRef` that we need to call. We will continue the loop and
2790
        // handle it.
2791

2792
        // Prior to handling the `FuncRef` in the next loop, it will move from ValueStack to fparam.
2793
        // Then, having a `continuation` set while processing the `FuncRef`, will create the proper
2794
        // jumps.
2795

2796
        continue;
847 ✔
2797
      }
2798
    }
2799

2800
    if ( impptrIf<BSpecialUserFuncJump>( imp ) )
279,680 ✔
2801
    {
2802
      cleanParams();
3 ✔
2803
      return;
3 ✔
2804
    }
2805
    BObjectRef& objref = ValueStack[stacksize - 1];
279,677 ✔
2806
    if ( func_result_ )
279,677 ✔
2807
    {
2808
      if ( imp )
48 ✔
2809
      {
2810
        BObject obj( imp );
36 ✔
2811
      }
36 ✔
2812

2813
      objref.set( func_result_ );
48 ✔
2814
      func_result_ = nullptr;
48 ✔
2815
    }
2816
    else if ( imp )
279,629 ✔
2817
    {
2818
      objref.set( imp );
279,548 ✔
2819
    }
2820
    else
2821
    {
2822
      objref.set( UninitObject::create() );
81 ✔
2823
    }
2824

2825
    cleanParams();
279,677 ✔
2826
    return;
279,677 ✔
2827
  }
2828
  // This condition should only ever evaluate to `true` once. In the second loop
2829
  // iteration, handling the FuncRef will return out of this method.
2830
  while ( continuation != nullptr );
847 ✔
2831
}
2832

2833
void Executor::ins_call_method( const Instruction& ins )
3,946 ✔
2834
{
2835
  unsigned nparams = ins.token.lval;
3,946 ✔
2836
  auto method_name = ins.token.tokval();
3,946 ✔
2837

2838
  getParams( nparams );
3,946 ✔
2839
  BObjectImp* callee = ValueStack.back()->impptr();
3,946 ✔
2840

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

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

2866
  if ( impptrIf<BSpecialUserFuncJump>( imp ) )
3,945 ✔
2867
  {
2868
    cleanParams();
2,574 ✔
2869
    return;
2,574 ✔
2870
  }
2871

2872
  BObjectRef& objref = ValueStack[stacksize - 1];
1,371 ✔
2873
  if ( func_result_ )
1,371 ✔
2874
  {
2875
    if ( imp )
1 ✔
2876
    {
UNCOV
2877
      BObject obj( imp );
×
UNCOV
2878
    }
×
2879

2880
    objref.set( func_result_ );
1 ✔
2881
    func_result_ = nullptr;
1 ✔
2882
  }
2883
  else if ( imp )
1,370 ✔
2884
  {
2885
    objref.set( imp );
266 ✔
2886
  }
2887
  else
2888
  {
2889
    objref.set( UninitObject::create() );
1,104 ✔
2890
  }
2891
  cleanParams();
1,371 ✔
2892
}
2893

2894
// CTRL_STATEMENTBEGIN:
2895
void Executor::ins_statementbegin( const Instruction& ins )
×
2896
{
UNCOV
2897
  if ( debug_level >= SOURCELINES && ins.token.tokval() )
×
UNCOV
2898
    INFO_PRINTLN( ins.token.tokval() );
×
UNCOV
2899
}
×
2900

2901
// case CTRL_PROGEND:
2902
void Executor::ins_progend( const Instruction& /*ins*/ )
10,846 ✔
2903
{
2904
  done = 1;
10,846 ✔
2905
  run_ok_ = false;
10,846 ✔
2906
  PC = 0;
10,846 ✔
2907
}
10,846 ✔
2908

2909

2910
// case CTRL_MAKELOCAL:
2911
void Executor::ins_makelocal( const Instruction& /*ins*/ )
95,161 ✔
2912
{
2913
  if ( Locals2 )
95,161 ✔
2914
    upperLocals2.push_back( Locals2 );
95,161 ✔
2915
  Locals2 = new BObjectRefVec;
95,161 ✔
2916
}
95,161 ✔
2917

2918
void Executor::ins_check_mro( const Instruction& ins )
280 ✔
2919
{
2920
  auto classinst_offset = ins.token.lval;
280 ✔
2921

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

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

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

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

2950
  auto ctor_addr = jsr_ins.token.lval;
280 ✔
2951

2952
  auto classinstref = classinst_ref->impptr_if<BClassInstanceRef>();
280 ✔
2953

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

2968
// CTRL_JSR_USERFUNC:
2969
void Executor::ins_jsr_userfunc( const Instruction& ins )
52,612 ✔
2970
{
2971
  jump( ins.token.lval, nullptr, nullptr );
52,612 ✔
2972
}
52,612 ✔
2973

2974
void Executor::jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref )
95,161 ✔
2975
{
2976
  ReturnContext rc;
95,161 ✔
2977
  rc.PC = PC;
95,161 ✔
2978
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
95,161 ✔
2979
  if ( continuation )
95,161 ✔
2980
  {
2981
    rc.Continuation.set( continuation );
37,497 ✔
2982
  }
2983

2984
  // Store our context if the function belongs to another program or to another executor: the
2985
  // jump target is an offset into the function reference's program, and the globals it closes
2986
  // over are the ones its creator had. The same executor can hold references to both, since it
2987
  // runs another program's code for the length of an external call.
2988
  if ( funcref != nullptr && ( funcref->prog() != prog_ || funcref->pid() != pid() ) )
95,161 ✔
2989
  {
2990
    // Store external context for the return path.
2991
    rc.ExternalContext = ReturnContext::External( prog_, std::move( execmodules ), Globals2 );
2,038 ✔
2992

2993
    if ( auto shared = funcref->globals.lock() )
4,076 ✔
2994
      Globals2 = shared;
2,036 ✔
2995
    else
2996
      Globals2 =
2997
          std::make_shared<BObjectRefVec>();  // empty but valid, access would stop the executor
2,038 ✔
2998

2999
    // Set the prog and globals to the external function's, updating nLines and
3000
    // execmodules.
3001
    prog_ = funcref->prog();
2,038 ✔
3002

3003

3004
    nLines = static_cast<unsigned int>( prog_->instr.size() );
2,038 ✔
3005

3006
    // Re-attach modules, as the external user function's module function call
3007
    // instructions refer to modules by index.
3008
    execmodules.clear();
2,038 ✔
3009

3010
    if ( !viewmode_ )
2,038 ✔
3011
    {
3012
      if ( !AttachFunctionalityModules() )
2,038 ✔
3013
      {
UNCOV
3014
        POLLOGLN( "Could not attach modules for external function call jump" );
×
UNCOV
3015
        seterror( true );
×
3016
      }
3017
    }
3018
  }
3019

3020
  // target_PC indexes the function reference's program, so it must be the one loaded.
3021
  passert( funcref == nullptr || funcref->prog() == prog_ );
95,161 ✔
3022

3023
  ControlStack.push_back( rc );
95,161 ✔
3024

3025
  PC = target_PC;
95,161 ✔
3026
  if ( ControlStack.size() >= escript_config.max_call_depth )
95,161 ✔
3027
  {
3028
    std::string tmp = fmt::format(
3029
        "Script {} exceeded maximum call depth\n"
3030
        "Return path PCs: ",
UNCOV
3031
        scriptname() );
×
UNCOV
3032
    while ( !ControlStack.empty() )
×
3033
    {
UNCOV
3034
      rc = ControlStack.back();
×
UNCOV
3035
      ControlStack.pop_back();
×
UNCOV
3036
      fmt::format_to( std::back_inserter( tmp ), "{} ", rc.PC );
×
3037
    }
UNCOV
3038
    POLLOGLN( tmp );
×
UNCOV
3039
    seterror( true );
×
UNCOV
3040
  }
×
3041
}
95,161 ✔
3042

3043

3044
namespace
3045
{
3046
// Runs `handler` for every frame of `ex`'s call stack with the frame's PC
3047
// resolved against the program's debug symbols. Only meaningful once
3048
// read_dbg_file() has succeeded; callers pick the symbol-less walk otherwise.
3049
void walk_call_stack_with_symbols(
436 ✔
3050
    Executor& ex,
3051
    const std::function<void( unsigned int /*pc*/, const std::string& /*file*/,
3052
                              unsigned int /*line*/, const std::string& /*functionName*/ )>&
3053
        handler )
3054
{
3055
  ex.walkCallStack(
436 ✔
3056
      [&]( unsigned int pc )
436 ✔
3057
      {
3058
        auto filename = ex.prog()->dbg_filenames[ex.prog()->dbg_filenum[pc]];
747 ✔
3059
        auto line = ex.prog()->dbg_linenum[pc];
747 ✔
3060
        auto dbgFunction =
3061
            std::find_if( ex.prog()->dbg_functions.begin(), ex.prog()->dbg_functions.end(),
747 ✔
3062
                          [&]( auto& i ) { return i.firstPC <= pc && pc <= i.lastPC; } );
8,125 ✔
3063

3064
        std::string functionName =
3065
            dbgFunction != ex.prog()->dbg_functions.end() ? dbgFunction->name : "<program>";
747 ✔
3066

3067
        handler( pc, filename, line, functionName );
747 ✔
3068
      } );
747 ✔
3069
}
436 ✔
3070
}  // namespace
3071

3072
BObjectImp* Executor::get_stacktrace( bool as_array )
83 ✔
3073
{
3074
  if ( !as_array )
83 ✔
3075
    return new String( stacktrace_string() );
79 ✔
3076

3077
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
4 ✔
3078

3079
  std::unique_ptr<ObjArray> result( new ObjArray );
4 ✔
3080

3081
  if ( has_symbols )
4 ✔
3082
  {
3083
    walk_call_stack_with_symbols( *this,
2 ✔
3084
                                  [&]( unsigned int pc, const std::string& filename,
4 ✔
3085
                                       unsigned int line, const std::string& functionName )
3086
                                  {
3087
                                    std::unique_ptr<BStruct> entry( new BStruct );
8 ✔
3088
                                    entry->addMember( "file", new String( filename ) );
8 ✔
3089
                                    entry->addMember( "line", new BLong( line ) );
8 ✔
3090
                                    entry->addMember( "name", new String( functionName ) );
8 ✔
3091
                                    entry->addMember( "pc", new BLong( pc ) );
8 ✔
3092
                                    result->addElement( entry.release() );
8 ✔
3093
                                  } );
8 ✔
3094
  }
3095
  else
3096
  {
3097
    walkCallStack(
2 ✔
3098
        [&]( unsigned int pc )
2 ✔
3099
        {
3100
          std::unique_ptr<BStruct> entry( new BStruct );
3 ✔
3101
          entry->addMember( "file", new String( scriptname() ) );
3 ✔
3102
          entry->addMember( "pc", new BLong( pc ) );
3 ✔
3103
          result->addElement( entry.release() );
3 ✔
3104
        } );
3 ✔
3105
  }
3106

3107
  return result.release();
4 ✔
3108
}
4 ✔
3109

3110
std::string Executor::stacktrace_string()
434 ✔
3111
{
3112
  bool has_symbols = prog_->read_dbg_file( true ) == 0;
434 ✔
3113

3114
  std::string result;
434 ✔
3115

3116
  if ( has_symbols )
434 ✔
3117
  {
3118
    walk_call_stack_with_symbols(
434 ✔
3119
        *this,
3120
        [&]( unsigned int /*pc*/, const std::string& filename, unsigned int line,
868 ✔
3121
             const std::string& functionName )
3122
        {
3123
          result.append( fmt::format( "{}at {} ({}:{})", result.empty() ? "" : "\n", functionName,
1,478 ✔
3124
                                      filename, line ) );
3125
        } );
739 ✔
3126
  }
3127
  else
3128
  {
UNCOV
3129
    walkCallStack(
×
UNCOV
3130
        [&]( unsigned int pc )
×
3131
        {
UNCOV
3132
          result.append(
×
UNCOV
3133
              fmt::format( "{}at {}+{}", result.empty() ? "" : "\n", scriptname(), pc ) );
×
UNCOV
3134
        } );
×
3135
  }
3136

3137
  return result;
434 ✔
UNCOV
3138
}
×
3139

3140
std::string Executor::script_stack_block()
354 ✔
3141
{
3142
  try
3143
  {
3144
    auto block = Clib::indent_stack_block( stacktrace_string(), MAX_REPORTED_ESCRIPT_FRAMES );
354 ✔
3145
    if ( block.empty() )
354 ✔
3146
      return {};
×
3147
    // indent_stack_block terminates its last line like every other; the caller's message ends
3148
    // where this block does.
3149
    block.pop_back();
354 ✔
3150
    return "\n" + block;
354 ✔
3151
  }
354 ✔
UNCOV
3152
  catch ( ... )
×
3153
  {
3154
    // Reading the debug file allocates and can fail. Losing the frames is acceptable; turning a
3155
    // diagnostic into a second failure is not.
UNCOV
3156
    return {};
×
UNCOV
3157
  }
×
3158
}
3159

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

3179
std::string format_execution_error( const std::string& script, unsigned int pid, size_t pc,
19 ✔
3180
                                    const std::string& thread_name, const std::string& what,
3181
                                    const std::string& escript_stack,
3182
                                    const std::string& native_stack )
3183
{
3184
  std::string result = fmt::format( "Exception in {}", script );
19 ✔
3185
  // A plain Executor has no pid; only scripts scheduled by the core do.
3186
  if ( pid )
19 ✔
3187
    fmt::format_to( std::back_inserter( result ), " (pid {})", pid );
34 ✔
3188
  fmt::format_to( std::back_inserter( result ), " PC {}", pc );
19 ✔
3189
  if ( !thread_name.empty() )
19 ✔
3190
    fmt::format_to( std::back_inserter( result ), ", thread {}", thread_name );
36 ✔
3191
  fmt::format_to( std::back_inserter( result ), "\n  {}\n", what );
19 ✔
3192

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

3199
  auto native = Clib::indent_stack_block( native_stack, Clib::MAX_REPORTED_NATIVE_FRAMES );
19 ✔
3200
  if ( !native.empty() )
19 ✔
3201
    result += "\nNative stack (throw site, innermost first):\n" + native;
11 ✔
3202

3203
  return result;
38 ✔
3204
}
19 ✔
3205

3206
void Executor::ins_pop_param( const Instruction& ins )
128,269 ✔
3207
{
3208
  popParam( ins.token );
128,269 ✔
3209
}
128,269 ✔
3210

3211
void Executor::ins_pop_param_byref( const Instruction& ins )
34,809 ✔
3212
{
3213
  popParamByRef( ins.token );
34,809 ✔
3214
}
34,809 ✔
3215

3216
void Executor::ins_get_arg( const Instruction& ins )
1,342 ✔
3217
{
3218
  getArg( ins.token );
1,342 ✔
3219
}
1,342 ✔
3220

3221
// CTRL_LEAVE_BLOCK:
3222
void Executor::ins_leave_block( const Instruction& ins )
209,548 ✔
3223
{
3224
  if ( Locals2 )
209,548 ✔
3225
  {
3226
    for ( int i = 0; i < ins.token.lval; i++ )
434,698 ✔
3227
      Locals2->pop_back();
225,150 ✔
3228
  }
3229
  else  // at global level.  ick.
3230
  {
3231
    for ( int i = 0; i < ins.token.lval; i++ )
×
3232
      Globals2->pop_back();
×
3233
  }
3234
}
209,548 ✔
3235

3236
void Executor::ins_gosub( const Instruction& ins )
×
3237
{
UNCOV
3238
  ReturnContext rc;
×
UNCOV
3239
  rc.PC = PC;
×
UNCOV
3240
  rc.ValueStackDepth = static_cast<unsigned int>( ValueStack.size() );
×
UNCOV
3241
  ControlStack.push_back( rc );
×
UNCOV
3242
  if ( Locals2 )
×
UNCOV
3243
    upperLocals2.push_back( Locals2 );
×
UNCOV
3244
  Locals2 = new BObjectRefVec;
×
3245

3246
  PC = (unsigned)ins.token.lval;
×
UNCOV
3247
}
×
3248

3249
// case RSV_RETURN
3250
void Executor::ins_return( const Instruction& /*ins*/ )
95,160 ✔
3251
{
3252
  if ( ControlStack.empty() )
95,160 ✔
3253
  {
3254
    // POLLOG, not ERROR_PRINTLN: the latter is stderr only, so a shard run as a service never
3255
    // saw this at all.
UNCOV
3256
    POLLOG_ERRORLN( "Return without GOSUB! (PC={}, {}){}", PC, scriptname(), script_stack_block() );
×
3257

UNCOV
3258
    seterror( true );
×
UNCOV
3259
    return;
×
3260
  }
3261
  BObjectRef continuation;
95,160 ✔
3262
  ReturnContext& rc = ControlStack.back();
95,160 ✔
3263
  PC = rc.PC;
95,160 ✔
3264

3265
  if ( rc.Continuation.get() != nullptr )
95,160 ✔
3266
  {
3267
    continuation = rc.Continuation;
37,497 ✔
3268
  }
3269

3270
  if ( Locals2 )
95,160 ✔
3271
  {
3272
    delete Locals2;
95,160 ✔
3273
    Locals2 = nullptr;
95,160 ✔
3274
  }
3275
  if ( !upperLocals2.empty() )
95,160 ✔
3276
  {
3277
    Locals2 = upperLocals2.back();
95,160 ✔
3278
    upperLocals2.pop_back();
95,160 ✔
3279
  }
3280

3281
  if ( rc.ExternalContext.has_value() )
95,160 ✔
3282
  {
3283
    prog_ = std::move( rc.ExternalContext->Program );
2,037 ✔
3284
    nLines = static_cast<unsigned int>( prog_->instr.size() );
2,037 ✔
3285
    execmodules = std::move( rc.ExternalContext->Modules );
2,037 ✔
3286
    Globals2 = std::move( rc.ExternalContext->Globals );
2,037 ✔
3287
  }
3288

3289
  // FIXME do something with rc.ValueStackDepth
3290
  ControlStack.pop_back();
95,160 ✔
3291

3292
  if ( continuation != nullptr )
95,160 ✔
3293
  {
3294
    auto result = ValueStack.back();
37,497 ✔
3295
    ValueStack.pop_back();
37,497 ✔
3296

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

3302
    // If the the continuation callback returned a continuation, handle the jump.
3303
    if ( auto* cont = impptrIf<BContinuation>( imp ) )
37,497 ✔
3304
    {
3305
      // Do not delete imp, as the ReturnContext created in `ins_jsr_userfunc`
3306
      // takes ownership.
3307

3308
      // Add function reference to stack
3309
      ValueStack.emplace_back( cont->func() );
36,650 ✔
3310

3311
      // Move all arguments to the fparams stack
3312
      fparams.insert( fparams.end(), std::make_move_iterator( cont->args.begin() ),
36,650 ✔
3313
                      std::make_move_iterator( cont->args.end() ) );
3314

3315
      cont->args.clear();
36,650 ✔
3316

3317
      printStack(
36,650 ✔
3318
          "continuation callback returned a continuation; continuation args added to fparams" );
3319

3320
      BObjectRef objref = ValueStack.back();
36,650 ✔
3321
      auto funcr = objref->impptr<BFunctionRef>();
36,650 ✔
3322
      Instruction jmp;
36,650 ✔
3323
      if ( funcr->validCall( MTH_CALL, *this, &jmp ) )
36,650 ✔
3324
      {
3325
        call_function_reference( funcr, cont, jmp );
36,650 ✔
3326
      }
3327
      else
3328
      {
3329
        // Delete `imp` since a ReturnContext was not created.
UNCOV
3330
        BObject bobj( imp );
×
UNCOV
3331
      }
×
3332
    }
36,650 ✔
3333
    else
3334
    {
3335
      // Remove the original `this` receiver from the stack, eg. remove `array{}` from
3336
      // `array{}.filter(...)`
3337
      ValueStack.pop_back();
847 ✔
3338

3339
      // Add the result to the stack.
3340
      ValueStack.emplace_back( imp );
847 ✔
3341
    }
3342
    printStack( fmt::format( "Continuation end of ins_return, jumping to PC={}", PC ) );
74,994 ✔
3343
  }
37,497 ✔
3344
}
95,160 ✔
3345

3346
void Executor::ins_exit( const Instruction& /*ins*/ )
3 ✔
3347
{
3348
  done = 1;
3 ✔
3349
  run_ok_ = false;
3 ✔
3350
}
3 ✔
3351

3352
void Executor::ins_double( const Instruction& ins )
5,387 ✔
3353
{
3354
  ValueStack.emplace_back( new Double( ins.token.dval ) );
5,387 ✔
3355
}
5,387 ✔
3356

3357
void Executor::ins_classinst( const Instruction& ins )
496 ✔
3358
{
3359
  ValueStack.emplace_back( new BConstObject(
496 ✔
3360
      new BClassInstanceRef( new BClassInstance( prog_, ins.token.lval, Globals2, pid() ) ) ) );
992 ✔
3361
}
496 ✔
3362

3363
void Executor::ins_string( const Instruction& ins )
447,106 ✔
3364
{
3365
  ValueStack.emplace_back( new String( ins.token.tokval() ) );
447,106 ✔
3366
}
447,106 ✔
3367

3368
void Executor::ins_regexp( const Instruction& )
150 ✔
3369
{
3370
  BObjectRef flags = ValueStack.back();
150 ✔
3371
  ValueStack.pop_back();
150 ✔
3372
  BObjectRef& pattern = ValueStack.back();
150 ✔
3373

3374
  pattern.set(
150 ✔
3375
      BRegExp::create( pattern->impptr()->getStringRep(), flags->impptr()->getStringRep() ) );
300 ✔
3376
}
150 ✔
3377

3378
void Executor::ins_error( const Instruction& /*ins*/ )
5,173 ✔
3379
{
3380
  ValueStack.emplace_back( new BError() );
5,173 ✔
3381
}
5,173 ✔
3382
void Executor::ins_struct( const Instruction& /*ins*/ )
5,785 ✔
3383
{
3384
  ValueStack.emplace_back( new BStruct );
5,785 ✔
3385
}
5,785 ✔
3386
void Executor::ins_spread( const Instruction& ins )
2,126 ✔
3387
{
3388
  bool spread_into = ins.token.lval;
2,126 ✔
3389

3390
  // Pops TOP, and spreads it into (new) TOP.
3391
  if ( spread_into )
2,126 ✔
3392
  {
3393
    BObjectRef rightref = ValueStack.back();
24 ✔
3394
    ValueStack.pop_back();
24 ✔
3395
    BObjectRef& leftref = ValueStack.back();
24 ✔
3396

3397
    BObject& right = *rightref;
24 ✔
3398
    BObject& left = *leftref;
24 ✔
3399

3400
    BObjectRef refIter( UninitObject::create() );
24 ✔
3401

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

3404
    while ( auto next = pIter->step() )
72 ✔
3405
    {
3406
      auto result = left->array_assign( refIter->impptr(), next->impptr(), true );
48 ✔
3407
      BObject res_obj( result );
48 ✔
3408
    }
48 ✔
3409
  }
24 ✔
3410
  // Pops TOP, pushing a new BSpread(TOP) onto the stack.
3411
  else
3412
  {
3413
    auto spread = new BSpread( ValueStack.back() );
2,102 ✔
3414
    ValueStack.pop_back();
2,102 ✔
3415
    ValueStack.emplace_back( spread );
2,102 ✔
3416
  }
3417
}
2,126 ✔
3418
void Executor::ins_array( const Instruction& /*ins*/ )
8,994 ✔
3419
{
3420
  ValueStack.emplace_back( new ObjArray );
8,994 ✔
3421
}
8,994 ✔
3422
void Executor::ins_dictionary( const Instruction& /*ins*/ )
215 ✔
3423
{
3424
  ValueStack.emplace_back( new BDictionary );
215 ✔
3425
}
215 ✔
3426
void Executor::ins_uninit( const Instruction& /*ins*/ )
139 ✔
3427
{
3428
  ValueStack.emplace_back( UninitObject::create() );
139 ✔
3429
}
139 ✔
UNCOV
3430
void Executor::ins_ident( const Instruction& /*ins*/ )
×
3431
{
UNCOV
3432
  ValueStack.emplace_back( new BError( "Please recompile this script" ) );
×
UNCOV
3433
}
×
3434

3435
// case TOK_UNMINUS:
3436
void Executor::ins_unminus( const Instruction& /*ins*/ )
165 ✔
3437
{
3438
  BObjectRef ref = getObjRef();
165 ✔
3439
  BObjectImp* newobj;
3440
  newobj = ref->impref().inverse();
165 ✔
3441

3442
  ValueStack.emplace_back( newobj );
165 ✔
3443
}
165 ✔
3444

3445
// case TOK_UNPLUSPLUS:
3446
void Executor::ins_unplusplus( const Instruction& /*ins*/ )
11,748 ✔
3447
{
3448
  BObjectRef& ref = ValueStack.back();
11,748 ✔
3449
  ref->impref().selfPlusPlus();
11,748 ✔
3450
}
11,748 ✔
3451

3452
// case TOK_UNMINUSMINUS:
3453
void Executor::ins_unminusminus( const Instruction& /*ins*/ )
134 ✔
3454
{
3455
  BObjectRef& ref = ValueStack.back();
134 ✔
3456
  ref->impref().selfMinusMinus();
134 ✔
3457
}
134 ✔
3458

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

3469
// case TOK_UNMINUSMINUS_POST:
3470
void Executor::ins_unminusminus_post( const Instruction& /*ins*/ )
100 ✔
3471
{
3472
  BObjectRef& ref = ValueStack.back();
100 ✔
3473
  BObjectImp* imp = ref->impptr();
100 ✔
3474
  BObject* n = ref->clone();
100 ✔
3475
  imp->selfMinusMinus();
100 ✔
3476
  ref.set( n );
100 ✔
3477
}
100 ✔
3478

3479
// case INS_SET_MEMBER_ID_UNPLUSPLUS:
3480
void Executor::ins_set_member_id_unplusplus( const Instruction& ins )
10 ✔
3481
{
3482
  BObjectRef& ref = ValueStack.back();
10 ✔
3483
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
10 ✔
3484
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
20 ✔
3485
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
10 ✔
3486
  {
3487
    tmp->impref().selfPlusPlus();
10 ✔
3488
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
10 ✔
3489
  }
3490
  ref.set( tmp.get() );
10 ✔
3491
}
10 ✔
3492

3493
// case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3494
void Executor::ins_set_member_id_unplusplus_post( const Instruction& ins )
3 ✔
3495
{
3496
  BObjectRef& ref = ValueStack.back();
3 ✔
3497
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3 ✔
3498
  BObject* res = tmp->clone();
3 ✔
3499
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6 ✔
3500
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3 ✔
3501
  {
3502
    tmp->impref().selfPlusPlus();
3 ✔
3503
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3 ✔
3504
  }
3505
  ref.set( res );
3 ✔
3506
}
3 ✔
3507

3508
// case INS_SET_MEMBER_ID_UNMINUSMINUS:
3509
void Executor::ins_set_member_id_unminusminus( const Instruction& ins )
13 ✔
3510
{
3511
  BObjectRef& ref = ValueStack.back();
13 ✔
3512
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
13 ✔
3513
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
26 ✔
3514
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
13 ✔
3515
  {
3516
    tmp->impref().selfMinusMinus();
13 ✔
3517
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
13 ✔
3518
  }
3519
  ref.set( tmp.get() );
13 ✔
3520
}
13 ✔
3521

3522
// case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3523
void Executor::ins_set_member_id_unminusminus_post( const Instruction& ins )
3 ✔
3524
{
3525
  BObjectRef& ref = ValueStack.back();
3 ✔
3526
  BObjectRef tmp = ref->impref().get_member_id( ins.token.lval );
3 ✔
3527
  BObject* res = tmp->clone();
3 ✔
3528
  if ( !tmp->isa( BObjectImp::OTUninit ) &&
6 ✔
3529
       !tmp->isa( BObjectImp::OTError ) )  // do nothing if curval is uninit or error
3 ✔
3530
  {
3531
    tmp->impref().selfMinusMinus();
3 ✔
3532
    ref->impref().set_member_id( ins.token.lval, tmp->impptr(), false );
3 ✔
3533
  }
3534
  ref.set( res );
3 ✔
3535
}
3 ✔
3536

3537
// case TOK_LOG_NOT:
3538
void Executor::ins_logical_not( const Instruction& /*ins*/ )
15,423 ✔
3539
{
3540
  BObjectRef ref = getObjRef();
15,423 ✔
3541
  ValueStack.emplace_back( new BLong( (int)!ref->impptr()->isTrue() ) );
15,423 ✔
3542
}
15,423 ✔
3543

3544
// case TOK_BITWISE_NOT:
3545
void Executor::ins_bitwise_not( const Instruction& /*ins*/ )
165 ✔
3546
{
3547
  BObjectRef ref = getObjRef();
165 ✔
3548
  ValueStack.emplace_back( ref->impptr()->bitnot() );
165 ✔
3549
}
165 ✔
3550

3551
// case TOK_FUNCREF:
3552
void Executor::ins_funcref( const Instruction& ins )
2,410 ✔
3553
{
3554
  if ( ins.token.lval >= static_cast<int>( prog_->function_references.size() ) )
2,410 ✔
3555
  {
UNCOV
3556
    POLLOG_ERRORLN( "Function reference index out of bounds: {} >= {}", ins.token.lval,
×
UNCOV
3557
                    prog_->function_references.size() );
×
UNCOV
3558
    seterror( true );
×
UNCOV
3559
    return;
×
3560
  }
3561

3562
  auto funcref_index = static_cast<unsigned>( ins.token.lval );
2,410 ✔
3563

3564
  ValueStack.emplace_back(
4,820 ✔
3565
      new BFunctionRef( prog_, pid(), funcref_index, Globals2, {} /* captures */ ) );
2,410 ✔
3566
}
3567

3568
void Executor::ins_functor( const Instruction& ins )
807 ✔
3569
{
3570
  auto funcref_index = static_cast<int>( ins.token.type );
807 ✔
3571

3572
  const auto& ep_funcref = prog_->function_references[funcref_index];
807 ✔
3573

3574
  int capture_count = ep_funcref.capture_count;
807 ✔
3575

3576
  auto captures = ValueStackCont();
807 ✔
3577
  while ( capture_count > 0 )
1,527 ✔
3578
  {
3579
    captures.push_back( ValueStack.back() );
720 ✔
3580
    ValueStack.pop_back();
720 ✔
3581
    capture_count--;
720 ✔
3582
  }
3583

3584
  auto func = new BFunctionRef( prog_, pid(), funcref_index, Globals2, std::move( captures ) );
807 ✔
3585

3586
  ValueStack.emplace_back( func );
807 ✔
3587

3588
  PC += ins.token.lval;
807 ✔
3589
}
807 ✔
3590

3591
void Executor::ins_logical_jump( const Instruction& ins )
692 ✔
3592
{
3593
  BObjectRef& objref = ValueStack.back();
692 ✔
3594
  // jmp if true for ||, jmp if false for &&
3595
  const bool obj_true = objref->impptr()->isTrue();
692 ✔
3596
  const bool jmp = ins.token.type == TYP_LOGICAL_JUMP_FALSE ? !obj_true : obj_true;
692 ✔
3597

3598
  if ( jmp )
692 ✔
3599
    PC = (unsigned)ins.token.lval;
162 ✔
3600
  // keep the obj on the stack if it should jump (ShortCircuit)
3601
  // (e.g. `true || 0` would skip `|| 0` but keep the `true` as result on the stack converted to
3602
  // BLong (original &&/|| convert to BLong bool)
3603
  if ( !jmp )
692 ✔
3604
    ValueStack.pop_back();
530 ✔
3605
  else
3606
    objref.set( new BLong( static_cast<int>( obj_true ) ) );
162 ✔
3607
}
692 ✔
3608

3609
void Executor::ins_logical_convert( const Instruction& /*ins*/ )
212 ✔
3610
{
3611
  BObjectRef& objref = ValueStack.back();
212 ✔
3612
  objref.set( new BLong( static_cast<int>( objref->impptr()->isTrue() ) ) );
212 ✔
3613
}
212 ✔
3614

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

3617
ExecInstrFunc Executor::GetInstrFunc( const Token& token )
362,387 ✔
3618
{
3619
  switch ( token.id )
362,387 ✔
3620
  {
3621
  case INS_INITFOREACH:
707 ✔
3622
    return &Executor::ins_initforeach;
707 ✔
3623
  case INS_STEPFOREACH:
707 ✔
3624
    return &Executor::ins_stepforeach;
707 ✔
3625
  case INS_INITFOR:
84 ✔
3626
    return &Executor::ins_initfor;
84 ✔
3627
  case INS_NEXTFOR:
84 ✔
3628
    return &Executor::ins_nextfor;
84 ✔
3629
  case INS_CASEJMP:
97 ✔
3630
    return &Executor::ins_casejmp;
97 ✔
3631
  case RSV_JMPIFTRUE:
4,462 ✔
3632
    return &Executor::ins_jmpiftrue;
4,462 ✔
3633
  case RSV_JMPIFFALSE:
6,531 ✔
3634
    return &Executor::ins_jmpiffalse;
6,531 ✔
3635
  case RSV_LOCAL:
7,869 ✔
3636
    return &Executor::ins_makeLocal;
7,869 ✔
3637
  case RSV_GLOBAL:
16,866 ✔
3638
  case TOK_GLOBALVAR:
3639
    return &Executor::ins_globalvar;
16,866 ✔
3640
  case TOK_LOCALVAR:
44,436 ✔
3641
    return &Executor::ins_localvar;
44,436 ✔
3642
  case TOK_LONG:
31,026 ✔
3643
    return &Executor::ins_long;
31,026 ✔
3644
  case TOK_DOUBLE:
1,489 ✔
3645
    return &Executor::ins_double;
1,489 ✔
3646
  case TOK_STRING:
47,892 ✔
3647
    return &Executor::ins_string;
47,892 ✔
3648
  case TOK_REGEXP:
150 ✔
3649
    return &Executor::ins_regexp;
150 ✔
3650
  case TOK_ERROR:
632 ✔
3651
    return &Executor::ins_error;
632 ✔
3652
  case TOK_STRUCT:
1,328 ✔
3653
    return &Executor::ins_struct;
1,328 ✔
3654
  case TOK_SPREAD:
670 ✔
3655
    return &Executor::ins_spread;
670 ✔
3656
  case TOK_CLASSINST:
267 ✔
3657
    return &Executor::ins_classinst;
267 ✔
3658
  case TOK_ARRAY:
3,486 ✔
3659
    return &Executor::ins_array;
3,486 ✔
3660
  case TOK_DICTIONARY:
210 ✔
3661
    return &Executor::ins_dictionary;
210 ✔
3662
  case TOK_FUNCREF:
747 ✔
3663
    return &Executor::ins_funcref;
747 ✔
3664
  case TOK_FUNCTOR:
708 ✔
3665
    return &Executor::ins_functor;
708 ✔
3666
  case INS_UNINIT:
117 ✔
3667
    return &Executor::ins_uninit;
117 ✔
UNCOV
3668
  case TOK_IDENT:
×
UNCOV
3669
    return &Executor::ins_ident;
×
3670
  case INS_ASSIGN_GLOBALVAR:
3,351 ✔
3671
    return &Executor::ins_assign_globalvar;
3,351 ✔
3672
  case INS_ASSIGN_LOCALVAR:
2,513 ✔
3673
    return &Executor::ins_assign_localvar;
2,513 ✔
3674
  case INS_ASSIGN_CONSUME:
301 ✔
3675
    return &Executor::ins_assign_consume;
301 ✔
3676
  case TOK_CONSUMER:
31,759 ✔
3677
    return &Executor::ins_consume;
31,759 ✔
3678
  case TOK_ASSIGN:
10,053 ✔
3679
    return &Executor::ins_assign;
10,053 ✔
3680
  case INS_SUBSCRIPT_ASSIGN:
9 ✔
3681
    return &Executor::ins_array_assign;
9 ✔
3682
  case INS_SUBSCRIPT_ASSIGN_CONSUME:
504 ✔
3683
    return &Executor::ins_array_assign_consume;
504 ✔
3684
  case INS_MULTISUBSCRIPT:
314 ✔
3685
    return &Executor::ins_multisubscript;
314 ✔
3686
  case INS_MULTISUBSCRIPT_ASSIGN:
30 ✔
3687
    return &Executor::ins_multisubscript_assign;
30 ✔
3688
  case INS_GET_MEMBER:
3,598 ✔
3689
    return &Executor::ins_get_member;
3,598 ✔
3690
  case INS_SET_MEMBER:
3 ✔
3691
    return &Executor::ins_set_member;
3 ✔
3692
  case INS_SET_MEMBER_CONSUME:
262 ✔
3693
    return &Executor::ins_set_member_consume;
262 ✔
3694

3695
  case INS_UNPACK_SEQUENCE:
146 ✔
3696
    return &Executor::ins_unpack_sequence;
146 ✔
3697
  case INS_UNPACK_INDICES:
89 ✔
3698
    return &Executor::ins_unpack_indices;
89 ✔
3699
  case INS_TAKE_GLOBAL:
183 ✔
3700
    return &Executor::ins_take_global;
183 ✔
3701
  case INS_TAKE_LOCAL:
398 ✔
3702
    return &Executor::ins_take_local;
398 ✔
3703

3704
  case INS_GET_MEMBER_ID:
5,559 ✔
3705
    return &Executor::ins_get_member_id;  // test id
5,559 ✔
3706
  case INS_SET_MEMBER_ID:
3 ✔
3707
    return &Executor::ins_set_member_id;  // test id
3 ✔
3708
  case INS_SET_MEMBER_ID_CONSUME:
462 ✔
3709
    return &Executor::ins_set_member_id_consume;  // test id
462 ✔
3710

3711
  case INS_SET_MEMBER_ID_CONSUME_PLUSEQUAL:
15 ✔
3712
    return &Executor::ins_set_member_id_consume_plusequal;  // test id
15 ✔
3713
  case INS_SET_MEMBER_ID_CONSUME_MINUSEQUAL:
6 ✔
3714
    return &Executor::ins_set_member_id_consume_minusequal;  // test id
6 ✔
3715
  case INS_SET_MEMBER_ID_CONSUME_TIMESEQUAL:
9 ✔
3716
    return &Executor::ins_set_member_id_consume_timesequal;  // test id
9 ✔
3717
  case INS_SET_MEMBER_ID_CONSUME_DIVIDEEQUAL:
6 ✔
3718
    return &Executor::ins_set_member_id_consume_divideequal;  // test id
6 ✔
3719
  case INS_SET_MEMBER_ID_CONSUME_MODULUSEQUAL:
6 ✔
3720
    return &Executor::ins_set_member_id_consume_modulusequal;  // test id
6 ✔
3721

3722
  case TOK_ADD:
5,311 ✔
3723
    return &Executor::ins_add;
5,311 ✔
3724
  case TOK_SUBTRACT:
598 ✔
3725
    return &Executor::ins_subtract;
598 ✔
3726
  case TOK_DIV:
163 ✔
3727
    return &Executor::ins_div;
163 ✔
3728
  case TOK_MULT:
532 ✔
3729
    return &Executor::ins_mult;
532 ✔
3730
  case TOK_MODULUS:
116 ✔
3731
    return &Executor::ins_modulus;
116 ✔
3732

3733
  case TOK_INSERTINTO:
7,136 ✔
3734
    return &Executor::ins_insert_into;
7,136 ✔
3735

3736
  case TOK_PLUSEQUAL:
115 ✔
3737
    return &Executor::ins_plusequal;
115 ✔
3738
  case TOK_MINUSEQUAL:
36 ✔
3739
    return &Executor::ins_minusequal;
36 ✔
3740
  case TOK_TIMESEQUAL:
27 ✔
3741
    return &Executor::ins_timesequal;
27 ✔
3742
  case TOK_DIVIDEEQUAL:
24 ✔
3743
    return &Executor::ins_divideequal;
24 ✔
3744
  case TOK_MODULUSEQUAL:
24 ✔
3745
    return &Executor::ins_modulusequal;
24 ✔
3746

3747
  case TOK_LESSTHAN:
578 ✔
3748
    return &Executor::ins_lessthan;
578 ✔
3749
  case TOK_LESSEQ:
205 ✔
3750
    return &Executor::ins_lessequal;
205 ✔
3751
  case RSV_GOTO:
3,197 ✔
3752
    return &Executor::ins_goto;
3,197 ✔
3753
  case TOK_ARRAY_SUBSCRIPT:
2,190 ✔
3754
    return &Executor::ins_arraysubscript;
2,190 ✔
3755
  case TOK_EQUAL:
1,430 ✔
3756
    return &Executor::ins_equal;
1,430 ✔
3757
  case TOK_FUNC:
21,290 ✔
3758
    return &Executor::ins_func;
21,290 ✔
3759
  case INS_CALL_METHOD:
946 ✔
3760
    return &Executor::ins_call_method;
946 ✔
3761
  case INS_CALL_METHOD_ID:
7,433 ✔
3762
    return &Executor::ins_call_method_id;
7,433 ✔
UNCOV
3763
  case CTRL_STATEMENTBEGIN:
×
UNCOV
3764
    return &Executor::ins_statementbegin;
×
3765
  case CTRL_MAKELOCAL:
15,626 ✔
3766
    return &Executor::ins_makelocal;
15,626 ✔
3767
  case INS_CHECK_MRO:
225 ✔
3768
    return &Executor::ins_check_mro;
225 ✔
3769
  case CTRL_JSR_USERFUNC:
15,626 ✔
3770
    return &Executor::ins_jsr_userfunc;
15,626 ✔
3771
  case INS_POP_PARAM:
6,302 ✔
3772
    return &Executor::ins_pop_param;
6,302 ✔
3773
  case INS_POP_PARAM_BYREF:
1,357 ✔
3774
    return &Executor::ins_pop_param_byref;
1,357 ✔
3775
  case INS_GET_ARG:
221 ✔
3776
    return &Executor::ins_get_arg;
221 ✔
3777
  case CTRL_LEAVE_BLOCK:
2,709 ✔
3778
    return &Executor::ins_leave_block;
2,709 ✔
3779
  case RSV_GOSUB:
×
3780
    return &Executor::ins_gosub;
×
3781
  case RSV_RETURN:
13,217 ✔
3782
    return &Executor::ins_return;
13,217 ✔
3783
  case RSV_EXIT:
18 ✔
3784
    return &Executor::ins_exit;
18 ✔
3785
  case INS_DECLARE_ARRAY:
72 ✔
3786
    return &Executor::ins_declareArray;
72 ✔
3787
  case TOK_UNMINUS:
23 ✔
3788
    return &Executor::ins_unminus;
23 ✔
UNCOV
3789
  case TOK_UNPLUS:
×
UNCOV
3790
    return &Executor::ins_nop;
×
3791
  case TOK_LOG_NOT:
671 ✔
3792
    return &Executor::ins_logical_not;
671 ✔
3793
  case TOK_BITWISE_NOT:
21 ✔
3794
    return &Executor::ins_bitwise_not;
21 ✔
3795
  case TOK_BSRIGHT:
120 ✔
3796
    return &Executor::ins_bitshift_right;
120 ✔
3797
  case TOK_BSLEFT:
130 ✔
3798
    return &Executor::ins_bitshift_left;
130 ✔
3799
  case TOK_BITAND:
121 ✔
3800
    return &Executor::ins_bitwise_and;
121 ✔
3801
  case TOK_BITXOR:
111 ✔
3802
    return &Executor::ins_bitwise_xor;
111 ✔
3803
  case TOK_BITOR:
114 ✔
3804
    return &Executor::ins_bitwise_or;
114 ✔
3805

3806
  case TOK_NEQ:
3,749 ✔
3807
    return &Executor::ins_notequal;
3,749 ✔
3808
  case TOK_GRTHAN:
294 ✔
3809
    return &Executor::ins_greaterthan;
294 ✔
3810
  case TOK_GREQ:
182 ✔
3811
    return &Executor::ins_greaterequal;
182 ✔
3812
  case TOK_AND:
499 ✔
3813
    return &Executor::ins_logical_and;
499 ✔
3814
  case TOK_OR:
1,374 ✔
3815
    return &Executor::ins_logical_or;
1,374 ✔
3816

3817
  case TOK_ADDMEMBER:
352 ✔
3818
    return &Executor::ins_addmember;
352 ✔
3819
  case TOK_DELMEMBER:
25 ✔
3820
    return &Executor::ins_removemember;
25 ✔
3821
  case TOK_CHKMEMBER:
142 ✔
3822
    return &Executor::ins_checkmember;
142 ✔
3823
  case INS_DICTIONARY_ADDMEMBER:
185 ✔
3824
    return &Executor::ins_dictionary_addmember;
185 ✔
3825
  case TOK_IN:
129 ✔
3826
    return &Executor::ins_in;
129 ✔
3827
  case TOK_IS:
56 ✔
3828
    return &Executor::ins_is;
56 ✔
3829
  case INS_ADDMEMBER2:
45 ✔
3830
    return &Executor::ins_addmember2;
45 ✔
3831
  case INS_ADDMEMBER_ASSIGN:
2,902 ✔
3832
    return &Executor::ins_addmember_assign;
2,902 ✔
3833
  case CTRL_PROGEND:
4,662 ✔
3834
    return &Executor::ins_progend;
4,662 ✔
3835
  case TOK_UNPLUSPLUS:
119 ✔
3836
    return &Executor::ins_unplusplus;
119 ✔
3837
  case TOK_UNMINUSMINUS:
60 ✔
3838
    return &Executor::ins_unminusminus;
60 ✔
3839
  case TOK_UNPLUSPLUS_POST:
100 ✔
3840
    return &Executor::ins_unplusplus_post;
100 ✔
3841
  case TOK_UNMINUSMINUS_POST:
57 ✔
3842
    return &Executor::ins_unminusminus_post;
57 ✔
3843
  case INS_SET_MEMBER_ID_UNPLUSPLUS:
10 ✔
3844
    return &Executor::ins_set_member_id_unplusplus;  // test id
10 ✔
3845
  case INS_SET_MEMBER_ID_UNMINUSMINUS:
13 ✔
3846
    return &Executor::ins_set_member_id_unminusminus;  // test id
13 ✔
3847
  case INS_SET_MEMBER_ID_UNPLUSPLUS_POST:
3 ✔
3848
    return &Executor::ins_set_member_id_unplusplus_post;  // test id
3 ✔
3849
  case INS_SET_MEMBER_ID_UNMINUSMINUS_POST:
3 ✔
3850
    return &Executor::ins_set_member_id_unminusminus_post;  // test id
3 ✔
3851
  case INS_SKIPIFTRUE_ELSE_CONSUME:
212 ✔
3852
    return &Executor::ins_skipiftrue_else_consume;
212 ✔
3853
  case TOK_INTERPOLATE_STRING:
5,955 ✔
3854
    return &Executor::ins_interpolate_string;
5,955 ✔
3855
  case TOK_FORMAT_EXPRESSION:
593 ✔
3856
    return &Executor::ins_format_expression;
593 ✔
3857
  case TOK_BOOL:
2,038 ✔
3858
    return &Executor::ins_bool;
2,038 ✔
3859
  case INS_LOGICAL_JUMP:
300 ✔
3860
    return &Executor::ins_logical_jump;
300 ✔
3861
  case INS_LOGICAL_CONVERT:
149 ✔
3862
    return &Executor::ins_logical_convert;
149 ✔
UNCOV
3863
  default:
×
UNCOV
3864
    throw std::runtime_error( "Undefined execution token " + Clib::tostring( token.id ) );
×
3865
  }
3866
}
3867

3868
void Executor::sethalt( bool halt )
17 ✔
3869
{
3870
  halt_ = halt;
17 ✔
3871

3872
  if ( halt && dbg_env_ )
17 ✔
3873
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
18 ✔
3874
      listener->on_halt();
9 ✔
3875

3876
  calcrunnable();
17 ✔
3877
}
17 ✔
3878

3879
void Executor::execInstr()
922,319,766 ✔
3880
{
3881
  unsigned onPC = PC;
922,319,766 ✔
3882
  try
3883
  {  // this is really more of a class invariant.
3884
    passert( run_ok_ );
922,319,766 ✔
3885
    passert( PC < nLines );
922,319,766 ✔
3886
    passert( !error_ );
922,319,766 ✔
3887
    passert( !done );
922,319,766 ✔
3888

3889
#ifdef NDEBUG
3890
    const Instruction& ins = prog_->instr[PC];
922,319,766 ✔
3891
#else
3892
    const Instruction& ins = prog_->instr.at( PC );
3893
#endif
3894
    if ( debug_level >= INSTRUCTIONS )
922,319,766 ✔
UNCOV
3895
      INFO_PRINTLN( "{}: {}", PC, ins.token );
×
3896

3897
    // If `on_instruction` returns false, do not execute this instruction.
3898
    if ( dbg_env_ && !dbg_env_->on_instruction( *this ) )
922,319,766 ✔
3899
    {
3900
      return;
9 ✔
3901
    }
3902

3903
    ++ins.cycles;
922,319,757 ✔
3904
    ++prog_->instr_cycles;
922,319,757 ✔
3905
    count_instr_cycle();
922,319,757 ✔
3906

3907
    ++PC;
922,319,757 ✔
3908

3909
    ( this->*( ins.func ) )( ins );
922,319,757 ✔
3910
  }
3911
  catch ( std::exception& ex )
1 ✔
3912
  {
3913
    std::string tmp = execution_error_report(
3914
        onPC, boost::core::demangle( typeid( ex ).name() ) + ": " + ex.what() );
1 ✔
3915
    if ( !run_ok_ )
1 ✔
UNCOV
3916
      tmp += "run_ok_ = false\n";
×
3917
    if ( PC < nLines )
1 ✔
3918
      fmt::format_to( std::back_inserter( tmp ), " PC < nLines: ({} < {})\n", PC, nLines );
2 ✔
3919
    if ( error_ )
1 ✔
3920
      tmp += "error_ = true\n";
×
3921
    if ( done )
1 ✔
UNCOV
3922
      tmp += "done = true\n";
×
3923

3924
    seterror( true );
1 ✔
3925
    POLLOG_ERROR( tmp );
1 ✔
3926

3927
    show_context( onPC );
1 ✔
3928
  }
1 ✔
3929
#ifdef __unix__
3930
  catch ( ... )
×
3931
  {
3932
    // No exception object to describe, but the throw site is still recorded --
3933
    // boost's hook does not care what was thrown.
UNCOV
3934
    std::string tmp = execution_error_report( onPC, "unclassified" );
×
3935

UNCOV
3936
    seterror( true );
×
UNCOV
3937
    POLLOG_ERROR( tmp );
×
3938

3939
    show_context( onPC );
×
UNCOV
3940
  }
×
3941
#endif
3942
}
3943

3944
std::string Executor::dbg_get_instruction( size_t atPC ) const
7 ✔
3945
{
3946
  std::string out;
7 ✔
3947
  dbg_get_instruction( atPC, out );
7 ✔
3948
  return out;
7 ✔
UNCOV
3949
}
×
3950

3951
void Executor::dbg_get_instruction( size_t atPC, std::string& os ) const
5,089 ✔
3952
{
3953
  bool has_breakpoint =
3954
      dbg_env_ ? dbg_env_->breakpoints.count( static_cast<unsigned>( atPC ) ) : false;
5,089 ✔
3955
  fmt::format_to( std::back_inserter( os ), "{}{}{} {}", ( atPC == PC ) ? ">" : " ", atPC,
5,089 ✔
3956
                  has_breakpoint ? "*" : ":", prog_->instr[atPC].token );
5,089 ✔
3957
}
5,089 ✔
3958

3959
void Executor::show_context( unsigned atPC )
1 ✔
3960
{
3961
  unsigned start, end;
3962
  if ( atPC >= 5 )
1 ✔
UNCOV
3963
    start = atPC - 5;
×
3964
  else
3965
    start = 0;
1 ✔
3966

3967
  end = atPC + 5;
1 ✔
3968

3969
  if ( end >= nLines )
1 ✔
3970
    end = nLines - 1;
1 ✔
3971

3972
  for ( unsigned i = start; i <= end; ++i )
8 ✔
3973
  {
3974
    POLLOGLN( "{}: {}", i, dbg_get_instruction( i ) );
7 ✔
3975
  }
3976
}
1 ✔
3977
void Executor::show_context( std::string& os, unsigned atPC )
462 ✔
3978
{
3979
  unsigned start, end;
3980
  if ( atPC >= 5 )
462 ✔
3981
    start = atPC - 5;
462 ✔
3982
  else
UNCOV
3983
    start = 0;
×
3984

3985
  end = atPC + 5;
462 ✔
3986

3987
  if ( end >= nLines )
462 ✔
UNCOV
3988
    end = nLines - 1;
×
3989

3990
  for ( unsigned i = start; i <= end; ++i )
5,544 ✔
3991
  {
3992
    dbg_get_instruction( i, os );
5,082 ✔
3993
    os += '\n';
5,082 ✔
3994
  }
3995
}
462 ✔
3996

3997
void Executor::call_function_reference( BFunctionRef* funcr, BContinuation* continuation,
42,549 ✔
3998
                                        const Instruction& jmp )
3999
{
4000
  // params need to be on the stack, without current objectref
4001
  ValueStack.pop_back();
42,549 ✔
4002
  // Push captured parameters onto the stack prior to function parameters.
4003
  for ( auto& p : funcr->captures )
49,659 ✔
4004
    ValueStack.push_back( p );
7,110 ✔
4005

4006
  auto nparams = static_cast<int>( fparams.size() );
42,549 ✔
4007

4008
  // Handle variadic functions special. Construct an array{} corresponding to
4009
  // the rest parameter (the last parameter for the function). The logic for the
4010
  // condition:
4011
  // - if true, the last argument in the call may not be an array{}, so we need
4012
  //   to construct one (which is why the condition is `>=` and not `>`).
4013
  // - if false, then the address we're jumping to will be a "default argument
4014
  //   address" and _not_ the user function directly, which will create the
4015
  //   array{}. (NB: The address/PC comes from BFunctionRef::validCall)
4016
  if ( funcr->variadic() && nparams >= funcr->numParams() )
42,549 ✔
4017
  {
4018
    auto num_nonrest_args = funcr->numParams() - 1;
641 ✔
4019

4020
    auto rest_arg = std::make_unique<ObjArray>();
641 ✔
4021

4022
    for ( int i = 0; i < static_cast<int>( fparams.size() ); ++i )
3,661 ✔
4023
    {
4024
      auto& p = fparams[i];
3,020 ✔
4025

4026
      if ( i < num_nonrest_args )
3,020 ✔
4027
      {
4028
        ValueStack.push_back( p );
617 ✔
4029
      }
4030
      else
4031
      {
4032
        rest_arg->ref_arr.push_back( p );
2,403 ✔
4033
      }
4034
    }
4035
    ValueStack.emplace_back( rest_arg.release() );
641 ✔
4036
  }
641 ✔
4037
  // The array{} will be created via the regular default-parameter handling by
4038
  // jumping to the address/PC which pushes an empty array{} on the ValueStack
4039
  // prior to jumping to the user function.
4040
  else
4041
  {
4042
    for ( auto& p : fparams )
90,024 ✔
4043
      ValueStack.push_back( p );
48,116 ✔
4044
  }
4045

4046
  // jump to function
4047
  jump( jmp.token.lval, continuation, funcr );
42,549 ✔
4048
  fparams.clear();
42,549 ✔
4049
  // switch to new block
4050
  ins_makelocal( jmp );
42,549 ✔
4051
}
42,549 ✔
4052

4053
bool Executor::exec()
8,741 ✔
4054
{
4055
  passert( prog_ok_ );
8,741 ✔
4056
  passert( !error_ );
8,741 ✔
4057

4058
  Clib::script_status.set_script( scriptname() );
8,741 ✔
4059

4060
  set_running_to_completion( true );
8,741 ✔
4061
  while ( runnable() )
857,504 ✔
4062
  {
4063
    Clib::script_status.set_pc( PC );
848,763 ✔
4064
    execInstr();
848,763 ✔
4065
  }
4066

4067
  return !error_;
8,741 ✔
4068
}
4069

4070
void Executor::reinitExec()
×
4071
{
4072
  PC = 0;
×
UNCOV
4073
  done = 0;
×
4074
  seterror( false );
×
4075

UNCOV
4076
  ValueStack.clear();
×
UNCOV
4077
  delete Locals2;
×
UNCOV
4078
  Locals2 = new BObjectRefVec;
×
4079

UNCOV
4080
  if ( !prog_ok_ )
×
4081
  {
UNCOV
4082
    seterror( true );
×
4083
  }
UNCOV
4084
}
×
4085

4086
void Executor::initForFnCall( unsigned in_PC )
7,931 ✔
4087
{
4088
#ifdef MEMORYLEAK
4089
  bool data_shown = false;
4090
#endif
4091

4092
  PC = in_PC;
7,931 ✔
4093
  done = 0;
7,931 ✔
4094
  seterror( false );
7,931 ✔
4095

4096
#ifdef MEMORYLEAK
4097
  while ( !ValueStack.empty() )
4098
  {
4099
    if ( Clib::memoryleak_debug )
4100
    {
4101
      if ( !data_shown )
4102
      {
4103
        LEAKLOG( "ValueStack... " );
4104
        data_shown = true;
4105
      }
4106

4107
      LEAKLOG( "{} [{}]", ValueStack.back()->impptr()->pack(),
4108
               ValueStack.back()->impptr()->sizeEstimate() );
4109
    }
4110
    ValueStack.pop_back();
4111
  }
4112
  if ( Clib::memoryleak_debug )
4113
    if ( data_shown )
4114
      LEAKLOGLN( " ...deleted" );
4115
#endif
4116

4117
  ValueStack.clear();
7,931 ✔
4118
  Locals2->clear();
7,931 ✔
4119
}
7,931 ✔
4120

4121
void Executor::pushArg( BObjectImp* arg )
12,017 ✔
4122
{
4123
  passert_always( arg );
12,017 ✔
4124
  ValueStack.emplace_back( arg );
12,017 ✔
4125
}
12,017 ✔
4126

4127
void Executor::pushArg( const BObjectRef& arg )
4 ✔
4128
{
4129
  ValueStack.push_back( arg );
4 ✔
4130
}
4 ✔
4131

4132
void Executor::addModule( ExecutorModule* module )
36,088 ✔
4133
{
4134
  availmodules.push_back( module );
36,088 ✔
4135
}
36,088 ✔
4136

4137

4138
ExecutorModule* Executor::findModule( const std::string& name )
15,516 ✔
4139
{
4140
  unsigned idx;
4141
  for ( idx = 0; idx < availmodules.size(); idx++ )
161,062 ✔
4142
  {
4143
    ExecutorModule* module = availmodules[idx];
161,062 ✔
4144
    if ( stricmp( module->moduleName.get().c_str(), name.c_str() ) == 0 )
161,062 ✔
4145
      return module;
15,516 ✔
4146
  }
UNCOV
4147
  return nullptr;
×
4148
}
4149

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

4174
  return true;
4 ✔
4175
}
4176

UNCOV
4177
void Executor::detach_debugger()
×
4178
{
UNCOV
4179
  dbg_env_.reset();
×
UNCOV
4180
  sethalt( false );
×
UNCOV
4181
}
×
4182

4183
void Executor::print_to_debugger( const std::string& message )
24,460 ✔
4184
{
4185
  if ( dbg_env_ )
24,460 ✔
4186
  {
4187
    if ( std::shared_ptr<ExecutorDebugListener> listener = dbg_env_->listener.lock() )
2 ✔
4188
      listener->on_print( message );
1 ✔
4189
  }
4190
}
24,460 ✔
4191

UNCOV
4192
void Executor::dbg_ins_trace()
×
4193
{
UNCOV
4194
  if ( dbg_env_ )
×
4195
  {
UNCOV
4196
    dbg_env_->debug_state = ExecutorDebugState::INS_TRACE;
×
4197
  }
UNCOV
4198
  sethalt( false );
×
UNCOV
4199
}
×
4200
void Executor::dbg_step_into()
1 ✔
4201
{
4202
  if ( dbg_env_ )
1 ✔
4203
  {
4204
    dbg_env_->debug_state = ExecutorDebugState::STEP_INTO;
1 ✔
4205
  }
4206
  sethalt( false );
1 ✔
4207
}
1 ✔
4208
void Executor::dbg_step_over()
2 ✔
4209
{
4210
  if ( dbg_env_ )
2 ✔
4211
  {
4212
    dbg_env_->debug_state = ExecutorDebugState::STEP_OVER;
2 ✔
4213
  }
4214
  sethalt( false );
2 ✔
4215
}
2 ✔
4216
void Executor::dbg_step_out()
1 ✔
4217
{
4218
  if ( dbg_env_ )
1 ✔
4219
  {
4220
    dbg_env_->debug_state = ExecutorDebugState::STEP_OUT;
1 ✔
4221
  }
4222
  sethalt( false );
1 ✔
4223
}
1 ✔
4224
void Executor::dbg_run()
4 ✔
4225
{
4226
  if ( dbg_env_ )
4 ✔
4227
  {
4228
    dbg_env_->debug_state = ExecutorDebugState::RUN;
4 ✔
4229
  }
4230
  sethalt( false );
4 ✔
4231
}
4 ✔
4232
void Executor::dbg_break()
1 ✔
4233
{
4234
  if ( dbg_env_ )
1 ✔
4235
  {
4236
    dbg_env_->debug_state = ExecutorDebugState::BREAK_INTO;
1 ✔
4237
  }
4238
}
1 ✔
4239

4240
void Executor::dbg_setbp( unsigned atPC )
2 ✔
4241
{
4242
  if ( dbg_env_ )
2 ✔
4243
  {
4244
    dbg_env_->breakpoints.insert( atPC );
2 ✔
4245
  }
4246
}
2 ✔
UNCOV
4247
void Executor::dbg_clrbp( unsigned atPC )
×
4248
{
UNCOV
4249
  if ( dbg_env_ )
×
4250
  {
UNCOV
4251
    dbg_env_->breakpoints.erase( atPC );
×
4252
  }
UNCOV
4253
}
×
4254

4255
void Executor::dbg_clrbps( const std::set<unsigned>& PCs )
1 ✔
4256
{
4257
  if ( dbg_env_ )
1 ✔
4258
  {
4259
    std::set<unsigned> result;
1 ✔
4260
    auto& breakpoints = dbg_env_->breakpoints;
1 ✔
4261
    std::set_difference( breakpoints.begin(), breakpoints.end(), PCs.begin(), PCs.end(),
1 ✔
4262
                         std::inserter( result, result.end() ) );
4263
    breakpoints = result;
1 ✔
4264
  }
1 ✔
4265
}
1 ✔
4266

UNCOV
4267
void Executor::dbg_clrallbp()
×
4268
{
UNCOV
4269
  if ( dbg_env_ )
×
4270
  {
UNCOV
4271
    dbg_env_->breakpoints.clear();
×
4272
  }
UNCOV
4273
}
×
4274

4275
size_t Executor::sizeEstimate() const
2,160 ✔
4276
{
4277
  size_t size = sizeof( *this );
2,160 ✔
4278
  size += Clib::memsize( upperLocals2 );
2,160 ✔
4279
  for ( const auto& bobjectrefvec : upperLocals2 )
2,161 ✔
4280
  {
4281
    size += Clib::memsize( *bobjectrefvec );
1 ✔
4282
    for ( const auto& bojectref : *bobjectrefvec )
1 ✔
4283
    {
UNCOV
4284
      if ( bojectref != nullptr )
×
UNCOV
4285
        size += bojectref->sizeEstimate();
×
4286
    }
4287
  }
4288
  size += Clib::memsize( ControlStack );
2,160 ✔
4289

4290
  size += Clib::memsize( *Locals2 );
2,160 ✔
4291
  for ( const auto& bojectref : *Locals2 )
2,275 ✔
4292
  {
4293
    if ( bojectref != nullptr )
115 ✔
4294
      size += bojectref->sizeEstimate();
115 ✔
4295
  }
4296
  size += Clib::memsize( *Globals2 );
2,160 ✔
4297
  for ( const auto& bojectref : *Globals2 )
4,691 ✔
4298
  {
4299
    if ( bojectref != nullptr )
2,531 ✔
4300
      size += bojectref->sizeEstimate();
2,531 ✔
4301
  }
4302
  size += Clib::memsize( ValueStack );
2,160 ✔
4303
  for ( const auto& bojectref : ValueStack )
2,199 ✔
4304
  {
4305
    if ( bojectref != nullptr )
39 ✔
4306
      size += bojectref->sizeEstimate();
39 ✔
4307
  }
4308
  size += Clib::memsize( fparams );
2,160 ✔
4309
  for ( const auto& bojectref : fparams )
2,160 ✔
4310
  {
UNCOV
4311
    if ( bojectref != nullptr )
×
UNCOV
4312
      size += bojectref->sizeEstimate();
×
4313
  }
4314
  for ( const auto& module : availmodules )
17,597 ✔
4315
  {
4316
    if ( module != nullptr )
15,437 ✔
4317
      size += module->sizeEstimate();
15,437 ✔
4318
  }
4319
  size += Clib::memsize( execmodules ) + Clib::memsize( availmodules );
2,160 ✔
4320
  size += dbg_env_ != nullptr ? dbg_env_->sizeEstimate() : 0;
2,160 ✔
4321
  size += func_result_ != nullptr ? func_result_->sizeEstimate() : 0;
2,160 ✔
4322
  size += Clib::memsize( class_methods );
2,160 ✔
4323
  return size;
2,160 ✔
4324
}
4325

4326
bool Executor::builtinMethodForced( const char*& methodname )
30 ✔
4327
{
4328
  if ( methodname[0] == '_' )
30 ✔
4329
  {
UNCOV
4330
    ++methodname;
×
UNCOV
4331
    return true;
×
4332
  }
4333
  return false;
30 ✔
4334
}
4335

4336
BContinuation* Executor::withContinuation( BContinuation* continuation, BObjectRefVec args )
36,650 ✔
4337
{
4338
  auto* func = continuation->func();
36,650 ✔
4339

4340
  // Add function arguments to value stack. Add arguments if there are not enough.  Remove if
4341
  // there are too many
4342
  while ( func->numParams() > static_cast<int>( args.size() ) )
36,656 ✔
4343
  {
4344
    args.emplace_back( UninitObject::create() );
6 ✔
4345
  }
4346

4347
  // Resize args only for non-varadic functions
4348
  if ( !func->variadic() )
36,650 ✔
4349
    args.resize( func->numParams() );
36,626 ✔
4350

4351
  continuation->args = std::move( args );
36,650 ✔
4352

4353
  return continuation;
36,650 ✔
4354
}
4355

4356
bool Executor::ClassMethodKey::operator<( const ClassMethodKey& other ) const
5,851 ✔
4357
{
4358
  // Compare the program pointers
4359
  if ( prog < other.prog )
5,851 ✔
UNCOV
4360
    return true;
×
4361
  if ( prog > other.prog )
5,851 ✔
UNCOV
4362
    return false;
×
4363

4364
  // Compare the indices
4365
  if ( index < other.index )
5,851 ✔
4366
    return true;
174 ✔
4367
  if ( index > other.index )
5,677 ✔
4368
    return false;
69 ✔
4369

4370
  // Perform a case-insensitive comparison for method_name using stricmp
4371
  return stricmp( method_name.c_str(), other.method_name.c_str() ) < 0;
5,608 ✔
4372
}
4373

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

4417
EscriptProfiler::~EscriptProfiler()
4418
{
4419
  auto profile_end = timer_.ellapsed().count();
4420
  auto itr = escript_profile_map_.find( name_ );
4421
  if ( itr != escript_profile_map_.end() )
4422
  {
4423
    itr->second.count++;
4424
    itr->second.sum += profile_end;
4425
    if ( itr->second.max < profile_end )
4426
      itr->second.max = profile_end;
4427
    else if ( itr->second.min > profile_end )
4428
      itr->second.min = profile_end;
4429
  }
4430
  else
4431
  {
4432
    escript_profile_map_[name_] = {
4433
        .sum = profile_end, .max = profile_end, .min = profile_end, .count = 1 };
4434
  }
4435
}
4436
std::string EscriptProfiler::result()
4437
{
4438
  std::string buffer = "FuncName,Count,Min,Max,Sum,Avarage\n";
4439
  for ( const auto& [name, profile] : escript_profile_map_ )
4440
  {
4441
    fmt::format_to( std::back_inserter( buffer ), "{},{},{},{},{},{:.2f}\n", name, profile.count,
4442
                    profile.min, profile.max, profile.sum, profile.sum / ( 1.0 * profile.count ) );
4443
  }
4444
  return buffer;
4445
}
4446
#endif
4447
}  // namespace Pol::Bscript
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc