sassc 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc1ddb620763b2dad7308dd18a9c91c82192162d
4
- data.tar.gz: c533165c997f081c2736e299e5de9da5bfe59d3b
3
+ metadata.gz: b426c4e47a4d6d041b25565605d6fe708847faaa
4
+ data.tar.gz: 36fd1baaa0ba7aaeb6b9100ee60aaeea8fa3d974
5
5
  SHA512:
6
- metadata.gz: addcd9ccee78f7b39e7c8931fbae374c19dee6601c14553255f3cc665c51985614c0b6a8abc223d3391b61f82170b296e64205693016fc71dd73c951f94b1a6b
7
- data.tar.gz: 29f40e46fd1713286196044415f168b1e3b7c07169f54f9c564ef528d5d70357325bcdf27164018b94a88a974116e7dfe0be514b5099826b43070e83eb1a57e4
6
+ metadata.gz: 54261aec807408c986d337a75e9051062ea0d5cb45affc510285a310cc5b0d95b6d1b810afe24f9e9d0f4762a6f2bbf3615ebbabc51a9bac5d26ac08a41fe0c4
7
+ data.tar.gz: 8264a970a2f76968e9eab211983c0f2c09c5f5a2ed634891146f239c25a5db7d8aa0bfa2a970e4136b1568dff97613e51e20578b112436cdffd10fa3e5db54c3
data/README.md CHANGED
@@ -6,7 +6,7 @@ This gem combines the speed of `libsass`, the [Sass C implementation](https://gi
6
6
 
7
7
  ### libsass Version
8
8
 
9
- [3.2.2](https://github.com/sass/libsass/releases/tag/3.2.2)
9
+ [3.2.5](https://github.com/sass/libsass/releases/tag/3.2.5)
10
10
 
11
11
  ## Usage
12
12
 
@@ -23,14 +23,13 @@ Additionally, you can use `SassC::Sass2Scss` to convert Sass syntax to Scss synt
23
23
  ### Project Setup
24
24
 
25
25
  1. Clone repo
26
- 2. Install dependencies - `bundle install`
27
- 3. Initialize the libsass submodule - `bundle exec rake submodule`
28
- 4. Run the tests - `bundle exec rake test`
26
+ 1. Install dependencies - `bundle install`
27
+ 1. Run the tests - `bundle exec rake test`
29
28
 
30
29
  ### Code Changes
31
30
 
32
31
  1. Fork it ( https://github.com/[my-github-username]/sassc/fork )
33
- 2. Create your feature branch (`git checkout -b my-new-feature`)
34
- 3. Commit your changes (`git commit -am 'Add some feature'`) - try to include tests
35
- 4. Push to the branch (`git push origin my-new-feature`)
36
- 5. Create a new Pull Request
32
+ 1. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 1. Commit your changes (`git commit -am 'Add some feature'`) - try to include tests
34
+ 1. Push to the branch (`git push origin my-new-feature`)
35
+ 1. Create a new Pull Request
data/Rakefile CHANGED
@@ -4,22 +4,12 @@ rescue LoadError
4
4
  puts 'Cannot load bundler/gem_tasks'
5
5
  end
6
6
 
7
- task default: :prepare
7
+ require 'tasks/libsass'
8
8
 
9
- task prepare: "ext/lib/libsass.so"
9
+ task default: :test
10
10
 
11
- file "ext/lib/libsass.so" do
12
- gem_dir = File.expand_path(File.dirname(__FILE__)) + "/"
13
- cd "ext/libsass"
14
- sh 'make lib/libsass.so LDFLAGS="-Wall -O2"'
15
- cd gem_dir
16
- end
17
-
18
- task test: :prepare do
11
+ desc "Run all tests"
12
+ task test: 'libsass:compile' do
19
13
  $LOAD_PATH.unshift('lib', 'test')
20
14
  Dir.glob('./test/**/*_test.rb') { |f| require f }
21
15
  end
22
-
23
- task :submodule do
24
- sh "git submodule update --init"
25
- end
@@ -0,0 +1,3 @@
1
+ require_relative '../lib/tasks/libsass'
2
+
3
+ task default: 'libsass:compile'
@@ -0,0 +1,10 @@
1
+ Serious about security
2
+ ======================
3
+
4
+ The LibSass team recognizes the important contributions the security research
5
+ community can make. We therefore encourage reporting security issues with the
6
+ code contained in this repository.
7
+
8
+ If you believe you have discovered a security vulnerability, please report it at
9
+ https://hackerone.com/libsass instead of GitHub.
10
+
@@ -423,8 +423,10 @@ namespace Sass {
423
423
  else
424
424
  {
425
425
  List* mq = merge_media_queries(static_cast<Media_Block*>(b->node()), static_cast<Media_Block*>(parent));
426
- static_cast<Media_Block*>(b->node())->media_queries(mq);
427
- ss = b->node();
426
+ if (mq->length()) {
427
+ static_cast<Media_Block*>(b->node())->media_queries(mq);
428
+ ss = b->node();
429
+ }
428
430
  }
429
431
 
430
432
  if (!ss) continue;
@@ -203,8 +203,8 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
203
203
  cerr << " (" << pstate_source_position(node) << ")";
204
204
  cerr << (block->is_interpolated() ? " [is_interpolated]": " -")
205
205
  << endl;
206
- debug_ast(block->feature(), ind + " f) ");
207
- debug_ast(block->value(), ind + " v) ");
206
+ debug_ast(block->feature(), ind + " feature) ");
207
+ debug_ast(block->value(), ind + " value) ");
208
208
 
209
209
  } else if (dynamic_cast<Media_Query*>(node)) {
210
210
  Media_Query* block = dynamic_cast<Media_Query*>(node);
@@ -452,10 +452,10 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
452
452
  cerr << ind << "Map " << expression;
453
453
  cerr << " (" << pstate_source_position(node) << ")";
454
454
  cerr << " [Hashed]" << endl;
455
- // for (auto i : expression->elements()) {
456
- // debug_ast(i.first, ind + " key: ");
457
- // debug_ast(i.second, ind + " val: ");
458
- // }
455
+ for (auto i : expression->elements()) {
456
+ debug_ast(i.first, ind + " key: ");
457
+ debug_ast(i.second, ind + " val: ");
458
+ }
459
459
  } else if (dynamic_cast<List*>(node)) {
460
460
  List* expression = dynamic_cast<List*>(node);
461
461
  cerr << ind << "List " << expression;
@@ -499,7 +499,9 @@ inline void debug_ast(AST_Node* node, string ind = "", Env* env = 0)
499
499
  } else if (dynamic_cast<String_Constant*>(node)) {
500
500
  String_Constant* expression = dynamic_cast<String_Constant*>(node);
501
501
  cerr << ind << "String_Constant " << expression;
502
- cerr << " " << expression->concrete_type() <<
502
+ if (expression->concrete_type()) {
503
+ cerr << " " << expression->concrete_type();
504
+ }
503
505
  cerr << " (" << pstate_source_position(node) << ")";
504
506
  cerr << " [" << prettyprint(expression->value()) << "]";
505
507
  if (expression->is_delayed()) cerr << " [delayed]";
@@ -393,16 +393,24 @@ namespace Sass {
393
393
  Expression* Eval::operator()(Map* m)
394
394
  {
395
395
  if (m->is_expanded()) return m;
396
+
397
+ // make sure we're not starting with duplicate keys.
398
+ // the duplicate key state will have been set in the parser phase.
399
+ if (m->has_duplicate_key()) {
400
+ To_String to_string(&ctx);
401
+ error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());
402
+ }
403
+
396
404
  Map* mm = new (ctx.mem) Map(m->pstate(),
397
405
  m->length());
398
406
  for (auto key : m->keys()) {
399
- *mm << std::make_pair(key->perform(this), m->at(key)->perform(this));
407
+ *mm << std::make_pair(key->perform(this), m->at(key)->perform(this));;
400
408
  }
401
409
 
402
- // check for duplicate keys
410
+ // check the evaluated keys aren't duplicates.
403
411
  if (mm->has_duplicate_key()) {
404
412
  To_String to_string(&ctx);
405
- error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());
413
+ error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + mm->perform(&to_string) + ".", mm->pstate());
406
414
  }
407
415
 
408
416
  mm->is_expanded(true);
@@ -790,6 +798,10 @@ namespace Sass {
790
798
  zero);
791
799
  break;
792
800
  case Textual::HEX: {
801
+ if (t->value().substr(0, 1) != "#") {
802
+ result = new (ctx.mem) String_Constant(t->pstate(), t->value());
803
+ break;
804
+ }
793
805
  string hext(t->value().substr(1)); // chop off the '#'
794
806
  if (hext.length() == 6) {
795
807
  string r(hext.substr(0,2));
@@ -923,6 +935,7 @@ namespace Sass {
923
935
  } else if (str->quote_mark()) {
924
936
  str->quote_mark('*');
925
937
  }
938
+ str->is_delayed(true);
926
939
  return str;
927
940
  }
928
941
 
@@ -997,8 +1010,16 @@ namespace Sass {
997
1010
  {
998
1011
  Expression* feature = e->feature();
999
1012
  feature = (feature ? feature->perform(this) : 0);
1013
+ if (feature && dynamic_cast<String_Quoted*>(feature)) {
1014
+ feature = new (ctx.mem) String_Constant(feature->pstate(),
1015
+ dynamic_cast<String_Quoted*>(feature)->value());
1016
+ }
1000
1017
  Expression* value = e->value();
1001
1018
  value = (value ? value->perform(this) : 0);
1019
+ if (value && dynamic_cast<String_Quoted*>(value)) {
1020
+ value = new (ctx.mem) String_Constant(value->pstate(),
1021
+ dynamic_cast<String_Quoted*>(value)->value());
1022
+ }
1002
1023
  return new (ctx.mem) Media_Query_Expression(e->pstate(),
1003
1024
  feature,
1004
1025
  value,
@@ -1102,8 +1123,10 @@ namespace Sass {
1102
1123
  } break;
1103
1124
 
1104
1125
  case Expression::STRING: {
1105
- return unquote(static_cast<String_Constant*>(lhs)->value()) ==
1106
- unquote(static_cast<String_Constant*>(rhs)->value());
1126
+ string slhs = static_cast<String_Quoted*>(lhs)->value();
1127
+ string srhs = static_cast<String_Quoted*>(rhs)->value();
1128
+ return unquote(slhs) == unquote(srhs) &&
1129
+ (!(is_quoted(slhs) || is_quoted(srhs)) || slhs[0] == srhs[0]);
1107
1130
  } break;
1108
1131
 
1109
1132
  case Expression::LIST: {
@@ -221,9 +221,9 @@ namespace Sass {
221
221
  double w2 = 1 - w1;
222
222
 
223
223
  return new (ctx.mem) Color(pstate,
224
- std::floor(w1*color1->r() + w2*color2->r()),
225
- std::floor(w1*color1->g() + w2*color2->g()),
226
- std::floor(w1*color1->b() + w2*color2->b()),
224
+ std::round(w1*color1->r() + w2*color2->r()),
225
+ std::round(w1*color1->g() + w2*color2->g()),
226
+ std::round(w1*color1->b() + w2*color2->b()),
227
227
  color1->a()*p + color2->a()*(1-p));
228
228
  }
229
229
 
@@ -1051,8 +1051,8 @@ namespace Sass {
1051
1051
  Number* least = 0;
1052
1052
  for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1053
1053
  Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));
1054
+ if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
1054
1055
  if (least) {
1055
- if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
1056
1056
  if (lt(xi, least, ctx)) least = xi;
1057
1057
  } else least = xi;
1058
1058
  }
@@ -1066,8 +1066,8 @@ namespace Sass {
1066
1066
  Number* greatest = 0;
1067
1067
  for (size_t i = 0, L = arglist->length(); i < L; ++i) {
1068
1068
  Number* xi = dynamic_cast<Number*>(arglist->value_at_index(i));
1069
+ if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
1069
1070
  if (greatest) {
1070
- if (!xi) error("`" + string(sig) + "` only takes numeric arguments", pstate);
1071
1071
  if (lt(greatest, xi, ctx)) greatest = xi;
1072
1072
  } else greatest = xi;
1073
1073
  }
@@ -582,6 +582,8 @@ namespace Sass {
582
582
  hexlet << hex << setw(2) << static_cast<unsigned long>(b);
583
583
  }
584
584
 
585
+ if (want_short && !c->is_delayed()) name = "";
586
+
585
587
  // retain the originally specified color definition if unchanged
586
588
  if (name != "") {
587
589
  ss << name;
@@ -85,6 +85,8 @@ namespace Sass {
85
85
  }
86
86
  }
87
87
 
88
+ bool semicolon = false;
89
+ string(error_message);
88
90
  lex< optional_spaces >();
89
91
  Selector_Lookahead lookahead_result;
90
92
  while (position < end) {
@@ -97,14 +99,16 @@ namespace Sass {
97
99
  (*root) << new (ctx.mem) Import_Stub(pstate, imp->files()[i]);
98
100
  }
99
101
  }
100
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @import directive must be terminated by ';'", pstate);
102
+ semicolon = true;
103
+ error_message = "top-level @import directive must be terminated by ';'";
101
104
  }
102
105
  else if (peek< kwd_mixin >() || peek< kwd_function >()) {
103
106
  (*root) << parse_definition();
104
107
  }
105
108
  else if (peek< variable >()) {
106
109
  (*root) << parse_assignment();
107
- if (!lex< one_plus< exactly<';'> > >()) error("top-level variable binding must be terminated by ';'", pstate);
110
+ semicolon = true;
111
+ error_message = "top-level variable binding must be terminated by ';'";
108
112
  }
109
113
  /*else if (peek< sequence< optional< exactly<'*'> >, alternatives< identifier_schema, identifier >, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
110
114
  (*root) << parse_propset();
@@ -112,7 +116,10 @@ namespace Sass {
112
116
  else if (peek< kwd_include >() /* || peek< exactly<'+'> >() */) {
113
117
  Mixin_Call* mixin_call = parse_mixin_call();
114
118
  (*root) << mixin_call;
115
- if (!mixin_call->block() && !lex< one_plus< exactly<';'> > >()) error("top-level @include directive must be terminated by ';'", pstate);
119
+ if (!mixin_call->block()) {
120
+ semicolon = true;
121
+ error_message = "top-level @include directive must be terminated by ';'";
122
+ }
116
123
  }
117
124
  else if (peek< kwd_if_directive >()) {
118
125
  (*root) << parse_if_directive();
@@ -137,15 +144,18 @@ namespace Sass {
137
144
  }
138
145
  else if (peek< kwd_warn >()) {
139
146
  (*root) << parse_warning();
140
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @warn directive must be terminated by ';'", pstate);
147
+ semicolon = true;
148
+ error_message = "top-level @warn directive must be terminated by ';'";
141
149
  }
142
150
  else if (peek< kwd_err >()) {
143
151
  (*root) << parse_error();
144
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @error directive must be terminated by ';'", pstate);
152
+ semicolon = true;
153
+ error_message = "top-level @error directive must be terminated by ';'";
145
154
  }
146
155
  else if (peek< kwd_dbg >()) {
147
156
  (*root) << parse_debug();
148
- if (!lex< one_plus< exactly<';'> > >()) error("top-level @debug directive must be terminated by ';'", pstate);
157
+ semicolon = true;
158
+ error_message = "top-level @debug directive must be terminated by ';'";
149
159
  }
150
160
  // ignore the @charset directive for now
151
161
  else if (lex< exactly< charset_kwd > >()) {
@@ -155,7 +165,10 @@ namespace Sass {
155
165
  else if (peek< at_keyword >()) {
156
166
  At_Rule* at_rule = parse_at_rule();
157
167
  (*root) << at_rule;
158
- if (!at_rule->block() && !lex< one_plus< exactly<';'> > >()) error("top-level directive must be terminated by ';'", pstate);
168
+ if (!at_rule->block()){
169
+ semicolon = true;
170
+ error_message = "top-level directive must be terminated by ';'";
171
+ }
159
172
  }
160
173
  else if ((lookahead_result = lookahead_for_selector(position)).found) {
161
174
  (*root) << parse_ruleset(lookahead_result);
@@ -168,6 +181,11 @@ namespace Sass {
168
181
  if (position >= end) break;
169
182
  error("invalid top-level expression", after_token);
170
183
  }
184
+ if (semicolon) {
185
+ if (!lex< one_plus< exactly<';'> > >() && peek_css< optional_css_whitespace >() != end)
186
+ { error(error_message, pstate); }
187
+ semicolon = false;
188
+ }
171
189
  lex< optional_spaces >();
172
190
  }
173
191
  block_stack.pop_back();
@@ -636,7 +654,7 @@ namespace Sass {
636
654
  seq->media_block(last_media_block);
637
655
  seq->last_block(block_stack.back());
638
656
  bool sawsomething = false;
639
- if (lex< exactly<'&'> >()) {
657
+ if (lex_css< exactly<'&'> >()) {
640
658
  // check if we have a parent selector on the root level block
641
659
  if (block_stack.back() && block_stack.back()->is_root()) {
642
660
  //error("Base-level rules cannot contain the parent-selector-referencing character '&'.", pstate);
@@ -985,6 +1003,7 @@ namespace Sass {
985
1003
  }
986
1004
  else if (lex< sequence< optional< exactly<'*'> >, identifier > >()) {
987
1005
  prop = new (ctx.mem) String_Quoted(pstate, lexed);
1006
+ prop->is_delayed(true);
988
1007
  }
989
1008
  else {
990
1009
  error("invalid property name", pstate);
@@ -1086,12 +1105,6 @@ namespace Sass {
1086
1105
  (*map) << make_pair(key, value);
1087
1106
  }
1088
1107
 
1089
- // Check was moved to eval step
1090
- // if (map->has_duplicate_key()) {
1091
- // To_String to_string(&ctx);
1092
- // error("Duplicate key \"" + map->get_duplicate_key()->perform(&to_string) + "\" in map " + map->perform(&to_string) + ".", pstate);
1093
- // }
1094
-
1095
1108
  ParserState ps = map->pstate();
1096
1109
  ps.offset = pstate - ps + pstate.offset;
1097
1110
  map->pstate(ps);
@@ -1174,7 +1187,7 @@ namespace Sass {
1174
1187
  exactly<ellipsis>,
1175
1188
  default_flag,
1176
1189
  global_flag
1177
- > >(position))
1190
+ > >(position)) && peek_css< optional_css_whitespace >() != end
1178
1191
  ) {
1179
1192
  (*space_list) << parse_disjunction();
1180
1193
  }
@@ -1453,6 +1466,14 @@ namespace Sass {
1453
1466
  return schema;
1454
1467
  }
1455
1468
 
1469
+ String_Constant* Parser::parse_static_expression()
1470
+ {
1471
+ if (peek< sequence< number, optional_spaces, exactly<'/'>, optional_spaces, number > >()) {
1472
+ return parse_static_value();
1473
+ }
1474
+ return 0;
1475
+ }
1476
+
1456
1477
  String_Constant* Parser::parse_static_value()
1457
1478
  {
1458
1479
  lex< static_value >();
@@ -1461,7 +1482,7 @@ namespace Sass {
1461
1482
  --position;
1462
1483
 
1463
1484
  String_Constant* str_node = new (ctx.mem) String_Constant(pstate, str.time_wspace());
1464
- // str_node->is_delayed(true);
1485
+ str_node->is_delayed(true);
1465
1486
  return str_node;
1466
1487
  }
1467
1488
 
@@ -1555,8 +1576,12 @@ namespace Sass {
1555
1576
  }
1556
1577
  else if (lex< interpolant >()) {
1557
1578
  Token insides(Token(lexed.begin + 2, lexed.end - 1));
1558
- Expression* interp_node = Parser::from_token(insides, ctx, pstate).parse_list();
1559
- interp_node->is_interpolant(true);
1579
+ Expression* interp_node;
1580
+ Parser p = Parser::from_token(insides, ctx, pstate);
1581
+ if (!(interp_node = p.parse_static_expression())) {
1582
+ interp_node = p.parse_list();
1583
+ interp_node->is_interpolant(true);
1584
+ }
1560
1585
  (*schema) << interp_node;
1561
1586
  }
1562
1587
  else if (lex< exactly<'%'> >()) {
@@ -2135,7 +2160,6 @@ namespace Sass {
2135
2160
  (q = peek< sequence< pseudo_prefix, identifier > >(p)) ||
2136
2161
  (q = peek< percentage >(p)) ||
2137
2162
  (q = peek< dimension >(p)) ||
2138
- (q = peek< variable >(p)) ||
2139
2163
  (q = peek< quoted_string >(p)) ||
2140
2164
  (q = peek< exactly<'*'> >(p)) ||
2141
2165
  (q = peek< exactly<'('> >(p)) ||
@@ -241,6 +241,7 @@ namespace Sass {
241
241
  Function_Call_Schema* parse_function_call_schema();
242
242
  String* parse_interpolated_chunk(Token, bool constant = false);
243
243
  String* parse_string();
244
+ String_Constant* parse_static_expression();
244
245
  String_Constant* parse_static_value();
245
246
  String* parse_ie_property();
246
247
  String* parse_ie_keyword_arg();
@@ -88,6 +88,9 @@ extern "C" {
88
88
  return (Sass_Importer_List) calloc(length + 1, sizeof(Sass_Importer_Entry));
89
89
  }
90
90
 
91
+ Sass_Importer_Entry ADDCALL sass_importer_get_list_entry(Sass_Importer_List list, size_t idx) { return list[idx]; }
92
+ void ADDCALL sass_importer_set_list_entry(Sass_Importer_List list, size_t idx, Sass_Importer_Entry cb) { list[idx] = cb; }
93
+
91
94
  // Creator for sass custom importer return argument list
92
95
  Sass_Import_List ADDCALL sass_make_import_list(size_t length)
93
96
  {
@@ -10,25 +10,45 @@ module SassC
10
10
 
11
11
  list = Native.make_function_list(Script.custom_functions.count)
12
12
 
13
- functs = FunctionWrapper.extend(Script::Functions)
14
- functs.options = @options
13
+ functions = FunctionWrapper.extend(Script::Functions)
14
+ functions.options = @options
15
15
 
16
16
  Script.custom_functions.each_with_index do |custom_function, i|
17
- @callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |s_args, cookie|
18
- length = Native.list_get_length(s_args)
17
+ @callbacks[custom_function] = FFI::Function.new(:pointer, [:pointer, :pointer]) do |native_argument_list, cookie|
18
+ native_argument_list_length = Native.list_get_length(native_argument_list)
19
+ custom_function_arguments = []
20
+ error_tag = nil
19
21
 
20
- v = Native.list_get_value(s_args, 0)
21
- v = Native.string_get_value(v).dup
22
+ (0...native_argument_list_length).each do |i|
23
+ native_value = Native.list_get_value(native_argument_list, i)
22
24
 
23
- s = Script::String.new(Script::String.unquote(v), Script::String.type(v))
25
+ case value_tag = Native.value_get_tag(native_value)
26
+ when :sass_null
27
+ # no-op
28
+ when :sass_string
29
+ native_string = Native.string_get_value(native_value)
30
+ argument = Script::String.new(Script::String.unquote(native_string), Script::String.type(native_string))
24
31
 
25
- value = functs.send(custom_function, s)
32
+ custom_function_arguments << argument
33
+ else
34
+ error_tag = error("Sass argument of type #{value_tag} unsupported")
35
+ break
36
+ end
37
+ end
38
+
39
+ next error_tag if error_tag
26
40
 
27
- if value
28
- value = Script::String.new(Script::String.unquote(value.to_s), value.type)
29
- value.to_native
30
- else
31
- Script::String.new("").to_native
41
+ begin
42
+ value = functions.send(custom_function, *custom_function_arguments)
43
+
44
+ if value
45
+ value = Script::String.new(Script::String.unquote(value.to_s), value.type)
46
+ value.to_native
47
+ else
48
+ Script::String.new("").to_native
49
+ end
50
+ rescue StandardError => exception
51
+ error(exception.message)
32
52
  end
33
53
  end
34
54
 
@@ -48,6 +68,21 @@ module SassC
48
68
 
49
69
  private
50
70
 
71
+ def error(message)
72
+ value = Native::SassValue.new
73
+ value[:unknown] = Native::SassUnknown.new
74
+
75
+ error = Native::SassError.new
76
+ error[:tag] = :sass_error
77
+
78
+ Native.error_set_message(error, Native.native_string(message))
79
+ $stderr.puts "[SassC::FunctionsHandler] #{message}"
80
+
81
+ value[:unknown][:tag] = :sass_error
82
+ value[:error] = error
83
+ value.pointer
84
+ end
85
+
51
86
  class FunctionWrapper
52
87
  class << self
53
88
  attr_accessor :options
@@ -48,7 +48,8 @@ module SassC
48
48
  end
49
49
 
50
50
  def self.native_string(string)
51
- string += "\0"
51
+ string = string.to_s
52
+ string << "\0"
52
53
  data = Native::LibC.malloc(string.bytesize)
53
54
  data.write_string(string)
54
55
  data
@@ -19,6 +19,7 @@ module SassC
19
19
 
20
20
  # ADDAPI enum Sass_Tag ADDCALL sass_value_get_tag (const union Sass_Value* v);
21
21
  attach_function :sass_value_get_tag, [:sass_value_ptr], SassTag
22
+ attach_function :sass_value_is_null, [:sass_value_ptr], :bool
22
23
 
23
24
  # ADDAPI const char* ADDCALL sass_string_get_value (const union Sass_Value* v);
24
25
  attach_function :sass_string_get_value, [:sass_value_ptr], :string
@@ -28,6 +29,11 @@ module SassC
28
29
  attach_function :sass_list_get_length, [:sass_value_ptr], :size_t
29
30
  attach_function :sass_list_get_value, [:sass_value_ptr, :size_t], :sass_value_ptr
30
31
 
32
+ # ADDAPI char* ADDCALL sass_error_get_message (const union Sass_Value* v);
33
+ # ADDAPI void ADDCALL sass_error_set_message (union Sass_Value* v, char* msg);
34
+ attach_function :sass_error_get_message, [:sass_value_ptr], :string
35
+ attach_function :sass_error_set_message, [:sass_value_ptr, :pointer], :void
36
+
31
37
  # Getters for custom function descriptors
32
38
  # ADDAPI const char* ADDCALL sass_function_get_signature (Sass_C_Function_Callback fn);
33
39
  # ADDAPI Sass_C_Function ADDCALL sass_function_get_function (Sass_C_Function_Callback fn);
@@ -71,12 +71,12 @@ module SassC
71
71
 
72
72
  class SassError < FFI::Struct
73
73
  layout :tag, SassTag,
74
- :messsage, :string
74
+ :message, :string
75
75
  end
76
76
 
77
77
  class SassWarning < FFI::Struct
78
78
  layout :tag, SassTag,
79
- :messsage, :string
79
+ :message, :string
80
80
  end
81
81
 
82
82
  class SassValue # < FFI::Union
@@ -8,10 +8,9 @@ module SassC
8
8
 
9
9
  def self.formatted_function_name(function_name)
10
10
  params = Functions.instance_method(function_name).parameters
11
- params = params.select { |param| param[0] == :req }
12
- .map(&:first)
13
- .map { |p| "$#{p}" }
11
+ params = params.map { |param_type, name| "$#{name}#{': null' if param_type == :opt}" }
14
12
  .join(", ")
13
+
15
14
  "#{function_name}(#{params})"
16
15
  end
17
16
  end
@@ -66,12 +66,12 @@ module SassC
66
66
  def self.unquote(contents)
67
67
  s = contents.dup
68
68
 
69
- case contents[0,1]
69
+ case contents[0, 1]
70
70
  when "'", '"', '`'
71
71
  s[0] = ''
72
72
  end
73
73
 
74
- case contents[-1,1]
74
+ case contents[-1, 1]
75
75
  when "'", '"', '`'
76
76
  s[-1] = ''
77
77
  end
@@ -1,3 +1,3 @@
1
1
  module SassC
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -0,0 +1,21 @@
1
+ namespace :libsass do
2
+ desc "Compile libsass"
3
+ task compile: "ext/libsass/lib/libsass.so"
4
+
5
+ file "ext/libsass/.git" do
6
+ sh "git submodule update --init"
7
+ end
8
+
9
+ file "ext/libsass/lib/libsass.so" => "ext/libsass/.git" do
10
+ libsass_path = ""
11
+ if Dir.pwd.end_with?('/ext')
12
+ libsass_path = "libsass"
13
+ else
14
+ libsass_path = "ext/libsass"
15
+ end
16
+
17
+ cd libsass_path do
18
+ sh 'make lib/libsass.so LDFLAGS="-Wall -O2"'
19
+ end
20
+ end
21
+ end
@@ -16,9 +16,10 @@ Gem::Specification.new do |spec|
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib", "ext"]
20
19
 
21
- spec.extensions = ["Rakefile"]
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.extensions = ["ext/Rakefile"]
22
23
 
23
24
  spec.add_development_dependency "rake"
24
25
  spec.add_development_dependency "minitest", "~> 5.5.1"
@@ -1,34 +1,16 @@
1
1
  require_relative "test_helper"
2
+ require "stringio"
2
3
 
3
4
  module SassC
4
5
  class FunctionsTest < MiniTest::Test
5
6
  include FixtureHelper
6
7
 
7
- SassString = Struct.new(:value, :type) do
8
- def to_s
9
- value
10
- end
8
+ def setup
9
+ @real_stderr, $stderr = $stderr, StringIO.new
11
10
  end
12
11
 
13
- module Script::Functions
14
- def javascript_path(path)
15
- Script::String.new("/js/#{path.value}", :string)
16
- end
17
-
18
- def no_return_path(path)
19
- nil
20
- end
21
-
22
- def sass_return_path(path)
23
- return SassString.new("'#{path.value}'", :string)
24
- end
25
-
26
- module Compass
27
- def stylesheet_path(path)
28
- Script::String.new("/css/#{path.value}", :identifier)
29
- end
30
- end
31
- include Compass
12
+ def teardown
13
+ $stderr = @real_stderr
32
14
  end
33
15
 
34
16
  def test_functions_may_return_sass_string_type
@@ -70,5 +52,89 @@ div {
70
52
  url: url(); }
71
53
  EOS
72
54
  end
55
+
56
+ def test_function_with_optional_arguments
57
+ engine = Engine.new("div {url: optional_arguments('first'); url: optional_arguments('second', 'qux')}")
58
+ assert_equal <<-EOS, engine.render
59
+ div {
60
+ url: "first/bar";
61
+ url: "second/qux"; }
62
+ EOS
63
+ end
64
+
65
+ def test_function_with_unsupported_tag
66
+ engine = Engine.new("div {url: function_with_unsupported_tag(red);}")
67
+
68
+ exception = assert_raises(SassC::SyntaxError) do
69
+ engine.render
70
+ end
71
+
72
+ assert_equal "Error: error in C function function_with_unsupported_tag: Sass argument of type sass_color unsupported\n\n Backtrace:\n \tstdin:1, in function `function_with_unsupported_tag`\n \tstdin:1\n on line 1 of stdin\n>> div {url: function_with_unsupported_tag(red);}\n ----------^\n", exception.message
73
+
74
+ assert_equal "[SassC::FunctionsHandler] Sass argument of type sass_color unsupported", stderr_output
75
+ end
76
+
77
+ def test_function_with_error
78
+ engine = Engine.new("div {url: function_that_raises_errors();}")
79
+ exception = assert_raises(SassC::SyntaxError) do
80
+ engine.render
81
+ end
82
+
83
+ assert_equal "Error: error in C function function_that_raises_errors: Intentional wrong thing happened somewhere inside the custom function
84
+
85
+ Backtrace:
86
+ \tstdin:1, in function `function_that_raises_errors`
87
+ \tstdin:1
88
+ on line 1 of stdin
89
+ >> div {url: function_that_raises_errors();}
90
+ ----------^
91
+ ", exception.message
92
+
93
+ assert_equal "[SassC::FunctionsHandler] Intentional wrong thing happened somewhere inside the custom function", stderr_output
94
+ end
95
+
96
+ private
97
+
98
+ SassString = Struct.new(:value, :type) do
99
+ def to_s
100
+ value
101
+ end
102
+ end
103
+
104
+ module Script::Functions
105
+ def javascript_path(path)
106
+ Script::String.new("/js/#{path.value}", :string)
107
+ end
108
+
109
+ def no_return_path(path)
110
+ nil
111
+ end
112
+
113
+ def sass_return_path(path)
114
+ return SassString.new("'#{path.value}'", :string)
115
+ end
116
+
117
+ def optional_arguments(path, optional = "bar")
118
+ return SassString.new("#{path}/#{optional}", :string)
119
+ end
120
+
121
+ def function_that_raises_errors()
122
+ raise StandardError, "Intentional wrong thing happened somewhere inside the custom function"
123
+ end
124
+
125
+ def function_with_unsupported_tag(color)
126
+ end
127
+
128
+ module Compass
129
+ def stylesheet_path(path)
130
+ Script::String.new("/css/#{path.value}", :identifier)
131
+ end
132
+ end
133
+ include Compass
134
+ end
135
+
136
+ def stderr_output
137
+ $stderr.string.gsub("\u0000\n", '')
138
+ end
73
139
  end
74
140
  end
@@ -9,7 +9,7 @@ module SassC
9
9
 
10
10
  class General < MiniTest::Test
11
11
  def test_it_reports_the_libsass_version
12
- assert_equal "3.2.4", Native.version
12
+ assert_equal "3.2.5", Native.version
13
13
  end
14
14
  end
15
15
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sassc
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Boland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-17 00:00:00.000000000 Z
11
+ date: 2015-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -113,7 +113,7 @@ email:
113
113
  - bolandryanm@gmail.com
114
114
  executables: []
115
115
  extensions:
116
- - Rakefile
116
+ - ext/Rakefile
117
117
  extra_rdoc_files: []
118
118
  files:
119
119
  - ".gitignore"
@@ -123,6 +123,7 @@ files:
123
123
  - LICENSE.txt
124
124
  - README.md
125
125
  - Rakefile
126
+ - ext/Rakefile
126
127
  - ext/libsass/.editorconfig
127
128
  - ext/libsass/.gitattributes
128
129
  - ext/libsass/.gitignore
@@ -133,6 +134,7 @@ files:
133
134
  - ext/libsass/Makefile
134
135
  - ext/libsass/Makefile.am
135
136
  - ext/libsass/Readme.md
137
+ - ext/libsass/SECURITY.md
136
138
  - ext/libsass/appveyor.yml
137
139
  - ext/libsass/ast.cpp
138
140
  - ext/libsass/ast.hpp
@@ -288,6 +290,7 @@ files:
288
290
  - lib/sassc/script/functions.rb
289
291
  - lib/sassc/script/string.rb
290
292
  - lib/sassc/version.rb
293
+ - lib/tasks/libsass.rb
291
294
  - sassc.gemspec
292
295
  - test/custom_importer_test.rb
293
296
  - test/engine_test.rb
@@ -305,7 +308,6 @@ post_install_message:
305
308
  rdoc_options: []
306
309
  require_paths:
307
310
  - lib
308
- - ext
309
311
  required_ruby_version: !ruby/object:Gem::Requirement
310
312
  requirements:
311
313
  - - ">="