• 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

99.24
/pol-core/bscript/compiler/astbuilder/UserFunctionBuilder.cpp
1
#include "UserFunctionBuilder.h"
2

3
#include "bscript/compiler/Report.h"
4
#include "bscript/compiler/ast/Argument.h"
5
#include "bscript/compiler/ast/ClassBody.h"
6
#include "bscript/compiler/ast/ClassDeclaration.h"
7
#include "bscript/compiler/ast/ClassParameterDeclaration.h"
8
#include "bscript/compiler/ast/ClassParameterList.h"
9
#include "bscript/compiler/ast/Expression.h"
10
#include "bscript/compiler/ast/FunctionBody.h"
11
#include "bscript/compiler/ast/FunctionCall.h"
12
#include "bscript/compiler/ast/FunctionParameterDeclaration.h"
13
#include "bscript/compiler/ast/FunctionParameterList.h"
14
#include "bscript/compiler/ast/Identifier.h"
15
#include "bscript/compiler/ast/Statement.h"
16
#include "bscript/compiler/ast/TopLevelStatements.h"
17
#include "bscript/compiler/ast/UninitializedFunctionDeclaration.h"
18
#include "bscript/compiler/ast/UserFunction.h"
19
#include "bscript/compiler/ast/VarStatement.h"
20
#include "bscript/compiler/astbuilder/BuilderWorkspace.h"
21
#include "bscript/compiler/astbuilder/FunctionResolver.h"
22
#include "bscript/compiler/model/ClassLink.h"
23
#include "bscript/compiler/model/CompilerWorkspace.h"
24
#include "bscript/compiler/model/FunctionLink.h"
25
#include "bscript/compiler/model/ScopeName.h"
26

27
#include <algorithm>
28

29
using EscriptGrammar::EscriptParser;
30

31
namespace Pol::Bscript::Compiler
32
{
33
UserFunctionBuilder::UserFunctionBuilder( const SourceFileIdentifier& source_file_identifier,
4,130✔
34
                                          BuilderWorkspace& workspace )
4,130✔
35
    : CompoundStatementBuilder( source_file_identifier, workspace )
4,130✔
36
{
37
}
4,130✔
38

39
std::unique_ptr<UserFunction> UserFunctionBuilder::function_declaration(
2,758✔
40
    EscriptParser::FunctionDeclarationContext* ctx, const std::string& class_name )
41
{
42
  std::string name = text( ctx->IDENTIFIER() );
2,758✔
43
  return make_function_like<UserFunction>( name, ctx, ctx->EXPORTED(), class_name,
5,516✔
44
                                           ctx->ENDFUNCTION() );
5,516✔
45
}
2,758✔
46

47
std::unique_ptr<UserFunction> UserFunctionBuilder::function_expression(
675✔
48
    EscriptGrammar::EscriptParser::FunctionExpressionContext* ctx )
49
{
50
  std::string name = FunctionResolver::function_expression_name( location_for( *ctx->AT() ) );
675✔
51
  return make_function_like<UserFunction>( name, ctx, false, "", ctx->RBRACE() );
1,350✔
52
}
675✔
53

54
std::unique_ptr<ClassDeclaration> UserFunctionBuilder::class_declaration(
697✔
55
    EscriptGrammar::EscriptParser::ClassDeclarationContext* ctx, Node* class_body )
56
{
57
  std::string class_name = text( ctx->IDENTIFIER() );
697✔
58

59
  if ( Clib::caseInsensitiveEqual( class_name, Compiler::SUPER ) )
697✔
60
  {
61
    workspace.report.error( location_for( *ctx->IDENTIFIER() ),
2✔
62
                            "The class name 'super' is reserved." );
63
    return nullptr;
2✔
64
  }
65

66
  std::vector<std::unique_ptr<ClassParameterDeclaration>> parameters;
695✔
67
  std::vector<std::unique_ptr<UninitializedFunctionDeclaration>> uninit_functions;
695✔
68
  std::vector<std::shared_ptr<ClassLink>> base_classes;
695✔
69
  ClassMethodMap methods;
695✔
70
  std::unique_ptr<FunctionLink> constructor_link;
695✔
71
  bool is_child = false;
695✔
72

73
  if ( auto function_parameters = ctx->classParameters() )
695✔
74
  {
75
    if ( auto param_list = function_parameters->classParameterList() )
695✔
76
    {
77
      for ( auto parameter_name : param_list->IDENTIFIER() )
749✔
78
      {
79
        auto baseclass_name = text( parameter_name );
441✔
80

81
        auto class_param_decl = std::make_unique<ClassParameterDeclaration>(
82
            location_for( *parameter_name ), baseclass_name );
441✔
83

84
        // Register with the FunctionResolver the class parameter's constructor
85
        // link. It will get resolved to the class constructor during the
86
        // second-pass AST visiting.
87
        base_classes.push_back(
441✔
88
            std::make_shared<ClassLink>( location_for( *parameter_name ), baseclass_name ) );
882✔
89

90
        workspace.function_resolver.register_class_link( ScopeName( baseclass_name ),
441✔
91
                                                         base_classes.back() );
441✔
92

93
        workspace.function_resolver.register_function_link(
441✔
94
            ScopableName( baseclass_name, baseclass_name ), class_param_decl->constructor_link );
882✔
95

96
        is_child = true;
441✔
97
        parameters.push_back( std::move( class_param_decl ) );
441✔
98
      }
749✔
99
    }
100
  }
101

102
  if ( auto classBody = ctx->classBody() )
695✔
103
  {
104
    for ( auto classStatement : classBody->classStatement() )
1,733✔
105
    {
106
      if ( auto func_decl = classStatement->functionDeclaration() )
1,038✔
107
      {
108
        auto func_name = text( func_decl->IDENTIFIER() );
896✔
109
        auto func_loc = location_for( *func_decl );
896✔
110

111
        // Register the user function as an available parse tree only if it is not `super` for child
112
        // classes.
113
        auto is_super = Clib::caseInsensitiveEqual( func_name, Compiler::SUPER );
896✔
114

115
        if ( is_super && is_child )
896✔
116
        {
117
          workspace.report.error( func_loc, "The 'super' function is reserved for child classes." );
2✔
118
        }
119
        else
120
        {
121
          workspace.function_resolver.register_available_scoped_function( func_loc, class_name,
894✔
122
                                                                          func_decl );
123
        }
124

125

126
        // Check if the function is a constructor:
127
        // 1. The function has parameters.
128
        if ( auto param_list = func_decl->functionParameters()->functionParameterList() )
896✔
129
        {
130
          if ( auto func_params = param_list->functionParameter(); !func_params.empty() )
841✔
131
          {
132
            std::string parameter_name = text( func_params.front()->IDENTIFIER() );
841✔
133

134
            // 2. The first parameter is named `this`.
135
            if ( Clib::caseInsensitiveEqual( parameter_name, "this" ) )
841✔
136
            {
137
              // 3. The function name is the same as the class name: constructor
138
              if ( Clib::caseInsensitiveEqual( func_name, class_name ) )
774✔
139
              {
140
                constructor_link = std::make_unique<FunctionLink>( func_loc, class_name,
484✔
141
                                                                   true /* requires_ctor */ );
968✔
142
              }
143
              // 3b. Otherwise: method
144
              else if ( !methods.contains( func_name ) )
290✔
145
              {
146
                methods[func_name] = std::make_shared<FunctionLink>( func_loc, func_name );
288✔
147
              }
148
            }
149
          }
1,682✔
150
        }
151

152
        workspace.compiler_workspace.all_function_locations.emplace(
896✔
153
            ScopableName( class_name, func_name ).string(), func_loc );
1,792✔
154
      }
896✔
155
      else if ( auto var_statement = classStatement->varStatement() )
142✔
156
      {
157
        std::vector<std::unique_ptr<Statement>> statements;
45✔
158

159
        add_var_statements( var_statement, class_name, statements );
45✔
160

161
        for ( auto& statement : statements )
90✔
162
        {
163
          class_body->children.push_back( std::move( statement ) );
45✔
164
        }
165
      }
45✔
166
      else if ( auto uninit_func_decl = classStatement->uninitFunctionDeclaration() )
97✔
167
      {
168
        auto func_name = text( uninit_func_decl->IDENTIFIER() );
97✔
169

170
        auto uf = make_function_like<UninitializedFunctionDeclaration>(
171
            func_name, uninit_func_decl, false, class_name, uninit_func_decl->SEMI() );
97✔
172

173
        uninit_functions.push_back( std::move( uf ) );
97✔
174
      }
97✔
175
    }
695✔
176
  }
177

178
  auto parameter_list =
179
      std::make_unique<ClassParameterList>( location_for( *ctx ), std::move( parameters ) );
695✔
180

181

182
  auto class_decl = std::make_unique<ClassDeclaration>(
183
      location_for( *ctx ), class_name, std::move( parameter_list ), std::move( constructor_link ),
695✔
184
      std::move( methods ), class_body, std::move( base_classes ), std::move( uninit_functions ) );
1,390✔
185

186
  // Only register the ClassDeclaration's ctor FunctionLink if there _is_ a ctor.
187
  if ( class_decl->constructor_link )
1,390✔
188
  {
189
    workspace.function_resolver.register_function_link( ScopableName( class_name, class_name ),
484✔
190
                                                        class_decl->constructor_link );
484✔
191
  }
192

193
  return class_decl;
695✔
194
}
697✔
195

196
template <typename FunctionTypeNode, typename ParserContext>
197
std::unique_ptr<FunctionTypeNode> UserFunctionBuilder::make_function_like(
3,530✔
198
    const std::string& name, ParserContext* ctx, bool exported, const std::string& class_name,
199
    antlr4::tree::TerminalNode* end_token )
200
{
201
  std::vector<std::unique_ptr<FunctionParameterDeclaration>> parameters;
3,530✔
202
  bool class_method = false;
3,530✔
203
  if ( auto function_parameters = ctx->functionParameters() )
3,530✔
204
  {
205
    if ( auto param_list = function_parameters->functionParameterList() )
3,490✔
206
    {
207
      // Determine if the function is a class method by checking if the first parameter is named
208
      // `this`. Only check if the function is a method (ie. class name is not empty).
209
      bool first = !class_name.empty();
2,657✔
210
      for ( auto param : param_list->functionParameter() )
7,006✔
211
      {
212
        ScopableName parameter_name( ScopeName::None, text( param->IDENTIFIER() ) );
4,349✔
213
        bool is_this_arg = false;
4,349✔
214

215
        if ( first )
4,349✔
216
        {
217
          if ( Clib::caseInsensitiveEqual( parameter_name.string(), "this" ) )
934✔
218
          {
219
            class_method = true;
867✔
220
            is_this_arg = true;
867✔
221
          }
222

223
          first = false;
934✔
224
        }
225

226
        std::unique_ptr<FunctionParameterDeclaration> parameter_declaration;
4,349✔
227
        bool byref = param->BYREF() != nullptr || is_this_arg;
4,349✔
228
        bool unused = param->UNUSED() != nullptr;
4,349✔
229
        bool uninit_default = param->DEFAULT();
4,349✔
230
        bool rest = param->ELLIPSIS() != nullptr;
4,349✔
231

232
        if ( auto expr_ctx = param->expression() )
4,349✔
233
        {
234
          auto default_value = expression( expr_ctx );
292✔
235
          parameter_declaration = std::make_unique<FunctionParameterDeclaration>(
292✔
236
              location_for( *param ), std::move( parameter_name ), byref, unused, uninit_default,
292✔
237
              rest, std::move( default_value ) );
292✔
238
        }
292✔
239
        else
240
        {
241
          parameter_declaration = std::make_unique<FunctionParameterDeclaration>(
4,057✔
242
              location_for( *param ), std::move( parameter_name ), byref, unused, uninit_default,
8,114✔
243
              rest );
244
        }
245

246
        parameters.push_back( std::move( parameter_declaration ) );
4,349✔
247
      }
248
    }
249
  }
250
  auto parameter_list =
3,530✔
251
      std::make_unique<FunctionParameterList>( location_for( *ctx ), std::move( parameters ) );
3,530✔
252

253
  constexpr bool expression =
3,530✔
254
      std::is_same<ParserContext, EscriptGrammar::EscriptParser::FunctionExpressionContext>::value;
255

256
  bool constructor_method = class_method && Clib::caseInsensitiveEqual( name, class_name );
3,530✔
257

258
  UserFunctionType type = !class_method        ? UserFunctionType::Static
4,397✔
259
                          : constructor_method ? UserFunctionType::Constructor
867✔
260
                                               : UserFunctionType::Method;
261

262

263
  if constexpr ( std::is_same_v<FunctionTypeNode, UserFunction> )
264
  {
265
    std::shared_ptr<ClassLink> class_link;
3,433✔
266
    if ( !class_name.empty() )
3,433✔
267
    {
268
      class_link = std::make_shared<ClassLink>( location_for( *ctx ), class_name );
890✔
269
      workspace.function_resolver.register_class_link( ScopeName( class_name ), class_link );
890✔
270
      auto cd = class_link->class_declaration();
890✔
271

272
      // Should never happen, since the only reason this user function can be
273
      // visited is because the class has been registered.
274
      if ( !cd )
890✔
UNCOV
275
        class_link->source_location.internal_error( "ClassLink has no ClassDeclaration" );
×
276
    }
277

278
    in_constructor_function.push( type == UserFunctionType::Constructor );
3,433✔
279
    auto body =
6,866✔
280
        std::make_unique<FunctionBody>( location_for( *ctx ), block_statements( ctx->block() ) );
6,866✔
281

282
    in_constructor_function.pop();
3,433✔
283

284
    return std::make_unique<FunctionTypeNode>(
285
        location_for( *ctx ), exported, expression, type, class_name, std::move( name ),
3,433✔
286
        std::move( parameter_list ), std::move( body ), location_for( *end_token ),
3,433✔
287
        std::move( class_link ) );
10,299✔
288
  }
3,433✔
289
  else if constexpr ( std::is_same_v<FunctionTypeNode, UninitializedFunctionDeclaration> )
290
  {
291
    return std::make_unique<UninitializedFunctionDeclaration>(
292
        location_for( *ctx ), type, class_name, std::move( name ), std::move( parameter_list ) );
194✔
293
  }
294
}
3,530✔
295
}  // 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