Coverage Report

Created: 2023-11-19 06:20

/src/libsass/src/context.cpp
Line
Count
Source (jump to first uncovered line)
1
// sass.hpp must go before all system headers to get the
2
// __EXTENSIONS__ fix on Solaris.
3
#include "sass.hpp"
4
#include "ast.hpp"
5
6
#include "remove_placeholders.hpp"
7
#include "sass_functions.hpp"
8
#include "check_nesting.hpp"
9
#include "fn_selectors.hpp"
10
#include "fn_strings.hpp"
11
#include "fn_numbers.hpp"
12
#include "fn_colors.hpp"
13
#include "fn_miscs.hpp"
14
#include "fn_lists.hpp"
15
#include "fn_maps.hpp"
16
#include "context.hpp"
17
#include "expand.hpp"
18
#include "parser.hpp"
19
#include "cssize.hpp"
20
#include "source.hpp"
21
22
namespace Sass {
23
  using namespace Constants;
24
  using namespace File;
25
  using namespace Sass;
26
27
  inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
28
0
  { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
29
30
  static sass::string safe_input(const char* in_path)
31
20
  {
32
20
    if (in_path == nullptr || in_path[0] == '\0') return "stdin";
33
0
    return in_path;
34
20
  }
35
36
  static sass::string safe_output(const char* out_path, sass::string input_path)
37
20
  {
38
20
    if (out_path == nullptr || out_path[0] == '\0') {
39
20
      if (input_path.empty()) return "stdout";
40
20
      return input_path.substr(0, input_path.find_last_of(".")) + ".css";
41
20
    }
42
0
    return out_path;
43
20
  }
44
45
  Context::Context(struct Sass_Context& c_ctx)
46
  : CWD(File::get_cwd()),
47
    c_options(c_ctx),
48
    entry_path(""),
49
    head_imports(0),
50
    plugins(),
51
    emitter(c_options),
52
53
    ast_gc(),
54
    strings(),
55
    resources(),
56
    sheets(),
57
    import_stack(),
58
    callee_stack(),
59
    traces(),
60
    extender(Extender::NORMAL, traces),
61
    c_compiler(NULL),
62
63
    c_headers               (sass::vector<Sass_Importer_Entry>()),
64
    c_importers             (sass::vector<Sass_Importer_Entry>()),
65
    c_functions             (sass::vector<Sass_Function_Entry>()),
66
67
    indent                  (safe_str(c_options.indent, "  ")),
68
    linefeed                (safe_str(c_options.linefeed, "\n")),
69
70
    input_path              (make_canonical_path(safe_input(c_options.input_path))),
71
    output_path             (make_canonical_path(safe_output(c_options.output_path, input_path))),
72
    source_map_file         (make_canonical_path(safe_str(c_options.source_map_file, ""))),
73
    source_map_root         (make_canonical_path(safe_str(c_options.source_map_root, "")))
74
75
20
  {
76
77
    // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default.
78
    // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment.
79
    // include_paths.push_back(CWD);
80
81
    // collect more paths from different options
82
20
    collect_include_paths(c_options.include_path);
83
20
    collect_include_paths(c_options.include_paths);
84
20
    collect_plugin_paths(c_options.plugin_path);
85
20
    collect_plugin_paths(c_options.plugin_paths);
86
87
    // load plugins and register custom behaviors
88
20
    for(auto plug : plugin_paths) plugins.load_plugins(plug);
89
20
    for(auto fn : plugins.get_headers()) c_headers.push_back(fn);
90
20
    for(auto fn : plugins.get_importers()) c_importers.push_back(fn);
91
20
    for(auto fn : plugins.get_functions()) c_functions.push_back(fn);
92
93
    // sort the items by priority (lowest first)
94
20
    sort (c_headers.begin(), c_headers.end(), sort_importers);
95
20
    sort (c_importers.begin(), c_importers.end(), sort_importers);
96
97
20
    emitter.set_filename(abs2rel(output_path, source_map_file, CWD));
98
99
20
  }
100
101
  void Context::add_c_function(Sass_Function_Entry function)
102
0
  {
103
0
    c_functions.push_back(function);
104
0
  }
105
  void Context::add_c_header(Sass_Importer_Entry header)
106
0
  {
107
0
    c_headers.push_back(header);
108
    // need to sort the array afterwards (no big deal)
109
0
    sort (c_headers.begin(), c_headers.end(), sort_importers);
110
0
  }
111
  void Context::add_c_importer(Sass_Importer_Entry importer)
112
0
  {
113
0
    c_importers.push_back(importer);
114
    // need to sort the array afterwards (no big deal)
115
0
    sort (c_importers.begin(), c_importers.end(), sort_importers);
116
0
  }
117
118
  Context::~Context()
119
20
  {
120
    // resources were allocated by malloc
121
40
    for (size_t i = 0; i < resources.size(); ++i) {
122
20
      free(resources[i].contents);
123
20
      free(resources[i].srcmap);
124
20
    }
125
    // free all strings we kept alive during compiler execution
126
40
    for (size_t n = 0; n < strings.size(); ++n) free(strings[n]);
127
    // everything that gets put into sources will be freed by us
128
    // this shouldn't have anything in it anyway!?
129
56
    for (size_t m = 0; m < import_stack.size(); ++m) {
130
36
      sass_import_take_source(import_stack[m]);
131
36
      sass_import_take_srcmap(import_stack[m]);
132
36
      sass_delete_import(import_stack[m]);
133
36
    }
134
    // clear inner structures (vectors) and input source
135
20
    resources.clear(); import_stack.clear();
136
20
    sheets.clear();
137
20
  }
138
139
  Data_Context::~Data_Context()
140
  {
141
    // --> this will be freed by resources
142
    // make sure we free the source even if not processed!
143
    // if (resources.size() == 0 && source_c_str) free(source_c_str);
144
    // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);
145
    // source_c_str = 0; srcmap_c_str = 0;
146
  }
147
148
  File_Context::~File_Context()
149
  {
150
  }
151
152
  void Context::collect_include_paths(const char* paths_str)
153
20
  {
154
20
    if (paths_str) {
155
0
      const char* beg = paths_str;
156
0
      const char* end = Prelexer::find_first<PATH_SEP>(beg);
157
158
0
      while (end) {
159
0
        sass::string path(beg, end - beg);
160
0
        if (!path.empty()) {
161
0
          if (*path.rbegin() != '/') path += '/';
162
0
          include_paths.push_back(path);
163
0
        }
164
0
        beg = end + 1;
165
0
        end = Prelexer::find_first<PATH_SEP>(beg);
166
0
      }
167
168
0
      sass::string path(beg);
169
0
      if (!path.empty()) {
170
0
        if (*path.rbegin() != '/') path += '/';
171
0
        include_paths.push_back(path);
172
0
      }
173
0
    }
174
20
  }
175
176
  void Context::collect_include_paths(string_list* paths_array)
177
20
  {
178
20
    while (paths_array)
179
0
    {
180
0
      collect_include_paths(paths_array->string);
181
0
      paths_array = paths_array->next;
182
0
    }
183
20
  }
184
185
  void Context::collect_plugin_paths(const char* paths_str)
186
20
  {
187
20
    if (paths_str) {
188
0
      const char* beg = paths_str;
189
0
      const char* end = Prelexer::find_first<PATH_SEP>(beg);
190
191
0
      while (end) {
192
0
        sass::string path(beg, end - beg);
193
0
        if (!path.empty()) {
194
0
          if (*path.rbegin() != '/') path += '/';
195
0
          plugin_paths.push_back(path);
196
0
        }
197
0
        beg = end + 1;
198
0
        end = Prelexer::find_first<PATH_SEP>(beg);
199
0
      }
200
201
0
      sass::string path(beg);
202
0
      if (!path.empty()) {
203
0
        if (*path.rbegin() != '/') path += '/';
204
0
        plugin_paths.push_back(path);
205
0
      }
206
0
    }
207
20
  }
208
209
  void Context::collect_plugin_paths(string_list* paths_array)
210
20
  {
211
20
    while (paths_array)
212
0
    {
213
0
      collect_plugin_paths(paths_array->string);
214
0
      paths_array = paths_array->next;
215
0
    }
216
20
  }
217
218
  // resolve the imp_path in base_path or include_paths
219
  // looks for alternatives and returns a list from one directory
220
  sass::vector<Include> Context::find_includes(const Importer& import)
221
0
  {
222
    // make sure we resolve against an absolute path
223
0
    sass::string base_path(rel2abs(import.base_path));
224
    // first try to resolve the load path relative to the base path
225
0
    sass::vector<Include> vec(resolve_includes(base_path, import.imp_path));
226
    // then search in every include path (but only if nothing found yet)
227
0
    for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)
228
0
    {
229
      // call resolve_includes and individual base path and append all results
230
0
      sass::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path));
231
0
      if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end());
232
0
    }
233
    // return vector
234
0
    return vec;
235
0
  }
236
237
  // register include with resolved path and its content
238
  // memory of the resources will be freed by us on exit
239
  void Context::register_resource(const Include& inc, const Resource& res)
240
20
  {
241
242
    // do not parse same resource twice
243
    // maybe raise an error in this case
244
    // if (sheets.count(inc.abs_path)) {
245
    //   free(res.contents); free(res.srcmap);
246
    //   throw std::runtime_error("duplicate resource registered");
247
    //   return;
248
    // }
249
250
    // get index for this resource
251
20
    size_t idx = resources.size();
252
253
    // tell emitter about new resource
254
20
    emitter.add_source_index(idx);
255
256
    // put resources under our control
257
    // the memory will be freed later
258
20
    resources.push_back(res);
259
260
    // add a relative link to the working directory
261
20
    included_files.push_back(inc.abs_path);
262
    // add a relative link  to the source map output file
263
20
    srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD));
264
265
    // get pointer to the loaded content
266
20
    Sass_Import_Entry import = sass_make_import(
267
20
      inc.imp_path.c_str(),
268
20
      inc.abs_path.c_str(),
269
20
      res.contents,
270
20
      res.srcmap
271
20
    );
272
    // add the entry to the stack
273
20
    import_stack.push_back(import);
274
275
    // get pointer to the loaded content
276
20
    const char* contents = resources[idx].contents;
277
20
    SourceFileObj source = SASS_MEMORY_NEW(SourceFile,
278
20
      inc.abs_path.c_str(), contents, idx);
279
280
    // create the initial parser state from resource
281
20
    SourceSpan pstate(source);
282
283
    // check existing import stack for possible recursion
284
20
    for (size_t i = 0; i < import_stack.size() - 2; ++i) {
285
0
      auto parent = import_stack[i];
286
0
      if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
287
0
        sass::string cwd(File::get_cwd());
288
        // make path relative to the current directory
289
0
        sass::string stack("An @import loop has been found:");
290
0
        for (size_t n = 1; n < i + 2; ++n) {
291
0
          stack += "\n    " + sass::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) +
292
0
            " imports " + sass::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd));
293
0
        }
294
        // implement error throw directly until we
295
        // decided how to handle full stack traces
296
0
        throw Exception::InvalidSyntax(pstate, traces, stack);
297
        // error(stack, prstate ? *prstate : pstate, import_stack);
298
0
      }
299
0
    }
300
301
    // create a parser instance from the given c_str buffer
302
20
    Parser p(source, *this, traces);
303
    // do not yet dispose these buffers
304
20
    sass_import_take_source(import);
305
20
    sass_import_take_srcmap(import);
306
    // then parse the root block
307
20
    Block_Obj root = p.parse();
308
    // delete memory of current stack frame
309
20
    sass_delete_import(import_stack.back());
310
    // remove current stack frame
311
20
    import_stack.pop_back();
312
    // create key/value pair for ast node
313
20
    std::pair<const sass::string, StyleSheet>
314
20
      ast_pair(inc.abs_path, { res, root });
315
    // register resulting resource
316
20
    sheets.insert(ast_pair);
317
20
  }
318
319
  // register include with resolved path and its content
320
  // memory of the resources will be freed by us on exit
321
  void Context::register_resource(const Include& inc, const Resource& res, SourceSpan& prstate)
322
0
  {
323
0
    traces.push_back(Backtrace(prstate));
324
0
    register_resource(inc, res);
325
0
    traces.pop_back();
326
0
  }
327
328
  // Add a new import to the context (called from `import_url`)
329
  Include Context::load_import(const Importer& imp, SourceSpan pstate)
330
0
  {
331
332
    // search for valid imports (ie. partials) on the filesystem
333
    // this may return more than one valid result (ambiguous imp_path)
334
0
    const sass::vector<Include> resolved(find_includes(imp));
335
336
    // error nicely on ambiguous imp_path
337
0
    if (resolved.size() > 1) {
338
0
      sass::ostream msg_stream;
339
0
      msg_stream << "It's not clear which file to import for ";
340
0
      msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";
341
0
      msg_stream << "Candidates:" << "\n";
342
0
      for (size_t i = 0, L = resolved.size(); i < L; ++i)
343
0
      { msg_stream << "  " << resolved[i].imp_path << "\n"; }
344
0
      msg_stream << "Please delete or rename all but one of these files." << "\n";
345
0
      error(msg_stream.str(), pstate, traces);
346
0
    }
347
348
    // process the resolved entry
349
0
    else if (resolved.size() == 1) {
350
0
      bool use_cache = c_importers.size() == 0;
351
      // use cache for the resource loading
352
0
      if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0];
353
      // try to read the content of the resolved file entry
354
      // the memory buffer returned must be freed by us!
355
0
      if (char* contents = read_file(resolved[0].abs_path)) {
356
        // register the newly resolved file resource
357
0
        register_resource(resolved[0], { contents, 0 }, pstate);
358
        // return resolved entry
359
0
        return resolved[0];
360
0
      }
361
0
    }
362
363
    // nothing found
364
0
    return { imp, "" };
365
366
0
  }
367
368
0
  void Context::import_url (Import* imp, sass::string load_path, const sass::string& ctx_path) {
369
370
0
    SourceSpan pstate(imp->pstate());
371
0
    sass::string imp_path(unquote(load_path));
372
0
    sass::string protocol("file");
373
374
0
    using namespace Prelexer;
375
0
    if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {
376
377
0
      protocol = sass::string(imp_path.c_str(), proto - 3);
378
      // if (protocol.compare("file") && true) { }
379
0
    }
380
381
    // add urls (protocol other than file) and urls without protocol to `urls` member
382
    // ToDo: if ctx_path is already a file resource, we should not add it here?
383
0
    if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {
384
0
      imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path));
385
0
    }
386
0
    else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {
387
0
      String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));
388
0
      Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);
389
0
      Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);
390
0
      loc_args->append(loc_arg);
391
0
      Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), loc_args);
392
0
      imp->urls().push_back(new_url);
393
0
    }
394
0
    else {
395
0
      const Importer importer(imp_path, ctx_path);
396
0
      Include include(load_import(importer, pstate));
397
0
      if (include.abs_path.empty()) {
398
0
        error("File to import not found or unreadable: " + imp_path + ".", pstate, traces);
399
0
      }
400
0
      imp->incs().push_back(include);
401
0
    }
402
403
0
  }
404
405
406
  // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet
407
  bool Context::call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector<Sass_Importer_Entry> importers, bool only_one)
408
18
  {
409
    // unique counter
410
18
    size_t count = 0;
411
    // need one correct import
412
18
    bool has_import = false;
413
    // process all custom importers (or custom headers)
414
18
    for (Sass_Importer_Entry& importer_ent : importers) {
415
      // int priority = sass_importer_get_priority(importer);
416
0
      Sass_Importer_Fn fn = sass_importer_get_function(importer_ent);
417
      // skip importer if it returns NULL
418
0
      if (Sass_Import_List includes =
419
0
          fn(load_path.c_str(), importer_ent, c_compiler)
420
0
      ) {
421
        // get c pointer copy to iterate over
422
0
        Sass_Import_List it_includes = includes;
423
0
        while (*it_includes) { ++count;
424
          // create unique path to use as key
425
0
          sass::string uniq_path = load_path;
426
0
          if (!only_one && count) {
427
0
            sass::ostream path_strm;
428
0
            path_strm << uniq_path << ":" << count;
429
0
            uniq_path = path_strm.str();
430
0
          }
431
          // create the importer struct
432
0
          Importer importer(uniq_path, ctx_path);
433
          // query data from the current include
434
0
          Sass_Import_Entry include_ent = *it_includes;
435
0
          char* source = sass_import_take_source(include_ent);
436
0
          char* srcmap = sass_import_take_srcmap(include_ent);
437
0
          size_t line = sass_import_get_error_line(include_ent);
438
0
          size_t column = sass_import_get_error_column(include_ent);
439
0
          const char *abs_path = sass_import_get_abs_path(include_ent);
440
          // handle error message passed back from custom importer
441
          // it may (or may not) override the line and column info
442
0
          if (const char* err_message = sass_import_get_error_message(include_ent)) {
443
0
            if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate);
444
0
            if (line == sass::string::npos && column == sass::string::npos) error(err_message, pstate, traces);
445
0
            else { error(err_message, { pstate.source, { line, column } }, traces); }
446
0
          }
447
          // content for import was set
448
0
          else if (source) {
449
            // resolved abs_path should be set by custom importer
450
            // use the created uniq_path as fallback (maybe enforce)
451
0
            sass::string path_key(abs_path ? abs_path : uniq_path);
452
            // create the importer struct
453
0
            Include include(importer, path_key);
454
            // attach information to AST node
455
0
            imp->incs().push_back(include);
456
            // register the resource buffers
457
0
            register_resource(include, { source, srcmap }, pstate);
458
0
          }
459
          // only a path was retuned
460
          // try to load it like normal
461
0
          else if(abs_path) {
462
            // checks some urls to preserve
463
            // `http://`, `https://` and `//`
464
            // or dispatchs to `import_file`
465
            // which will check for a `.css` extension
466
            // or resolves the file on the filesystem
467
            // added and resolved via `add_file`
468
            // finally stores everything on `imp`
469
0
            import_url(imp, abs_path, ctx_path);
470
0
          }
471
          // move to next
472
0
          ++it_includes;
473
0
        }
474
        // deallocate the returned memory
475
0
        sass_delete_import_list(includes);
476
        // set success flag
477
0
        has_import = true;
478
        // break out of loop
479
0
        if (only_one) break;
480
0
      }
481
0
    }
482
    // return result
483
18
    return has_import;
484
18
  }
485
486
  void register_function(Context&, Signature sig, Native_Function f, Env* env);
487
  void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env);
488
  void register_overload_stub(Context&, sass::string name, Env* env);
489
  void register_built_in_functions(Context&, Env* env);
490
  void register_c_functions(Context&, Env* env, Sass_Function_List);
491
  void register_c_function(Context&, Env* env, Sass_Function_Entry);
492
493
  char* Context::render(Block_Obj root)
494
4
  {
495
    // check for valid block
496
4
    if (!root) return 0;
497
    // start the render process
498
4
    root->perform(&emitter);
499
    // finish emitter stream
500
4
    emitter.finalize();
501
    // get the resulting buffer from stream
502
4
    OutputBuffer emitted = emitter.get_buffer();
503
    // should we append a source map url?
504
4
    if (!c_options.omit_source_map_url) {
505
      // generate an embedded source map
506
4
      if (c_options.source_map_embed) {
507
0
        emitted.buffer += linefeed;
508
0
        emitted.buffer += format_embedded_source_map();
509
0
      }
510
      // or just link the generated one
511
4
      else if (source_map_file != "") {
512
0
        emitted.buffer += linefeed;
513
0
        emitted.buffer += format_source_mapping_url(source_map_file);
514
0
      }
515
4
    }
516
    // create a copy of the resulting buffer string
517
    // this must be freed or taken over by implementor
518
4
    return sass_copy_c_string(emitted.buffer.c_str());
519
4
  }
520
521
  void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, SourceSpan pstate)
522
18
  {
523
    // create a custom import to resolve headers
524
18
    Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);
525
    // dispatch headers which will add custom functions
526
    // custom headers are added to the import instance
527
18
    call_headers(entry_path, ctx_path, pstate, imp);
528
    // increase head count to skip later
529
18
    head_imports += resources.size() - 1;
530
    // add the statement if we have urls
531
18
    if (!imp->urls().empty()) root->append(imp);
532
    // process all other resources (add Import_Stub nodes)
533
18
    for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
534
0
      root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));
535
0
    }
536
18
  }
537
538
  Block_Obj File_Context::parse()
539
0
  {
540
541
    // check if entry file is given
542
0
    if (input_path.empty()) return {};
543
544
    // create absolute path from input filename
545
    // ToDo: this should be resolved via custom importers
546
0
    sass::string abs_path(rel2abs(input_path, CWD));
547
548
    // try to load the entry file
549
0
    char* contents = read_file(abs_path);
550
551
    // alternatively also look inside each include path folder
552
    // I think this differs from ruby sass (IMO too late to remove)
553
0
    for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {
554
      // build absolute path for this include path entry
555
0
      abs_path = rel2abs(input_path, include_paths[i]);
556
      // try to load the resulting path
557
0
      contents = read_file(abs_path);
558
0
    }
559
560
    // abort early if no content could be loaded (various reasons)
561
0
    if (!contents) throw std::runtime_error(
562
0
      "File to read not found or unreadable: "
563
0
      + std::string(input_path.c_str()));
564
565
    // store entry path
566
0
    entry_path = abs_path;
567
568
    // create entry only for import stack
569
0
    Sass_Import_Entry import = sass_make_import(
570
0
      input_path.c_str(),
571
0
      entry_path.c_str(),
572
0
      contents,
573
0
      0
574
0
    );
575
    // add the entry to the stack
576
0
    import_stack.push_back(import);
577
578
    // create the source entry for file entry
579
0
    register_resource({{ input_path, "." }, abs_path }, { contents, 0 });
580
581
    // create root ast tree node
582
0
    return compile();
583
584
0
  }
585
586
  Block_Obj Data_Context::parse()
587
20
  {
588
589
    // check if source string is given
590
20
    if (!source_c_str) return {};
591
592
    // convert indented sass syntax
593
20
    if(c_options.is_indented_syntax_src) {
594
      // call sass2scss to convert the string
595
0
      char * converted = sass2scss(source_c_str,
596
        // preserve the structure as much as possible
597
0
        SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
598
      // replace old source_c_str with converted
599
0
      free(source_c_str); source_c_str = converted;
600
0
    }
601
602
    // remember entry path (defaults to stdin for string)
603
20
    entry_path = input_path.empty() ? "stdin" : input_path;
604
605
    // ToDo: this may be resolved via custom importers
606
20
    sass::string abs_path(rel2abs(entry_path));
607
20
    char* abs_path_c_str = sass_copy_c_string(abs_path.c_str());
608
20
    strings.push_back(abs_path_c_str);
609
610
    // create entry only for the import stack
611
20
    Sass_Import_Entry import = sass_make_import(
612
20
      entry_path.c_str(),
613
20
      abs_path_c_str,
614
20
      source_c_str,
615
20
      srcmap_c_str
616
20
    );
617
    // add the entry to the stack
618
20
    import_stack.push_back(import);
619
620
    // register a synthetic resource (path does not really exist, skip in includes)
621
20
    register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str });
622
623
    // create root ast tree node
624
20
    return compile();
625
20
  }
626
627
  // parse root block from includes
628
  Block_Obj Context::compile()
629
4
  {
630
    // abort if there is no data
631
4
    if (resources.size() == 0) return {};
632
    // get root block from the first style sheet
633
4
    Block_Obj root = sheets.at(entry_path).root;
634
    // abort on invalid root
635
4
    if (root.isNull()) return {};
636
4
    Env global; // create root environment
637
    // register built-in functions on env
638
4
    register_built_in_functions(*this, &global);
639
    // register custom functions (defined via C-API)
640
4
    for (size_t i = 0, S = c_functions.size(); i < S; ++i)
641
0
    { register_c_function(*this, &global, c_functions[i]); }
642
    // create initial backtrace entry
643
    // create crtp visitor objects
644
4
    Expand expand(*this, &global);
645
4
    Cssize cssize(*this);
646
4
    CheckNesting check_nesting;
647
    // check nesting in all files
648
4
    for (auto sheet : sheets) {
649
4
      auto styles = sheet.second;
650
4
      check_nesting(styles.root);
651
4
    }
652
    // expand and eval the tree
653
4
    root = expand(root);
654
655
4
    Extension unsatisfied;
656
    // check that all extends were used
657
4
    if (extender.checkForUnsatisfiedExtends(unsatisfied)) {
658
0
      throw Exception::UnsatisfiedExtend(traces, unsatisfied);
659
0
    }
660
661
    // check nesting
662
4
    check_nesting(root);
663
    // merge and bubble certain rules
664
4
    root = cssize(root);
665
666
    // clean up by removing empty placeholders
667
    // ToDo: maybe we can do this somewhere else?
668
4
    Remove_Placeholders remove_placeholders;
669
4
    root->perform(&remove_placeholders);
670
671
    // return processed tree
672
4
    return root;
673
4
  }
674
  // EO compile
675
676
  sass::string Context::format_embedded_source_map()
677
0
  {
678
0
    sass::string map = emitter.render_srcmap(*this);
679
0
    sass::istream is( map.c_str() );
680
0
    sass::ostream buffer;
681
0
    base64::encoder E;
682
0
    E.encode(is, buffer);
683
0
    sass::string url = "data:application/json;base64," + buffer.str();
684
0
    url.erase(url.size() - 1);
685
0
    return "/*# sourceMappingURL=" + url + " */";
686
0
  }
687
688
  sass::string Context::format_source_mapping_url(const sass::string& file)
689
0
  {
690
0
    sass::string url = abs2rel(file, output_path, CWD);
691
0
    return "/*# sourceMappingURL=" + url + " */";
692
0
  }
693
694
  char* Context::render_srcmap()
695
4
  {
696
4
    if (source_map_file == "") return 0;
697
0
    sass::string map = emitter.render_srcmap(*this);
698
0
    return sass_copy_c_string(map.c_str());
699
4
  }
700
701
702
  // for data context we want to start after "stdin"
703
  // we probably always want to skip the header includes?
704
  sass::vector<sass::string> Context::get_included_files(bool skip, size_t headers)
705
4
  {
706
      // create a copy of the vector for manipulations
707
4
      sass::vector<sass::string> includes = included_files;
708
4
      if (includes.size() == 0) return includes;
709
4
      if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); }
710
0
      else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); }
711
4
      includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() );
712
4
      std::sort( includes.begin() + (skip ? 0 : 1), includes.end() );
713
4
      return includes;
714
4
  }
715
716
  void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)
717
336
  {
718
336
    Definition* def = make_native_function(sig, f, ctx);
719
336
    def->environment(env);
720
336
    (*env)[def->name() + "[f]"] = def;
721
336
  }
722
723
  void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env)
724
8
  {
725
8
    Definition* def = make_native_function(sig, f, ctx);
726
8
    sass::ostream ss;
727
8
    ss << def->name() << "[f]" << arity;
728
8
    def->environment(env);
729
8
    (*env)[ss.str()] = def;
730
8
  }
731
732
  void register_overload_stub(Context& ctx, sass::string name, Env* env)
733
4
  {
734
4
    Definition* stub = SASS_MEMORY_NEW(Definition,
735
4
                                       SourceSpan{ "[built-in function]" },
736
4
                                       nullptr,
737
4
                                       name,
738
4
                                       Parameters_Obj{},
739
4
                                       nullptr,
740
4
                                       true);
741
4
    (*env)[name + "[f]"] = stub;
742
4
  }
743
744
745
  void register_built_in_functions(Context& ctx, Env* env)
746
4
  {
747
4
    using namespace Functions;
748
    // RGB Functions
749
4
    register_function(ctx, rgb_sig, rgb, env);
750
4
    register_overload_stub(ctx, "rgba", env);
751
4
    register_function(ctx, rgba_4_sig, rgba_4, 4, env);
752
4
    register_function(ctx, rgba_2_sig, rgba_2, 2, env);
753
4
    register_function(ctx, red_sig, red, env);
754
4
    register_function(ctx, green_sig, green, env);
755
4
    register_function(ctx, blue_sig, blue, env);
756
4
    register_function(ctx, mix_sig, mix, env);
757
    // HSL Functions
758
4
    register_function(ctx, hsl_sig, hsl, env);
759
4
    register_function(ctx, hsla_sig, hsla, env);
760
4
    register_function(ctx, hue_sig, hue, env);
761
4
    register_function(ctx, saturation_sig, saturation, env);
762
4
    register_function(ctx, lightness_sig, lightness, env);
763
4
    register_function(ctx, adjust_hue_sig, adjust_hue, env);
764
4
    register_function(ctx, lighten_sig, lighten, env);
765
4
    register_function(ctx, darken_sig, darken, env);
766
4
    register_function(ctx, saturate_sig, saturate, env);
767
4
    register_function(ctx, desaturate_sig, desaturate, env);
768
4
    register_function(ctx, grayscale_sig, grayscale, env);
769
4
    register_function(ctx, complement_sig, complement, env);
770
4
    register_function(ctx, invert_sig, invert, env);
771
    // Opacity Functions
772
4
    register_function(ctx, alpha_sig, alpha, env);
773
4
    register_function(ctx, opacity_sig, alpha, env);
774
4
    register_function(ctx, opacify_sig, opacify, env);
775
4
    register_function(ctx, fade_in_sig, opacify, env);
776
4
    register_function(ctx, transparentize_sig, transparentize, env);
777
4
    register_function(ctx, fade_out_sig, transparentize, env);
778
    // Other Color Functions
779
4
    register_function(ctx, adjust_color_sig, adjust_color, env);
780
4
    register_function(ctx, scale_color_sig, scale_color, env);
781
4
    register_function(ctx, change_color_sig, change_color, env);
782
4
    register_function(ctx, ie_hex_str_sig, ie_hex_str, env);
783
    // String Functions
784
4
    register_function(ctx, unquote_sig, sass_unquote, env);
785
4
    register_function(ctx, quote_sig, sass_quote, env);
786
4
    register_function(ctx, str_length_sig, str_length, env);
787
4
    register_function(ctx, str_insert_sig, str_insert, env);
788
4
    register_function(ctx, str_index_sig, str_index, env);
789
4
    register_function(ctx, str_slice_sig, str_slice, env);
790
4
    register_function(ctx, to_upper_case_sig, to_upper_case, env);
791
4
    register_function(ctx, to_lower_case_sig, to_lower_case, env);
792
    // Number Functions
793
4
    register_function(ctx, percentage_sig, percentage, env);
794
4
    register_function(ctx, round_sig, round, env);
795
4
    register_function(ctx, ceil_sig, ceil, env);
796
4
    register_function(ctx, floor_sig, floor, env);
797
4
    register_function(ctx, abs_sig, abs, env);
798
4
    register_function(ctx, min_sig, min, env);
799
4
    register_function(ctx, max_sig, max, env);
800
4
    register_function(ctx, random_sig, random, env);
801
    // List Functions
802
4
    register_function(ctx, length_sig, length, env);
803
4
    register_function(ctx, nth_sig, nth, env);
804
4
    register_function(ctx, set_nth_sig, set_nth, env);
805
4
    register_function(ctx, index_sig, index, env);
806
4
    register_function(ctx, join_sig, join, env);
807
4
    register_function(ctx, append_sig, append, env);
808
4
    register_function(ctx, zip_sig, zip, env);
809
4
    register_function(ctx, list_separator_sig, list_separator, env);
810
4
    register_function(ctx, is_bracketed_sig, is_bracketed, env);
811
    // Map Functions
812
4
    register_function(ctx, map_get_sig, map_get, env);
813
4
    register_function(ctx, map_merge_sig, map_merge, env);
814
4
    register_function(ctx, map_remove_sig, map_remove, env);
815
4
    register_function(ctx, map_keys_sig, map_keys, env);
816
4
    register_function(ctx, map_values_sig, map_values, env);
817
4
    register_function(ctx, map_has_key_sig, map_has_key, env);
818
4
    register_function(ctx, keywords_sig, keywords, env);
819
    // Introspection Functions
820
4
    register_function(ctx, type_of_sig, type_of, env);
821
4
    register_function(ctx, unit_sig, unit, env);
822
4
    register_function(ctx, unitless_sig, unitless, env);
823
4
    register_function(ctx, comparable_sig, comparable, env);
824
4
    register_function(ctx, variable_exists_sig, variable_exists, env);
825
4
    register_function(ctx, global_variable_exists_sig, global_variable_exists, env);
826
4
    register_function(ctx, function_exists_sig, function_exists, env);
827
4
    register_function(ctx, mixin_exists_sig, mixin_exists, env);
828
4
    register_function(ctx, feature_exists_sig, feature_exists, env);
829
4
    register_function(ctx, call_sig, call, env);
830
4
    register_function(ctx, content_exists_sig, content_exists, env);
831
4
    register_function(ctx, get_function_sig, get_function, env);
832
    // Boolean Functions
833
4
    register_function(ctx, not_sig, sass_not, env);
834
4
    register_function(ctx, if_sig, sass_if, env);
835
    // Misc Functions
836
4
    register_function(ctx, inspect_sig, inspect, env);
837
4
    register_function(ctx, unique_id_sig, unique_id, env);
838
    // Selector functions
839
4
    register_function(ctx, selector_nest_sig, selector_nest, env);
840
4
    register_function(ctx, selector_append_sig, selector_append, env);
841
4
    register_function(ctx, selector_extend_sig, selector_extend, env);
842
4
    register_function(ctx, selector_replace_sig, selector_replace, env);
843
4
    register_function(ctx, selector_unify_sig, selector_unify, env);
844
4
    register_function(ctx, is_superselector_sig, is_superselector, env);
845
4
    register_function(ctx, simple_selectors_sig, simple_selectors, env);
846
4
    register_function(ctx, selector_parse_sig, selector_parse, env);
847
4
  }
848
849
  void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)
850
0
  {
851
0
    while (descrs && *descrs) {
852
0
      register_c_function(ctx, env, *descrs);
853
0
      ++descrs;
854
0
    }
855
0
  }
856
  void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)
857
0
  {
858
0
    Definition* def = make_c_function(descr, ctx);
859
0
    def->environment(env);
860
0
    (*env)[def->name() + "[f]"] = def;
861
0
  }
862
863
}