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 #include <vector>
8 
9 #include "cssize.hpp"
10 #include "context.hpp"
11 
12 namespace Sass {
13 
Cssize(Context & ctx)14   Cssize::Cssize(Context& ctx)
15   : traces(ctx.traces),
16     block_stack(BlockStack()),
17     p_stack(sass::vector<Statement*>())
18   { }
19 
parent()20   Statement* Cssize::parent()
21   {
22     return p_stack.size() ? p_stack.back() : block_stack.front();
23   }
24 
operator ()(Block * b)25   Block* Cssize::operator()(Block* b)
26   {
27     Block_Obj bb = SASS_MEMORY_NEW(Block, b->pstate(), b->length(), b->is_root());
28     // bb->tabs(b->tabs());
29     block_stack.push_back(bb);
30     append_block(b, bb);
31     block_stack.pop_back();
32     return bb.detach();
33   }
34 
operator ()(Trace * t)35   Statement* Cssize::operator()(Trace* t)
36   {
37     traces.push_back(Backtrace(t->pstate()));
38     auto result = t->block()->perform(this);
39     traces.pop_back();
40     return result;
41   }
42 
operator ()(Declaration * d)43   Statement* Cssize::operator()(Declaration* d)
44   {
45     String_Obj property = Cast<String>(d->property());
46 
47     if (Declaration* dd = Cast<Declaration>(parent())) {
48       String_Obj parent_property = Cast<String>(dd->property());
49       property = SASS_MEMORY_NEW(String_Constant,
50                                  d->property()->pstate(),
51                                  parent_property->to_string() + "-" + property->to_string());
52       if (!dd->value()) {
53         d->tabs(dd->tabs() + 1);
54       }
55     }
56 
57     Declaration_Obj dd = SASS_MEMORY_NEW(Declaration,
58                                       d->pstate(),
59                                       property,
60                                       d->value(),
61                                       d->is_important(),
62                                       d->is_custom_property());
63     dd->is_indented(d->is_indented());
64     dd->tabs(d->tabs());
65 
66     p_stack.push_back(dd);
67     Block_Obj bb = d->block() ? operator()(d->block()) : NULL;
68     p_stack.pop_back();
69 
70     if (bb && bb->length()) {
71       if (dd->value() && !dd->value()->is_invisible()) {
72         bb->unshift(dd);
73       }
74       return bb.detach();
75     }
76     else if (dd->value() && !dd->value()->is_invisible()) {
77       return dd.detach();
78     }
79 
80     return 0;
81   }
82 
operator ()(AtRule * r)83   Statement* Cssize::operator()(AtRule* r)
84   {
85     if (!r->block() || !r->block()->length()) return r;
86 
87     if (parent()->statement_type() == Statement::RULESET)
88     {
89       return r->is_keyframes() ? SASS_MEMORY_NEW(Bubble, r->pstate(), r) : bubble(r);
90     }
91 
92     p_stack.push_back(r);
93     AtRuleObj rr = SASS_MEMORY_NEW(AtRule,
94                                   r->pstate(),
95                                   r->keyword(),
96                                   r->selector(),
97                                   r->block() ? operator()(r->block()) : 0);
98     if (r->value()) rr->value(r->value());
99     p_stack.pop_back();
100 
101     bool directive_exists = false;
102     size_t L = rr->block() ? rr->block()->length() : 0;
103     for (size_t i = 0; i < L && !directive_exists; ++i) {
104       Statement_Obj s = r->block()->at(i);
105       if (s->statement_type() != Statement::BUBBLE) directive_exists = true;
106       else {
107         Bubble_Obj s_obj = Cast<Bubble>(s);
108         s = s_obj->node();
109         if (s->statement_type() != Statement::DIRECTIVE) directive_exists = false;
110         else directive_exists = (Cast<AtRule>(s)->keyword() == rr->keyword());
111       }
112 
113     }
114 
115     Block* result = SASS_MEMORY_NEW(Block, rr->pstate());
116     if (!(directive_exists || rr->is_keyframes()))
117     {
118       AtRule* empty_node = Cast<AtRule>(rr);
119       empty_node->block(SASS_MEMORY_NEW(Block, rr->block() ? rr->block()->pstate() : rr->pstate()));
120       result->append(empty_node);
121     }
122 
123     Block_Obj db = rr->block();
124     if (db.isNull()) db = SASS_MEMORY_NEW(Block, rr->pstate());
125     Block_Obj ss = debubble(db, rr);
126     for (size_t i = 0, L = ss->length(); i < L; ++i) {
127       result->append(ss->at(i));
128     }
129 
130     return result;
131   }
132 
operator ()(Keyframe_Rule * r)133   Statement* Cssize::operator()(Keyframe_Rule* r)
134   {
135     if (!r->block() || !r->block()->length()) return r;
136 
137     Keyframe_Rule_Obj rr = SASS_MEMORY_NEW(Keyframe_Rule,
138                                         r->pstate(),
139                                         operator()(r->block()));
140     if (!r->name().isNull()) rr->name(r->name());
141 
142     return debubble(rr->block(), rr);
143   }
144 
operator ()(StyleRule * r)145   Statement* Cssize::operator()(StyleRule* r)
146   {
147     p_stack.push_back(r);
148     // this can return a string schema
149     // string schema is not a statement!
150     // r->block() is already a string schema
151     // and that is coming from propset expand
152     Block* bb = operator()(r->block());
153     // this should protect us (at least a bit) from our mess
154     // fixing this properly is harder that it should be ...
155     if (Cast<Statement>(bb) == NULL) {
156       error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces);
157     }
158     StyleRuleObj rr = SASS_MEMORY_NEW(StyleRule,
159                                   r->pstate(),
160                                   r->selector(),
161                                   bb);
162 
163     rr->is_root(r->is_root());
164     // rr->tabs(r->block()->tabs());
165     p_stack.pop_back();
166 
167     if (!rr->block()) {
168       error("Illegal nesting: Only properties may be nested beneath properties.", r->block()->pstate(), traces);
169     }
170 
171     Block_Obj props = SASS_MEMORY_NEW(Block, rr->block()->pstate());
172     Block* rules = SASS_MEMORY_NEW(Block, rr->block()->pstate());
173     for (size_t i = 0, L = rr->block()->length(); i < L; i++)
174     {
175       Statement* s = rr->block()->at(i);
176       if (bubblable(s)) rules->append(s);
177       if (!bubblable(s)) props->append(s);
178     }
179 
180     if (props->length())
181     {
182       Block_Obj pb = SASS_MEMORY_NEW(Block, rr->block()->pstate());
183       pb->concat(props);
184       rr->block(pb);
185 
186       for (size_t i = 0, L = rules->length(); i < L; i++)
187       {
188         Statement* stm = rules->at(i);
189         stm->tabs(stm->tabs() + 1);
190       }
191 
192       rules->unshift(rr);
193     }
194 
195     Block* ptr = rules;
196     rules = debubble(rules);
197     void* lp = ptr;
198     void* rp = rules;
199     if (lp != rp) {
200       Block_Obj obj = ptr;
201     }
202 
203     if (!(!rules->length() ||
204           !bubblable(rules->last()) ||
205           parent()->statement_type() == Statement::RULESET))
206     {
207       rules->last()->group_end(true);
208     }
209     return rules;
210   }
211 
operator ()(Null * m)212   Statement* Cssize::operator()(Null* m)
213   {
214     return 0;
215   }
216 
operator ()(CssMediaRule * m)217   Statement* Cssize::operator()(CssMediaRule* m)
218   {
219     if (parent()->statement_type() == Statement::RULESET)
220     {
221       return bubble(m);
222     }
223 
224     if (parent()->statement_type() == Statement::MEDIA)
225     {
226       return SASS_MEMORY_NEW(Bubble, m->pstate(), m);
227     }
228 
229     p_stack.push_back(m);
230 
231     CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule, m->pstate(), m->block());
232     mm->concat(m->elements());
233     mm->block(operator()(m->block()));
234     mm->tabs(m->tabs());
235 
236     p_stack.pop_back();
237 
238     return debubble(mm->block(), mm);
239   }
240 
operator ()(SupportsRule * m)241   Statement* Cssize::operator()(SupportsRule* m)
242   {
243     if (!m->block()->length())
244     { return m; }
245 
246     if (parent()->statement_type() == Statement::RULESET)
247     { return bubble(m); }
248 
249     p_stack.push_back(m);
250 
251     SupportsRuleObj mm = SASS_MEMORY_NEW(SupportsRule,
252                                        m->pstate(),
253                                        m->condition(),
254                                        operator()(m->block()));
255     mm->tabs(m->tabs());
256 
257     p_stack.pop_back();
258 
259     return debubble(mm->block(), mm);
260   }
261 
operator ()(AtRootRule * m)262   Statement* Cssize::operator()(AtRootRule* m)
263   {
264     bool tmp = false;
265     for (size_t i = 0, L = p_stack.size(); i < L; ++i) {
266       Statement* s = p_stack[i];
267       tmp |= m->exclude_node(s);
268     }
269 
270     if (!tmp && m->block())
271     {
272       Block* bb = operator()(m->block());
273       for (size_t i = 0, L = bb->length(); i < L; ++i) {
274         // (bb->elements())[i]->tabs(m->tabs());
275         Statement_Obj stm = bb->at(i);
276         if (bubblable(stm)) stm->tabs(stm->tabs() + m->tabs());
277       }
278       if (bb->length() && bubblable(bb->last())) bb->last()->group_end(m->group_end());
279       return bb;
280     }
281 
282     if (m->exclude_node(parent()))
283     {
284       return SASS_MEMORY_NEW(Bubble, m->pstate(), m);
285     }
286 
287     return bubble(m);
288   }
289 
bubble(AtRule * m)290   Statement* Cssize::bubble(AtRule* m)
291   {
292     Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());
293     ParentStatementObj new_rule = Cast<ParentStatement>(SASS_MEMORY_COPY(this->parent()));
294     new_rule->block(bb);
295     new_rule->tabs(this->parent()->tabs());
296     new_rule->block()->concat(m->block());
297 
298     Block_Obj wrapper_block = SASS_MEMORY_NEW(Block, m->block() ? m->block()->pstate() : m->pstate());
299     wrapper_block->append(new_rule);
300     AtRuleObj mm = SASS_MEMORY_NEW(AtRule,
301                                   m->pstate(),
302                                   m->keyword(),
303                                   m->selector(),
304                                   wrapper_block);
305     if (m->value()) mm->value(m->value());
306 
307     Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
308     return bubble;
309   }
310 
bubble(AtRootRule * m)311   Statement* Cssize::bubble(AtRootRule* m)
312   {
313     if (!m || !m->block()) return NULL;
314     Block* bb = SASS_MEMORY_NEW(Block, this->parent()->pstate());
315     ParentStatementObj new_rule = Cast<ParentStatement>(SASS_MEMORY_COPY(this->parent()));
316     Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());
317     if (new_rule) {
318       new_rule->block(bb);
319       new_rule->tabs(this->parent()->tabs());
320       new_rule->block()->concat(m->block());
321       wrapper_block->append(new_rule);
322     }
323 
324     AtRootRule* mm = SASS_MEMORY_NEW(AtRootRule,
325                                         m->pstate(),
326                                         wrapper_block,
327                                         m->expression());
328     Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
329     return bubble;
330   }
331 
bubble(SupportsRule * m)332   Statement* Cssize::bubble(SupportsRule* m)
333   {
334     StyleRuleObj parent = Cast<StyleRule>(SASS_MEMORY_COPY(this->parent()));
335 
336     Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());
337     StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule,
338                                         parent->pstate(),
339                                         parent->selector(),
340                                         bb);
341     new_rule->tabs(parent->tabs());
342     new_rule->block()->concat(m->block());
343 
344     Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());
345     wrapper_block->append(new_rule);
346     SupportsRule* mm = SASS_MEMORY_NEW(SupportsRule,
347                                        m->pstate(),
348                                        m->condition(),
349                                        wrapper_block);
350 
351     mm->tabs(m->tabs());
352 
353     Bubble* bubble = SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
354     return bubble;
355   }
356 
bubble(CssMediaRule * m)357   Statement* Cssize::bubble(CssMediaRule* m)
358   {
359     StyleRuleObj parent = Cast<StyleRule>(SASS_MEMORY_COPY(this->parent()));
360 
361     Block* bb = SASS_MEMORY_NEW(Block, parent->block()->pstate());
362     StyleRule* new_rule = SASS_MEMORY_NEW(StyleRule,
363       parent->pstate(),
364       parent->selector(),
365       bb);
366     new_rule->tabs(parent->tabs());
367     new_rule->block()->concat(m->block());
368 
369     Block* wrapper_block = SASS_MEMORY_NEW(Block, m->block()->pstate());
370     wrapper_block->append(new_rule);
371     CssMediaRuleObj mm = SASS_MEMORY_NEW(CssMediaRule,
372       m->pstate(),
373       wrapper_block);
374     mm->concat(m->elements());
375 
376     mm->tabs(m->tabs());
377 
378     return SASS_MEMORY_NEW(Bubble, mm->pstate(), mm);
379   }
380 
bubblable(Statement * s)381   bool Cssize::bubblable(Statement* s)
382   {
383     return Cast<StyleRule>(s) || (s && s->bubbles());
384   }
385 
flatten(const Block * b)386   Block* Cssize::flatten(const Block* b)
387   {
388     Block* result = SASS_MEMORY_NEW(Block, b->pstate(), 0, b->is_root());
389     for (size_t i = 0, L = b->length(); i < L; ++i) {
390       Statement* ss = b->at(i);
391       if (const Block* bb = Cast<Block>(ss)) {
392         Block_Obj bs = flatten(bb);
393         for (size_t j = 0, K = bs->length(); j < K; ++j) {
394           result->append(bs->at(j));
395         }
396       }
397       else {
398         result->append(ss);
399       }
400     }
401     return result;
402   }
403 
slice_by_bubble(Block * b)404   sass::vector<std::pair<bool, Block_Obj>> Cssize::slice_by_bubble(Block* b)
405   {
406     sass::vector<std::pair<bool, Block_Obj>> results;
407 
408     for (size_t i = 0, L = b->length(); i < L; ++i) {
409       Statement_Obj value = b->at(i);
410       bool key = Cast<Bubble>(value) != NULL;
411 
412       if (!results.empty() && results.back().first == key)
413       {
414         Block_Obj wrapper_block = results.back().second;
415         wrapper_block->append(value);
416       }
417       else
418       {
419         Block* wrapper_block = SASS_MEMORY_NEW(Block, value->pstate());
420         wrapper_block->append(value);
421         results.push_back(std::make_pair(key, wrapper_block));
422       }
423     }
424     return results;
425   }
426 
debubble(Block * children,Statement * parent)427   Block* Cssize::debubble(Block* children, Statement* parent)
428   {
429     ParentStatementObj previous_parent;
430     sass::vector<std::pair<bool, Block_Obj>> baz = slice_by_bubble(children);
431     Block_Obj result = SASS_MEMORY_NEW(Block, children->pstate());
432 
433     for (size_t i = 0, L = baz.size(); i < L; ++i) {
434       bool is_bubble = baz[i].first;
435       Block_Obj slice = baz[i].second;
436 
437       if (!is_bubble) {
438         if (!parent) {
439           result->append(slice);
440         }
441         else if (previous_parent) {
442           previous_parent->block()->concat(slice);
443         }
444         else {
445           previous_parent = SASS_MEMORY_COPY(parent);
446           previous_parent->block(slice);
447           previous_parent->tabs(parent->tabs());
448 
449           result->append(previous_parent);
450         }
451         continue;
452       }
453 
454       for (size_t j = 0, K = slice->length(); j < K; ++j)
455       {
456         Statement_Obj ss;
457         Statement_Obj stm = slice->at(j);
458         // this has to go now here (too bad)
459         Bubble_Obj node = Cast<Bubble>(stm);
460 
461         CssMediaRule* rule1 = NULL;
462         CssMediaRule* rule2 = NULL;
463         if (parent) rule1 = Cast<CssMediaRule>(parent);
464         if (node) rule2 = Cast<CssMediaRule>(node->node());
465         if (rule1 || rule2) {
466           ss = node->node();
467         }
468 
469         ss = node->node();
470 
471         if (!ss) {
472           continue;
473         }
474 
475         ss->tabs(ss->tabs() + node->tabs());
476         ss->group_end(node->group_end());
477 
478         Block_Obj bb = SASS_MEMORY_NEW(Block,
479                                     children->pstate(),
480                                     children->length(),
481                                     children->is_root());
482         auto evaled = ss->perform(this);
483         if (evaled) bb->append(evaled);
484 
485         Block_Obj wrapper_block = SASS_MEMORY_NEW(Block,
486                                               children->pstate(),
487                                               children->length(),
488                                               children->is_root());
489 
490         Block* wrapper = flatten(bb);
491         wrapper_block->append(wrapper);
492 
493         if (wrapper->length()) {
494           previous_parent = {};
495         }
496 
497         if (wrapper_block) {
498           result->append(wrapper_block);
499         }
500       }
501     }
502 
503     return flatten(result);
504   }
505 
append_block(Block * b,Block * cur)506   void Cssize::append_block(Block* b, Block* cur)
507   {
508     for (size_t i = 0, L = b->length(); i < L; ++i) {
509       Statement_Obj ith = b->at(i)->perform(this);
510       if (Block_Obj bb = Cast<Block>(ith)) {
511         for (size_t j = 0, K = bb->length(); j < K; ++j) {
512           cur->append(bb->at(j));
513         }
514       }
515       else if (ith) {
516         cur->append(ith);
517       }
518     }
519   }
520 
521 }
522