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

polserver / polserver / 25918451630

15 May 2026 12:43PM UTC coverage: 60.929% (+2.1%) from 58.859%
25918451630

push

github

turleypol
added dynamic property which returns a pointer of the object instead of
a copy like the current imp.
needed to be able to eg store a vector

43 of 61 new or added lines in 2 files covered. (70.49%)

14455 existing lines in 345 files now uncovered.

44695 of 73356 relevant lines covered (60.93%)

449621.59 hits per line

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

93.98
/pol-core/bscript/compiler/optimizer/Optimizer.cpp
1
#include "Optimizer.h"
2

3
#include <optional>
4
#include <utility>
5

6
#include "bscript/compiler/Report.h"
7
#include "bscript/compiler/analyzer/Constants.h"
8
#include "bscript/compiler/ast/BinaryOperator.h"
9
#include "bscript/compiler/ast/Block.h"
10
#include "bscript/compiler/ast/BooleanValue.h"
11
#include "bscript/compiler/ast/BranchSelector.h"
12
#include "bscript/compiler/ast/ClassDeclaration.h"
13
#include "bscript/compiler/ast/ConditionalOperator.h"
14
#include "bscript/compiler/ast/ConstDeclaration.h"
15
#include "bscript/compiler/ast/ConstantPredicateLoop.h"
16
#include "bscript/compiler/ast/DoWhileLoop.h"
17
#include "bscript/compiler/ast/ElvisOperator.h"
18
#include "bscript/compiler/ast/FloatValue.h"
19
#include "bscript/compiler/ast/Identifier.h"
20
#include "bscript/compiler/ast/IfThenElseStatement.h"
21
#include "bscript/compiler/ast/IntegerValue.h"
22
#include "bscript/compiler/ast/Program.h"
23
#include "bscript/compiler/ast/RepeatUntilLoop.h"
24
#include "bscript/compiler/ast/Statement.h"
25
#include "bscript/compiler/ast/StringValue.h"
26
#include "bscript/compiler/ast/TopLevelStatements.h"
27
#include "bscript/compiler/ast/UnaryOperator.h"
28
#include "bscript/compiler/ast/UninitializedValue.h"
29
#include "bscript/compiler/ast/UserFunction.h"
30
#include "bscript/compiler/ast/ValueConsumer.h"
31
#include "bscript/compiler/ast/WhileLoop.h"
32
#include "bscript/compiler/astbuilder/SimpleValueCloner.h"
33
#include "bscript/compiler/model/CompilerWorkspace.h"
34
#include "bscript/compiler/optimizer/BinaryOperatorOptimizer.h"
35
#include "bscript/compiler/optimizer/ConstantValidator.h"
36
#include "bscript/compiler/optimizer/ReferencedFunctionGatherer.h"
37
#include "bscript/compiler/optimizer/UnaryOperatorOptimizer.h"
38
#include "bscript/compiler/optimizer/ValueConsumerOptimizer.h"
39
#include "compiler/model/ScopableName.h"
40

41
namespace Pol::Bscript::Compiler
42
{
43
Optimizer::Optimizer( Constants& constants, Report& report )
2,407✔
44
    : constants( constants ), report( report ), current_constant_scope_name( ScopeName::Global )
2,407✔
45
{
46
}
2,407✔
47

48
void Optimizer::optimize( CompilerWorkspace& workspace,
2,407✔
49
                          UserFunctionInclusion user_function_inclusion )
50
{
51
  workspace.accept( *this );
2,407✔
52

53
  std::vector<UserFunction*> all_user_functions;
2,407✔
54
  for ( auto& user_function : workspace.user_functions )
5,999✔
55
  {
56
    if ( user_function_inclusion == UserFunctionInclusion::All ||
7,184✔
57
         user_function->type == UserFunctionType::Method ||
6,898✔
58
         user_function->type == UserFunctionType::Constructor )
3,306✔
59
    {
60
      all_user_functions.push_back( user_function.get() );
786✔
61
    }
62
  }
63

64
  std::vector<UserFunction*> exported_functions;
2,407✔
65
  for ( auto& user_function : workspace.user_functions )
5,999✔
66
  {
67
    if ( user_function->exported )
3,592✔
68
      exported_functions.push_back( user_function.get() );
611✔
69
  }
70
  ReferencedFunctionGatherer gatherer( workspace.module_function_declarations,
2,407✔
71
                                       std::move( workspace.user_functions ) );
2,407✔
72
  workspace.top_level_statements->accept( gatherer );
2,407✔
73
  if ( auto program = workspace.program.get() )
2,407✔
74
  {
75
    program->accept( gatherer );
454✔
76
  }
77
  for ( auto uf : exported_functions )
3,018✔
78
  {
79
    gatherer.reference( uf );
611✔
80
  }
81
  for ( auto& uf : all_user_functions )
3,193✔
82
  {
83
    if ( user_function_inclusion == UserFunctionInclusion::All ||
786✔
84
         uf->type == UserFunctionType::Method || uf->type == UserFunctionType::Constructor )
786✔
85
    {
86
      gatherer.reference( uf );
786✔
87
    }
88
  }
89

90
  for ( auto& cd : workspace.class_declarations )
3,090✔
91
  {
92
    cd->accept( gatherer );
683✔
93
  }
94

95
  workspace.referenced_module_function_declarations =
96
      gatherer.take_referenced_module_function_declarations();
2,407✔
97

98
  workspace.user_functions = gatherer.take_referenced_user_functions();
2,407✔
99
}
2,407✔
100

101
void Optimizer::visit_children( Node& node )
867,851✔
102
{
103
  unsigned i = 0;
867,851✔
104
  for ( auto& child : node.children )
1,749,035✔
105
  {
106
    child->accept( *this );
881,184✔
107

108
    if ( optimized_replacement )
881,184✔
109
    {
110
      node.children[i] = std::move( optimized_replacement );
33,617✔
111
    }
112

113
    ++i;
881,184✔
114
  }
115
}
867,851✔
116

117
void Optimizer::visit_binary_operator( BinaryOperator& binary_operator )
11,462✔
118
{
119
  visit_children( binary_operator );
11,462✔
120

121
  optimized_replacement = BinaryOperatorOptimizer( binary_operator, report ).optimize();
11,462✔
122
}
11,462✔
123

124
void Optimizer::visit_branch_selector( BranchSelector& selector )
4,437✔
125
{
126
  visit_children( selector );
4,437✔
127

128
  auto predicate = selector.predicate();
4,437✔
129
  if ( auto unary_operator = dynamic_cast<UnaryOperator*>( predicate ) )
4,437✔
130
  {
131
    if ( unary_operator->token_id == TOK_LOG_NOT )
1,796✔
132
    {
133
      BranchSelector::BranchType branch_type;
134
      switch ( selector.branch_type )
1,781✔
135
      {
UNCOV
136
      case BranchSelector::IfTrue:
×
UNCOV
137
        branch_type = BranchSelector::IfFalse;
×
UNCOV
138
        break;
×
139
      case BranchSelector::IfFalse:
1,781✔
140
        branch_type = BranchSelector::IfTrue;
1,781✔
141
        break;
1,781✔
UNCOV
142
      default:
×
UNCOV
143
        selector.internal_error( "Expected conditional branch with predicate" );
×
144
      }
145
      optimized_replacement = std::make_unique<BranchSelector>(
1,781✔
146
          selector.source_location, branch_type, unary_operator->take_operand() );
3,562✔
147
    }
148
  }
149
  else if ( auto decision = branch_decision( predicate ); decision.has_value() )
2,641✔
150
  {
151
    BranchSelector::BranchType branch_type;
152
    switch ( selector.branch_type )
287✔
153
    {
UNCOV
154
    case BranchSelector::IfTrue:
×
UNCOV
155
      branch_type = decision.value() ? BranchSelector::Always : BranchSelector::Never;
×
UNCOV
156
      break;
×
157
    case BranchSelector::IfFalse:
287✔
158
      branch_type = !decision.value() ? BranchSelector::Always : BranchSelector::Never;
287✔
159
      break;
287✔
UNCOV
160
    default:
×
161
      selector.internal_error( "Expected conditional branch with predicate" );
×
162
    }
163
    optimized_replacement =
164
        std::make_unique<BranchSelector>( selector.source_location, branch_type );
287✔
165
  }
166
}
4,437✔
167

168
void Optimizer::visit_const_declaration( ConstDeclaration& constant )
169,453✔
169
{
170
  current_constant_scope_name = constant.name.scope;
169,453✔
171
  visit_children( constant );
169,453✔
172
  current_constant_scope_name = ScopeName::Global;
169,453✔
173
  if ( !ConstantValidator().validate( constant.expression() ) )
169,453✔
174
  {
175
    report.error( constant,
10✔
176
                  "Const expression must be optimizable.\n"
177
                  "{}",
178
                  constant );
179
  }
180
}
169,453✔
181

182
void Optimizer::visit_identifier( Identifier& identifier )
53,135✔
183
{
184
  if ( identifier.scoped_name.scope.empty() && !current_constant_scope_name.empty() )
53,135✔
185
  {
186
    auto scoped_name =
187
        ScopableName( current_constant_scope_name, identifier.scoped_name.name ).string();
52,818✔
188

189
    if ( auto constant = constants.find( scoped_name ) )
52,818✔
190
    {
191
      SimpleValueCloner cloner( report, identifier.source_location );
20,703✔
192
      optimized_replacement = cloner.clone( constant->expression() );
20,703✔
193
      return;
20,703✔
194
    }
20,703✔
195
  }
52,818✔
196

197
  auto name = identifier.string();
32,432✔
198
  if ( auto constant = constants.find( name ) )
32,432✔
199
  {
200
    SimpleValueCloner cloner( report, identifier.source_location );
54✔
201
    optimized_replacement = cloner.clone( constant->expression() );
54✔
202
  }
54✔
203
}
32,432✔
204

205
void Optimizer::visit_if_then_else_statement( IfThenElseStatement& if_then_else )
4,437✔
206
{
207
  visit_children( if_then_else );
4,437✔
208

209
  if ( auto else_block = dynamic_cast<Block*>( if_then_else.alternative() ) )
4,437✔
210
  {
211
    if ( else_block->children.empty() )
589✔
212
      if_then_else.children.erase( if_then_else.children.begin() + 2 );
6✔
213
  }
214

215
  auto& branch_selector = if_then_else.branch_selector();
4,437✔
216
  if ( branch_selector.branch_type == BranchSelector::Never )
4,437✔
217
  {
218
    optimized_replacement = if_then_else.take_consequent();
194✔
219
  }
220
  else if ( branch_selector.branch_type == BranchSelector::Always )
4,243✔
221
  {
222
    if ( auto alternative = if_then_else.take_alternative() )
93✔
223
    {
224
      optimized_replacement = std::move( alternative );
72✔
225
    }
226
    else
227
    {
228
      std::vector<std::unique_ptr<Statement>> empty;
21✔
229
      optimized_replacement =
230
          std::make_unique<Block>( if_then_else.source_location, std::move( empty ) );
21✔
231
    }
114✔
232
  }
233
}
4,437✔
234

235
void Optimizer::visit_unary_operator( UnaryOperator& unary_operator )
9,254✔
236
{
237
  visit_children( unary_operator );
9,254✔
238

239
  optimized_replacement = UnaryOperatorOptimizer( unary_operator ).optimize();
9,254✔
240
}
9,254✔
241

242
void Optimizer::visit_value_consumer( ValueConsumer& consume_value )
17,025✔
243
{
244
  visit_children( consume_value );
17,025✔
245

246
  optimized_replacement = ValueConsumerOptimizer().optimize( consume_value );
17,025✔
247
}
17,025✔
248

249
void Optimizer::visit_conditional_operator( ConditionalOperator& conditional )
165✔
250
{
251
  visit_children( conditional );
165✔
252

253
  auto optimize_branch = branch_decision( &conditional.conditional() );
165✔
254
  if ( optimize_branch.has_value() )
165✔
255
  {
256
    if ( optimize_branch.value() )
60✔
257
      optimized_replacement = conditional.take_consequent();
24✔
258
    else
259
      optimized_replacement = conditional.take_alternate();
36✔
260
  }
261
}
165✔
262

263
void Optimizer::visit_elvis_operator( ElvisOperator& elvisop )
244✔
264
{
265
  visit_children( elvisop );
244✔
266

267
  auto optimize_branch = branch_decision( &elvisop.lhs() );
244✔
268
  if ( optimize_branch.has_value() )
244✔
269
  {
270
    if ( optimize_branch.value() )
39✔
271
      optimized_replacement = elvisop.take_lhs();
12✔
272
    else
273
      optimized_replacement = elvisop.take_rhs();
27✔
274
  }
275
}
244✔
276

277
std::optional<bool> Optimizer::branch_decision( Expression* exp ) const
3,557✔
278
{
279
  std::optional<bool> optimize_branch;
3,557✔
280
  if ( auto iv = dynamic_cast<IntegerValue*>( exp ) )
3,557✔
281
    optimize_branch = iv->value;
511✔
282
  else if ( auto fv = dynamic_cast<FloatValue*>( exp ) )
3,046✔
283
    optimize_branch = fv->value != 0.0;
21✔
284
  else if ( auto bv = dynamic_cast<BooleanValue*>( exp ) )
3,025✔
285
    optimize_branch = bv->value;
34✔
286
  else if ( auto sv = dynamic_cast<StringValue*>( exp ) )
2,991✔
287
    optimize_branch = !sv->value.empty();
45✔
288
  else if ( dynamic_cast<UninitializedValue*>( exp ) )
2,946✔
289
    optimize_branch = false;
12✔
290
  return optimize_branch;
3,557✔
291
}
292

293
void Optimizer::visit_while_loop( WhileLoop& loop )
421✔
294
{
295
  visit_children( loop );
421✔
296
  if ( auto optimize_branch = branch_decision( &loop.predicate() ) )
421✔
297
  {
298
    if ( optimize_branch.value() )
200✔
299
      optimized_replacement = std::make_unique<ConstantPredicateLoop>(
197✔
300
          loop.source_location, loop.get_label(), loop.take_block(), true );
394✔
301
    else
302
      optimized_replacement = std::make_unique<Block>( loop.source_location,
6✔
303
                                                       std::vector<std::unique_ptr<Statement>>{} );
6✔
304
  }
305
}
421✔
306

307
void Optimizer::visit_do_while_loop( DoWhileLoop& loop )
39✔
308
{
309
  visit_children( loop );
39✔
310
  if ( auto optimize_branch = branch_decision( &loop.predicate() ) )
39✔
311
  {
312
    optimized_replacement = std::make_unique<ConstantPredicateLoop>(
56✔
313
        loop.source_location, loop.get_label(), loop.take_block(), optimize_branch.value() );
56✔
314
  }
315
}
39✔
316

317
void Optimizer::visit_repeat_until_loop( RepeatUntilLoop& loop )
47✔
318
{
319
  visit_children( loop );
47✔
320
  if ( auto optimize_branch = branch_decision( &loop.expression() ) )
47✔
321
  {
322
    // inverted logic if the predicate is true its not an endleas loop
323
    optimized_replacement = std::make_unique<ConstantPredicateLoop>(
9✔
324
        loop.source_location, loop.get_label(), loop.take_block(), !optimize_branch.value() );
18✔
325
  }
326
}
47✔
327
}  // namespace Pol::Bscript::Compiler
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