sassc 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
  - - ">="