1// sass.hpp must go before all system headers to get the
2// __EXTENSIONS__ fix on Solaris.
3#include "sass.hpp"
4
5#include <iostream>
6#include <typeinfo>
7
8#include "ast.hpp"
9#include "expand.hpp"
10#include "bind.hpp"
11#include "eval.hpp"
12#include "backtrace.hpp"
13#include "context.hpp"
14#include "parser.hpp"
15#include "sass_functions.hpp"
16#include "error_handling.hpp"
17
18namespace Sass {
19
20 // simple endless recursion protection
21 const size_t maxRecursion = 500;
22
23 Expand::Expand(Context& ctx, Env* env, SelectorStack* stack, SelectorStack* originals)
24 : ctx(ctx),
25 traces(ctx.traces),
26 eval(Eval(*this)),
27 recursions(0),
28 in_keyframes(false),
29 at_root_without_rule(false),
30 old_at_root_without_rule(false),
31 env_stack(),
32 block_stack(),
33 call_stack(),
34 selector_stack(),
35 originalStack(),
36 mediaStack()
37 {
38 env_stack.push_back(x: nullptr);
39 env_stack.push_back(x: env);
40 block_stack.push_back(x: nullptr);
41 call_stack.push_back(x: {});
42 if (stack == NULL) { pushToSelectorStack(selector: {}); }
43 else {
44 for (auto item : *stack) {
45 if (item.isNull()) pushToSelectorStack(selector: {});
46 else pushToSelectorStack(selector: item);
47 }
48 }
49 if (originals == NULL) { pushToOriginalStack(selector: {}); }
50 else {
51 for (auto item : *stack) {
52 if (item.isNull()) pushToOriginalStack(selector: {});
53 else pushToOriginalStack(selector: item);
54 }
55 }
56 mediaStack.push_back(x: {});
57 }
58
59 Env* Expand::environment()
60 {
61 if (env_stack.size() > 0)
62 return env_stack.back();
63 return 0;
64 }
65
66 void Expand::pushNullSelector()
67 {
68 pushToSelectorStack(selector: {});
69 pushToOriginalStack(selector: {});
70 }
71
72 void Expand::popNullSelector()
73 {
74 popFromOriginalStack();
75 popFromSelectorStack();
76 }
77
78 SelectorStack Expand::getOriginalStack()
79 {
80 return originalStack;
81 }
82
83 SelectorStack Expand::getSelectorStack()
84 {
85 return selector_stack;
86 }
87
88 SelectorListObj& Expand::selector()
89 {
90 if (selector_stack.size() > 0) {
91 auto& sel = selector_stack.back();
92 if (sel.isNull()) return sel;
93 return sel;
94 }
95 // Avoid the need to return copies
96 // We always want an empty first item
97 selector_stack.push_back(x: {});
98 return selector_stack.back();;
99 }
100
101 SelectorListObj& Expand::original()
102 {
103 if (originalStack.size() > 0) {
104 auto& sel = originalStack.back();
105 if (sel.isNull()) return sel;
106 return sel;
107 }
108 // Avoid the need to return copies
109 // We always want an empty first item
110 originalStack.push_back(x: {});
111 return originalStack.back();
112 }
113
114 SelectorListObj Expand::popFromSelectorStack()
115 {
116 SelectorListObj last = selector_stack.back();
117 if (selector_stack.size() > 0)
118 selector_stack.pop_back();
119 if (last.isNull()) return {};
120 return last;
121 }
122
123 void Expand::pushToSelectorStack(SelectorListObj selector)
124 {
125 selector_stack.push_back(x: selector);
126 }
127
128 SelectorListObj Expand::popFromOriginalStack()
129 {
130 SelectorListObj last = originalStack.back();
131 if (originalStack.size() > 0)
132 originalStack.pop_back();
133 if (last.isNull()) return {};
134 return last;
135 }
136
137 void Expand::pushToOriginalStack(SelectorListObj selector)
138 {
139 originalStack.push_back(x: selector);
140 }
141
142 // blocks create new variable scopes
143 Block* Expand::operator()(Block* b)
144 {
145 // create new local environment
146 // set the current env as parent
147 Env env(environment());
148 // copy the block object (add items later)
149 Block_Obj bb = SASS_MEMORY_NEW(Block,
150 b->pstate(),
151 b->length(),
152 b->is_root());
153 // setup block and env stack
154 this->block_stack.push_back(x: bb);
155 this->env_stack.push_back(x: &env);
156 // operate on block
157 // this may throw up!
158 this->append_block(b);
159 // revert block and env stack
160 this->block_stack.pop_back();
161 this->env_stack.pop_back();
162 // return copy
163 return bb.detach();
164 }
165
166 Statement* Expand::operator()(StyleRule* r)
167 {
168 LOCAL_FLAG(old_at_root_without_rule, at_root_without_rule);
169
170 if (in_keyframes) {
171 Block* bb = operator()(b: r->block());
172 Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);
173 if (r->schema()) {
174 pushNullSelector();
175 k->name(name__: eval(r->schema()));
176 popNullSelector();
177 }
178 else if (r->selector()) {
179 if (SelectorListObj s = r->selector()) {
180 pushNullSelector();
181 k->name(name__: eval(s));
182 popNullSelector();
183 }
184 }
185
186 return k.detach();
187 }
188
189 if (r->schema()) {
190 SelectorListObj sel = eval(r->schema());
191 r->selector(selector__: sel);
192 for (auto complex : sel->elements()) {
193 // ToDo: maybe we can get rid of chroots?
194 complex->chroots(chroots__: complex->has_real_parent_ref());
195 }
196
197 }
198
199 // reset when leaving scope
200 LOCAL_FLAG(at_root_without_rule, false);
201
202 SelectorListObj evaled = eval(r->selector());
203 // do not connect parent again
204 Env env(environment());
205 if (block_stack.back()->is_root()) {
206 env_stack.push_back(x: &env);
207 }
208 Block_Obj blk;
209 pushToSelectorStack(selector: evaled);
210 // The copy is needed for parent reference evaluation
211 // dart-sass stores it as `originalSelector` member
212 pushToOriginalStack(SASS_MEMORY_COPY(evaled));
213 ctx.extender.addSelector(selector: evaled, mediaContext: mediaStack.back());
214 if (r->block()) blk = operator()(b: r->block());
215 popFromOriginalStack();
216 popFromSelectorStack();
217 StyleRule* rr = SASS_MEMORY_NEW(StyleRule,
218 r->pstate(),
219 evaled,
220 blk);
221
222 if (block_stack.back()->is_root()) {
223 env_stack.pop_back();
224 }
225
226 rr->is_root(is_root__: r->is_root());
227 rr->tabs(tabs__: r->tabs());
228
229 return rr;
230 }
231
232 Statement* Expand::operator()(SupportsRule* f)
233 {
234 ExpressionObj condition = f->condition()->perform(op: &eval);
235 SupportsRuleObj ff = SASS_MEMORY_NEW(SupportsRule,
236 f->pstate(),
237 Cast<SupportsCondition>(condition),
238 operator()(f->block()));
239 return ff.detach();
240 }
241
242 sass::vector<CssMediaQuery_Obj> Expand::mergeMediaQueries(
243 const sass::vector<CssMediaQuery_Obj>& lhs,
244 const sass::vector<CssMediaQuery_Obj>& rhs)
245 {
246 sass::vector<CssMediaQuery_Obj> queries;
247 for (CssMediaQuery_Obj query1 : lhs) {
248 for (CssMediaQuery_Obj query2 : rhs) {
249 CssMediaQuery_Obj result = query1->merge(other&: query2);
250 if (result && !result->empty()) {
251 queries.push_back(x: result);
252 }
253 }
254 }
255 return queries;
256 }
257
258 Statement* Expand::operator()(MediaRule* m)
259 {
260 ExpressionObj mq = eval(m->schema());
261 sass::string str_mq(mq->to_css(opt: ctx.c_options));
262 ItplFile* source = SASS_MEMORY_NEW(ItplFile,
263 str_mq.c_str(), m->pstate());
264 Parser parser(source, ctx, traces);
265 // Create a new CSS only representation of the media rule
266 CssMediaRuleObj css = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());
267 sass::vector<CssMediaQuery_Obj> parsed = parser.parseCssMediaQueries();
268 if (mediaStack.size() && mediaStack.back()) {
269 auto& parent = mediaStack.back()->elements();
270 css->concat(v: mergeMediaQueries(lhs: parent, rhs: parsed));
271 }
272 else {
273 css->concat(v: parsed);
274 }
275 mediaStack.push_back(x: css);
276 css->block(block__: operator()(b: m->block()));
277 mediaStack.pop_back();
278 return css.detach();
279
280 }
281
282 Statement* Expand::operator()(AtRootRule* a)
283 {
284 Block_Obj ab = a->block();
285 ExpressionObj ae = a->expression();
286
287 if (ae) ae = ae->perform(op: &eval);
288 else ae = SASS_MEMORY_NEW(At_Root_Query, a->pstate());
289
290 LOCAL_FLAG(at_root_without_rule, Cast<At_Root_Query>(ae)->exclude("rule"));
291 LOCAL_FLAG(in_keyframes, false);
292
293 ;
294
295 Block_Obj bb = ab ? operator()(b: ab) : NULL;
296 AtRootRuleObj aa = SASS_MEMORY_NEW(AtRootRule,
297 a->pstate(),
298 bb,
299 Cast<At_Root_Query>(ae));
300 return aa.detach();
301 }
302
303 Statement* Expand::operator()(AtRule* a)
304 {
305 LOCAL_FLAG(in_keyframes, a->is_keyframes());
306 Block* ab = a->block();
307 SelectorList* as = a->selector();
308 Expression* av = a->value();
309 pushNullSelector();
310 if (av) av = av->perform(op: &eval);
311 if (as) as = eval(as);
312 popNullSelector();
313 Block* bb = ab ? operator()(b: ab) : NULL;
314 AtRule* aa = SASS_MEMORY_NEW(AtRule,
315 a->pstate(),
316 a->keyword(),
317 as,
318 bb,
319 av);
320 return aa;
321 }
322
323 Statement* Expand::operator()(Declaration* d)
324 {
325 Block_Obj ab = d->block();
326 String_Obj old_p = d->property();
327 ExpressionObj prop = old_p->perform(op: &eval);
328 String_Obj new_p = Cast<String>(ptr: prop);
329 // we might get a color back
330 if (!new_p) {
331 sass::string str(prop->to_string(opt: ctx.c_options));
332 new_p = SASS_MEMORY_NEW(String_Constant, old_p->pstate(), str);
333 }
334 ExpressionObj value = d->value();
335 if (value) value = value->perform(op: &eval);
336 Block_Obj bb = ab ? operator()(b: ab) : NULL;
337 if (!bb) {
338 if (!value || (value->is_invisible() && !d->is_important())) {
339 if (d->is_custom_property()) {
340 error(msg: "Custom property values may not be empty.", pstate: d->value()->pstate(), traces);
341 } else {
342 return nullptr;
343 }
344 }
345 }
346 Declaration* decl = SASS_MEMORY_NEW(Declaration,
347 d->pstate(),
348 new_p,
349 value,
350 d->is_important(),
351 d->is_custom_property(),
352 bb);
353 decl->tabs(tabs__: d->tabs());
354 return decl;
355 }
356
357 Statement* Expand::operator()(Assignment* a)
358 {
359 Env* env = environment();
360 const sass::string& var(a->variable());
361 if (a->is_global()) {
362 if (!env->has_global(key: var)) {
363 deprecated(
364 msg: "!global assignments won't be able to declare new variables in future versions.",
365 msg2: "Consider adding `" + var + ": null` at the top level.",
366 with_column: true, pstate: a->pstate());
367 }
368 if (a->is_default()) {
369 if (env->has_global(key: var)) {
370 ExpressionObj e = Cast<Expression>(ptr: env->get_global(key: var));
371 if (!e || e->concrete_type() == Expression::NULL_VAL) {
372 env->set_global(key: var, val: a->value()->perform(op: &eval));
373 }
374 }
375 else {
376 env->set_global(key: var, val: a->value()->perform(op: &eval));
377 }
378 }
379 else {
380 env->set_global(key: var, val: a->value()->perform(op: &eval));
381 }
382 }
383 else if (a->is_default()) {
384 if (env->has_lexical(key: var)) {
385 auto cur = env;
386 while (cur && cur->is_lexical()) {
387 if (cur->has_local(key: var)) {
388 if (AST_Node_Obj node = cur->get_local(key: var)) {
389 ExpressionObj e = Cast<Expression>(ptr: node);
390 if (!e || e->concrete_type() == Expression::NULL_VAL) {
391 cur->set_local(key: var, val: a->value()->perform(op: &eval));
392 }
393 }
394 else {
395 throw std::runtime_error("Env not in sync");
396 }
397 return 0;
398 }
399 cur = cur->parent();
400 }
401 throw std::runtime_error("Env not in sync");
402 }
403 else if (env->has_global(key: var)) {
404 if (AST_Node_Obj node = env->get_global(key: var)) {
405 ExpressionObj e = Cast<Expression>(ptr: node);
406 if (!e || e->concrete_type() == Expression::NULL_VAL) {
407 env->set_global(key: var, val: a->value()->perform(op: &eval));
408 }
409 }
410 }
411 else if (env->is_lexical()) {
412 env->set_local(key: var, val: a->value()->perform(op: &eval));
413 }
414 else {
415 env->set_local(key: var, val: a->value()->perform(op: &eval));
416 }
417 }
418 else {
419 env->set_lexical(key: var, val: a->value()->perform(op: &eval));
420 }
421 return 0;
422 }
423
424 Statement* Expand::operator()(Import* imp)
425 {
426 Import_Obj result = SASS_MEMORY_NEW(Import, imp->pstate());
427 if (imp->import_queries() && imp->import_queries()->size()) {
428 ExpressionObj ex = imp->import_queries()->perform(op: &eval);
429 result->import_queries(import_queries__: Cast<List>(ptr: ex));
430 }
431 for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {
432 result->urls().push_back(x: imp->urls()[i]->perform(op: &eval));
433 }
434 // all resources have been dropped for Input_Stubs
435 // for ( size_t i = 0, S = imp->incs().size(); i < S; ++i) {}
436 return result.detach();
437 }
438
439 Statement* Expand::operator()(Import_Stub* i)
440 {
441 traces.push_back(x: Backtrace(i->pstate()));
442 // get parent node from call stack
443 AST_Node_Obj parent = call_stack.back();
444 if (Cast<Block>(ptr: parent) == NULL) {
445 error(msg: "Import directives may not be used within control directives or mixins.", pstate: i->pstate(), traces);
446 }
447 // we don't seem to need that actually afterall
448 Sass_Import_Entry import = sass_make_import(
449 imp_path: i->imp_path().c_str(),
450 abs_base: i->abs_path().c_str(),
451 source: 0, srcmap: 0
452 );
453 ctx.import_stack.push_back(x: import);
454
455 Block_Obj trace_block = SASS_MEMORY_NEW(Block, i->pstate());
456 Trace_Obj trace = SASS_MEMORY_NEW(Trace, i->pstate(), i->imp_path(), trace_block, 'i');
457 block_stack.back()->append(element: trace);
458 block_stack.push_back(x: trace_block);
459
460 const sass::string& abs_path(i->resource().abs_path);
461 append_block(ctx.sheets.at(k: abs_path).root);
462 sass_delete_import(ctx.import_stack.back());
463 ctx.import_stack.pop_back();
464 block_stack.pop_back();
465 traces.pop_back();
466 return 0;
467 }
468
469 Statement* Expand::operator()(WarningRule* w)
470 {
471 // eval handles this too, because warnings may occur in functions
472 w->perform(op: &eval);
473 return 0;
474 }
475
476 Statement* Expand::operator()(ErrorRule* e)
477 {
478 // eval handles this too, because errors may occur in functions
479 e->perform(op: &eval);
480 return 0;
481 }
482
483 Statement* Expand::operator()(DebugRule* d)
484 {
485 // eval handles this too, because warnings may occur in functions
486 d->perform(op: &eval);
487 return 0;
488 }
489
490 Statement* Expand::operator()(Comment* c)
491 {
492 if (ctx.output_style() == COMPRESSED) {
493 // comments should not be evaluated in compact
494 // https://github.com/sass/libsass/issues/2359
495 if (!c->is_important()) return NULL;
496 }
497 eval.is_in_comment = true;
498 Comment* rv = SASS_MEMORY_NEW(Comment, c->pstate(), Cast<String>(c->text()->perform(&eval)), c->is_important());
499 eval.is_in_comment = false;
500 // TODO: eval the text, once we're parsing/storing it as a String_Schema
501 return rv;
502 }
503
504 Statement* Expand::operator()(If* i)
505 {
506 Env env(environment(), true);
507 env_stack.push_back(x: &env);
508 call_stack.push_back(x: i);
509 ExpressionObj rv = i->predicate()->perform(op: &eval);
510 if (*rv) {
511 append_block(i->block());
512 }
513 else {
514 Block* alt = i->alternative();
515 if (alt) append_block(alt);
516 }
517 call_stack.pop_back();
518 env_stack.pop_back();
519 return 0;
520 }
521
522 // For does not create a new env scope
523 // But iteration vars are reset afterwards
524 Statement* Expand::operator()(ForRule* f)
525 {
526 sass::string variable(f->variable());
527 ExpressionObj low = f->lower_bound()->perform(op: &eval);
528 if (low->concrete_type() != Expression::NUMBER) {
529 traces.push_back(x: Backtrace(low->pstate()));
530 throw Exception::TypeMismatch(traces, *low, "integer");
531 }
532 ExpressionObj high = f->upper_bound()->perform(op: &eval);
533 if (high->concrete_type() != Expression::NUMBER) {
534 traces.push_back(x: Backtrace(high->pstate()));
535 throw Exception::TypeMismatch(traces, *high, "integer");
536 }
537 Number_Obj sass_start = Cast<Number>(ptr: low);
538 Number_Obj sass_end = Cast<Number>(ptr: high);
539 // check if units are valid for sequence
540 if (sass_start->unit() != sass_end->unit()) {
541 sass::ostream msg; msg << "Incompatible units: '"
542 << sass_start->unit() << "' and '"
543 << sass_end->unit() << "'.";
544 error(msg: msg.str(), pstate: low->pstate(), traces);
545 }
546 double start = sass_start->value();
547 double end = sass_end->value();
548 // only create iterator once in this environment
549 Env env(environment(), true);
550 env_stack.push_back(x: &env);
551 call_stack.push_back(x: f);
552 Block* body = f->block();
553 if (start < end) {
554 if (f->is_inclusive()) ++end;
555 for (double i = start;
556 i < end;
557 ++i) {
558 Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());
559 env.set_local(key: variable, val: it);
560 append_block(body);
561 }
562 } else {
563 if (f->is_inclusive()) --end;
564 for (double i = start;
565 i > end;
566 --i) {
567 Number_Obj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit());
568 env.set_local(key: variable, val: it);
569 append_block(body);
570 }
571 }
572 call_stack.pop_back();
573 env_stack.pop_back();
574 return 0;
575 }
576
577 // Eval does not create a new env scope
578 // But iteration vars are reset afterwards
579 Statement* Expand::operator()(EachRule* e)
580 {
581 sass::vector<sass::string> variables(e->variables());
582 ExpressionObj expr = e->list()->perform(op: &eval);
583 List_Obj list;
584 Map_Obj map;
585 if (expr->concrete_type() == Expression::MAP) {
586 map = Cast<Map>(ptr: expr);
587 }
588 else if (SelectorList * ls = Cast<SelectorList>(ptr: expr)) {
589 ExpressionObj rv = Listize::perform(node: ls);
590 list = Cast<List>(ptr: rv);
591 }
592 else if (expr->concrete_type() != Expression::LIST) {
593 list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);
594 list->append(element: expr);
595 }
596 else {
597 list = Cast<List>(ptr: expr);
598 }
599 // remember variables and then reset them
600 Env env(environment(), true);
601 env_stack.push_back(x: &env);
602 call_stack.push_back(x: e);
603 Block* body = e->block();
604
605 if (map) {
606 for (auto key : map->keys()) {
607 ExpressionObj k = key->perform(op: &eval);
608 ExpressionObj v = map->at(k: key)->perform(op: &eval);
609
610 if (variables.size() == 1) {
611 List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);
612 variable->append(element: k);
613 variable->append(element: v);
614 env.set_local(key: variables[0], val: variable);
615 } else {
616 env.set_local(key: variables[0], val: k);
617 env.set_local(key: variables[1], val: v);
618 }
619 append_block(body);
620 }
621 }
622 else {
623 // bool arglist = list->is_arglist();
624 if (list->length() == 1 && Cast<SelectorList>(ptr: list)) {
625 list = Cast<List>(ptr: list);
626 }
627 for (size_t i = 0, L = list->length(); i < L; ++i) {
628 ExpressionObj item = list->at(i);
629 // unwrap value if the expression is an argument
630 if (Argument_Obj arg = Cast<Argument>(ptr: item)) item = arg->value();
631 // check if we got passed a list of args (investigate)
632 if (List_Obj scalars = Cast<List>(ptr: item)) {
633 if (variables.size() == 1) {
634 List_Obj var = scalars;
635 // if (arglist) var = (*scalars)[0];
636 env.set_local(key: variables[0], val: var);
637 } else {
638 for (size_t j = 0, K = variables.size(); j < K; ++j) {
639 env.set_local(key: variables[j], val: j >= scalars->length()
640 ? SASS_MEMORY_NEW(Null, expr->pstate())
641 : (*scalars)[j]->perform(op: &eval));
642 }
643 }
644 } else {
645 if (variables.size() > 0) {
646 env.set_local(key: variables.at(n: 0), val: item);
647 for (size_t j = 1, K = variables.size(); j < K; ++j) {
648 ExpressionObj res = SASS_MEMORY_NEW(Null, expr->pstate());
649 env.set_local(key: variables[j], val: res);
650 }
651 }
652 }
653 append_block(body);
654 }
655 }
656 call_stack.pop_back();
657 env_stack.pop_back();
658 return 0;
659 }
660
661 Statement* Expand::operator()(WhileRule* w)
662 {
663 ExpressionObj pred = w->predicate();
664 Block* body = w->block();
665 Env env(environment(), true);
666 env_stack.push_back(x: &env);
667 call_stack.push_back(x: w);
668 ExpressionObj cond = pred->perform(op: &eval);
669 while (!cond->is_false()) {
670 append_block(body);
671 cond = pred->perform(op: &eval);
672 }
673 call_stack.pop_back();
674 env_stack.pop_back();
675 return 0;
676 }
677
678 Statement* Expand::operator()(Return* r)
679 {
680 error(msg: "@return may only be used within a function", pstate: r->pstate(), traces);
681 return 0;
682 }
683
684 Statement* Expand::operator()(ExtendRule* e)
685 {
686
687 // evaluate schema first
688 if (e->schema()) {
689 e->selector(selector__: eval(e->schema()));
690 e->isOptional(isOptional__: e->selector()->is_optional());
691 }
692 // evaluate the selector
693 e->selector(selector__: eval(e->selector()));
694
695 if (e->selector()) {
696
697 for (auto complex : e->selector()->elements()) {
698
699 if (complex->length() != 1) {
700 error(msg: "complex selectors may not be extended.", pstate: complex->pstate(), traces);
701 }
702
703 if (const CompoundSelector* compound = complex->first()->getCompound()) {
704
705 if (compound->length() != 1) {
706
707 sass::ostream sels; bool addComma = false;
708 sels << "Compound selectors may no longer be extended.\n";
709 sels << "Consider `@extend ";
710 for (auto sel : compound->elements()) {
711 if (addComma) sels << ", ";
712 sels << sel->to_sass();
713 addComma = true;
714 }
715 sels << "` instead.\n";
716 sels << "See http://bit.ly/ExtendCompound for details.";
717
718 warning(msg: sels.str(), pstate: compound->pstate());
719
720 // Make this an error once deprecation is over
721 for (SimpleSelectorObj simple : compound->elements()) {
722 // Pass every selector we ever see to extender (to make them findable for extend)
723 ctx.extender.addExtension(extender: selector(), target: simple, mediaQueryContext: mediaStack.back(), is_optional: e->isOptional());
724 }
725
726 }
727 else {
728 // Pass every selector we ever see to extender (to make them findable for extend)
729 ctx.extender.addExtension(extender: selector(), target: compound->first(), mediaQueryContext: mediaStack.back(), is_optional: e->isOptional());
730 }
731
732 }
733 else {
734 error(msg: "complex selectors may not be extended.", pstate: complex->pstate(), traces);
735 }
736 }
737 }
738
739 return nullptr;
740
741 }
742
743 Statement* Expand::operator()(Definition* d)
744 {
745 Env* env = environment();
746 Definition_Obj dd = SASS_MEMORY_COPY(d);
747 env->local_frame()[d->name() +
748 (d->type() == Definition::MIXIN ? "[m]" : "[f]")] = dd;
749
750 if (d->type() == Definition::FUNCTION && (
751 Prelexer::calc_fn_call(src: d->name().c_str()) ||
752 d->name() == "element" ||
753 d->name() == "expression" ||
754 d->name() == "url"
755 )) {
756 deprecated(
757 msg: "Naming a function \"" + d->name() + "\" is disallowed and will be an error in future versions of Sass.",
758 msg2: "This name conflicts with an existing CSS function with special parse rules.",
759 with_column: false, pstate: d->pstate()
760 );
761 }
762
763 // set the static link so we can have lexical scoping
764 dd->environment(environment__: env);
765 return 0;
766 }
767
768 Statement* Expand::operator()(Mixin_Call* c)
769 {
770
771 if (recursions > maxRecursion) {
772 throw Exception::StackError(traces, *c);
773 }
774
775 recursions ++;
776
777 Env* env = environment();
778 sass::string full_name(c->name() + "[m]");
779 if (!env->has(key: full_name)) {
780 error(msg: "no mixin named " + c->name(), pstate: c->pstate(), traces);
781 }
782 Definition_Obj def = Cast<Definition>(ptr: (*env)[full_name]);
783 Block_Obj body = def->block();
784 Parameters_Obj params = def->parameters();
785
786 if (c->block() && c->name() != "@content" && !body->has_content()) {
787 error(msg: "Mixin \"" + c->name() + "\" does not accept a content block.", pstate: c->pstate(), traces);
788 }
789 ExpressionObj rv = c->arguments()->perform(op: &eval);
790 Arguments_Obj args = Cast<Arguments>(ptr: rv);
791 sass::string msg(", in mixin `" + c->name() + "`");
792 traces.push_back(x: Backtrace(c->pstate(), msg));
793 ctx.callee_stack.push_back(x: {
794 .name: c->name().c_str(),
795 .path: c->pstate().getPath(),
796 .line: c->pstate().getLine(),
797 .column: c->pstate().getColumn(),
798 .type: SASS_CALLEE_MIXIN,
799 .env: { .frame: env }
800 });
801
802 Env new_env(def->environment());
803 env_stack.push_back(x: &new_env);
804 if (c->block()) {
805 Parameters_Obj params = c->block_parameters();
806 if (!params) params = SASS_MEMORY_NEW(Parameters, c->pstate());
807 // represent mixin content blocks as thunks/closures
808 Definition_Obj thunk = SASS_MEMORY_NEW(Definition,
809 c->pstate(),
810 "@content",
811 params,
812 c->block(),
813 Definition::MIXIN);
814 thunk->environment(environment__: env);
815 new_env.local_frame()["@content[m]"] = thunk;
816 }
817
818 bind(type: sass::string("Mixin"), name: c->name(), params, args, &new_env, &eval, traces);
819
820 Block_Obj trace_block = SASS_MEMORY_NEW(Block, c->pstate());
821 Trace_Obj trace = SASS_MEMORY_NEW(Trace, c->pstate(), c->name(), trace_block);
822
823 env->set_global(key: "is_in_mixin", val: bool_true);
824 if (Block* pr = block_stack.back()) {
825 trace_block->is_root(is_root__: pr->is_root());
826 }
827 block_stack.push_back(x: trace_block);
828 for (auto bb : body->elements()) {
829 if (StyleRule* r = Cast<StyleRule>(ptr: bb)) {
830 r->is_root(is_root__: trace_block->is_root());
831 }
832 Statement_Obj ith = bb->perform(op: this);
833 if (ith) trace->block()->append(element: ith);
834 }
835 block_stack.pop_back();
836 env->del_global(key: "is_in_mixin");
837
838 ctx.callee_stack.pop_back();
839 env_stack.pop_back();
840 traces.pop_back();
841
842 recursions --;
843 return trace.detach();
844 }
845
846 Statement* Expand::operator()(Content* c)
847 {
848 Env* env = environment();
849 // convert @content directives into mixin calls to the underlying thunk
850 if (!env->has(key: "@content[m]")) return 0;
851 Arguments_Obj args = c->arguments();
852 if (!args) args = SASS_MEMORY_NEW(Arguments, c->pstate());
853
854 Mixin_Call_Obj call = SASS_MEMORY_NEW(Mixin_Call,
855 c->pstate(),
856 "@content",
857 args);
858
859 Trace_Obj trace = Cast<Trace>(ptr: call->perform(op: this));
860 return trace.detach();
861 }
862
863 // process and add to last block on stack
864 inline void Expand::append_block(Block* b)
865 {
866 if (b->is_root()) call_stack.push_back(x: b);
867 for (size_t i = 0, L = b->length(); i < L; ++i) {
868 Statement* stm = b->at(i);
869 Statement_Obj ith = stm->perform(op: this);
870 if (ith) block_stack.back()->append(element: ith);
871 }
872 if (b->is_root()) call_stack.pop_back();
873 }
874
875}
876

source code of gtk/subprojects/libsass/src/expand.cpp