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

polserver / polserver / 20881646624

10 Jan 2026 05:12PM UTC coverage: 60.504%. Remained the same
20881646624

push

github

web-flow
Class method if it's a method_id call now works if parameters are given (#849)

* modifed test to trigger the failure

* getParams modifies the ValueStack and thus cannot be called twice.

* docs

6 of 9 new or added lines in 2 files covered. (66.67%)

1 existing line in 1 file now uncovered.

44455 of 73474 relevant lines covered (60.5%)

511339.0 hits per line

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

78.95
/pol-core/bscript/executor.h
1
/** @file
2
 *
3
 * @par History
4
 * - 2009/09/05 Turley: Added struct .? and .- as shortcut for .exists() and .erase()
5
 */
6

7

8
#ifndef __EXECUTOR_H
9
#define __EXECUTOR_H
10

11
#include "pol_global_config.h"
12

13
#ifndef __EXECTYPE_H
14
#include "exectype.h"
15
#endif
16

17
#ifndef BSCRIPT_BOBJECT_H
18
#include "bobject.h"
19
#endif
20

21
#include <exception>
22
#include <map>
23
#include <memory>
24
#include <optional>
25
#include <set>
26
#include <stack>
27
#include <string>
28
#include <utility>
29
#include <vector>
30

31
#include "../clib/refptr.h"
32
#include "../clib/spinlock.h"
33
#include "berror.h"
34
#include "bobject.h"
35
#include "continueimp.h"
36
#include "eprog.h"
37
#include "executortype.h"
38

39
#ifdef ESCRIPT_PROFILE
40
#include "clib/timer.h"
41
#include <map>
42
#endif
43

44
namespace Pol
45
{
46
namespace Core
47
{
48
class UOExecutor;
49

50
void list_script( UOExecutor* uoexec );
51
}  // namespace Core
52
namespace Bscript
53
{
54
class BContinuation;
55
class BFunctionRef;
56
class Executor;
57
class EScriptProgram;
58
class ExecutorModule;
59
class ModuleFunction;
60
class String;
61
class Token;
62

63
class ExecutorDebugListener
64
{
65
public:
NEW
66
  virtual void on_halt() {};
×
NEW
67
  virtual void on_destroy() {};
×
NEW
68
  virtual void on_print( const std::string& /*str*/ ) {};
×
69
};
70

71
// FIXME: how to make this a nested struct in Executor?
72
struct ReturnContext
73
{
74
  struct External
75
  {
76
    External( ref_ptr<EScriptProgram> program, std::vector<ExecutorModule*> modules,
115✔
77
              std::shared_ptr<BObjectRefVec> globals )
78
        : Program( std::move( program ) ),
115✔
79
          Modules( std::move( modules ) ),
115✔
80
          Globals( std::move( globals ) )
115✔
81
    {
82
    }
115✔
83
    ref_ptr<EScriptProgram> Program;
84
    std::vector<ExecutorModule*> Modules;
85
    std::shared_ptr<BObjectRefVec> Globals;
86
  };
87

88
  unsigned PC;
89
  unsigned ValueStackDepth;
90
  BObjectRef Continuation = BObjectRef();
91

92
  std::optional<External> ExternalContext;
93
};
94

95
struct BackupStruct
96
{
97
  std::unique_ptr<BObjectRefVec> Locals;
98
  ValueStackCont ValueStack;
99
  unsigned PC;
100
};
101

102
enum class ExecutorType
103
{
104
  EXECUTOR,
105
  POL
106
};
107

108
enum class ExecutorDebugState
109
{
110
  ATTACHING,
111
  ATTACHED,
112
  INS_TRACE,
113
  RUN,
114
  BREAK_INTO,
115
  STEP_INTO,
116
  STEPPING_INTO,
117
  STEP_OVER,
118
  STEPPING_OVER,
119
  STEP_OUT,
120
};
121

122
class ExecutorDebugEnvironment
123
{
124
public:
125
  ExecutorDebugEnvironment( std::weak_ptr<ExecutorDebugListener> listener, bool set_attaching );
126

127
  // Return `false` to skip the instruction from executing.
128
  bool on_instruction( Executor& );
129
  size_t sizeEstimate() const;
130

131
  ExecutorDebugState debug_state;
132
  std::set<unsigned> breakpoints;
133
  std::set<unsigned> tmpbreakpoints;
134
  struct
135
  {
136
    unsigned line;
137
    size_t control;
138
  } break_on_linechange_from;
139
  unsigned bp_skip;
140
  std::weak_ptr<ExecutorDebugListener> listener;
141
};
142

143
class Executor
144
{
145
public:
146
  static Clib::SpinLock _executor_lock;
147
  virtual size_t sizeEstimate() const;
148

149
  friend void Core::list_script( Core::UOExecutor* uoexec );
150
  int done;
151
  void seterror( bool err );
152
  bool error() const;
153
  bool error_;
154
  bool halt_;
155
  bool run_ok_;
156

157
  virtual ExecutorType type() { return ExecutorType::EXECUTOR; }
×
158

159
  enum DEBUG_LEVEL
160
  {
161
    NONE,
162
    SOURCELINES,
163
    INSTRUCTIONS
164
  };
165
  DEBUG_LEVEL debug_level;
166
  unsigned PC;  // program counter
167

168
  bool AttachFunctionalityModules();
169

170

171
  bool setProgram( EScriptProgram* prog );
172

173
  std::shared_ptr<BObjectRefVec> Globals2;
174

175
  std::vector<BObjectRefVec*> upperLocals2;
176

177
  std::vector<ReturnContext> ControlStack;
178

179
  BObjectRefVec* Locals2;
180

181
  static UninitObject* m_SharedUninitObject;
182

183

184
  ValueStackCont ValueStack;
185

186
  static ExecInstrFunc GetInstrFunc( const Token& token );
187

188
  /*
189
      These must both be deleted.  instr references _symbols, so it should be deleted first.
190
      FIXME: there should be a separate object, called EProgram or something,
191
      that owns both the instructions and the symbols.  It should be ref_counted,
192
      so a code repository can store programs that multiple Executors use.
193
      That means debugger stuff has to come out of Instruction.
194
      */
195
  unsigned nLines;
196

197
  std::vector<BObjectRef> fparams;
198

199
  friend class ExecutorModule;
200
  void setFunctionResult( BObjectImp* imp );
201

202
protected:
203
  int getParams( unsigned howMany );
204
  void expandParams();
205
  void cleanParams();
206

207
public:
208
  // Creates a new continuation object to call `funcref` with arguments `args`,
209
  // calling a core `callback` with the return value.
210
  //
211
  // Responsible for moving the arguments to the `ValueStack` stack,
212
  // expanding/shrinking as needed. The executor, when seeing a `BContinuation`
213
  // inside `ins_call_method_id`, will "translate" it to a `BFunctionRef` to
214
  // call. The handling of `BFunctionRef`s _inside_ `ins_call_method_id`
215
  // requires arguments to be on `ValueStack`.
216
  //
217
  // Since this runs inside `ins_call_method_id`, it can **only** be used in
218
  // `<object>.<method>` calls. Extending where this is called will require
219
  // changing implementation.
220
  //
221
  // Returns `BContinuation*` on success, `BError*` on failure (if provided
222
  // funcref is not a BFunctionRef).
223
  template <typename Callback>
224
  BObjectImp* makeContinuation( BObjectRef funcref, Callback&& callback, BObjectRefVec args = {} );
225

226
  // Runs the existing continuation object with arguments `args`.
227
  //
228
  // Responsible for moving the arguments to the `fparams` stack. The handling
229
  // of `BFunctionRef`s _outside_ `ins_call_method_id` requires arguments to be
230
  // on `fparams` (as they are moved to `ValueStack` in `ins_call_method_id`).
231
  BContinuation* withContinuation( BContinuation* continuation, BObjectRefVec args = {} );
232

233
  // Runs `callback( unsigned int PC )` for every frame in the call stack.
234
  template <typename Callback>
235
  void walkCallStack( Callback&& callback );
236

237
  int makeString( unsigned param );
238
  bool hasParams( unsigned howmany ) const { return ( fparams.size() >= howmany ); }
2,264✔
239
  size_t numParams() const { return fparams.size(); }
109,396✔
240
  BObjectImp* getParamImp( unsigned param );
241
  BObjectImp* getParamImp( unsigned param, BObjectImp::BObjectType type );
242
  BObjectImp* getParamImp2( unsigned param, BObjectImp::BObjectType type );
243
  BObject* getParamObj( unsigned param );
244

245
  const String* getStringParam( unsigned param );
246
  const BLong* getLongParam( unsigned param );
247

248
  bool getStringParam( unsigned param, const String*& pstr );
249
  bool getUnicodeStringParam( unsigned param, const String*& pstr );  // accepts also BLong array
250
  bool getParam( unsigned param, int& value );
251
  bool getParam( unsigned param, int& value, int maxval );
252
  bool getParam( unsigned param, int& value, int minval, int maxval );
253
  bool getRealParam( unsigned param, double& value );
254
  bool getObjArrayParam( unsigned param, ObjArray*& pobjarr );
255

256
  bool getParam( unsigned param, unsigned& value );
257

258
  bool getParam( unsigned param, unsigned short& value );
259
  bool getParam( unsigned param, unsigned short& value, unsigned short maxval );
260
  bool getParam( unsigned param, unsigned short& value, unsigned short minval,
261
                 unsigned short maxval );
262

263
  bool getParam( unsigned param, short& value );
264
  bool getParam( unsigned param, short& value, short maxval );
265
  bool getParam( unsigned param, short& value, short minval, short maxval );
266
  bool getParam( unsigned param, signed char& value );
267

268
  bool getParam( unsigned param, bool& value );
269

270
  void* getApplicPtrParam( unsigned param, const BApplicObjType* pointer_type );
271
  BApplicObjBase* getApplicObjParam( unsigned param, const BApplicObjType* object_type );
272

273

274
  const char* paramAsString( unsigned param );
275
  double paramAsDouble( unsigned param );
276
  int paramAsLong( unsigned param );
277

278
protected:
279
  int makeDouble( unsigned param );
280

281

282
  BObject* getParam( unsigned param );
283

284
  BObject getValue( void );
285
  BObjectRef getObjRef( void );
286

287
public:
288
  int getToken( Token& token, unsigned position );
289
  BObjectRef& LocalVar( unsigned int varnum );
290
  BObjectRef& GlobalVar( unsigned int varnum );
291
  int makeGlobal( const Token& token );
292
  void popParam( const Token& token );
293
  void popParamByRef( const Token& token );
294
  void getArg( const Token& token );
295
  void pushArg( BObjectImp* arg );
296
  void pushArg( const BObjectRef& ref );
297

298
  static BObjectRef addmember( BObject& left, const BObject& right );
299
  static BObjectRef removemember( BObject& left, const BObject& right );
300
  static BObjectRef checkmember( BObject& left, const BObject& right );
301
  void addmember2( BObject& left, const BObject& right );
302
  static bool builtinMethodForced( const char*& methodname );
303

304
  // execmodules: modules associated with the current program.  References modules owned by
305
  // availmodules.
306
  std::vector<ExecutorModule*> execmodules;
307
  std::vector<ExecutorModule*> availmodules;  // owns
308

309
public:
310
  Executor();
311
  virtual ~Executor();
312
  Executor( const Executor& exec ) = delete;
313
  Executor& operator=( const Executor& exec ) = delete;
314

315
  void addModule( ExecutorModule* module );  // NOTE, executor deletes its modules when done
316
  ExecutorModule* findModule( const std::string& name );
317

318
  ModuleFunction* current_module_function;
319
  // NOTE: the debugger code expects these to be virtual..
320
  void execFunc( const Token& token );
321
  void execInstr();
322

323
  void ins_nop( const Instruction& ins );
324
  void ins_jmpiftrue( const Instruction& ins );
325
  void ins_jmpiffalse( const Instruction& ins );
326
  void ins_skipiftrue_else_consume( const Instruction& ins );
327
  void ins_globalvar( const Instruction& ins );
328
  void ins_localvar( const Instruction& ins );
329
  void ins_makeLocal( const Instruction& ins );
330
  void ins_declareArray( const Instruction& ins );
331
  void ins_bool( const Instruction& ins );
332
  void ins_long( const Instruction& ins );
333
  void ins_double( const Instruction& ins );
334
  void ins_classinst( const Instruction& ins );
335
  void ins_string( const Instruction& ins );
336
  void ins_regexp( const Instruction& ins );
337
  void ins_error( const Instruction& ins );
338
  void ins_struct( const Instruction& ins );
339
  void ins_spread( const Instruction& ins );
340
  void ins_array( const Instruction& ins );
341
  void ins_dictionary( const Instruction& ins );
342
  void ins_uninit( const Instruction& ins );
343
  void ins_ident( const Instruction& ins );
344
  void ins_unminus( const Instruction& ins );
345
  void ins_unplusplus( const Instruction& ins );
346
  void ins_unminusminus( const Instruction& ins );
347
  void ins_unplusplus_post( const Instruction& ins );
348
  void ins_unminusminus_post( const Instruction& ins );
349

350
  void ins_logical_and( const Instruction& ins );
351
  void ins_logical_or( const Instruction& ins );
352
  void ins_logical_not( const Instruction& ins );
353

354
  void ins_bitwise_not( const Instruction& ins );
355

356
  void ins_set_member( const Instruction& ins );
357
  void ins_set_member_consume( const Instruction& ins );
358
  void ins_get_member( const Instruction& ins );
359
  void ins_get_member_id( const Instruction& ins );                       // test id
360
  void ins_set_member_id( const Instruction& ins );                       // test id
361
  void ins_set_member_id_consume( const Instruction& ins );               // test id
362
  void ins_set_member_id_consume_plusequal( const Instruction& ins );     // test id
363
  void ins_set_member_id_consume_minusequal( const Instruction& ins );    // test id
364
  void ins_set_member_id_consume_timesequal( const Instruction& ins );    // test id
365
  void ins_set_member_id_consume_divideequal( const Instruction& ins );   // test id
366
  void ins_set_member_id_consume_modulusequal( const Instruction& ins );  // test id
367
  void ins_set_member_id_unplusplus( const Instruction& ins );            // test id
368
  void ins_set_member_id_unminusminus( const Instruction& ins );          // test id
369
  void ins_set_member_id_unplusplus_post( const Instruction& ins );       // test id
370
  void ins_set_member_id_unminusminus_post( const Instruction& ins );     // test id
371

372
  void ins_assign_localvar( const Instruction& ins );
373
  void ins_assign_globalvar( const Instruction& ins );
374
  void ins_assign_consume( const Instruction& ins );
375
  void ins_consume( const Instruction& ins );
376
  void ins_assign( const Instruction& ins );
377
  void ins_array_assign( const Instruction& ins );
378
  void ins_array_assign_consume( const Instruction& ins );
379
  void ins_multisubscript_assign( const Instruction& ins );
380
  void ins_multisubscript_assign_consume( const Instruction& ins );
381
  void ins_multisubscript( const Instruction& ins );
382

383
  void ins_unpack_sequence( const Instruction& ins );
384
  void ins_unpack_indices( const Instruction& ins );
385
  void ins_take_local( const Instruction& ins );
386
  void ins_take_global( const Instruction& ins );
387

388
  void ins_add( const Instruction& ins );
389
  void ins_subtract( const Instruction& ins );
390
  void ins_mult( const Instruction& ins );
391
  void ins_div( const Instruction& ins );
392
  void ins_modulus( const Instruction& ins );
393

394
  void ins_is( const Instruction& ins );
395

396
  void ins_interpolate_string( const Instruction& ins );
397
  void ins_format_expression( const Instruction& ins );
398

399
  void ins_insert_into( const Instruction& ins );
400

401
  void ins_plusequal( const Instruction& ins );
402
  void ins_minusequal( const Instruction& ins );
403
  void ins_timesequal( const Instruction& ins );
404
  void ins_divideequal( const Instruction& ins );
405
  void ins_modulusequal( const Instruction& ins );
406

407
  void ins_bitshift_right( const Instruction& ins );
408
  void ins_bitshift_left( const Instruction& ins );
409
  void ins_bitwise_and( const Instruction& ins );
410
  void ins_bitwise_xor( const Instruction& ins );
411
  void ins_bitwise_or( const Instruction& ins );
412

413
  void ins_equal( const Instruction& ins );
414
  void ins_notequal( const Instruction& ins );
415
  void ins_lessthan( const Instruction& ins );
416
  void ins_lessequal( const Instruction& ins );
417
  void ins_greaterthan( const Instruction& ins );
418
  void ins_greaterequal( const Instruction& ins );
419

420
  void ins_goto( const Instruction& ins );
421
  void ins_arraysubscript( const Instruction& ins );
422
  void ins_func( const Instruction& ins );
423
  void ins_call_method( const Instruction& ins, bool params_expanded );
424
  void ins_call_method( const Instruction& ins );
425
  void ins_call_method_id( const Instruction& ins );
426
  void ins_statementbegin( const Instruction& ins );
427
  void ins_progend( const Instruction& ins );
428
  void ins_makelocal( const Instruction& ins );
429
  void ins_check_mro( const Instruction& ins );
430
  void ins_jsr_userfunc( const Instruction& ins );
431
  void ins_pop_param( const Instruction& ins );
432
  void ins_pop_param_byref( const Instruction& ins );
433
  void ins_get_arg( const Instruction& ins );
434
  void ins_leave_block( const Instruction& ins );
435
  void ins_gosub( const Instruction& ins );
436
  void ins_return( const Instruction& ins );
437
  void ins_exit( const Instruction& ins );
438

439
  void ins_member( const Instruction& ins );
440
  void ins_addmember( const Instruction& ins );
441
  void ins_removemember( const Instruction& ins );
442
  void ins_checkmember( const Instruction& ins );
443
  void ins_dictionary_addmember( const Instruction& ins );
444
  void ins_addmember2( const Instruction& ins );
445
  void ins_addmember_assign( const Instruction& ins );
446
  void ins_in( const Instruction& ins );
447

448
  void ins_initforeach( const Instruction& ins );
449
  void ins_stepforeach( const Instruction& ins );
450

451
  void ins_casejmp( const Instruction& ins );
452
  void ins_initfor( const Instruction& ins );
453
  void ins_nextfor( const Instruction& ins );
454

455
  void ins_funcref( const Instruction& ins );
456
  void ins_functor( const Instruction& ins );
457

458
  void ins_logical_jump( const Instruction& ins );
459
  void ins_logical_convert( const Instruction& ins );
460

461
  static int ins_casejmp_findlong( const Token& token, BLong* blong );
462
  static int ins_casejmp_findbool( const Token& token, BBoolean* bbool );
463
  static int ins_casejmp_findstring( const Token& token, String* bstringimp );
464
  static int ins_casejmp_finduninit( const Token& token );
465
  static int ins_casejmp_finddefault( const Token& token );
466

467
  bool running_to_completion() const;
468
  void set_running_to_completion( bool to_completion );
469

470
  bool runnable() const;
471
  void calcrunnable();
472

473
  bool halt() const;
474
  void sethalt( bool halt );
475

476
  // Takes ownership of `continuation`. If `funcref` is present, checks if jump
477
  // is "external" (ie. current program is different than funcref's), and if so,
478
  // sets up members (Globals2, nLines, prog_, execmodules) accordingly.
479
  void jump( int target_PC, BContinuation* continuation, BFunctionRef* funcref );
480

481
  // Returns the current execution stack of this executor. The returned object
482
  // is either a string containing a line for each stack frame, or an array of
483
  // objects representing each stack frame.
484
  //
485
  // Will load the debug symbols for the current program. If debug symbols are
486
  // not available, only program counter information will be available, and
487
  // filename+line+function name will be empty.
488
  BObjectImp* get_stacktrace( bool as_array );
489

490
  bool attach_debugger( std::weak_ptr<ExecutorDebugListener> listener = {},
491
                        bool set_attaching = true );
492
  void detach_debugger();
493
  void print_to_debugger( const std::string& message );
494
  std::string dbg_get_instruction( size_t atPC ) const;
495
  void dbg_get_instruction( size_t atPC, std::string& os ) const;
496
  void dbg_ins_trace();
497
  void dbg_step_into();
498
  void dbg_step_over();
499
  void dbg_step_out();
500
  void dbg_run();
501
  void dbg_break();
502
  void dbg_setbp( unsigned atPC );
503
  void dbg_clrbp( unsigned atPC );
504
  void dbg_clrbps( const std::set<unsigned>& PCs );
505
  void dbg_clrallbp();
506

507
  bool exec();
508
  void reinitExec();
509
  void initForFnCall( unsigned in_PC );
510
  void show_context( unsigned atPC );
511
  void show_context( std::string& os, unsigned atPC );
512

513
  void call_function_reference( BFunctionRef* funcref, BContinuation* continuation,
514
                                const Instruction& jmp );
515

516
  int getDebugLevel() { return debug_level; }
×
517
  void setDebugLevel( DEBUG_LEVEL level ) { debug_level = level; }
2,284✔
518
  void setViewMode( bool vm ) { viewmode_ = vm; }
×
519
  const std::string& scriptname() const;
520
  bool empty_scriptname();
521
  const EScriptProgram* prog() const;
522

523
private:
524
  ref_ptr<EScriptProgram> prog_;
525
  bool prog_ok_;
526
  bool viewmode_;
527

528
  bool runs_to_completion_;
529

530
  std::unique_ptr<ExecutorDebugEnvironment> dbg_env_;
531

532
  BObjectImp* func_result_;
533

534
  void printStack( const std::string& message );
535

536
protected:
537
  void cleanup();
538

539
  struct ClassMethodKey
540
  {
541
    ref_ptr<EScriptProgram> prog;
542
    unsigned index;
543
    std::string method_name;
544
    bool operator<( const ClassMethodKey& other ) const;
545
  };
546

547
  std::map<ClassMethodKey, BObjectRef /*function ref*/> class_methods;
548
};
549

550
inline const std::string& Executor::scriptname() const
35,676,548✔
551
{
552
  return prog_->name;
35,676,548✔
553
}
554

555
inline bool Executor::empty_scriptname()
×
556
{
557
  return prog_->name.get().empty();
×
558
}
559

560
inline const EScriptProgram* Executor::prog() const
1,937✔
561
{
562
  return prog_.get();
1,937✔
563
}
564

565
inline bool Executor::runnable( void ) const
179,271,401✔
566
{
567
  return run_ok_;
179,271,401✔
568
}
569
inline void Executor::calcrunnable()
5,465✔
570
{
571
  run_ok_ = !error_ && !halt_;
5,465✔
572
}
5,465✔
573

574
inline void Executor::seterror( bool err )
5,448✔
575
{
576
  error_ = err;
5,448✔
577
  calcrunnable();
5,448✔
578
}
5,448✔
579
inline bool Executor::error() const
1,852✔
580
{
581
  return error_;
1,852✔
582
}
583

584
inline bool Executor::halt() const
25✔
585
{
586
  return halt_;
25✔
587
}
588

589
inline bool Executor::running_to_completion() const
332✔
590
{
591
  return runs_to_completion_;
332✔
592
}
593
inline void Executor::set_running_to_completion( bool to_completion )
2,607✔
594
{
595
  runs_to_completion_ = to_completion;
2,607✔
596
}
2,607✔
597

598
#ifdef ESCRIPT_PROFILE
599
class EscriptProfiler
600
{
601
public:
602
  EscriptProfiler( ExecutorModule* em, const ModuleFunction* modfunc,
603
                   const std::vector<BObjectRef>& fparams );
604

605
  EscriptProfiler( const Instruction& ins, const BObjectRef& leftref,
606
                   const std::vector<BObjectRef>& fparams );
607
  EscriptProfiler( const Instruction& ins, const BObjectImp* callee, const char* method_name,
608
                   const std::vector<BObjectRef>& fparams );
609
  ~EscriptProfiler();
610
  static std::string result();
611

612
private:
613
  Tools::HighPerfTimer timer_{};
614
  std::string name_{};
615
  struct profile_instr
616
  {
617
    int64_t sum;
618
    int64_t max;
619
    int64_t min;
620
    int64_t count;
621
  };
622
  static std::map<std::string, profile_instr> escript_profile_map_;
623
};
624
#define ESCRIPT_PROFILER EscriptProfiler escript_profiler
625
#else
626
#define ESCRIPT_PROFILER( ... )
627
#endif
628
}  // namespace Bscript
629
}  // namespace Pol
630
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc