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

polserver / polserver / 29808310540

21 Jul 2026 06:50AM UTC coverage: 61.522% (+0.1%) from 61.394%
29808310540

push

github

web-flow
removed printOn method these debug strings where only used at a single (#899)

place
use formatter for Token instead of ostream

33 of 169 new or added lines in 4 files covered. (19.53%)

16 existing lines in 4 files now uncovered.

45084 of 73281 relevant lines covered (61.52%)

449728.5 hits per line

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

82.32
/pol-core/bscript/barray.cpp
1
#include "barray.h"
2

3
#include "clib/logfacility.h"
4
#include "clib/random.h"
5
#include "clib/stlutil.h"
6

7
#include "bdouble.h"
8
#include "berror.h"
9
#include "blong.h"
10
#include "bobject.h"
11
#include "bstring.h"
12
#include "buninit.h"
13
#include "executor.h"
14
#include "executor.inl.h"
15
#include "objmembers.h"
16
#include "objmethods.h"
17
#include "str.h"
18

19
#include <fmt/compile.h>
20

21
namespace Pol::Bscript
22
{
23
using namespace fmt::literals;
24

25
ObjArray::ObjArray() : BObjectImp( OTArray ), name_arr(), ref_arr() {}
6,954✔
26

27
ObjArray::ObjArray( BObjectType type ) : BObjectImp( type ), name_arr(), ref_arr() {}
×
28

29
ObjArray::ObjArray( const ObjArray& copyfrom )
5,022✔
30
    : BObjectImp( copyfrom.type() ), name_arr( copyfrom.name_arr ), ref_arr( copyfrom.ref_arr )
5,022✔
31
{
32
  deepcopy();
5,022✔
33
}
5,022✔
34

35
void ObjArray::deepcopy()
5,022✔
36
{
37
  for ( auto& elem : ref_arr )
53,928✔
38
  {
39
    if ( elem.get() )
48,906✔
40
    {
41
      /*
42
          NOTE: all BObjectRefs in an ObjArray reference BNamedObjects not BObjects
43
          HMMM, can this BNamedObject get destructed before we're done with it?
44
          No, we're making a copy, leaving the original be.
45
          (SO, bno's refcount should be >1 here)
46
          */
47

48
      BObject* bo = elem.get();
48,894✔
49
      elem.set( new BObject( bo->impptr()->copy() ) );
48,894✔
50
    }
51
  }
52
}
5,022✔
53

54
BObjectImp* ObjArray::copy() const
4,966✔
55
{
56
  auto nobj = new ObjArray( *this );
4,966✔
57
  return nobj;
4,966✔
58
}
59

60
size_t ObjArray::sizeEstimate() const
579✔
61
{
62
  size_t size = sizeof( ObjArray );
579✔
63
  size += Clib::memsize( ref_arr );
579✔
64
  for ( const auto& elem : ref_arr )
2,288✔
65
  {
66
    size += elem.sizeEstimate();
1,709✔
67
  }
68
  size += Clib::memsize( name_arr );
579✔
69
  for ( const auto& elem : name_arr )
615✔
70
  {
71
    size += elem.capacity();
36✔
72
  }
73
  return size;
579✔
74
}
75

76
/**
77
 * Equality for arrays:
78
 * if the other guy is an array, check each element
79
 * otherwise not equal.
80
 * note that struct names aren't checked.
81
 *
82
 * @todo check structure names too?
83
 */
84
bool ObjArray::operator==( const BObjectImp& imp ) const
115✔
85
{
86
  if ( !imp.isa( OTArray ) )
115✔
87
    return false;
27✔
88

89
  const ObjArray& thatarr = static_cast<const ObjArray&>( imp );
88✔
90
  if ( thatarr.ref_arr.size() != ref_arr.size() )
88✔
91
    return false;
3✔
92

93
  for ( unsigned i = 0; i < ref_arr.size(); ++i )
368✔
94
  {
95
    const BObjectRef& thisref = ref_arr[i];
286✔
96
    const BObjectRef& thatref = thatarr.ref_arr[i];
286✔
97

98
    const BObject* thisobj = thisref.get();
286✔
99
    const BObject* thatobj = thatref.get();
286✔
100
    if ( thisobj != nullptr && thatobj != nullptr )
286✔
101
    {
102
      const BObjectImp& thisimp = thisobj->impref();
286✔
103
      const BObjectImp& thatimp = thatobj->impref();
286✔
104

105
      if ( thisimp == thatimp )
286✔
106
        continue;
283✔
107
      return false;
3✔
108
    }
109
    if ( thisobj == nullptr && thatobj == nullptr )
×
110
    {
111
      continue;
×
112
    }
113

114
    return false;
×
115
  }
116
  return true;
82✔
117
}
118

119
const BObjectImp* ObjArray::imp_at( unsigned index /* 1-based */ ) const
×
120
{
121
  assert( index > 0 );
122
  if ( index > ref_arr.size() )
×
123
    return nullptr;
×
124
  const BObjectRef& ref = ref_arr[index - 1];
×
125
  if ( ref.get() == nullptr )
×
126
    return nullptr;
×
127
  return ref.get()->impptr();
×
128
}
129

130
BObjectImp* ObjArray::array_assign( BObjectImp* idx, BObjectImp* target, bool copy )
525✔
131
{
132
  if ( idx->isa( OTLong ) )
525✔
133
  {
134
    BLong& lng = (BLong&)*idx;
525✔
135

136
    unsigned index = (unsigned)lng.value();
525✔
137
    if ( index > ref_arr.size() )
525✔
138
      ref_arr.resize( index );
300✔
139
    else if ( index <= 0 )
225✔
140
      return new BError( "Array index out of bounds" );
×
141

142
    BObjectRef& ref = ref_arr[index - 1];
525✔
143
    BObject* refobj = ref.get();
525✔
144

145
    BObjectImp* new_target = copy ? target->copy() : target;
525✔
146

147
    if ( refobj != nullptr )
525✔
148
    {
149
      refobj->setimp( new_target );
219✔
150
    }
151
    else
152
    {
153
      ref.set( new BObject( new_target ) );
306✔
154
    }
155
    return ref->impptr();
525✔
156
  }
157

158
  return UninitObject::create();
×
159
}
160

161
void ObjArray::operInsertInto( BObject& /*obj*/, const BObjectImp& objimp )
9,355✔
162
{
163
  ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
9,355✔
164
}
9,355✔
165

166
BObjectImp* ObjArray::selfPlusObj( const BObjectImp& objimp ) const
9✔
167
{
168
  std::unique_ptr<ObjArray> result( new ObjArray( *this ) );
9✔
169
  result->ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
9✔
170
  return result.release();
18✔
171
}
9✔
172
BObjectImp* ObjArray::selfPlusObj( const BLong& objimp ) const
6✔
173
{
174
  std::unique_ptr<ObjArray> result( new ObjArray( *this ) );
6✔
175
  result->ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
6✔
176
  return result.release();
12✔
177
}
6✔
178
BObjectImp* ObjArray::selfPlusObj( const Double& objimp ) const
3✔
179
{
180
  std::unique_ptr<ObjArray> result( new ObjArray( *this ) );
3✔
181
  result->ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
3✔
182
  return result.release();
6✔
183
}
3✔
184
BObjectImp* ObjArray::selfPlusObj( const String& objimp ) const
9✔
185
{
186
  std::unique_ptr<ObjArray> result( new ObjArray( *this ) );
9✔
187
  result->ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
9✔
188
  return result.release();
18✔
189
}
9✔
190

191
BObjectImp* ObjArray::selfPlusObj( const ObjArray& objimp ) const
26✔
192
{
193
  std::unique_ptr<ObjArray> result( new ObjArray( *this ) );
26✔
194

195
  for ( const auto& elem : objimp.ref_arr )
77✔
196
  {
197
    if ( elem.get() )
51✔
198
    {
199
      /*
200
          NOTE: all BObjectRefs in an ObjArray reference BNamedObjects not BObjects
201
          HMMM, can this BNamedObject get destructed before we're done with it?
202
          No, we're making a copy, leaving the original be.
203
          (SO, bno's refcount should be >1 here)
204
          */
205
      BObject* bo = elem.get();
42✔
206

207
      result->ref_arr.push_back( BObjectRef( new BObject( ( *bo )->copy() ) ) );
42✔
208
    }
209
    else
210
    {
211
      result->ref_arr.emplace_back();
9✔
212
    }
213
  }
214
  return result.release();
52✔
215
}
26✔
216

217
BObjectImp* ObjArray::selfPlusObjImp( const BObjectImp& other ) const
224✔
218
{
219
  return other.selfPlusObj( *this );
224✔
220
}
221
void ObjArray::selfPlusObjImp( BObjectImp& objimp, BObject& obj )
27✔
222
{
223
  objimp.selfPlusObj( *this, obj );
27✔
224
}
27✔
225
void ObjArray::selfPlusObj( BObjectImp& objimp, BObject& /*obj*/ )
9✔
226
{
227
  ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
9✔
228
}
9✔
229
void ObjArray::selfPlusObj( BLong& objimp, BObject& /*obj*/ )
3✔
230
{
231
  ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
3✔
232
}
3✔
233
void ObjArray::selfPlusObj( Double& objimp, BObject& /*obj*/ )
3✔
234
{
235
  ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
3✔
236
}
3✔
237
void ObjArray::selfPlusObj( String& objimp, BObject& /*obj*/ )
3✔
238
{
239
  ref_arr.push_back( BObjectRef( new BObject( objimp.copy() ) ) );
3✔
240
}
3✔
241
void ObjArray::selfPlusObj( ObjArray& objimp, BObject& /*obj*/ )
3✔
242
{
243
  for ( const auto& itr : objimp.ref_arr )
3✔
244
  {
245
    if ( itr.get() )
×
246
    {
247
      /*
248
          NOTE: all BObjectRefs in an ObjArray reference BNamedObjects not BObjects
249
          HMMM, can this BNamedObject get destructed before we're done with it?
250
          No, we're making a copy, leaving the original be.
251
          (SO, bno's refcount should be >1 here)
252
          */
253
      BObject* bo = itr.get();
×
254

255
      ref_arr.push_back( BObjectRef( new BObject( ( *bo )->copy() ) ) );
×
256
    }
257
    else
258
    {
259
      ref_arr.emplace_back();
×
260
    }
261
  }
262
}
3✔
263

264
BObjectRef ObjArray::OperMultiSubscript( std::stack<BObjectRef>& indices )
9✔
265
{
266
  BObjectRef start_ref = indices.top();
9✔
267
  indices.pop();
9✔
268
  BObjectRef length_ref = indices.top();
9✔
269
  indices.pop();
9✔
270

271
  BObject& length_obj = *length_ref;
9✔
272
  BObject& start_obj = *start_ref;
9✔
273

274
  BObjectImp& length = length_obj.impref();
9✔
275
  BObjectImp& start = start_obj.impref();
9✔
276

277
  // first deal with the start position.
278
  // return BObjectRef(new BError( "Subscript out of range" ));
279

280
  unsigned index;
281
  if ( start.isa( OTLong ) )
9✔
282
  {
283
    BLong& lng = (BLong&)start;
9✔
284
    index = (unsigned)lng.value();
9✔
285
    if ( index == 0 || index > ref_arr.size() )
9✔
286
      return BObjectRef( new BError( "Array start index out of bounds" ) );
×
287
  }
288
  else
289
    return BObjectRef( copy() );
×
290

291
  // now the end index
292

293
  unsigned end;
294
  if ( length.isa( OTLong ) )
9✔
295
  {
296
    BLong& lng = (BLong&)length;
6✔
297
    end = (unsigned)lng.value();
6✔
298
    if ( end == 0 || end > ref_arr.size() )
6✔
299
      return BObjectRef( new BError( "Array end index out of bounds" ) );
×
300
  }
301
  else
302
    return BObjectRef( copy() );
3✔
303

304
  auto str = new ObjArray();
6✔
305

306

307
  // std::unique_ptr<ObjArray> result (new ObjArray());
308
  unsigned i = 0;
6✔
309
  for ( const auto& itr : ref_arr )
663✔
310
  {
311
    if ( ++i < index )
660✔
312
      continue;
534✔
313
    if ( i > end )
126✔
314
      break;
3✔
315
    if ( itr.get() )
123✔
316
    {
317
      BObject* bo = itr.get();
123✔
318
      str->ref_arr.push_back( BObjectRef( new BObject( ( *bo )->copy() ) ) );
123✔
319
    }
320
    else
321
    {
322
      str->ref_arr.emplace_back();
×
323
    }
324
  }
325
  /*
326
  for (unsigned i = index; i < end; i++) {
327
  BObject *bo = ref_arr[i];
328
  if (bo != 0)
329
  str->ref_arr.push_back( BObjectRef( new BObject( (*bo)->copy() ) ) );
330
  else
331
  result->ref_arr.push_back( BObjectRef() );
332
  } */
333
  //  return result.release();
334
  return BObjectRef( str );
6✔
335
}
9✔
336

337
BObjectRef ObjArray::OperSubscript( const BObject& rightobj )
3,245✔
338
{
339
  const BObjectImp& right = rightobj.impref();
3,245✔
340
  if ( right.isa( OTLong ) )  // vector
3,245✔
341
  {
342
    BLong& lng = (BLong&)right;
3,245✔
343

344
    unsigned index = (unsigned)lng.value();
3,245✔
345
    if ( index > ref_arr.size() )
3,245✔
346
    {
347
      return BObjectRef( new BError( "Array index out of bounds" ) );
17✔
348
    }
349
    if ( index <= 0 )
3,228✔
350
      return BObjectRef( new BError( "Array index out of bounds" ) );
4✔
351

352
    BObjectRef& ref = ref_arr[index - 1];
3,224✔
353
    if ( ref.get() == nullptr )
3,224✔
354
      ref.set( new BObject( UninitObject::create() ) );
×
355
    return ref;
3,224✔
356
  }
357
  if ( right.isa( OTString ) )
×
358
  {
359
    // TODO: search for named variables (structure members)
360
    return BObjectRef( copy() );
×
361
  }
362

363
  // TODO: crap out
364
  return BObjectRef( copy() );
×
365
}
366

367
BObjectRef ObjArray::get_member( const char* membername )
60✔
368
{
369
  int i = 0;
60✔
370
  for ( const_name_iterator itr = name_arr.begin(), end = name_arr.end(); itr != end; ++itr, ++i )
111✔
371
  {
372
    const std::string& name = ( *itr );
102✔
373
    if ( stricmp( name.c_str(), membername ) == 0 )
102✔
374
    {
375
      return ref_arr[i];
102✔
376
    }
377
  }
378

379
  return BObjectRef( UninitObject::create() );
9✔
380
}
381

382
BObjectRef ObjArray::set_member( const char* membername, BObjectImp* valueimp, bool copy )
12✔
383
{
384
  int i = 0;
12✔
385
  for ( const_name_iterator itr = name_arr.begin(), end = name_arr.end(); itr != end; ++itr, ++i )
24✔
386
  {
387
    const std::string& name = ( *itr );
24✔
388
    if ( stricmp( name.c_str(), membername ) == 0 )
24✔
389
    {
390
      BObjectImp* target = copy ? valueimp->copy() : valueimp;
12✔
391
      ref_arr[i].get()->setimp( target );
12✔
392
      return ref_arr[i];
24✔
393
    }
394
  }
395
  return BObjectRef( UninitObject::create() );
×
396
}
397

398
BObjectRef ObjArray::operDotPlus( const char* name )
27✔
399
{
400
  for ( auto& elem : name_arr )
36✔
401
  {
402
    if ( stricmp( name, elem.c_str() ) == 0 )
9✔
403
    {
404
      return BObjectRef( new BError( "Member already exists" ) );
×
405
    }
406
  }
407
  name_arr.emplace_back( name );
27✔
408
  auto pnewobj = new BObject( UninitObject::create() );
27✔
409
  ref_arr.emplace_back( pnewobj );
27✔
410
  return BObjectRef( pnewobj );
27✔
411
}
412

413
void ObjArray::addElement( BObjectImp* imp )
44,349✔
414
{
415
  ref_arr.push_back( BObjectRef( new BObject( imp ) ) );
44,349✔
416
}
44,349✔
417

418
std::string ObjArray::getStringRep() const
3,417✔
419
{
420
  std::string rep{ "{ " };
3,417✔
421
  bool any = false;
3,417✔
422
  for ( const auto& elem : ref_arr )
17,631✔
423
  {
424
    if ( any )
14,214✔
425
      rep += ", ";
11,690✔
426
    else
427
      any = true;
2,524✔
428

429
    BObject* bo = elem.get();
14,214✔
430

431
    if ( bo != nullptr )
14,214✔
432
      rep += bo->impptr()->getStringRep();
14,172✔
433
  }
434
  rep += " }";
3,417✔
435

436
  return rep;
3,417✔
437
}
×
438

439
long ObjArray::contains( const BObjectImp& imp ) const
881✔
440
{
441
  for ( auto itr = ref_arr.begin(), end = ref_arr.end(); itr != end; ++itr )
1,720✔
442
  {
443
    if ( itr->get() )
1,129✔
444
    {
445
      BObject* bo = ( itr->get() );
1,129✔
446
      if ( bo == nullptr )
1,129✔
447
      {
448
        INFO_PRINTLN( "{} - '{} in array{{}}' check. Invalid data at index {}",
×
NEW
449
                      Clib::scripts_thread_script, imp.getStringRep(),
×
NEW
450
                      ( itr - ref_arr.begin() ) + 1 );
×
UNCOV
451
        continue;
×
452
      }
453
      if ( *( bo->impptr() ) == imp )
1,129✔
454
      {
455
        return ( static_cast<long>( ( itr - ref_arr.begin() ) + 1 ) );
290✔
456
      }
457
    }
458
  }
459
  return 0;
591✔
460
}
461

462
class objref_cmp
463
{
464
public:
465
  bool operator()( const BObjectRef& x1, const BObjectRef& x2 ) const
565✔
466
  {
467
    const BObject* b1 = x1.get();
565✔
468
    const BObject* b2 = x2.get();
565✔
469
    if ( b1 == nullptr || b2 == nullptr )
565✔
470
      return ( &x1 < &x2 );
×
471

472
    const BObject& r1 = *b1;
565✔
473
    const BObject& r2 = *b2;
565✔
474
    return ( r1 < r2 );
565✔
475
  }
476
};
477

478
BObjectImp* ObjArray::call_method_id( const int id, Executor& ex, bool /*forcebuiltin*/ )
10,534✔
479
{
480
  switch ( id )
10,534✔
481
  {
482
  case MTH_SIZE:
1,091✔
483
    if ( ex.numParams() == 0 )
1,091✔
484
      return new BLong( static_cast<int>( ref_arr.size() ) );
1,091✔
485
    else
486
      return new BError( "array.size() doesn't take parameters." );
×
487

488
  case MTH_ERASE:
31✔
489
    if ( name_arr.empty() )
31✔
490
    {
491
      if ( ex.numParams() == 1 )
31✔
492
      {
493
        int idx;
494
        if ( ex.getParam( 0, idx, 1, static_cast<int>( ref_arr.size() ) ) )  // 1-based index
31✔
495
        {
496
          ref_arr.erase( ref_arr.begin() + idx - 1 );
22✔
497
          return new BLong( 1 );
22✔
498
        }
499

500
        return nullptr;
9✔
501
      }
502
      return new BError( "array.erase(index) requires a parameter." );
×
503
    }
504
    break;
×
505
  case MTH_EXISTS:
×
506
    if ( name_arr.empty() )
×
507
    {
508
      if ( ex.numParams() == 1 )
×
509
      {
510
        int idx;
511
        if ( ex.getParam( 0, idx ) && idx >= 0 )
×
512
        {
513
          bool exists = ( idx <= (int)ref_arr.size() );
×
514
          return new BLong( exists ? 1 : 0 );
×
515
        }
516

517
        return new BError( "Invalid parameter type" );
×
518
      }
519
      return new BError( "array.exists(index) requires a parameter." );
×
520
    }
521
    break;
×
522
  case MTH_INSERT:
30✔
523
    if ( name_arr.empty() )
30✔
524
    {
525
      if ( ex.numParams() == 2 )
30✔
526
      {
527
        int idx;
528
        BObjectImp* imp = ex.getParamImp( 1 );
30✔
529
        if ( ex.getParam( 0, idx, 1, static_cast<int>( ref_arr.size() + 1 ) ) &&
30✔
530
             imp != nullptr )  // 1-based index
531
        {
532
          --idx;
30✔
533
          BObjectRef tmp;
30✔
534
          ref_arr.insert( ref_arr.begin() + idx, tmp );
30✔
535
          BObjectRef& ref = ref_arr[idx];
30✔
536
          ref.set( new BObject( imp->copy() ) );
30✔
537
        }
30✔
538
        else
539
        {
540
          return new BError( "Invalid parameter type" );
×
541
        }
542
      }
543
      else
544
        return new BError( "array.insert(index,value) requires two parameters." );
×
545
    }
546
    break;
30✔
547
  case MTH_SHRINK:
260✔
548
    if ( name_arr.empty() )
260✔
549
    {
550
      if ( ex.numParams() == 1 )
260✔
551
      {
552
        int idx;
553
        if ( ex.getParam( 0, idx, 0, static_cast<int>( ref_arr.size() ) ) )  // 1-based index
260✔
554
        {
555
          ref_arr.erase( ref_arr.begin() + idx, ref_arr.end() );
254✔
556
          return new BLong( 1 );
254✔
557
        }
558

559
        return new BError( "Invalid parameter type" );
6✔
560
      }
561
      return new BError( "array.shrink(nelems) requires a parameter." );
×
562
    }
563
    break;
×
564
  case MTH_APPEND:
8,103✔
565
    if ( name_arr.empty() )
8,103✔
566
    {
567
      if ( ex.numParams() == 1 )
8,103✔
568
      {
569
        BObjectImp* imp = ex.getParamImp( 0 );
8,103✔
570
        if ( imp )
8,103✔
571
        {
572
          ref_arr.push_back( BObjectRef( new BObject( imp->copy() ) ) );
8,103✔
573

574
          return new BLong( 1 );
8,103✔
575
        }
576

577
        return new BError( "Invalid parameter type" );
×
578
      }
579
      return new BError( "array.append(value) requires a parameter." );
×
580
    }
581
    break;
×
582
  case MTH_REVERSE:
9✔
583
    if ( name_arr.empty() )
9✔
584
    {
585
      if ( ex.numParams() == 0 )
9✔
586
      {
587
        reverse( ref_arr.begin(), ref_arr.end() );
9✔
588
        return new BLong( 1 );
9✔
589
      }
590
      return new BError( "array.reverse() doesn't take parameters." );
×
591
    }
592
    break;
×
593
  case MTH_SORT:
54✔
594
    if ( name_arr.empty() )
54✔
595
    {
596
      if ( ex.numParams() == 0 )
54✔
597
      {
598
        sort( ref_arr.begin(), ref_arr.end(), objref_cmp() );
25✔
599
        return new BLong( 1 );
25✔
600
      }
601
      if ( ex.numParams() == 1 )
29✔
602
      {
603
        int sub_index;
604
        if ( !ex.getParam( 0, sub_index ) )
29✔
605
          return new BError( "Invalid parameter type" );
×
606
        if ( sub_index < 1 )
29✔
607
          return new BError( "Invalid sub_index value" );
3✔
608
        for ( const auto& ref : ref_arr )
107✔
609
        {
610
          if ( ref.get() == nullptr || !ref.get()->isa( OTArray ) )
93✔
611
            return new BError( "Invalid array" );
12✔
612
          auto sub_arr = ref.get()->impptr<ObjArray>();
87✔
613
          if ( sub_arr->ref_arr.size() < static_cast<size_t>( sub_index ) )
87✔
614
            return new BError( "Subindex to large" );
6✔
615
        }
616
        sort( ref_arr.begin(), ref_arr.end(),
14✔
617
              [=]( const BObjectRef& x1, const BObjectRef& x2 ) -> bool
131✔
618
              {
619
                auto sub_arr1 = x1.get()->impptr<ObjArray>();
131✔
620
                auto sub_arr2 = x2.get()->impptr<ObjArray>();
131✔
621
                auto sub1 = sub_arr1->ref_arr[sub_index - 1];
131✔
622
                auto sub2 = sub_arr2->ref_arr[sub_index - 1];
131✔
623
                const BObject* b1 = sub1.get();
131✔
624
                const BObject* b2 = sub2.get();
131✔
625
                if ( b1 == nullptr || b2 == nullptr )
131✔
626
                  return ( &x1 < &x2 );
6✔
627
                return ( *b1 < *b2 );
125✔
628
              } );
131✔
629
        return new BLong( 1 );
14✔
630
      }
631
      return new BError( "array.sort(sub_index=0) takes at most one parameter." );
×
632
    }
633
    break;
×
634
  case MTH_RANDOMENTRY:
×
635
    if ( name_arr.empty() )
×
636
    {
637
      if ( ex.numParams() == 0 )
×
638
      {
639
        if ( !ref_arr.empty() )
×
640
        {
641
          const BObjectRef& ref =
642
              ref_arr[Clib::random_int( static_cast<int>( ref_arr.size() ) - 1 )];
×
643
          if ( ref.get() == nullptr )
×
644
            return nullptr;
×
645
          return ref.get()->impptr();
×
646
        }
647
      }
648
      else
649
        return new BError( "array.randomentry() doesn't take parameters." );
×
650
    }
651
    break;
×
652
  case MTH_FILTER:
193✔
653
    if ( name_arr.empty() )
193✔
654
    {
655
      if ( ex.numParams() < 1 )
193✔
656
        return new BError( "Invalid parameter type" );
3✔
657

658
      BObjectImp* param0 = ex.getParamImp( 0, BObjectType::OTFuncRef );
190✔
659

660
      if ( !param0 )
190✔
661
        return new BError( "Invalid parameter type" );
3✔
662

663
      // The filter callback allows optional arguments, so no need to check the
664
      // number of arguments for the passed function reference. Arguments passed
665
      // will be shrunk and expanded (with uninit) as needed.
666

667
      // If nothing to filter, return an empty array, since nothing to call the function with.
668
      if ( ref_arr.empty() )
187✔
669
        return new ObjArray();
×
670

671
      // Arguments for user function call.
672
      // - the element
673
      // - the index of the element
674
      // - the array itself
675
      BObjectRefVec args;
187✔
676
      args.push_back( ref_arr.front() );
187✔
677
      args.push_back( BObjectRef( new BLong( 1 ) ) );
187✔
678
      args.emplace_back( this );
187✔
679

680
      // The ContinuationCallback receives three arguments:
681
      //
682
      // - `Executor&`
683
      // - `BContinuation* continuation`: The continuation, with methods to handle
684
      //   the continuation (call the function again; finalize)
685
      // - `BObjectRef result`: The result of the user function call specified in
686
      //   `makeContinuation`.
687
      //
688
      // We pass to the lambda a reference to the element in case the user
689
      // function modifies ref_arr.
690
      //
691
      // Returns a `BObjectImp`:
692
      // - Call the user function again by returning the same continuation via
693
      //   `ex.withContinuation`.
694
      // - Return something else (in this case, the filtered array) to provide
695
      //   that value back to the script.
696
      auto callback = [elementRef = args[0], processed = 1, thisArray = args[2],
187✔
697
                       filteredRef = BObjectRef( new ObjArray ),
×
698
                       initialSize = static_cast<int>( ref_arr.size() )](
187✔
699
                          Executor& ex, BContinuation* continuation,
700
                          BObjectRef result ) mutable -> BObjectImp*
89,686✔
701
      {
702
        auto filtered = filteredRef->impptr<ObjArray>();
22,561✔
703

704
        // Do something with result.
705
        // If the result is true, add it to the filtered array.
706
        if ( result->isTrue() )
22,561✔
707
        {
708
          filtered->ref_arr.emplace_back( elementRef->impptr() );
14,841✔
709
        }
710

711
        // If thisArray was modified for some reason to no longer be an array,
712
        // return the filtered array.
713
        if ( !thisArray->isa( OTArray ) )
22,561✔
714
          return filtered;
×
715

716
        const auto& ref_arr = thisArray->impptr<ObjArray>()->ref_arr;
22,561✔
717

718
        // If the processed index is the last element, return the filtered
719
        // array. Also check if the processed index is greater than the initial
720
        // size of the array, as the user function may have modified the array.
721
        if ( processed >= initialSize || processed >= static_cast<int>( ref_arr.size() ) )
22,561✔
722
        {
723
          return filtered;
187✔
724
        }
725
        // Otherwise, increment the processed index and call the function again.
726

727
        // Increment the processed counter.
728
        ++processed;
22,374✔
729

730
        BObjectRefVec args;
22,374✔
731
        args.push_back( ref_arr[processed - 1] );
22,374✔
732
        args.push_back( BObjectRef( new BObject( new BLong( processed ) ) ) );
22,374✔
733
        args.push_back( thisArray );
22,374✔
734

735
        elementRef = args[0];
22,374✔
736

737
        // Return this continuation with the new arguments.
738
        return ex.withContinuation( continuation, std::move( args ) );
22,374✔
739
      };
22,561✔
740

741
      // Create a new continuation for a user function call.
742
      return ex.makeContinuation( BObjectRef( new BObject( param0 ) ), std::move( callback ),
374✔
743
                                  std::move( args ) );
374✔
744
    }
187✔
745
    break;
×
746

747
  case MTH_MAP:
507✔
748
    if ( name_arr.empty() )
507✔
749
    {
750
      if ( ex.numParams() < 1 )
498✔
751
        return new BError( "Invalid parameter type" );
3✔
752

753
      BObjectImp* param0 = ex.getParamImp( 0, BObjectType::OTFuncRef );
495✔
754

755
      if ( !param0 )
495✔
756
        return new BError( "Invalid parameter type" );
3✔
757

758
      if ( ref_arr.empty() )
492✔
759
        return new ObjArray();
105✔
760

761
      // Arguments for user function call.
762
      // - the element
763
      // - the index of the element
764
      // - the array itself
765
      BObjectRefVec args;
387✔
766
      args.push_back( ref_arr.front() );
387✔
767
      args.push_back( BObjectRef( new BLong( 1 ) ) );
387✔
768
      args.emplace_back( this );
387✔
769

770
      auto callback = [elementRef = args[0], processed = 1, thisArray = args[2],
387✔
771
                       mappedRef = BObjectRef( new ObjArray ),
×
772
                       initialSize = static_cast<int>( ref_arr.size() )](
387✔
773
                          Executor& ex, BContinuation* continuation,
774
                          BObjectRef result ) mutable -> BObjectImp*
8,211✔
775
      {
776
        auto mapped = mappedRef->impptr<ObjArray>();
2,343✔
777

778
        mapped->ref_arr.emplace_back( result->impptr() );
2,343✔
779

780
        if ( !thisArray->isa( OTArray ) )
2,343✔
781
          return mapped;
×
782

783
        const auto& ref_arr = thisArray->impptr<ObjArray>()->ref_arr;
2,343✔
784

785
        if ( processed >= initialSize || processed >= static_cast<int>( ref_arr.size() ) )
2,343✔
786
        {
787
          return mapped;
387✔
788
        }
789

790
        // Increment the processed counter.
791
        ++processed;
1,956✔
792

793
        BObjectRefVec args;
1,956✔
794
        args.push_back( ref_arr[processed - 1] );
1,956✔
795
        args.push_back( BObjectRef( new BObject( new BLong( processed ) ) ) );
1,956✔
796
        args.push_back( thisArray );
1,956✔
797

798
        elementRef = args[0];
1,956✔
799

800
        return ex.withContinuation( continuation, std::move( args ) );
1,956✔
801
      };
2,343✔
802

803
      return ex.makeContinuation( BObjectRef( new BObject( param0 ) ), std::move( callback ),
774✔
804
                                  std::move( args ) );
774✔
805
    }
387✔
806
    break;
9✔
807

808
  case MTH_REDUCE:
81✔
809
    if ( name_arr.empty() )
81✔
810
    {
811
      if ( ex.numParams() < 1 )
78✔
812
        return new BError( "Invalid parameter type" );
3✔
813

814
      BObjectImp* param0 = ex.getParamImp( 0, BObjectType::OTFuncRef );
75✔
815

816
      if ( !param0 )
75✔
817
        return new BError( "Invalid parameter type" );
3✔
818

819
      BObjectImp* accumulator;
820
      int processed;
821

822
      // If an initial accumulator value was passed in, use it. Otherwise, use
823
      // the first element of the array, erroring if the array is empty.
824
      if ( ex.numParams() > 1 )
72✔
825
      {
826
        accumulator = ex.getParamImp( 1 );
63✔
827
        processed = 1;
63✔
828
      }
829
      else if ( ref_arr.empty() )
9✔
830
      {
831
        return new BError( "Reduce of empty array with no initial value" );
3✔
832
      }
833
      else
834
      {
835
        accumulator = ref_arr[0]->impptr();
6✔
836
        processed = 2;
6✔
837
      }
838

839
      // Return the accumulator if there is no more to process, eg:
840
      // {}.reduce(@{}, "accum") or {"accum"}.reduce(@{})
841
      if ( processed > static_cast<int>( ref_arr.size() ) )
69✔
842
      {
843
        return accumulator;
6✔
844
      }
845

846
      // Arguments for user function call.
847
      // - accumulator
848
      // - current value
849
      // - current index
850
      // - the array itself
851
      BObjectRefVec args;
63✔
852
      args.emplace_back( accumulator );
63✔
853
      args.emplace_back( ref_arr[processed - 1] );
63✔
854
      args.push_back( BObjectRef( new BLong( processed ) ) );
63✔
855
      args.emplace_back( this );
63✔
856

857
      auto callback = [thisArray = args[3], processed = processed,
63✔
858
                       initialSize = static_cast<int>( ref_arr.size() )](
63✔
859
                          Executor& ex, BContinuation* continuation,
860
                          BObjectRef result /* accumulator */ ) mutable -> BObjectImp*
7,993✔
861
      {
862
        if ( !thisArray->isa( OTArray ) )
1,649✔
863
          return result->impptr();
×
864

865
        const auto& ref_arr = thisArray->impptr<ObjArray>()->ref_arr;
1,649✔
866

867
        if ( processed >= initialSize || processed >= static_cast<int>( ref_arr.size() ) )
1,649✔
868
        {
869
          return result->impptr();
63✔
870
        }
871

872
        ++processed;
1,586✔
873

874
        BObjectRefVec args;
1,586✔
875
        args.push_back( result );
1,586✔
876
        args.push_back( ref_arr[processed - 1] );
1,586✔
877
        args.push_back( BObjectRef( new BObject( new BLong( processed ) ) ) );
1,586✔
878
        args.push_back( thisArray );
1,586✔
879

880
        return ex.withContinuation( continuation, std::move( args ) );
1,586✔
881
      };
1,649✔
882

883
      return ex.makeContinuation( BObjectRef( new BObject( param0 ) ), std::move( callback ),
126✔
884
                                  std::move( args ) );
126✔
885
    }
63✔
886
    break;
3✔
887

888
  case MTH_FIND:
25✔
889
    if ( name_arr.empty() )
25✔
890
    {
891
      if ( ex.numParams() < 1 )
25✔
892
        return new BError( "Invalid parameter type" );
3✔
893

894
      BObjectImp* param0 = ex.getParamImp( 0, BObjectType::OTFuncRef );
22✔
895

896
      if ( !param0 )
22✔
897
        return new BError( "Invalid parameter type" );
3✔
898

899
      if ( ref_arr.empty() )
19✔
900
        return new ObjArray();
6✔
901

902
      // Arguments for user function call.
903
      // - the element
904
      // - the index of the element
905
      // - the array itself
906
      BObjectRefVec args;
13✔
907
      args.push_back( ref_arr.front() );
13✔
908
      args.push_back( BObjectRef( new BLong( 1 ) ) );
13✔
909
      args.emplace_back( this );
13✔
910

911
      auto callback = [elementRef = args[0], processed = 1, thisArray = args[2],
13✔
912
                       initialSize = static_cast<int>( ref_arr.size() )](
13✔
913
                          Executor& ex, BContinuation* continuation,
914
                          BObjectRef result ) mutable -> BObjectImp*
115✔
915
      {
916
        if ( result->isTrue() )
41✔
917
        {
918
          return elementRef->impptr();
10✔
919
        }
920

921
        if ( !thisArray->isa( OTArray ) )
31✔
922
          return UninitObject::create();
×
923

924
        const auto& ref_arr = thisArray->impptr<ObjArray>()->ref_arr;
31✔
925

926
        if ( processed >= initialSize || processed >= static_cast<int>( ref_arr.size() ) )
31✔
927
        {
928
          return UninitObject::create();
3✔
929
        }
930

931
        ++processed;
28✔
932

933
        BObjectRefVec args;
28✔
934
        args.push_back( ref_arr[processed - 1] );
28✔
935
        args.push_back( BObjectRef( new BObject( new BLong( processed ) ) ) );
28✔
936
        args.push_back( thisArray );
28✔
937

938
        elementRef = args[0];
28✔
939

940
        return ex.withContinuation( continuation, std::move( args ) );
28✔
941
      };
41✔
942

943
      return ex.makeContinuation( BObjectRef( new BObject( param0 ) ), std::move( callback ),
26✔
944
                                  std::move( args ) );
26✔
945
    }
13✔
946
    break;
×
947

948
  case MTH_FINDINDEX:
132✔
949
    if ( name_arr.empty() )
132✔
950
    {
951
      if ( ex.numParams() < 1 )
129✔
952
        return new BError( "Invalid parameter type" );
3✔
953

954
      BObjectImp* param0 = ex.getParamImp( 0, BObjectType::OTFuncRef );
126✔
955

956
      if ( !param0 )
126✔
957
        return new BError( "Invalid parameter type" );
3✔
958

959
      if ( ref_arr.empty() )
123✔
960
        return new BLong( 0 );
3✔
961

962
      // Arguments for user function call.
963
      // - the element
964
      // - the index of the element
965
      // - the array itself
966
      BObjectRefVec args;
120✔
967
      args.push_back( ref_arr.front() );
120✔
968
      args.push_back( BObjectRef( new BLong( 1 ) ) );
120✔
969
      args.emplace_back( this );
120✔
970

971
      auto callback =
120✔
972
          [processed = 1, thisArray = args[2], initialSize = static_cast<int>( ref_arr.size() )](
120✔
973
              Executor& ex, BContinuation* continuation, BObjectRef result ) mutable -> BObjectImp*
36,692✔
974
      {
975
        if ( result->isTrue() )
9,263✔
976
        {
977
          return new BLong( processed );
117✔
978
        }
979

980
        if ( !thisArray->isa( OTArray ) )
9,146✔
981
          return new BLong( 0 );
×
982

983
        const auto& ref_arr = thisArray->impptr<ObjArray>()->ref_arr;
9,146✔
984

985
        if ( processed >= initialSize || processed >= static_cast<int>( ref_arr.size() ) )
9,146✔
986
        {
987
          return new BLong( 0 );
3✔
988
        }
989

990
        ++processed;
9,143✔
991

992
        BObjectRefVec args;
9,143✔
993
        args.push_back( ref_arr[processed - 1] );
9,143✔
994
        args.push_back( BObjectRef( new BObject( new BLong( processed ) ) ) );
9,143✔
995
        args.push_back( thisArray );
9,143✔
996

997
        return ex.withContinuation( continuation, std::move( args ) );
9,143✔
998
      };
9,263✔
999

1000
      return ex.makeContinuation( BObjectRef( new BObject( param0 ) ), std::move( callback ),
240✔
1001
                                  std::move( args ) );
240✔
1002
    }
120✔
1003
    break;
3✔
1004

1005
  case MTH_CYCLE:
×
1006
    if ( name_arr.empty() )
×
1007
    {
1008
      int shift_by;
1009

1010
      if ( ex.numParams() > 0 )
×
1011
      {
1012
        if ( !ex.getParam( 0, shift_by ) )
×
1013
          return new BError( "Invalid parameter type" );
×
1014
        if ( shift_by == 0 )
×
1015
          return new BLong( 0 );
×
1016
      }
1017
      else
1018
        shift_by = 1;
×
1019

1020
      if ( ref_arr.empty() || std::abs( shift_by ) > (int)ref_arr.size() )
×
1021
        return new BLong( 0 );
×
1022

1023
      if ( shift_by > 0 )
×
1024
        std::rotate( ref_arr.begin(), ref_arr.end() - shift_by, ref_arr.end() );
×
1025
      else
1026
        std::rotate( ref_arr.begin(), ref_arr.begin() - shift_by, ref_arr.end() );
×
1027

1028
      return new BLong( 1 );
×
1029
    }
1030
    break;
×
1031

1032
  case MTH_SORTEDINSERT:
18✔
1033
  {
1034
    if ( !name_arr.empty() )
18✔
1035
      break;
×
1036
    if ( ex.numParams() == 0 )
18✔
1037
      return new BError(
1038
          "array.sorted_insert(obj, sub_index:=0, reverse:=0) takes at least one parameter." );
18✔
1039
    BObjectImp* imp = ex.getParamImp( 0 );
18✔
1040
    if ( !imp )
18✔
1041
      return new BError( "Invalid parameter type" );
×
1042
    bool reverse = false;
18✔
1043
    int sub_index = 0;
18✔
1044
    if ( ex.numParams() >= 2 )
18✔
1045
    {
1046
      if ( !ex.getParam( 1, sub_index ) )
15✔
1047
        return new BError( "Invalid parameter type" );
×
1048
      if ( sub_index < 0 )
15✔
1049
        return new BError( "Invalid sub_index value" );
×
1050
    }
1051
    if ( ex.numParams() >= 3 )
18✔
1052
    {
1053
      int reverseparam;
1054
      if ( !ex.getParam( 2, reverseparam ) )
9✔
1055
        return new BError( "Invalid parameter type" );
×
1056
      reverse = reverseparam != 0;
9✔
1057
    }
1058
    BObjectRef item( new BObject( imp->copy() ) );
18✔
1059
    if ( !sub_index )
18✔
1060
    {
1061
      if ( reverse )
9✔
1062
      {
1063
        ref_arr.insert( std::lower_bound( ref_arr.begin(), ref_arr.end(), item,
3✔
1064
                                          []( const BObjectRef& x1, const BObjectRef& x2 ) -> bool
9✔
1065
                                          {
1066
                                            const BObject* b1 = x1.get();
9✔
1067
                                            const BObject* b2 = x2.get();
9✔
1068
                                            if ( b1 == nullptr || b2 == nullptr )
9✔
1069
                                              return ( &x1 > &x2 );
×
1070
                                            const BObject& r1 = *b1;
9✔
1071
                                            const BObject& r2 = *b2;
9✔
1072
                                            return ( r1 > r2 );
9✔
1073
                                          } ),
1074
                        item );
1075
      }
1076
      else
1077
      {
1078
        ref_arr.insert( std::upper_bound( ref_arr.begin(), ref_arr.end(), item, objref_cmp() ),
6✔
1079
                        item );
1080
      }
1081
    }
1082
    else
1083
    {
1084
      auto cmp_func = [=]( const BObjectRef& x1, const BObjectRef& x2 ) -> bool
21✔
1085
      {
1086
        if ( x1.get() == nullptr || !x1.get()->isa( OTArray ) )
21✔
1087
          return false;
×
1088
        if ( x2.get() == nullptr || !x2.get()->isa( OTArray ) )
21✔
1089
          return false;
×
1090
        auto sub_arr1 = x1.get()->impptr<ObjArray>();
21✔
1091
        auto sub_arr2 = x2.get()->impptr<ObjArray>();
21✔
1092
        if ( sub_arr1->ref_arr.size() < static_cast<size_t>( sub_index ) )
21✔
1093
          return false;
×
1094
        if ( sub_arr2->ref_arr.size() < static_cast<size_t>( sub_index ) )
21✔
1095
          return false;
×
1096
        auto sub1 = sub_arr1->ref_arr[sub_index - 1];
21✔
1097
        auto sub2 = sub_arr2->ref_arr[sub_index - 1];
21✔
1098
        const BObject* b1 = sub1.get();
21✔
1099
        const BObject* b2 = sub2.get();
21✔
1100
        if ( !reverse )
21✔
1101
        {
1102
          if ( b1 == nullptr || b2 == nullptr )
15✔
1103
            return ( &x1 < &x2 );
×
1104
          return ( *b1 < *b2 );
15✔
1105
        }
1106

1107
        if ( b1 == nullptr || b2 == nullptr )
6✔
1108
          return ( &x1 > &x2 );
×
1109
        return ( *b1 > *b2 );
6✔
1110
      };
21✔
1111
      if ( reverse )
9✔
1112
      {
1113
        ref_arr.insert( std::lower_bound( ref_arr.begin(), ref_arr.end(), item, cmp_func ), item );
3✔
1114
      }
1115
      else
1116
      {
1117
        ref_arr.insert( std::upper_bound( ref_arr.begin(), ref_arr.end(), item, cmp_func ), item );
6✔
1118
      }
1119
    }
1120
    return new BLong( 1 );
18✔
1121
    break;
1122
  }
18✔
1123
  default:
×
1124
    return nullptr;
×
1125
  }
1126
  return nullptr;
45✔
1127
}
1128

1129
BObjectImp* ObjArray::call_method( const char* methodname, Executor& ex )
3✔
1130
{
1131
  ObjMethod* objmethod = getKnownObjMethod( methodname );
3✔
1132
  if ( objmethod != nullptr )
3✔
1133
    return this->call_method_id( objmethod->id, ex );
×
1134
  return nullptr;
3✔
1135
}
1136

1137
void ObjArray::packonto( std::string& str ) const
41✔
1138
{
1139
  fmt::format_to( std::back_inserter( str ), "a{}:"_cf, ref_arr.size() );
41✔
1140
  for ( const auto& elem : ref_arr )
173✔
1141
  {
1142
    if ( elem.get() )
132✔
1143
    {
1144
      BObject* bo = elem.get();
132✔
1145
      bo->impptr()->packonto( str );
132✔
1146
    }
1147
    else
1148
    {
1149
      str += "x";
×
1150
    }
1151
  }
1152
}
41✔
1153

1154
BObjectImp* ObjArray::unpack( std::istream& is )
19✔
1155
{
1156
  unsigned arrsize;
1157
  char colon;
1158
  if ( !( is >> arrsize >> colon ) )
19✔
1159
  {
1160
    return new BError( "Unable to unpack array elemcount" );
×
1161
  }
1162
  if ( (int)arrsize < 0 )
19✔
1163
  {
1164
    return new BError( "Unable to unpack array elemcount. Invalid length!" );
×
1165
  }
1166
  if ( colon != ':' )
19✔
1167
  {
1168
    return new BError( "Unable to unpack array elemcount. Bad format. Colon not found!" );
×
1169
  }
1170
  std::unique_ptr<ObjArray> arr( new ObjArray );
19✔
1171
  arr->ref_arr.resize( arrsize );
19✔
1172
  for ( unsigned i = 0; i < arrsize; ++i )
93✔
1173
  {
1174
    BObjectImp* imp = BObjectImp::unpack( is );
74✔
1175
    if ( imp != nullptr && !imp->isa( OTUninit ) )
74✔
1176
    {
1177
      arr->ref_arr[i].set( new BObject( imp ) );
74✔
1178
    }
1179
  }
1180
  return arr.release();
19✔
1181
}
19✔
1182
}  // 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