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 
18 namespace Sass {
19 
20   // simple endless recursion protection
21   const size_t maxRecursion = 500;
22 
Expand(Context & ctx,Env * env,SelectorStack * stack,SelectorStack * originals)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(nullptr);
39     env_stack.push_back(env);
40     block_stack.push_back(nullptr);
41     call_stack.push_back({});
42     if (stack == NULL) { pushToSelectorStack({}); }
43     else {
44       for (auto item : *stack) {
45         if (item.isNull()) pushToSelectorStack({});
46         else pushToSelectorStack(item);
47       }
48     }
49     if (originals == NULL) { pushToOriginalStack({}); }
50     else {
51       for (auto item : *stack) {
52         if (item.isNull()) pushToOriginalStack({});
53         else pushToOriginalStack(item);
54       }
55     }
56     mediaStack.push_back({});
57   }
58 
environment()59   Env* Expand::environment()
60   {
61     if (env_stack.size() > 0)
62       return env_stack.back();
63     return 0;
64   }
65 
pushNullSelector()66   void Expand::pushNullSelector()
67   {
68     pushToSelectorStack({});
69     pushToOriginalStack({});
70   }
71 
popNullSelector()72   void Expand::popNullSelector()
73   {
74     popFromOriginalStack();
75     popFromSelectorStack();
76   }
77 
getOriginalStack()78   SelectorStack Expand::getOriginalStack()
79   {
80     return originalStack;
81   }
82 
getSelectorStack()83   SelectorStack Expand::getSelectorStack()
84   {
85     return selector_stack;
86   }
87 
selector()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({});
98     return selector_stack.back();;
99   }
100 
original()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({});
111     return originalStack.back();
112   }
113 
popFromSelectorStack()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 
pushToSelectorStack(SelectorListObj selector)123   void Expand::pushToSelectorStack(SelectorListObj selector)
124   {
125     selector_stack.push_back(selector);
126   }
127 
popFromOriginalStack()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 
pushToOriginalStack(SelectorListObj selector)137   void Expand::pushToOriginalStack(SelectorListObj selector)
138   {
139     originalStack.push_back(selector);
140   }
141 
142   // blocks create new variable scopes
operator ()(Block * b)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(bb);
155     this->env_stack.push_back(&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 
operator ()(StyleRule * r)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()(r->block());
172       Keyframe_Rule_Obj k = SASS_MEMORY_NEW(Keyframe_Rule, r->pstate(), bb);
173       if (r->schema()) {
174         pushNullSelector();
175         k->name(eval(r->schema()));
176         popNullSelector();
177       }
178       else if (r->selector()) {
179         if (SelectorListObj s = r->selector()) {
180           pushNullSelector();
181           k->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(sel);
192       for (auto complex : sel->elements()) {
193         // ToDo: maybe we can get rid of chroots?
194         complex->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(&env);
207     }
208     Block_Obj blk;
209     pushToSelectorStack(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(evaled, mediaStack.back());
214     if (r->block()) blk = operator()(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(r->is_root());
227     rr->tabs(r->tabs());
228 
229     return rr;
230   }
231 
operator ()(SupportsRule * f)232   Statement* Expand::operator()(SupportsRule* f)
233   {
234     ExpressionObj condition = f->condition()->perform(&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 
mergeMediaQueries(const sass::vector<CssMediaQuery_Obj> & lhs,const sass::vector<CssMediaQuery_Obj> & rhs)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(query2);
250         if (result && !result->empty()) {
251           queries.push_back(result);
252         }
253       }
254     }
255     return queries;
256   }
257 
operator ()(MediaRule * m)258   Statement* Expand::operator()(MediaRule* m)
259   {
260     ExpressionObj mq = eval(m->schema());
261     sass::string str_mq(mq->to_css(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(mergeMediaQueries(parent, parsed));
271     }
272     else {
273       css->concat(parsed);
274     }
275     mediaStack.push_back(css);
276     css->block(operator()(m->block()));
277     mediaStack.pop_back();
278     return css.detach();
279 
280   }
281 
operator ()(AtRootRule * a)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(&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()(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 
operator ()(AtRule * a)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(&eval);
311     if (as) as = eval(as);
312     popNullSelector();
313     Block* bb = ab ? operator()(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 
operator ()(Declaration * d)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(&eval);
328     String_Obj new_p = Cast<String>(prop);
329     // we might get a color back
330     if (!new_p) {
331       sass::string str(prop->to_string(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(&eval);
336     Block_Obj bb = ab ? operator()(ab) : NULL;
337     if (!bb) {
338       if (!value || (value->is_invisible() && !d->is_important())) {
339         if (d->is_custom_property()) {
340           error("Custom property values may not be empty.", 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(d->tabs());
354     return decl;
355   }
356 
operator ()(Assignment * a)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(var)) {
363         deprecated(
364           "!global assignments won't be able to declare new variables in future versions.",
365           "Consider adding `" + var + ": null` at the top level.",
366           true, a->pstate());
367       }
368       if (a->is_default()) {
369         if (env->has_global(var)) {
370           ExpressionObj e = Cast<Expression>(env->get_global(var));
371           if (!e || e->concrete_type() == Expression::NULL_VAL) {
372             env->set_global(var, a->value()->perform(&eval));
373           }
374         }
375         else {
376           env->set_global(var, a->value()->perform(&eval));
377         }
378       }
379       else {
380         env->set_global(var, a->value()->perform(&eval));
381       }
382     }
383     else if (a->is_default()) {
384       if (env->has_lexical(var)) {
385         auto cur = env;
386         while (cur && cur->is_lexical()) {
387           if (cur->has_local(var)) {
388             if (AST_Node_Obj node = cur->get_local(var)) {
389               ExpressionObj e = Cast<Expression>(node);
390               if (!e || e->concrete_type() == Expression::NULL_VAL) {
391                 cur->set_local(var, a->value()->perform(&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(var)) {
404         if (AST_Node_Obj node = env->get_global(var)) {
405           ExpressionObj e = Cast<Expression>(node);
406           if (!e || e->concrete_type() == Expression::NULL_VAL) {
407             env->set_global(var, a->value()->perform(&eval));
408           }
409         }
410       }
411       else if (env->is_lexical()) {
412         env->set_local(var, a->value()->perform(&eval));
413       }
414       else {
415         env->set_local(var, a->value()->perform(&eval));
416       }
417     }
418     else {
419       env->set_lexical(var, a->value()->perform(&eval));
420     }
421     return 0;
422   }
423 
operator ()(Import * imp)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(&eval);
429       result->import_queries(Cast<List>(ex));
430     }
431     for ( size_t i = 0, S = imp->urls().size(); i < S; ++i) {
432       result->urls().push_back(imp->urls()[i]->perform(&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 
operator ()(Import_Stub * i)439   Statement* Expand::operator()(Import_Stub* i)
440   {
441     traces.push_back(Backtrace(i->pstate()));
442     // get parent node from call stack
443     AST_Node_Obj parent = call_stack.back();
444     if (Cast<Block>(parent) == NULL) {
445       error("Import directives may not be used within control directives or mixins.", i->pstate(), traces);
446     }
447     // we don't seem to need that actually afterall
448     Sass_Import_Entry import = sass_make_import(
449       i->imp_path().c_str(),
450       i->abs_path().c_str(),
451       0, 0
452     );
453     ctx.import_stack.push_back(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(trace);
458     block_stack.push_back(trace_block);
459 
460     const sass::string& abs_path(i->resource().abs_path);
461     append_block(ctx.sheets.at(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 
operator ()(WarningRule * w)469   Statement* Expand::operator()(WarningRule* w)
470   {
471     // eval handles this too, because warnings may occur in functions
472     w->perform(&eval);
473     return 0;
474   }
475 
operator ()(ErrorRule * e)476   Statement* Expand::operator()(ErrorRule* e)
477   {
478     // eval handles this too, because errors may occur in functions
479     e->perform(&eval);
480     return 0;
481   }
482 
operator ()(DebugRule * d)483   Statement* Expand::operator()(DebugRule* d)
484   {
485     // eval handles this too, because warnings may occur in functions
486     d->perform(&eval);
487     return 0;
488   }
489 
operator ()(Comment * c)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 
operator ()(If * i)504   Statement* Expand::operator()(If* i)
505   {
506     Env env(environment(), true);
507     env_stack.push_back(&env);
508     call_stack.push_back(i);
509     ExpressionObj rv = i->predicate()->perform(&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
operator ()(ForRule * f)524   Statement* Expand::operator()(ForRule* f)
525   {
526     sass::string variable(f->variable());
527     ExpressionObj low = f->lower_bound()->perform(&eval);
528     if (low->concrete_type() != Expression::NUMBER) {
529       traces.push_back(Backtrace(low->pstate()));
530       throw Exception::TypeMismatch(traces, *low, "integer");
531     }
532     ExpressionObj high = f->upper_bound()->perform(&eval);
533     if (high->concrete_type() != Expression::NUMBER) {
534       traces.push_back(Backtrace(high->pstate()));
535       throw Exception::TypeMismatch(traces, *high, "integer");
536     }
537     Number_Obj sass_start = Cast<Number>(low);
538     Number_Obj sass_end = Cast<Number>(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.str(), 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(&env);
551     call_stack.push_back(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(variable, 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(variable, 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
operator ()(EachRule * e)579   Statement* Expand::operator()(EachRule* e)
580   {
581     sass::vector<sass::string> variables(e->variables());
582     ExpressionObj expr = e->list()->perform(&eval);
583     List_Obj list;
584     Map_Obj map;
585     if (expr->concrete_type() == Expression::MAP) {
586       map = Cast<Map>(expr);
587     }
588     else if (SelectorList * ls = Cast<SelectorList>(expr)) {
589       ExpressionObj rv = Listize::perform(ls);
590       list = Cast<List>(rv);
591     }
592     else if (expr->concrete_type() != Expression::LIST) {
593       list = SASS_MEMORY_NEW(List, expr->pstate(), 1, SASS_COMMA);
594       list->append(expr);
595     }
596     else {
597       list = Cast<List>(expr);
598     }
599     // remember variables and then reset them
600     Env env(environment(), true);
601     env_stack.push_back(&env);
602     call_stack.push_back(e);
603     Block* body = e->block();
604 
605     if (map) {
606       for (auto key : map->keys()) {
607         ExpressionObj k = key->perform(&eval);
608         ExpressionObj v = map->at(key)->perform(&eval);
609 
610         if (variables.size() == 1) {
611           List_Obj variable = SASS_MEMORY_NEW(List, map->pstate(), 2, SASS_SPACE);
612           variable->append(k);
613           variable->append(v);
614           env.set_local(variables[0], variable);
615         } else {
616           env.set_local(variables[0], k);
617           env.set_local(variables[1], v);
618         }
619         append_block(body);
620       }
621     }
622     else {
623       // bool arglist = list->is_arglist();
624       if (list->length() == 1 && Cast<SelectorList>(list)) {
625         list = Cast<List>(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>(item)) item = arg->value();
631         // check if we got passed a list of args (investigate)
632         if (List_Obj scalars = Cast<List>(item)) {
633           if (variables.size() == 1) {
634             List_Obj var = scalars;
635             // if (arglist) var = (*scalars)[0];
636             env.set_local(variables[0], var);
637           } else {
638             for (size_t j = 0, K = variables.size(); j < K; ++j) {
639               env.set_local(variables[j], j >= scalars->length()
640                 ? SASS_MEMORY_NEW(Null, expr->pstate())
641                 : (*scalars)[j]->perform(&eval));
642             }
643           }
644         } else {
645           if (variables.size() > 0) {
646             env.set_local(variables.at(0), 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(variables[j], 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 
operator ()(WhileRule * w)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(&env);
667     call_stack.push_back(w);
668     ExpressionObj cond = pred->perform(&eval);
669     while (!cond->is_false()) {
670       append_block(body);
671       cond = pred->perform(&eval);
672     }
673     call_stack.pop_back();
674     env_stack.pop_back();
675     return 0;
676   }
677 
operator ()(Return * r)678   Statement* Expand::operator()(Return* r)
679   {
680     error("@return may only be used within a function", r->pstate(), traces);
681     return 0;
682   }
683 
operator ()(ExtendRule * e)684   Statement* Expand::operator()(ExtendRule* e)
685   {
686 
687     // evaluate schema first
688     if (e->schema()) {
689       e->selector(eval(e->schema()));
690       e->isOptional(e->selector()->is_optional());
691     }
692     // evaluate the selector
693     e->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("complex selectors may not be extended.", 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(sels.str(), 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(selector(), simple, mediaStack.back(), 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(selector(), compound->first(), mediaStack.back(), e->isOptional());
730           }
731 
732         }
733         else {
734           error("complex selectors may not be extended.", complex->pstate(), traces);
735         }
736       }
737     }
738 
739     return nullptr;
740 
741   }
742 
operator ()(Definition * d)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(d->name().c_str()) ||
752       d->name() == "element"    ||
753       d->name() == "expression" ||
754       d->name() == "url"
755     )) {
756       deprecated(
757         "Naming a function \"" + d->name() + "\" is disallowed and will be an error in future versions of Sass.",
758         "This name conflicts with an existing CSS function with special parse rules.",
759         false, d->pstate()
760       );
761     }
762 
763     // set the static link so we can have lexical scoping
764     dd->environment(env);
765     return 0;
766   }
767 
operator ()(Mixin_Call * c)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(full_name)) {
780       error("no mixin named " + c->name(), c->pstate(), traces);
781     }
782     Definition_Obj def = Cast<Definition>((*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("Mixin \"" + c->name() + "\" does not accept a content block.", c->pstate(), traces);
788     }
789     ExpressionObj rv = c->arguments()->perform(&eval);
790     Arguments_Obj args = Cast<Arguments>(rv);
791     sass::string msg(", in mixin `" + c->name() + "`");
792     traces.push_back(Backtrace(c->pstate(), msg));
793     ctx.callee_stack.push_back({
794       c->name().c_str(),
795       c->pstate().getPath(),
796       c->pstate().getLine(),
797       c->pstate().getColumn(),
798       SASS_CALLEE_MIXIN,
799       { env }
800     });
801 
802     Env new_env(def->environment());
803     env_stack.push_back(&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(env);
815       new_env.local_frame()["@content[m]"] = thunk;
816     }
817 
818     bind(sass::string("Mixin"), 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("is_in_mixin", bool_true);
824     if (Block* pr = block_stack.back()) {
825       trace_block->is_root(pr->is_root());
826     }
827     block_stack.push_back(trace_block);
828     for (auto bb : body->elements()) {
829       if (StyleRule* r = Cast<StyleRule>(bb)) {
830         r->is_root(trace_block->is_root());
831       }
832       Statement_Obj ith = bb->perform(this);
833       if (ith) trace->block()->append(ith);
834     }
835     block_stack.pop_back();
836     env->del_global("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 
operator ()(Content * c)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("@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>(call->perform(this));
860     return trace.detach();
861   }
862 
863   // process and add to last block on stack
append_block(Block * b)864   inline void Expand::append_block(Block* b)
865   {
866     if (b->is_root()) call_stack.push_back(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(this);
870       if (ith) block_stack.back()->append(ith);
871     }
872     if (b->is_root()) call_stack.pop_back();
873   }
874 
875 }
876