1 // sass.hpp must go before all system headers to get the 2 // __EXTENSIONS__ fix on Solaris. 3 #include "sass.hpp" 4 5 #include <cstdint> 6 #include <cstdlib> 7 #include <cmath> 8 #include <ctime> 9 #include <random> 10 #include <sstream> 11 #include <iomanip> 12 #include <algorithm> 13 #include <thread> 14 15 #include "ast.hpp" 16 #include "units.hpp" 17 #include "fn_utils.hpp" 18 #include "fn_numbers.hpp" 19 20 #ifdef __MINGW32__ 21 #include "windows.h" 22 #include "wincrypt.h" 23 #endif 24 25 namespace Sass { 26 27 namespace Functions { 28 29 #ifdef __MINGW32__ GetSeed()30 uint64_t GetSeed() 31 { 32 HCRYPTPROV hp = 0; 33 BYTE rb[8]; 34 CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); 35 CryptGenRandom(hp, sizeof(rb), rb); 36 CryptReleaseContext(hp, 0); 37 38 uint64_t seed; 39 memcpy(&seed, &rb[0], sizeof(seed)); 40 41 return seed; 42 } 43 #else 44 uint64_t GetSeed() 45 { 46 // Init universe entropy 47 uint64_t rnd = 42; 48 // Try to get random number from system 49 try { 50 std::random_device rd; 51 rnd = rd(); 52 } 53 // On certain system this can throw since either 54 // underlying hardware or software can be buggy. 55 // https://github.com/sass/libsass/issues/3151 56 catch (std::exception&) { 57 } 58 // Don't trust anyone to be random, so we 59 // add a little entropy of our own. 60 rnd ^= std::time(NULL) ^ std::clock() ^ 61 std::hash<std::thread::id>() 62 (std::this_thread::get_id()); 63 // Return entropy 64 return rnd; 65 } 66 #endif 67 68 // note: the performance of many implementations of 69 // random_device degrades sharply once the entropy pool 70 // is exhausted. For practical use, random_device is 71 // generally only used to seed a PRNG such as mt19937. 72 static std::mt19937 rand(static_cast<unsigned int>(GetSeed())); 73 74 /////////////////// 75 // NUMBER FUNCTIONS 76 /////////////////// 77 78 Signature percentage_sig = "percentage($number)"; BUILT_IN(percentage)79 BUILT_IN(percentage) 80 { 81 Number_Obj n = ARGN("$number"); 82 if (!n->is_unitless()) error("argument $number of `" + sass::string(sig) + "` must be unitless", pstate, traces); 83 return SASS_MEMORY_NEW(Number, pstate, n->value() * 100, "%"); 84 } 85 86 Signature round_sig = "round($number)"; BUILT_IN(round)87 BUILT_IN(round) 88 { 89 Number_Obj r = ARGN("$number"); 90 r->value(Sass::round(r->value(), ctx.c_options.precision)); 91 r->pstate(pstate); 92 return r.detach(); 93 } 94 95 Signature ceil_sig = "ceil($number)"; BUILT_IN(ceil)96 BUILT_IN(ceil) 97 { 98 Number_Obj r = ARGN("$number"); 99 r->value(std::ceil(r->value())); 100 r->pstate(pstate); 101 return r.detach(); 102 } 103 104 Signature floor_sig = "floor($number)"; BUILT_IN(floor)105 BUILT_IN(floor) 106 { 107 Number_Obj r = ARGN("$number"); 108 r->value(std::floor(r->value())); 109 r->pstate(pstate); 110 return r.detach(); 111 } 112 113 Signature abs_sig = "abs($number)"; BUILT_IN(abs)114 BUILT_IN(abs) 115 { 116 Number_Obj r = ARGN("$number"); 117 r->value(std::abs(r->value())); 118 r->pstate(pstate); 119 return r.detach(); 120 } 121 122 Signature min_sig = "min($numbers...)"; BUILT_IN(min)123 BUILT_IN(min) 124 { 125 List* arglist = ARG("$numbers", List); 126 Number_Obj least; 127 size_t L = arglist->length(); 128 if (L == 0) { 129 error("At least one argument must be passed.", pstate, traces); 130 } 131 for (size_t i = 0; i < L; ++i) { 132 ExpressionObj val = arglist->value_at_index(i); 133 Number_Obj xi = Cast<Number>(val); 134 if (!xi) { 135 error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `min'", pstate, traces); 136 } 137 if (least) { 138 if (*xi < *least) least = xi; 139 } else least = xi; 140 } 141 return least.detach(); 142 } 143 144 Signature max_sig = "max($numbers...)"; BUILT_IN(max)145 BUILT_IN(max) 146 { 147 List* arglist = ARG("$numbers", List); 148 Number_Obj greatest; 149 size_t L = arglist->length(); 150 if (L == 0) { 151 error("At least one argument must be passed.", pstate, traces); 152 } 153 for (size_t i = 0; i < L; ++i) { 154 ExpressionObj val = arglist->value_at_index(i); 155 Number_Obj xi = Cast<Number>(val); 156 if (!xi) { 157 error("\"" + val->to_string(ctx.c_options) + "\" is not a number for `max'", pstate, traces); 158 } 159 if (greatest) { 160 if (*greatest < *xi) greatest = xi; 161 } else greatest = xi; 162 } 163 return greatest.detach(); 164 } 165 166 Signature random_sig = "random($limit:false)"; BUILT_IN(random)167 BUILT_IN(random) 168 { 169 AST_Node_Obj arg = env["$limit"]; 170 Value* v = Cast<Value>(arg); 171 Number* l = Cast<Number>(arg); 172 Boolean* b = Cast<Boolean>(arg); 173 if (l) { 174 double lv = l->value(); 175 if (lv < 1) { 176 sass::ostream err; 177 err << "$limit " << lv << " must be greater than or equal to 1 for `random'"; 178 error(err.str(), pstate, traces); 179 } 180 bool eq_int = std::fabs(trunc(lv) - lv) < NUMBER_EPSILON; 181 if (!eq_int) { 182 sass::ostream err; 183 err << "Expected $limit to be an integer but got " << lv << " for `random'"; 184 error(err.str(), pstate, traces); 185 } 186 std::uniform_real_distribution<> distributor(1, lv + 1); 187 uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand)); 188 return SASS_MEMORY_NEW(Number, pstate, (double)distributed); 189 } 190 else if (b) { 191 std::uniform_real_distribution<> distributor(0, 1); 192 double distributed = static_cast<double>(distributor(rand)); 193 return SASS_MEMORY_NEW(Number, pstate, distributed); 194 } else if (v) { 195 traces.push_back(Backtrace(pstate)); 196 throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number", v); 197 } else { 198 traces.push_back(Backtrace(pstate)); 199 throw Exception::InvalidArgumentType(pstate, traces, "random", "$limit", "number"); 200 } 201 } 202 203 Signature unique_id_sig = "unique-id()"; BUILT_IN(unique_id)204 BUILT_IN(unique_id) 205 { 206 sass::ostream ss; 207 std::uniform_real_distribution<> distributor(0, 4294967296); // 16^8 208 uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand)); 209 ss << "u" << std::setfill('0') << std::setw(8) << std::hex << distributed; 210 return SASS_MEMORY_NEW(String_Quoted, pstate, ss.str()); 211 } 212 213 Signature unit_sig = "unit($number)"; BUILT_IN(unit)214 BUILT_IN(unit) 215 { 216 Number_Obj arg = ARGN("$number"); 217 sass::string str(quote(arg->unit(), '"')); 218 return SASS_MEMORY_NEW(String_Quoted, pstate, str); 219 } 220 221 Signature unitless_sig = "unitless($number)"; BUILT_IN(unitless)222 BUILT_IN(unitless) 223 { 224 Number_Obj arg = ARGN("$number"); 225 bool unitless = arg->is_unitless(); 226 return SASS_MEMORY_NEW(Boolean, pstate, unitless); 227 } 228 229 Signature comparable_sig = "comparable($number1, $number2)"; BUILT_IN(comparable)230 BUILT_IN(comparable) 231 { 232 Number_Obj n1 = ARGN("$number1"); 233 Number_Obj n2 = ARGN("$number2"); 234 if (n1->is_unitless() || n2->is_unitless()) { 235 return SASS_MEMORY_NEW(Boolean, pstate, true); 236 } 237 // normalize into main units 238 n1->normalize(); n2->normalize(); 239 Units &lhs_unit = *n1, &rhs_unit = *n2; 240 bool is_comparable = (lhs_unit == rhs_unit); 241 return SASS_MEMORY_NEW(Boolean, pstate, is_comparable); 242 } 243 244 } 245 246 } 247