Coverage Report

Created: 2025-10-10 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsass/src/fn_miscs.cpp
Line
Count
Source
1
#include "ast.hpp"
2
#include "expand.hpp"
3
#include "fn_utils.hpp"
4
#include "fn_miscs.hpp"
5
#include "util_string.hpp"
6
7
namespace Sass {
8
9
  namespace Functions {
10
11
    //////////////////////////
12
    // INTROSPECTION FUNCTIONS
13
    //////////////////////////
14
15
    Signature type_of_sig = "type-of($value)";
16
    BUILT_IN(type_of)
17
0
    {
18
0
      Expression* v = ARG("$value", Expression);
19
0
      return SASS_MEMORY_NEW(String_Quoted, pstate, v->type());
20
0
    }
21
22
    Signature variable_exists_sig = "variable-exists($name)";
23
    BUILT_IN(variable_exists)
24
0
    {
25
0
      sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
26
27
0
      if(d_env.has("$"+s)) {
28
0
        return SASS_MEMORY_NEW(Boolean, pstate, true);
29
0
      }
30
0
      else {
31
0
        return SASS_MEMORY_NEW(Boolean, pstate, false);
32
0
      }
33
0
    }
34
35
    Signature global_variable_exists_sig = "global-variable-exists($name)";
36
    BUILT_IN(global_variable_exists)
37
0
    {
38
0
      sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
39
40
0
      if(d_env.has_global("$"+s)) {
41
0
        return SASS_MEMORY_NEW(Boolean, pstate, true);
42
0
      }
43
0
      else {
44
0
        return SASS_MEMORY_NEW(Boolean, pstate, false);
45
0
      }
46
0
    }
47
48
    Signature function_exists_sig = "function-exists($name)";
49
    BUILT_IN(function_exists)
50
0
    {
51
0
      String_Constant* ss = Cast<String_Constant>(env["$name"]);
52
0
      if (!ss) {
53
0
        error("$name: " + (env["$name"]->to_string()) + " is not a string for `function-exists'", pstate, traces);
54
0
      }
55
56
0
      sass::string name = Util::normalize_underscores(unquote(ss->value()));
57
58
0
      if(d_env.has(name+"[f]")) {
59
0
        return SASS_MEMORY_NEW(Boolean, pstate, true);
60
0
      }
61
0
      else {
62
0
        return SASS_MEMORY_NEW(Boolean, pstate, false);
63
0
      }
64
0
    }
65
66
    Signature mixin_exists_sig = "mixin-exists($name)";
67
    BUILT_IN(mixin_exists)
68
0
    {
69
0
      sass::string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
70
71
0
      if(d_env.has(s+"[m]")) {
72
0
        return SASS_MEMORY_NEW(Boolean, pstate, true);
73
0
      }
74
0
      else {
75
0
        return SASS_MEMORY_NEW(Boolean, pstate, false);
76
0
      }
77
0
    }
78
79
    Signature feature_exists_sig = "feature-exists($feature)";
80
    BUILT_IN(feature_exists)
81
0
    {
82
0
      sass::string s = unquote(ARG("$feature", String_Constant)->value());
83
84
0
      static const auto *const features = new std::unordered_set<sass::string> {
85
0
        "global-variable-shadowing",
86
0
        "extend-selector-pseudoclass",
87
0
        "at-error",
88
0
        "units-level-3",
89
0
        "custom-property"
90
0
      };
91
0
      return SASS_MEMORY_NEW(Boolean, pstate, features->find(s) != features->end());
92
0
    }
93
94
    Signature call_sig = "call($function, $args...)";
95
    BUILT_IN(call)
96
0
    {
97
0
      sass::string function;
98
0
      Function* ff = Cast<Function>(env["$function"]);
99
0
      String_Constant* ss = Cast<String_Constant>(env["$function"]);
100
101
0
      if (ss) {
102
0
        function = Util::normalize_underscores(unquote(ss->value()));
103
0
        std::cerr << "DEPRECATION WARNING: ";
104
0
        std::cerr << "Passing a string to call() is deprecated and will be illegal" << std::endl;
105
0
        std::cerr << "in Sass 4.0. Use call(get-function(" + quote(function) + ")) instead." << std::endl;
106
0
        std::cerr << std::endl;
107
0
      } else if (ff) {
108
0
        function = ff->name();
109
0
      }
110
111
0
      List_Obj arglist = SASS_MEMORY_COPY(ARG("$args", List));
112
113
0
      Arguments_Obj args = SASS_MEMORY_NEW(Arguments, pstate);
114
      // sass::string full_name(name + "[f]");
115
      // Definition* def = d_env.has(full_name) ? Cast<Definition>((d_env)[full_name]) : 0;
116
      // Parameters* params = def ? def->parameters() : 0;
117
      // size_t param_size = params ? params->length() : 0;
118
0
      for (size_t i = 0, L = arglist->length(); i < L; ++i) {
119
0
        ExpressionObj expr = arglist->value_at_index(i);
120
        // if (params && params->has_rest_parameter()) {
121
        //   Parameter_Obj p = param_size > i ? (*params)[i] : 0;
122
        //   List* list = Cast<List>(expr);
123
        //   if (list && p && !p->is_rest_parameter()) expr = (*list)[0];
124
        // }
125
0
        if (arglist->is_arglist()) {
126
0
          ExpressionObj obj = arglist->at(i);
127
0
          Argument_Obj arg = (Argument*) obj.ptr(); // XXX
128
0
          args->append(SASS_MEMORY_NEW(Argument,
129
0
                                       pstate,
130
0
                                       expr,
131
0
                                       arg ? arg->name() : "",
132
0
                                       arg ? arg->is_rest_argument() : false,
133
0
                                       arg ? arg->is_keyword_argument() : false));
134
0
        } else {
135
0
          args->append(SASS_MEMORY_NEW(Argument, pstate, expr));
136
0
        }
137
0
      }
138
0
      Function_Call_Obj func = SASS_MEMORY_NEW(Function_Call, pstate, function, args);
139
140
0
      Expand expand(ctx, &d_env, &selector_stack, &original_stack);
141
0
      func->via_call(true); // calc invoke is allowed
142
0
      if (ff) func->func(ff);
143
0
      return Cast<PreValue>(func->perform(&expand.eval));
144
0
    }
145
146
    ////////////////////
147
    // BOOLEAN FUNCTIONS
148
    ////////////////////
149
150
    Signature not_sig = "not($value)";
151
    BUILT_IN(sass_not)
152
0
    {
153
0
      return SASS_MEMORY_NEW(Boolean, pstate, ARG("$value", Expression)->is_false());
154
0
    }
155
156
    Signature if_sig = "if($condition, $if-true, $if-false)";
157
    BUILT_IN(sass_if)
158
0
    {
159
0
      Expand expand(ctx, &d_env, &selector_stack, &original_stack);
160
0
      ExpressionObj cond = ARG("$condition", Expression)->perform(&expand.eval);
161
0
      bool is_true = !cond->is_false();
162
0
      ExpressionObj res = ARG(is_true ? "$if-true" : "$if-false", Expression);
163
0
      ExpressionObj rv = res->perform(&expand.eval);
164
0
      ValueObj value = Cast<Value>(rv);
165
0
      if (value != nullptr) {
166
0
        value->set_delayed(false);
167
0
        return value.detach();
168
0
      }
169
0
      rv->set_delayed(false);
170
0
      return nullptr;
171
0
    }
172
173
    //////////////////////////
174
    // MISCELLANEOUS FUNCTIONS
175
    //////////////////////////
176
177
    Signature inspect_sig = "inspect($value)";
178
    BUILT_IN(inspect)
179
0
    {
180
0
      Expression* v = ARG("$value", Expression);
181
0
      if (v->concrete_type() == Expression::NULL_VAL) {
182
0
        return SASS_MEMORY_NEW(String_Constant, pstate, "null");
183
0
      } else if (v->concrete_type() == Expression::BOOLEAN && v->is_false()) {
184
0
        return SASS_MEMORY_NEW(String_Constant, pstate, "false");
185
0
      } else if (v->concrete_type() == Expression::STRING) {
186
0
        String_Constant *s = Cast<String_Constant>(v);
187
0
        if (s->quote_mark()) {
188
0
          return SASS_MEMORY_NEW(String_Constant, pstate, quote(s->value(), s->quote_mark()));
189
0
        } else {
190
0
          return s;
191
0
        }
192
0
      } else {
193
        // ToDo: fix to_sass for nested parentheses
194
0
        Sass_Output_Style old_style;
195
0
        old_style = ctx.c_options.output_style;
196
0
        ctx.c_options.output_style = TO_SASS;
197
0
        Emitter emitter(ctx.c_options);
198
0
        Inspect i(emitter);
199
0
        i.in_declaration = false;
200
0
        v->perform(&i);
201
0
        ctx.c_options.output_style = old_style;
202
0
        return SASS_MEMORY_NEW(String_Quoted, pstate, i.get_buffer());
203
0
      }
204
0
    }
205
206
    Signature content_exists_sig = "content-exists()";
207
    BUILT_IN(content_exists)
208
0
    {
209
0
      if (!d_env.has_global("is_in_mixin")) {
210
0
        error("Cannot call content-exists() except within a mixin.", pstate, traces);
211
0
      }
212
0
      return SASS_MEMORY_NEW(Boolean, pstate, d_env.has_lexical("@content[m]"));
213
0
    }
214
215
    Signature get_function_sig = "get-function($name, $css: false)";
216
    BUILT_IN(get_function)
217
0
    {
218
0
      String_Constant* ss = Cast<String_Constant>(env["$name"]);
219
0
      if (!ss) {
220
0
        error("$name: " + (env["$name"]->to_string()) + " is not a string for `get-function'", pstate, traces);
221
0
      }
222
223
0
      sass::string name = Util::normalize_underscores(unquote(ss->value()));
224
0
      sass::string full_name = name + "[f]";
225
226
0
      Boolean_Obj css = ARG("$css", Boolean);
227
0
      if (!css->is_false()) {
228
0
        Definition* def = SASS_MEMORY_NEW(Definition,
229
0
                                         pstate,
230
0
                                         name,
231
0
                                         SASS_MEMORY_NEW(Parameters, pstate),
232
0
                                         SASS_MEMORY_NEW(Block, pstate, 0, false),
233
0
                                         Definition::FUNCTION);
234
0
        return SASS_MEMORY_NEW(Function, pstate, def, true);
235
0
      }
236
237
238
0
      if (!d_env.has_global(full_name)) {
239
0
        error("Function not found: " + name, pstate, traces);
240
0
      }
241
242
0
      Definition* def = Cast<Definition>(d_env[full_name]);
243
0
      return SASS_MEMORY_NEW(Function, pstate, def, false);
244
0
    }
245
246
  }
247
248
}