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