diff --git a/docs/dev-env-stacks.md b/docs/dev-env-stacks.md new file mode 100644 index 0000000000..8ec7da2b48 --- /dev/null +++ b/docs/dev-env-stacks.md @@ -0,0 +1,148 @@ +# How LibSass handles variables, functions and mixins + +This document is intended for developers of LibSass only and are of now use +for implementers. It documents how variable stacks are implemented. + +## Foreword + +LibSass uses an optimized stack approach similar to how C compilers have always +done it, by using a growable stack where we can push and pop items of. Unfortunately +Sass has proven to be a bit more dynamic than static optimizers like, therefore we +had to adopt the principle a little to accommodate the edge-cases due to this. + +There are three different kind of entities on the stack during runtime, namely +variables, functions and mixins. Each has it's own dedicated stack to optimize +the lookups. In this doc we will often only cover one case, but it should be +applicable to any other stack object (with some small differences). + +Also for regular sass code and style-rules we wouldn't need this setup, but +it becomes essential to correctly support mixins and functions, since those +can be called recursively. It is also vital for loops, like @for or @each. + +## Overview + +The whole process is split into two main phases. In order to correctly support +@import we had to introduce the preloader phase, where all @use, @forward and +@import rules are loaded first, before any evaluation happens. This ensures that +we know all entities before the evaluation phase in order to correctly setup +all stack frames. + +## Basic example + +Let's assume we have the following scss code: + +```scss +$a: 1; +b { + $a: 2; +} +``` + +This will allocate two independent variables on the stack. For easier reference +we can think of them as variable 0 and variable 1. So let's see what happens if +we introduce some VariableExpressions: + +```scss +$a: 1; +b { + a0: $a; + $a: 2; + a1: $a; +} +c { + a: $a; +} +``` + +As you may have guesses, the `a0` expression will reference variable 0 and the +`a1` expression will reference variable 1, while the last one will reference +variable 0 again. Given this easy example this might seem overengineered, but +let's see what happens if we introduce a loop: + +``` +$a: 1; +b { + @for $x from 1 through 2 { + a0: $a; + $a: 2; + a1: $a; + } +} +c { + a: $a; +} +``` + +Here I want to concentrate on `a0`. In most programing languages, `a0: $a` would +always point to variable 0, but in Sass this is more dynamic. It will actually +reference variable 0 on the first run, and variable 1 on consecutive runs. + +## What is an EnvFrame and EnvRef + +Whenever we encounter a new scope while parsing, we will create a new EnvFrame. +Every EnvFrame (often also just called idxs) knows the variables, functions and +mixins that are declared within that scope. Each entity is simply referenced by +it's integer offset (first variable, second variable and so on). Each frame is +stored as long as the context/compiler lives. In order to find functions, each +frame keeps a hash-map to get the local offset for an entity name (e.g. varIdxs). +An EnvRef is just a struct with the env-frame address and the local entity offset. + +## Where are entities actually stored during runtime + +The `EnvRoot` has a growable stack for each entity type. Whenever we evaluate +a lexical scope, we will push the entities to the stack to bring them live. +By doing this, we also update the current pointer for the given env-frame to +point to the correct position within that stack. Let's see how this works: + +```scss +$a: 1; +@function recursive($abort) { + $a: $a + 1; + @if ($abort) { + @return $a; + } + @else { + @return recursive(true); + } +} +a { + b: recursive(false); +} +``` + +Here we call the recursive function twice, so the `$a` inside must be independent. +The stack allocation would look the following in this case: + +- Entering root scope + - pushing one variable on the runtime var stack. +- Entering for scope for the first time + - updating varFramePtr to 1 since there is already one variable. + - pushing another variable on the runtime var stack +- Entering for scope for the second time + - updating varFramePtr to 2 since there are now two variable. + - pushing another variable on the runtime var stack +- Exiting second for scope and restoring old state +- Exiting first for scope and restoring old state +- Exiting root scope + +So in the second for loop run, when we have to resolve the variable expression for `$a`, +we first get the base frame pointer (often called stack frame pointer in C). Then we only +need to add the local offset to get to the current frame instance of the variable. Once we +exit a scope, we simply need to pop those entities off the stack and reset the frame pointer. + +## How ambiguous/dynamic lookup is done in loops + +Unfortunately we are not able to fully statically optimize the variable lookups (as explained +earlier, due to the full dynamic nature of Sass). IMO we can do it for functions and mixins, +as they always have to be declared and defined at the same time. But variables can also just +be declared and defined later. So in order to optimize this situation we will first fetch and +cache all possible variable declarations (vector of VarRef). Then on evaluation we simply need +to check and return the first entity that was actually defined (assigned to). + +## Afterword + +I hope this example somehow clarifies how variable stacks are implemented in LibSass. This +optimization can easily bring 50% or more performance in contrast to always do the dynamic +lookup via the also available hash-maps. They are needed anyway for meta functions, like +`function-exists`. It is also not possible to (easily) create new variables once the parsing +is done, so the C-API doesn't allow to create new variables during runtime. diff --git a/src/ast_expressions.hpp b/src/ast_expressions.hpp index 8a73f6285e..7f5b7fd5d7 100644 --- a/src/ast_expressions.hpp +++ b/src/ast_expressions.hpp @@ -282,8 +282,8 @@ namespace Sass { class VariableExpression final : public Expression { ADD_CONSTREF(EnvKey, name); - ADD_REF(sass::vector, vidxs); - // ADD_REF(EnvIdx, vidx2); + ADD_REF(sass::vector, vidxs); + // ADD_REF(EnvRef, vidx2); ADD_CONSTREF(sass::string, ns); ADD_PROPERTY(bool, withinLoop); public: @@ -390,7 +390,7 @@ namespace Sass { ADD_CONSTREF(sass::string, name); // The frame offset for the function - ADD_REF(EnvIdx, fidx2); + ADD_REF(EnvRef, fidx2); // Internal optimization flag ADD_CONSTREF(bool, withinLoop); diff --git a/src/ast_fwd_decl.hpp b/src/ast_fwd_decl.hpp index 297d9e5520..b970f4dcef 100644 --- a/src/ast_fwd_decl.hpp +++ b/src/ast_fwd_decl.hpp @@ -19,15 +19,17 @@ ///////////////////////////////////////////// namespace Sass { - class Env; - class EnvRoot; + // Forward declare class EnvKey; + class EnvRef; + class EnvRefs; + class EnvRoot; + class EnvFrame; + class Module; class BuiltInMod; class WithConfig; - class EnvIdx; - class EnvRefs; class Logger; class Compiler; class EnvFrame; diff --git a/src/ast_nodes.cpp b/src/ast_nodes.cpp index 1fc2693b32..b601287b36 100644 --- a/src/ast_nodes.cpp +++ b/src/ast_nodes.cpp @@ -296,7 +296,7 @@ namespace Sass { // The SassScript `>` operation. bool Value::greaterThan(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " > " + other->inspect() + "\".", @@ -307,7 +307,7 @@ namespace Sass { // The SassScript `>=` operation. bool Value::greaterThanOrEquals(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " >= " + other->inspect() + "\".", @@ -318,7 +318,7 @@ namespace Sass { // The SassScript `<` operation. bool Value::lessThan(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " < " + other->inspect() + "\".", @@ -329,7 +329,7 @@ namespace Sass { // The SassScript `<=` operation. bool Value::lessThanOrEquals(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " <= " + other->inspect() + "\".", @@ -340,7 +340,7 @@ namespace Sass { // The SassScript `*` operation. Value* Value::times(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " * " + other->inspect() + "\".", @@ -351,7 +351,7 @@ namespace Sass { // The SassScript `%` operation. Value* Value::modulo(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " % " + other->inspect() + "\".", @@ -441,7 +441,7 @@ namespace Sass { // Assert and return a color or throws if incompatible const Color* Value::assertColor(Logger& logger, const sass::string& name) const { - logger.addFinalStackTrace(pstate()); + callStackFrame csf(logger, pstate()); throw Exception::SassScriptException( inspect() + " is not a color.", logger, pstate(), name); @@ -450,7 +450,7 @@ namespace Sass { // Assert and return a function or throws if incompatible Function* Value::assertFunction(Logger& logger, const sass::string& name) { - logger.addFinalStackTrace(pstate()); + callStackFrame csf(logger, pstate()); throw Exception::SassScriptException( inspect() + " is not a function reference.", logger, pstate(), name); @@ -459,7 +459,7 @@ namespace Sass { // Assert and return a map or throws if incompatible Map* Value::assertMap(Logger& logger, const sass::string& name) { - logger.addFinalStackTrace(pstate()); + callStackFrame csf(logger, pstate()); throw Exception::SassScriptException( inspect() + " is not a map.", logger, pstate(), name); @@ -468,7 +468,7 @@ namespace Sass { // Assert and return a number or throws if incompatible Number* Value::assertNumber(Logger& logger, const sass::string& name) { - logger.addFinalStackTrace(pstate()); + callStackFrame csf(logger, pstate()); throw Exception::SassScriptException( inspect() + " is not a number.", logger, pstate(), name); @@ -484,7 +484,7 @@ namespace Sass { // Assert and return a string or throws if incompatible String* Value::assertString(Logger& logger, const sass::string& name) { - logger.addFinalStackTrace(pstate()); + callStackFrame csf(logger, pstate()); throw Exception::SassScriptException( inspect() + " is not a string.", logger, pstate(), name); @@ -508,7 +508,7 @@ namespace Sass { // Assert and return an argument list or throws if incompatible ArgumentList* Value::assertArgumentList(Logger& logger, const sass::string& name) { - logger.addFinalStackTrace(pstate()); + callStackFrame csf(logger, pstate()); throw Exception::SassScriptException( inspect() + " is not an argument list.", logger, pstate(), name); diff --git a/src/ast_statements.cpp b/src/ast_statements.cpp index 24160dd0c8..7fa03bce04 100644 --- a/src/ast_statements.cpp +++ b/src/ast_statements.cpp @@ -55,7 +55,7 @@ namespace Sass { for (auto cfgvar : config) { if (cfgvar.second.wasUsed == false) { if (cfgvar.second.isGuarded == false) { - logger.addFinalStackTrace(cfgvar.second.pstate2); + callStackFrame csf(logger, cfgvar.second.pstate2); throw Exception::RuntimeException(logger, "$" + cfgvar.second.name + " was not declared " "with !default in the @used module."); @@ -633,7 +633,7 @@ namespace Sass { const EnvKey& variable, bool withinLoop, const sass::string ns, - sass::vector vidxs, + sass::vector vidxs, Expression* value, bool is_default, bool is_global) : diff --git a/src/ast_statements.hpp b/src/ast_statements.hpp index e715a6e91a..0e758d85e5 100644 --- a/src/ast_statements.hpp +++ b/src/ast_statements.hpp @@ -391,7 +391,7 @@ namespace Sass { public CallableDeclaration { // Content function reference - ADD_CONSTREF(EnvIdx, cidx); + ADD_CONSTREF(EnvRef, cidx); ADD_CONSTREF(UserDefinedCallableObj, cmixin); @@ -419,7 +419,7 @@ namespace Sass { public CallableDeclaration { // Function reference - ADD_CONSTREF(EnvIdx, fidx); + ADD_CONSTREF(EnvRef, fidx); public: // Value constructor FunctionRule( @@ -445,9 +445,9 @@ namespace Sass { public CallableDeclaration { // Mixin function reference - ADD_CONSTREF(EnvIdx, midx); + ADD_CONSTREF(EnvRef, midx); // Content function reference - // ADD_CONSTREF(EnvIdx, cidx33); + // ADD_CONSTREF(EnvRef, cidx33); public: // Value constructor MixinRule( @@ -750,9 +750,9 @@ namespace Sass { ADD_CONSTREF(EnvKey, variable); ADD_CONSTREF(sass::string, ns); ADD_CONSTREF(ExpressionObj, value); - ADD_REF(sass::vector, vidxs); + ADD_REF(sass::vector, vidxs); - ADD_REF(EnvIdx, vidx2); + ADD_REF(EnvRef, vidx2); ADD_PROPERTY(bool, withinLoop); ADD_CONSTREF(bool, is_default); // ToDO rename @@ -764,7 +764,7 @@ namespace Sass { const EnvKey& variable, bool withinLoop, const sass::string ns, - sass::vector vidxs, + sass::vector vidxs, Expression* value, bool is_default = false, bool is_global = false); @@ -792,7 +792,7 @@ namespace Sass { // The name of the mixin being invoked. ADD_CONSTREF(EnvKey, name); - ADD_CONSTREF(EnvIdx, midx); + ADD_CONSTREF(EnvRef, midx); // The block that will be invoked for [ContentRule]s in the mixin // being invoked, or `null` if this doesn't pass a content block. diff --git a/src/ast_values.cpp b/src/ast_values.cpp index 3102ce839a..a8886b1cc8 100644 --- a/src/ast_values.cpp +++ b/src/ast_values.cpp @@ -143,7 +143,7 @@ namespace Sass { Value* Color::plus(Value* other, Logger& logger, const SourceSpan& pstate) const { if (other->isaNumber() || other->isaColor()) { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " + " + other->inspect() + "\".", @@ -155,7 +155,7 @@ namespace Sass { Value* Color::minus(Value* other, Logger& logger, const SourceSpan& pstate) const { if (other->isaNumber() || other->isaColor()) { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " - " + other->inspect() + "\".", @@ -167,7 +167,7 @@ namespace Sass { Value* Color::dividedBy(Value* other, Logger& logger, const SourceSpan& pstate) const { if (other->isaNumber() || other->isaColor()) { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " / " + other->inspect() + "\".", @@ -178,7 +178,7 @@ namespace Sass { Value* Color::modulo(Value* other, Logger& logger, const SourceSpan& pstate) const { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " % " + other->inspect() + "\".", @@ -697,11 +697,11 @@ namespace Sass { return l.value() > r.value(); } // Throw error, unit are incompatible - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::UnitMismatch( logger, this, rhs); } - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " > " + other->inspect() + "\".", @@ -726,11 +726,11 @@ namespace Sass { return l.value() >= r.value(); } // Throw error, unit are incompatible - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::UnitMismatch( logger, this, rhs); } - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " >= " + other->inspect() + "\".", @@ -755,11 +755,11 @@ namespace Sass { return l.value() < r.value(); } // Throw error, unit are incompatible - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::UnitMismatch( logger, this, rhs); } - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " < " + other->inspect() + "\".", @@ -784,11 +784,11 @@ namespace Sass { return l.value() <= r.value(); } // Throw error, unit are incompatible - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::UnitMismatch( logger, this, rhs); } - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " <= " + other->inspect() + "\".", @@ -941,7 +941,7 @@ namespace Sass { double f(right.getUnitConvertFactor(left)); // Returns zero on incompatible units if (f == 0.0) { - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::UnitMismatch( logger, left, right); } @@ -960,7 +960,7 @@ namespace Sass { return operate(add, *nr, logger, pstate); } if (!other->isaColor()) return Value::plus(other, logger, pstate); - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " + " + other->inspect() + "\".", @@ -974,7 +974,7 @@ namespace Sass { return operate(sub, *nr, logger, pstate); } if (!other->isaColor()) return Value::minus(other, logger, pstate); - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " - " + other->inspect() + "\".", @@ -988,7 +988,7 @@ namespace Sass { return operate(mul, *nr, logger, pstate); } if (!other->isaColor()) return Value::times(other, logger, pstate); - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " * " + other->inspect() + "\".", @@ -1002,7 +1002,7 @@ namespace Sass { return operate(mod, *nr, logger, pstate); } if (!other->isaColor()) return Value::plus(other, logger, pstate); - logger.addFinalStackTrace(pstate); + callStackFrame csf(logger, pstate); throw Exception::SassScriptException( "Undefined operation \"" + inspect() + " % " + other->inspect() + "\".", @@ -1045,7 +1045,7 @@ namespace Sass { return lround(value_); } SourceSpan span(this->pstate()); - logger.addFinalStackTrace(span); + callStackFrame csf(logger, span); throw Exception::SassScriptException( inspect() + " is not an int.", logger, span, name); @@ -1055,7 +1055,7 @@ namespace Sass { { if (!hasUnits()) return this; SourceSpan span(this->pstate()); - logger.addFinalStackTrace(span); + callStackFrame csf(logger, span); throw Exception::SassScriptException( "Expected " + inspect() + " to have no units.", logger, span, name); @@ -1065,7 +1065,7 @@ namespace Sass { { if (hasUnit(unit)) return this; SourceSpan span(this->pstate()); - logger.addFinalStackTrace(span); + callStackFrame csf(logger, span); throw Exception::SassScriptException( "Expected " + inspect() + " to have unit \"" + unit + "\".", logger, span, name); @@ -1078,7 +1078,7 @@ namespace Sass { msg << "Expected " << inspect() << " to be within "; msg << min << unit() << " and " << max << unit() << "."; SourceSpan span(this->pstate()); - logger.addFinalStackTrace(span); + callStackFrame csf(logger, span); throw Exception::SassScriptException( msg.str(), logger, span, name); } diff --git a/src/backtrace.hpp b/src/backtrace.hpp index 2a0fd8cc7a..1cc3683c5f 100644 --- a/src/backtrace.hpp +++ b/src/backtrace.hpp @@ -51,6 +51,11 @@ namespace Sass { return name; } + bool operator==(const StackTrace& other) const { + return pstate == other.pstate && + name == other.name && fn == other.fn; + } + bool isFn() const override final { return fn; } diff --git a/src/capi_functions.cpp b/src/capi_functions.cpp index f99b21c018..bc56044605 100644 --- a/src/capi_functions.cpp +++ b/src/capi_functions.cpp @@ -14,7 +14,7 @@ extern "C" { { struct SassFunction* cb = new SassFunction{}; if (cb == 0 || signature == 0) return 0; - cb->signature = sass_copy_c_string(signature); + cb->signature = signature; cb->function = callback; cb->cookie = cookie; return cb; @@ -22,7 +22,6 @@ extern "C" { void ADDCALL sass_delete_function(struct SassFunction* function) { - sass_free_c_string(function->signature); delete function; } @@ -32,7 +31,7 @@ extern "C" { const char* ADDCALL sass_function_get_signature(struct SassFunction* function) { - return function->signature; + return function->signature.c_str(); } SassFunctionLambda ADDCALL sass_function_get_function(struct SassFunction* function) diff --git a/src/capi_functions.hpp b/src/capi_functions.hpp index b8eff8bdb7..592a71da42 100644 --- a/src/capi_functions.hpp +++ b/src/capi_functions.hpp @@ -7,7 +7,7 @@ // Struct to hold custom function callback struct SassFunction { - char* signature; + sass::string signature; SassFunctionLambda function; void* cookie; }; diff --git a/src/color_maps.cpp b/src/color_maps.cpp index 7fd4d1c3cb..dca8f86aa7 100644 --- a/src/color_maps.cpp +++ b/src/color_maps.cpp @@ -1,8 +1,8 @@ -///////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////// +/*****************************************************************************/ +/* Part of LibSass, released under the MIT license (See LICENSE.txt). */ +/*****************************************************************************/ #include "color_maps.hpp" -#include "util_string.hpp" #include "string_utils.hpp" namespace Sass diff --git a/src/compiler.cpp b/src/compiler.cpp index f1291763f7..1669aeb6b4 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -25,6 +25,8 @@ #include "fn_colors.hpp" #include "fn_selectors.hpp" +#include "preloader.hpp" + #include #include #ifdef _MSC_VER @@ -96,7 +98,7 @@ namespace Sass { // Value& val(Value::unwrap(value)); // Compiler& compiler(Compiler::unwrap(comp)); // compiler.setVariable(key.assertString(compiler, "name")->getText(), &val, false, false); - return sass_make_null(); + return value; } // so far only pow has two arguments @@ -239,6 +241,9 @@ struct SassValue* fn_##fn(struct SassValue* s_args, Sass_Function_Entry cb, stru Eval eval(*this, *this, plainCss); + // Preloader preloader(*this, root); + // preloader.process(); + CssRootObj compiled = eval.acceptRoot(root); // 50% // debug_ast(compiled); @@ -466,7 +471,7 @@ struct SassValue* fn_##fn(struct SassValue* s_args, Sass_Function_Entry cb, stru for (struct SassImporter* importer : importers) { // Get the external importer function - SassImporterLambda fn = sass_importer_get_callback(importer); + SassImporterLambda fn = importer->importer; // std::cerr << "Calling custom loader " << fn << "\n"; @@ -730,35 +735,22 @@ struct SassValue* fn_##fn(struct SassValue* s_args, Sass_Function_Entry cb, stru // Interface for external custom functions ///////////////////////////////////////////////////////////////////////// - // Create a new external callable from the sass function. Parses - // function signature into function name and argument declaration. - ExternalCallable* Compiler::makeExternalCallable(struct SassFunction* function) + // Register an external custom sass function on the global scope. + // Main entry point for custom functions passed through the C-API. + void Compiler::registerCustomFunction(struct SassFunction* function) { - // Create temporary source object for signature - SourceStringObj source = SASS_MEMORY_NEW(SourceString, - "sass://signature", function->signature); - // Create a new scss parser instance + EnvRoot root(*this); + SourceDataObj source = SASS_MEMORY_NEW(SourceString, + "sass://signature", sass::string(function->signature)); ScssParser parser(*this, source.ptr()); ExternalCallable* callable = parser.parseExternalCallable(); callable->function(function); - return callable; - } - // EO makeExternalCallable - - // Register an external custom sass function on the global scope. - // Main entry point for custom functions passed through the C-API. - void Compiler::registerCustomFunction(struct SassFunction* function) - { - // EnvRoot root(*this); - // Create a new external callable from the sass function - // ExternalCallable* callable = makeExternalCallable(function); - // Currently external functions are treated globally - // if (fnLookup.count(callable->envkey()) == 0) { - // fnLookup.insert(std::make_pair(callable->envkey(), callable)); - // varRoot.createFunction(callable->envkey()); - // fnList.push_back(callable); - // } + auto& functions(varRoot.intFunction); + uint32_t offset((uint32_t)functions.size()); + varRoot.intFunction.push_back(callable); + varRoot.idxs->fnIdxs[callable->name()] = offset; + varRoot.privateFnOffset = offset; } // EO registerCustomFunction @@ -898,7 +890,7 @@ struct SassValue* fn_##fn(struct SassValue* s_args, Sass_Function_Entry cb, stru // registerCustomFunction(qwe); - // registerCustomFunction(sass_make_function("set-local($name, $value)", fn_set_local, (void*)31)); + registerCustomFunction(sass_make_function("set-local($name, $value)", fn_set_local, (void*)31)); // registerCustomFunction(sass_make_function("set-global($name, $value)", fn_set_global, (void*)31)); // registerCustomFunction(sass_make_function("set-lexical($name, $value)", fn_set_lexical, (void*)31)); // diff --git a/src/compiler.hpp b/src/compiler.hpp index 193c301df5..f9ffbefb04 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -212,13 +212,6 @@ namespace Sass { bool callCustomLoader(const sass::string& imp_path, SourceSpan& pstate, ImportRule* rule, const sass::vector& importers, bool singletone = true); - ///////////////////////////////////////////////////////////////////////// - // Create a new external callable from the sass function. Parses - // function signature into function name and argument declaration. - // The function you pass in will be taken over and freed by us! - ///////////////////////////////////////////////////////////////////////// - ExternalCallable* makeExternalCallable(struct SassFunction* function); - ///////////////////////////////////////////////////////////////////////// // Register an external custom sass function on the global scope. // Main entry point for custom functions passed through the C-API. diff --git a/src/context.hpp b/src/context.hpp index 515547f72d..278ab86302 100644 --- a/src/context.hpp +++ b/src/context.hpp @@ -52,11 +52,6 @@ namespace Sass { // Same order needed for function stack std::vector fnList; - // Lookup functions by function name - // Due to EnvKayMap case insensitive. - EnvKeyMap fnLookup; - - public: // sass::vector* withConfig = nullptr; diff --git a/src/debugger.hpp b/src/debugger.hpp index e2a9714546..3b8d63801b 100644 --- a/src/debugger.hpp +++ b/src/debugger.hpp @@ -332,7 +332,7 @@ inline std::string parent_block(EnvRefs* node) { std::stringstream str; if (!node) return ""; - str << "SG: " << (node->isPermeable ? "true" : "false"); + str << "SG: " << (node->isSemiGlobal ? "true" : "false"); return str.str(); } diff --git a/src/environment.cpp b/src/environment.cpp index b95be78b2c..0a69171e52 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -57,7 +57,7 @@ namespace Sass { Value* Eval::visitAssignRule(AssignRule* a) { - if (!a->withinLoop() && a->vidx2().isValid()) { + if (a->vidx2().isValid()) { assigne = &compiler.varRoot.getVariable(a->vidx2()); ValueObj result = a->value()->accept(this); compiler.varRoot.setVariable( @@ -79,7 +79,7 @@ namespace Sass { // If we have a config and the variable is already set // we still overwrite the variable beside being guarded WithConfigVar* wconf = nullptr; - if (compiler.wconfig && frame->varFrame == 0xFFFFFFFF && a->ns().empty()) { + if (compiler.wconfig && frame->framePtr == 0xFFFFFFFF && a->ns().empty()) { wconf = wconfig->getCfgVar(vname); } if (wconf) { @@ -104,7 +104,7 @@ namespace Sass { bool hasVar = false; if (it != rframe->varIdxs.end()) { - EnvIdx vidx(rframe->varFrame, it->second); + EnvRef vidx(rframe->framePtr, it->second); auto& value = compiler.varRoot.getVariable(vidx); if (value != nullptr) hasVar = true; } @@ -116,14 +116,14 @@ namespace Sass { for (auto fwds : rframe->forwards) { auto it = fwds->varIdxs.find(a->variable()); if (it != fwds->varIdxs.end()) { - EnvIdx vidx(0xFFFFFFFF, it->second); + EnvRef vidx(0xFFFFFFFF, it->second); auto& value = compiler.varRoot.getVariable(vidx); if (value != nullptr) hasVar = true; } auto fwd = fwds->module->mergedFwdVar.find(a->variable()); if (fwd != fwds->module->mergedFwdVar.end()) { - EnvIdx vidx(0xFFFFFFFF, fwd->second); + EnvRef vidx(0xFFFFFFFF, fwd->second); auto& value = compiler.varRoot.getVariable(vidx); if (value != nullptr) hasVar = true; } @@ -170,11 +170,11 @@ namespace Sass { auto it = mod->module->moduse.find(a->ns()); if (it == mod->module->moduse.end()) { - compiler.addFinalStackTrace(a->pstate()); + callStackFrame csf(compiler, a->pstate()); throw Exception::ModuleUnknown(compiler, a->ns()); } else if (it->second.second && !it->second.second->isCompiled) { - compiler.addFinalStackTrace(a->pstate()); + callStackFrame csf(compiler, a->pstate()); throw Exception::ModuleUnknown(compiler, a->ns()); } @@ -272,7 +272,7 @@ namespace Sass { break; } } - if (chroot->isImport || chroot->isPermeable) { + if (chroot->isImport || chroot->isSemiGlobal) { chroot = chroot->pscope; } else { @@ -332,7 +332,7 @@ namespace Sass { LOCAL_FLAG(mixinHasContent, false); while (frame->isImport) frame = frame->pscope; - EnvIdx midx = frame->createMixin(name); + EnvRef midx = frame->createMixin(name); MixinRule* rule = withChildren( &StylesheetParser::readChildStatement, start, name, arguments, local.idxs); @@ -526,24 +526,17 @@ namespace Sass { ImportObj loaded = compiler.loadImport(resolved[0]); ImportStackFrame iframe(compiler, loaded); - Root* sheet = nullptr; sass::string abspath(loaded->getAbsPath()); auto cached = compiler.sheets.find(abspath); if (cached != compiler.sheets.end()) { - sheet = cached->second; + return cached->second; } - else { - // Permeable seems to have minor negative impact!? - EnvFrame local(compiler, false, true, isImport); // correct - sheet = compiler.registerImport(loaded); - sheet->idxs = local.idxs; - sheet->import = loaded; - } - - // rule->module(sheet); - // rule->sheet(sheet); - // wconfig.finalize(); + // Permeable seems to have minor negative impact!? + EnvFrame local(compiler, false, true, isImport); // correct + Root* sheet = compiler.registerImport(loaded); + sheet->idxs = local.idxs; + sheet->import = loaded; return sheet; } @@ -796,7 +789,6 @@ namespace Sass { rule->wasExported(true); } else if (frame->module->moduse.count(rule->ns())) { - compiler.addFinalStackTrace(rule->pstate()); throw Exception::ModuleAlreadyKnown(compiler, rule->ns()); } else { @@ -843,7 +835,7 @@ namespace Sass { pframe = pframe->pscope; } - if (pframe->varFrame == 0xFFFFFFFF) { + if (pframe->framePtr == 0xFFFFFFFF) { // Import to forward for (auto& asd : rule->root()->mergedFwdVar) { @@ -863,30 +855,27 @@ namespace Sass { // Merge it up through all imports for (auto& var : cidxs->varIdxs) { - auto it = pframe->varIdxs.find(var.first); - if (it == pframe->varIdxs.end()) { + if (pframe->varIdxs.count(var.first) == 0) { pframe->createVariable(var.first); } } // Merge it up through all imports for (auto& fn : cidxs->fnIdxs) { - auto it = pframe->fnIdxs.find(fn.first); - if (it == pframe->fnIdxs.end()) { + if (pframe->fnIdxs.count(fn.first) == 0) { pframe->createFunction(fn.first); } } // Merge it up through all imports for (auto& mix : cidxs->mixIdxs) { - auto it = pframe->mixIdxs.find(mix.first); - if (it == pframe->mixIdxs.end()) { + if (pframe->mixIdxs.count(mix.first) == 0) { pframe->createMixin(mix.first); } } - if (pframe->varFrame != 0xFFFFFFFF) { + if (pframe->framePtr != 0xFFFFFFFF) { if (udbg) std::cerr << "Importing into parent frame '" << rule->url() << "' " << compiler.implicitWithConfig << "\n"; @@ -897,9 +886,7 @@ namespace Sass { rule->root()->idxs); } - - - if (pframe->varFrame == 0xFFFFFFFF) { + else { // Import to forward for (auto& asd : rule->root()->mergedFwdVar) { diff --git a/src/environment_stack.cpp b/src/environment_stack.cpp index 25dccfd7b1..a765cc5005 100644 --- a/src/environment_stack.cpp +++ b/src/environment_stack.cpp @@ -18,8 +18,7 @@ namespace Sass { // Each parsed scope gets its own environment frame ///////////////////////////////////////////////////////////////////////// - const EnvIdx nullidx{ 0xFFFFFFFF, 0xFFFFFFFF }; - // const EnvIdx nomodidx{ 0xFFFFFFFE, 0xFFFFFFFF }; + const EnvRef nullidx{ 0xFFFFFFFF, 0xFFFFFFFF }; // The root is used for all runtime state // Also contains parsed root scope stack @@ -31,17 +30,15 @@ namespace Sass { *this, nullptr, 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF, false, false)) { - variables.reserve(256); - functions.reserve(256); - mixins.reserve(128); - varFramePtr.reserve(256); - mixFramePtr.reserve(128); - fnFramePtr.reserve(256); + varStack.reserve(256); + fnStack.reserve(256); + mixStack.reserve(128); + varStackPtr.reserve(256); + mixStackPtr.reserve(128); + fnStackPtr.reserve(256); intVariables.reserve(256); intFunction.reserve(256); intMixin.reserve(128); @@ -56,29 +53,25 @@ namespace Sass { // Value constructor EnvFrame::EnvFrame( Compiler& compiler, - bool isPermeable, + bool isSemiGlobal, bool isModule, bool isImport) : stack(compiler.varRoot.stack), idxs(new EnvRefs( compiler.varRoot, compiler.varRoot.stack.back(), - uint32_t(compiler.varRoot.varFramePtr.size()), - uint32_t(compiler.varRoot.mixFramePtr.size()), - uint32_t(compiler.varRoot.fnFramePtr.size()), - isPermeable, isImport)) + uint32_t(compiler.varRoot.varStackPtr.size()), + isSemiGlobal, isImport)) { if (isModule) { // Lives in built-in scope - idxs->varFrame = 0xFFFFFFFF; - idxs->mixFrame = 0xFFFFFFFF; - idxs->fnFrame = 0xFFFFFFFF; + idxs->framePtr = 0xFFFFFFFF; } else { // Initialize stacks as not active yet - idxs->root.varFramePtr.push_back(0xFFFFFFFF); - idxs->root.mixFramePtr.push_back(0xFFFFFFFF); - idxs->root.fnFramePtr.push_back(0xFFFFFFFF); + idxs->root.varStackPtr.push_back(0xFFFFFFFF); + idxs->root.mixStackPtr.push_back(0xFFFFFFFF); + idxs->root.fnStackPtr.push_back(0xFFFFFFFF); } // Check and prevent stack smashing if (stack.size() > MAX_NESTING) { @@ -105,35 +98,23 @@ namespace Sass { // Register new variable on local stack // Invoked mostly by stylesheet parser - EnvIdx EnvRefs::createVariable( + EnvRef EnvRefs::createVariable( const EnvKey& name) { - // Check for existing function - // auto it = varIdxs.find(name); - // if (it != varIdxs.end()) { - // return { idxs->varFrame, it->second }; - // } - if (varFrame == 0xFFFFFFFF) { + if (framePtr == 0xFFFFFFFF) { uint32_t offset = (uint32_t)root.intVariables.size(); if (stkdbg) std::cerr << "Create global variable " << name.orig() << " at " << offset << "\n"; root.intVariables.resize(offset + 1); - //root.intVariables[offset] = SASS_MEMORY_NEW(Null, - // SourceSpan::tmp("null")); varIdxs[name] = offset; return { 0xFFFFFFFF, offset }; } - - //if (isImport) { - // return pscope->createVariable(name); - //} - // Get local offset to new variable uint32_t offset = (uint32_t)varIdxs.size(); if (stkdbg) std::cerr << "Create local variable " << name.orig() << " at " << offset << "\n"; // Remember the variable name varIdxs[name] = offset; // Return stack index reference - return { varFrame, offset }; + return { framePtr, offset }; } // EO createVariable @@ -141,16 +122,10 @@ namespace Sass { // Mostly invoked by built-in functions // Then invoked for custom C-API function // Finally for every parsed function rule - EnvIdx EnvRefs::createFunction( + EnvRef EnvRefs::createFunction( const EnvKey& name) { - // Check for existing function - auto it = fnIdxs.find(name); - if (it != fnIdxs.end()) { - if (it->second > root.privateFnOffset) - return { fnFrame, it->second }; - } - if (fnFrame == 0xFFFFFFFF) { + if (framePtr == 0xFFFFFFFF) { uint32_t offset = (uint32_t)root.intFunction.size(); if (fndbg) std::cerr << "Create global function " << name.orig() << " at " << offset << "\n"; root.intFunction.resize(offset + 1); @@ -163,23 +138,17 @@ namespace Sass { // Remember the function name fnIdxs[name] = offset; // Return stack index reference - return { fnFrame, offset }; + return { framePtr, offset }; } // EO createFunction // Register new mixin on local stack // Only invoked for mixin rules // But also for content blocks - EnvIdx EnvRefs::createMixin( + EnvRef EnvRefs::createMixin( const EnvKey& name) { - // Check for existing mixin - auto it = mixIdxs.find(name); - if (it != mixIdxs.end()) { - if (it->second > root.privateMixOffset) - return { mixFrame, it->second }; - } - if (mixFrame == 0xFFFFFFFF) { + if (framePtr == 0xFFFFFFFF) { uint32_t offset = (uint32_t)root.intMixin.size(); root.intMixin.resize(offset + 1); if (mixdbg) std::cerr << "Create global mixin " << name.orig() << " at " << offset << "\n"; @@ -192,7 +161,7 @@ namespace Sass { // Remember the mixin name mixIdxs[name] = offset; // Return stack index reference - return { mixFrame, offset }; + return { framePtr, offset }; } // EO createMixin @@ -202,7 +171,7 @@ namespace Sass { // Get value instance by stack index reference // Just converting and returning reference to array offset - ValueObj& EnvRoot::getVariable(const EnvIdx& vidx) + ValueObj& EnvRoot::getVariable(const EnvRef& vidx) { if (vidx.frame == 0xFFFFFFFF) { // if (stkdbg) std::cerr << "Get global variable " << vidx.offset << "\n"; @@ -210,7 +179,7 @@ namespace Sass { } else { // if (stkdbg) std::cerr << "Get variable " << vidx.toString() << "\n"; - return variables[size_t(varFramePtr[vidx.frame]) + vidx.offset]; + return varStack[size_t(varStackPtr[vidx.frame]) + vidx.offset]; } } // EO getVariable @@ -238,23 +207,23 @@ namespace Sass { return intMixin[offset]; } // EO findFunction - // Get function instance by stack index reference + // Get function instance by stack index reference // Just converting and returning reference to array offset - CallableObj& EnvRoot::getFunction(const EnvIdx& fidx) + CallableObj& EnvRoot::getFunction(const EnvRef& fidx) { if (fidx.frame == 0xFFFFFFFF) { return intFunction[fidx.offset]; } else { - return functions[size_t(fnFramePtr[fidx.frame]) + fidx.offset]; + return fnStack[size_t(fnStackPtr[fidx.frame]) + fidx.offset]; } } // EO findFunction // Get mixin instance by stack index reference // Just converting and returning reference to array offset - CallableObj& EnvRoot::getMixin(const EnvIdx& midx) + CallableObj& EnvRoot::getMixin(const EnvRef& midx) { if (midx.frame == 0xFFFFFFFF) { if (mixdbg) std::cerr << "Get global mixin " << midx.offset << "\n"; @@ -262,7 +231,7 @@ namespace Sass { } else { if (mixdbg) std::cerr << "Get mixin " << midx.toString() << "\n"; - return mixins[size_t(mixFramePtr[midx.frame]) + midx.offset]; + return mixStack[size_t(mixStackPtr[midx.frame]) + midx.offset]; } } // EO getMixin @@ -288,7 +257,7 @@ namespace Sass { // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning - void EnvRoot::setVariable(const EnvIdx& vidx, Value* value, bool guarded) + void EnvRoot::setVariable(const EnvRef& vidx, Value* value, bool guarded) { if (vidx.frame == 0xFFFFFFFF) { ValueObj& slot(intVariables[vidx.offset]); @@ -302,7 +271,7 @@ namespace Sass { } else { if (stkdbg) std::cerr << "Set variable " << vidx.toString() << " - " << value->inspect() << "\n"; - ValueObj& slot(variables[size_t(varFramePtr[vidx.frame]) + vidx.offset]); + ValueObj& slot(varStack[size_t(varStackPtr[vidx.frame]) + vidx.offset]); if (slot == nullptr || guarded == false) slot = value; } } @@ -320,7 +289,7 @@ namespace Sass { } else { if (stkdbg) std::cerr << "Set variable " << frame << ":" << offset << " - " << value->inspect() << "\n"; - ValueObj& slot(variables[size_t(varFramePtr[frame]) + offset]); + ValueObj& slot(varStack[size_t(varStackPtr[frame]) + offset]); if (!guarded || !slot || slot->isaNull()) slot = value; } } @@ -328,14 +297,14 @@ namespace Sass { // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning - void EnvRoot::setFunction(const EnvIdx& fidx, UserDefinedCallable* value, bool guarded) + void EnvRoot::setFunction(const EnvRef& fidx, UserDefinedCallable* value, bool guarded) { if (fidx.frame == 0xFFFFFFFF) { if (!guarded || intFunction[fidx.offset] == nullptr) intFunction[fidx.offset] = value; } else { - CallableObj& slot(functions[size_t(fnFramePtr[fidx.frame]) + fidx.offset]); + CallableObj& slot(fnStack[size_t(fnStackPtr[fidx.frame]) + fidx.offset]); if (!guarded || !slot) slot = value; } } @@ -343,14 +312,14 @@ namespace Sass { // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning - void EnvRoot::setMixin(const EnvIdx& midx, UserDefinedCallable* value, bool guarded) + void EnvRoot::setMixin(const EnvRef& midx, UserDefinedCallable* value, bool guarded) { if (midx.frame == 0xFFFFFFFF) { if (!guarded || intMixin[midx.offset] == nullptr) intMixin[midx.offset] = value; } else { - CallableObj& slot(mixins[size_t(mixFramePtr[midx.frame]) + midx.offset]); + CallableObj& slot(mixStack[size_t(mixStackPtr[midx.frame]) + midx.offset]); if (!guarded || !slot) slot = value; } } @@ -363,7 +332,7 @@ namespace Sass { // Will lookup from the last runtime stack scope. // We will move up the runtime stack until we either // find a defined function or run out of parent scopes. - EnvIdx EnvRefs::findMixIdx(const EnvKey& name) const + EnvRef EnvRefs::findMixIdx(const EnvKey& name) const { for (const EnvRefs* current = this; current; current = current->pscope) { @@ -371,7 +340,7 @@ namespace Sass { for (auto fwds : current->forwards) { auto fwd = fwds->mixIdxs.find(name); if (fwd != fwds->mixIdxs.end()) { - return { fwds->mixFrame, fwd->second }; + return { fwds->framePtr, fwd->second }; } if (Module* mod = fwds->module) { auto fwd = mod->mergedFwdMix.find(name); @@ -384,7 +353,7 @@ namespace Sass { if (current->isImport) continue; auto it = current->mixIdxs.find(name); if (it != current->mixIdxs.end()) { - return { current->mixFrame, it->second }; + return { current->framePtr, it->second }; } } return nullidx; @@ -396,7 +365,7 @@ namespace Sass { // Will lookup from the last runtime stack scope. // We will move up the runtime stack until we either // find a defined function or run out of parent scopes. - EnvIdx EnvRefs::findFnIdx(const EnvKey& name) const + EnvRef EnvRefs::findFnIdx(const EnvKey& name) const { for (const EnvRefs* current = this; current; current = current->pscope) { @@ -404,7 +373,7 @@ namespace Sass { for (auto fwds : current->forwards) { auto fwd = fwds->fnIdxs.find(name); if (fwd != fwds->fnIdxs.end()) { - return { fwds->fnFrame, fwd->second }; + return { fwds->framePtr, fwd->second }; } if (Module* mod = fwds->module) { auto fwd = mod->mergedFwdFn.find(name); @@ -417,7 +386,7 @@ namespace Sass { if (current->isImport) continue; auto it = current->fnIdxs.find(name); if (it != current->fnIdxs.end()) { - return { current->fnFrame, it->second }; + return { current->framePtr, it->second }; } } return nullidx; @@ -429,7 +398,7 @@ namespace Sass { // Otherwise lookup will be from the last runtime stack scope. // We will move up the runtime stack until we either find a // defined variable with a value or run out of parent scopes. - EnvIdx EnvRefs::findVarIdx(const EnvKey& name) const + EnvRef EnvRefs::findVarIdx(const EnvKey& name) const { for (const EnvRefs* current = this; current; current = current->pscope) { @@ -441,7 +410,7 @@ namespace Sass { "Private members can't be accessed " "from outside their modules."); } - return { fwds->varFrame, fwd->second }; + return { fwds->framePtr, fwd->second }; } if (Module* mod = fwds->module) { auto fwd = mod->mergedFwdVar.find(name); @@ -458,7 +427,7 @@ namespace Sass { if (current->isImport) continue; auto it = current->varIdxs.find(name); if (it != current->varIdxs.end()) { - return { current->varFrame, it->second }; + return { current->framePtr, it->second }; } } return nullidx; @@ -470,28 +439,28 @@ namespace Sass { // Otherwise lookup will be from the last runtime stack scope. // We will move up the runtime stack until we either find a // defined variable with a value or run out of parent scopes. - void EnvRefs::findVarIdxs(sass::vector& vidxs, const EnvKey& name) const + void EnvRefs::findVarIdxs(sass::vector& vidxs, const EnvKey& name) const { for (const EnvRefs* current = this; current; current = current->pscope) { if (current->isImport == false) { auto it = current->varIdxs.find(name); if (it != current->varIdxs.end()) { - vidxs.emplace_back(EnvIdx{ - current->varFrame, it->second }); + vidxs.emplace_back(EnvRef{ + current->framePtr, it->second }); } } if (name.isPrivate()) continue; for (auto fwds : current->forwards) { auto fwd = fwds->varIdxs.find(name); if (fwd != fwds->varIdxs.end()) { - vidxs.emplace_back(EnvIdx{ - fwds->varFrame, fwd->second }); + vidxs.emplace_back(EnvRef{ + fwds->framePtr, fwd->second }); } if (Module* mod = fwds->module) { auto fwd = mod->mergedFwdVar.find(name); if (fwd != mod->mergedFwdVar.end()) { - vidxs.emplace_back(EnvIdx{ + vidxs.emplace_back(EnvRef{ 0xFFFFFFFF, fwd->second }); } } @@ -500,7 +469,7 @@ namespace Sass { } // EO getVariable - EnvIdx EnvRefs::setModVar(const EnvKey& name, Value* value, bool guarded, const SourceSpan& pstate) const + EnvRef EnvRefs::setModVar(const EnvKey& name, Value* value, bool guarded, const SourceSpan& pstate) const { auto it = varIdxs.find(name); if (it != varIdxs.end()) { @@ -529,10 +498,10 @@ namespace Sass { auto it = mod->moduse.find(ns); if (it == mod->moduse.end()) continue; auto fwd = it->second.first->varIdxs.find(name); - if (fwd != it->second.first->varIdxs.end()) { - ValueObj& slot(root.getVariable({ 0xFFFFFFFF, fwd->second })); - return slot != nullptr; - } + // if (fwd != it->second.first->varIdxs.end()) { + // ValueObj& slot(root.getVariable({ 0xFFFFFFFF, fwd->second })); + // return slot != nullptr; + // } if (it->second.second) { return it->second.second->isCompiled; } @@ -543,26 +512,25 @@ namespace Sass { return false; } - EnvIdx EnvRefs::findVarIdx(const EnvKey& name, const sass::string& ns) const + EnvRef EnvRefs::findVarIdx(const EnvKey& name, const sass::string& ns) const { for (const EnvRefs* current = this; current; current = current->pscope) { if (current->isImport) continue; Module* mod = current->module; if (mod == nullptr) continue; - // Check if the namespace was registered auto it = mod->moduse.find(ns); if (it == mod->moduse.end()) continue; if (EnvRefs* idxs = it->second.first) { auto it = idxs->varIdxs.find(name); if (it != idxs->varIdxs.end()) { - return { idxs->varFrame, it->second }; + return { idxs->framePtr, it->second }; } } if (Module* mod = it->second.second) { auto fwd = mod->mergedFwdVar.find(name); if (fwd != mod->mergedFwdVar.end()) { - EnvIdx vidx{ 0xFFFFFFFF, fwd->second }; + EnvRef vidx{ 0xFFFFFFFF, fwd->second }; ValueObj& val = root.getVariable(vidx); if (val != nullptr) return vidx; } @@ -572,7 +540,7 @@ namespace Sass { } - EnvIdx EnvRefs::findMixIdx22(const EnvKey& name, const sass::string& ns) const + EnvRef EnvRefs::findMixIdx22(const EnvKey& name, const sass::string& ns) const { for (const EnvRefs* current = this; current; current = current->pscope) { @@ -584,7 +552,7 @@ namespace Sass { if (EnvRefs* idxs = it->second.first) { auto it = idxs->mixIdxs.find(name); if (it != idxs->mixIdxs.end()) { - return { idxs->mixFrame, it->second }; + return { idxs->framePtr, it->second }; } } if (Module* mod = it->second.second) { @@ -597,7 +565,7 @@ namespace Sass { return nullidx; } - EnvIdx EnvRefs::findFnIdx22(const EnvKey& name, const sass::string& ns) const + EnvRef EnvRefs::findFnIdx22(const EnvKey& name, const sass::string& ns) const { for (const EnvRefs* current = this; current; current = current->pscope) { @@ -609,7 +577,7 @@ namespace Sass { if (EnvRefs* idxs = it->second.first) { auto it = idxs->fnIdxs.find(name); if (it != idxs->fnIdxs.end()) { - return { idxs->fnFrame, it->second }; + return { idxs->framePtr, it->second }; } } if (Module* mod = it->second.second) { @@ -622,7 +590,7 @@ namespace Sass { return nullidx; } - EnvIdx EnvRoot::findVarIdx(const EnvKey& name, const sass::string& ns, bool global) const + EnvRef EnvRoot::findVarIdx(const EnvKey& name, const sass::string& ns, bool global) const { if (stack.empty()) return nullidx; auto& frame = global ? stack.front() : stack.back(); @@ -632,7 +600,7 @@ namespace Sass { // Find a function reference for [name] within the current scope stack. // If [ns] is not empty, we will only look within loaded modules. - EnvIdx EnvRoot::findFnIdx(const EnvKey& name, const sass::string& ns) const + EnvRef EnvRoot::findFnIdx(const EnvKey& name, const sass::string& ns) const { if (stack.empty()) return nullidx; if (ns.empty()) return stack.back()->findFnIdx(name); @@ -641,14 +609,14 @@ namespace Sass { // Find a function reference for [name] within the current scope stack. // If [ns] is not empty, we will only look within loaded modules. - EnvIdx EnvRoot::findMixIdx(const EnvKey& name, const sass::string& ns) const + EnvRef EnvRoot::findMixIdx(const EnvKey& name, const sass::string& ns) const { if (stack.empty()) return nullidx; if (ns.empty()) return stack.back()->findMixIdx(name); else return stack.back()->findMixIdx22(name, ns); } - void EnvRoot::findVarIdxs(sass::vector& vidxs, const EnvKey& name) const + void EnvRoot::findVarIdxs(sass::vector& vidxs, const EnvKey& name) const { if (stack.empty()) return; stack.back()->findVarIdxs(vidxs, name); @@ -657,7 +625,7 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// - EnvIdx EnvRoot::setModVar2(const EnvKey& name, const sass::string& ns, Value* value, bool guarded, const SourceSpan& pstate) + EnvRef EnvRoot::setModVar2(const EnvKey& name, const sass::string& ns, Value* value, bool guarded, const SourceSpan& pstate) { if (stack.empty()) return nullidx; return stack.back()->setModVar(name, ns, value, guarded, pstate); @@ -666,7 +634,7 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// - EnvIdx EnvRefs::setModVar(const EnvKey& name, const sass::string& ns, Value* value, bool guarded, const SourceSpan& pstate) + EnvRef EnvRefs::setModVar(const EnvKey& name, const sass::string& ns, Value* value, bool guarded, const SourceSpan& pstate) { for (const EnvRefs* current = this; current; current = current->pscope) { @@ -685,7 +653,7 @@ namespace Sass { } } if (EnvRefs* idxs = it->second.first) { - EnvIdx vidx = idxs->setModVar(name, value, guarded, pstate); + EnvRef vidx = idxs->setModVar(name, value, guarded, pstate); if (vidx.isValid()) return vidx; } } @@ -696,7 +664,7 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// // Very small helper for debugging - sass::string EnvIdx::toString() const + sass::string EnvRef::toString() const { sass::sstream strm; strm << frame << ":" << offset; diff --git a/src/environment_stack.hpp b/src/environment_stack.hpp index 1b361b3171..b460be310a 100644 --- a/src/environment_stack.hpp +++ b/src/environment_stack.hpp @@ -1,8 +1,8 @@ /*****************************************************************************/ /* Part of LibSass, released under the MIT license (See LICENSE.txt). */ /*****************************************************************************/ -#ifndef SASS_VAR_STACK_HPP -#define SASS_VAR_STACK_HPP +#ifndef SASS_ENV_STACK_HPP +#define SASS_ENV_STACK_HPP // sass.hpp must go before all system headers // to get the __EXTENSIONS__ fix on Solaris. @@ -16,80 +16,78 @@ namespace Sass { ///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////// - // Forward declare - class EnvIdx; - class EnvRefs; - class EnvRoot; - class EnvFrame; - - ///////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////// - // Helper typedef for our frame stack type typedef sass::vector EnvFrameVector; - extern const EnvIdx nullidx; + // Constant similar to nullptr + extern const EnvRef nullidx; ///////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////// - // Base class/struct for variable references. Each variable belongs to an - // environment frame (determined as we see variables during parsing). This - // is similar to how C organizes local stack variables via frame offsets. - class EnvIdx { - + // environment frame (determined as we see variables during parsing). Similar + // to how C organizes local variables via function stack pointers. + ///////////////////////////////////////////////////////////////////////// + class EnvRef + { public: + // The lexical frame pointer + // Each parsed scope gets its own uint32_t frame; + + // Local offset within the frame uint32_t offset; - EnvIdx() : + // Default constructor + EnvRef() : frame(0xFFFFFFFF), offset(0xFFFFFFFF) {} - EnvIdx( + // Value constructor + EnvRef( uint32_t frame, - uint32_t offset, - bool overwrites = false) : + uint32_t offset) : frame(frame), offset(offset) {} - bool operator==(const EnvIdx& rhs) const { + // Implement native equality operator + bool operator==(const EnvRef& rhs) const { return frame == rhs.frame && offset == rhs.offset; } - bool operator!=(const EnvIdx& rhs) const { + // Implement native inequality operator + bool operator!=(const EnvRef& rhs) const { return frame != rhs.frame || offset != rhs.offset; } - bool operator<(const EnvIdx& rhs) const { + // Implement operator to allow use in sets + bool operator<(const EnvRef& rhs) const { if (frame < rhs.frame) return true; return offset < rhs.offset; } + // Check if reference is valid bool isValid() const { // 3% return offset != 0xFFFFFFFF; } + // Check if entity is read-only bool isPrivate(uint32_t privateOffset) { return frame == 0xFFFFFFFF && offset <= privateOffset; } - //operator bool() const { - // return isValid(); - //} - - // Very small helper for debugging + // Small helper for debugging sass::string toString() const; }; ///////////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////////// // Runtime query structure @@ -106,9 +104,7 @@ namespace Sass { EnvRefs* pscope; // Global scope pointers - uint32_t varFrame; - uint32_t mixFrame; - uint32_t fnFrame; + uint32_t framePtr; // Lexical scope entries VidxEnvKeyMap varIdxs; @@ -132,35 +128,30 @@ namespace Sass { // Modules are global so we just link them Module* module = nullptr; - // Rules like `@if`, `@for` etc. are semi-global (permeable). - // Assignments directly in those can bleed to the root scope. - bool isPermeable = false; - // Imports are transparent for variables, functions and mixins // We always need to create entities inside the parent scope bool isImport = false; + // Rules like `@if`, `@for` etc. are semi-global (permeable). + // Assignments directly in those can bleed to the root scope. + bool isSemiGlobal = false; + // Set to true once we are compiled via use or forward // An import does load the sheet, but does not compile it // Compiling it means hard-baking the config vars into it bool isCompiled = false; - // Value constructor EnvRefs(EnvRoot& root, EnvRefs* pscope, - uint32_t varFrame, - uint32_t mixFrame, - uint32_t fnFrame, - bool isPermeable, + uint32_t framePtr, + bool isSemiGlobal, bool isImport) : root(root), pscope(pscope), - varFrame(varFrame), - mixFrame(mixFrame), - fnFrame(fnFrame), - isPermeable(isPermeable), - isImport(isImport) + framePtr(framePtr), + isImport(isImport), + isSemiGlobal(isSemiGlobal) {} ///////////////////////////////////////////////////////////////////////// @@ -171,18 +162,18 @@ namespace Sass { // Register new variable on local stack // Invoked mostly by stylesheet parser - EnvIdx createVariable(const EnvKey& name); + EnvRef createVariable(const EnvKey& name); // Register new function on local stack // Mostly invoked by built-in functions // Then invoked for custom C-API function // Finally for every parsed function rule - EnvIdx createFunction(const EnvKey& name); + EnvRef createFunction(const EnvKey& name); // Register new mixin on local stack // Only invoked for mixin rules // But also for content blocks - EnvIdx createMixin(const EnvKey& name); + EnvRef createMixin(const EnvKey& name); // Get a mixin associated with the under [name]. // Will lookup from the last runtime stack scope. @@ -201,25 +192,25 @@ namespace Sass { // We will move up the runtime stack until we either find a // defined variable with a value or run out of parent scopes. - void findVarIdxs(sass::vector& vidxs, const EnvKey& name) const; + void findVarIdxs(sass::vector& vidxs, const EnvKey& name) const; - EnvIdx findVarIdx(const EnvKey& name, const sass::string& ns) const; - EnvIdx findFnIdx22(const EnvKey& name, const sass::string& ns) const; - EnvIdx findMixIdx22(const EnvKey& name, const sass::string& ns) const; + EnvRef findVarIdx(const EnvKey& name, const sass::string& ns) const; + EnvRef findFnIdx22(const EnvKey& name, const sass::string& ns) const; + EnvRef findMixIdx22(const EnvKey& name, const sass::string& ns) const; - EnvIdx findVarIdx(const EnvKey& name) const; - EnvIdx findFnIdx(const EnvKey& name) const; - EnvIdx findMixIdx(const EnvKey& name) const; + EnvRef findVarIdx(const EnvKey& name) const; + EnvRef findFnIdx(const EnvKey& name) const; + EnvRef findMixIdx(const EnvKey& name) const; bool hasNameSpace(const sass::string& ns, const EnvKey& name) const; // Find function only in local frame - EnvIdx setModVar(const EnvKey& name, Value* value, bool guarded, const SourceSpan& pstate) const; + EnvRef setModVar(const EnvKey& name, Value* value, bool guarded, const SourceSpan& pstate) const; - EnvIdx setModVar(const EnvKey& name, const sass::string& ns, Value* value, bool guarded, const SourceSpan& pstate); + EnvRef setModVar(const EnvKey& name, const sass::string& ns, Value* value, bool guarded, const SourceSpan& pstate); }; @@ -245,7 +236,7 @@ namespace Sass { // Value constructor EnvFrame( Compiler& compiler, - bool isPermeable, + bool isSemiGlobal, bool isModule = false, bool isImport = false); @@ -267,7 +258,7 @@ namespace Sass { // We manage it ourself EnvFrameVector& stack; - // Our runtime object + // Root runtime env EnvRefs* idxs; private: @@ -276,20 +267,18 @@ namespace Sass { friend class Compiler; friend class EnvScope; friend class EnvFrame; - friend class Preloader; friend class EnvRefs; - friend class Eval; - // Growable runtime stack (get offset by xxxFramePtr). + // Growable runtime stack (get offset by xxxStackPtr). // These vectors are the main stacks during runtime. // When a scope with two variables is executed, two // new items are added to the variables stack. If the // same scope is called more than once, its variables // are added multiple times so we can revert to them. - // variables[varFramePtr[vidx.frame] + vidx.offset] - sass::vector variables; - sass::vector mixins; - sass::vector functions; + // variables[varStackPtr[vidx.frame] + vidx.offset] + sass::vector varStack; + sass::vector mixStack; + sass::vector fnStack; // Every scope we execute in sass gets an entry here. // The value stored here is the base address of the @@ -297,9 +286,9 @@ namespace Sass { // Gives current offset into growable runtime stack. // Old values are restored when scopes are exited. // Access it by absolute `frameOffset` - sass::vector varFramePtr; - sass::vector mixFramePtr; - sass::vector fnFramePtr; + sass::vector varStackPtr; + sass::vector mixStackPtr; + sass::vector fnStackPtr; // Internal functions are stored here sass::vector intFunction; @@ -339,24 +328,24 @@ namespace Sass { // Get value instance by stack index reference // Just converting and returning reference to array offset - ValueObj& getVariable(const EnvIdx& vidx); + ValueObj& getVariable(const EnvRef& vidx); ValueObj& getModVar(const uint32_t offset); // Get function instance by stack index reference // Just converting and returning reference to array offset - CallableObj& getFunction(const EnvIdx& vidx); + CallableObj& getFunction(const EnvRef& vidx); CallableObj& getModFn(const uint32_t offset); // Get mixin instance by stack index reference // Just converting and returning reference to array offset - CallableObj& getMixin(const EnvIdx& midx); + CallableObj& getMixin(const EnvRef& midx); CallableObj& getModMix(const uint32_t offset); // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning - void setVariable(const EnvIdx& vidx, Value* value, bool guarded); + void setVariable(const EnvRef& vidx, Value* value, bool guarded); void setModVar(const uint32_t offset, Value* value, bool guarded, const SourceSpan& pstate); - EnvIdx setModVar2(const EnvKey& name, const sass::string& ns, Value* value, bool guraded, const SourceSpan& pstate); + EnvRef setModVar2(const EnvKey& name, const sass::string& ns, Value* value, bool guraded, const SourceSpan& pstate); // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning @@ -364,34 +353,33 @@ namespace Sass { // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning - void setFunction(const EnvIdx& fidx, UserDefinedCallable* value, bool guarded); + void setFunction(const EnvRef& fidx, UserDefinedCallable* value, bool guarded); // Set items on runtime/evaluation phase via references // Just converting reference to array offset and assigning - void setMixin(const EnvIdx& midx, UserDefinedCallable* value, bool guarded); + void setMixin(const EnvRef& midx, UserDefinedCallable* value, bool guarded); // Get a value associated with the variable under [name]. // If [global] flag is given, the lookup will be in the root. // Otherwise lookup will be from the last runtime stack scope. // We will move up the runtime stack until we either find a // defined variable with a value or run out of parent scopes. - Value* findVariable33(const EnvKey& name, const sass::string& ns) const; - EnvIdx findFnIdx( + EnvRef findFnIdx( const EnvKey& name, const sass::string& ns) const; - EnvIdx findMixIdx( + EnvRef findMixIdx( const EnvKey& name, const sass::string& ns) const; - EnvIdx findVarIdx( + EnvRef findVarIdx( const EnvKey& name, const sass::string& ns, bool global = false) const; void findVarIdxs( - sass::vector& vidxs, + sass::vector& vidxs, const EnvKey& name) const; }; @@ -424,8 +412,6 @@ namespace Sass { uint32_t oldFnFrame; uint32_t oldFnOffset; - bool wasActive; - public: // Put frame onto stack @@ -446,45 +432,42 @@ namespace Sass { // Meaning it no scoped items at all if (idxs == nullptr) return; - wasActive = idxs->isCompiled; - idxs->isCompiled = true; - - if (idxs->varFrame != 0xFFFFFFFF) { + if (idxs->framePtr != 0xFFFFFFFF) { // Check if we have scoped variables if (idxs->varIdxs.size() != 0) { // Get offset into variable vector - oldVarOffset = (uint32_t)env.variables.size(); + oldVarOffset = (uint32_t)env.varStack.size(); // Remember previous frame "addresses" - oldVarFrame = env.varFramePtr[idxs->varFrame]; + oldVarFrame = env.varStackPtr[idxs->framePtr]; // Update current frame offset address - env.varFramePtr[idxs->varFrame] = oldVarOffset; + env.varStackPtr[idxs->framePtr] = oldVarOffset; // Create space for variables in this frame scope - env.variables.resize(oldVarOffset + idxs->varIdxs.size()); + env.varStack.resize(oldVarOffset + idxs->varIdxs.size()); } // Check if we have scoped mixins if (idxs->mixIdxs.size() != 0) { // Get offset into mixin vector - oldMixOffset = (uint32_t)env.mixins.size(); + oldMixOffset = (uint32_t)env.mixStack.size(); // Remember previous frame "addresses" - oldMixFrame = env.mixFramePtr[idxs->mixFrame]; + oldMixFrame = env.mixStackPtr[idxs->framePtr]; // Update current frame offset address - env.mixFramePtr[idxs->mixFrame] = oldMixOffset; + env.mixStackPtr[idxs->framePtr] = oldMixOffset; // Create space for mixins in this frame scope - env.mixins.resize(oldMixOffset + idxs->mixIdxs.size()); + env.mixStack.resize(oldMixOffset + idxs->mixIdxs.size()); } // Check if we have scoped functions if (idxs->fnIdxs.size() != 0) { // Get offset into function vector - oldFnOffset = (uint32_t)env.functions.size(); + oldFnOffset = (uint32_t)env.fnStack.size(); // Remember previous frame "addresses" - oldFnFrame = env.fnFramePtr[idxs->fnFrame]; + oldFnFrame = env.fnStackPtr[idxs->framePtr]; // Update current frame offset address - env.fnFramePtr[idxs->fnFrame] = oldFnOffset; + env.fnStackPtr[idxs->framePtr] = oldFnOffset; // Create space for functions in this frame scope - env.functions.resize(oldFnOffset + idxs->fnIdxs.size()); + env.fnStack.resize(oldFnOffset + idxs->fnIdxs.size()); } } @@ -505,42 +488,40 @@ namespace Sass { // Meaning it no scoped items at all if (idxs == nullptr) return; - if (idxs->varFrame != 0xFFFFFFFF) { + if (idxs->framePtr != 0xFFFFFFFF) { // Check if we had scoped variables if (idxs->varIdxs.size() != 0) { // Truncate variable vector - env.variables.resize( + env.varStack.resize( oldVarOffset); // Restore old frame address - env.varFramePtr[idxs->varFrame] = + env.varStackPtr[idxs->framePtr] = oldVarFrame; } // Check if we had scoped mixins if (idxs->mixIdxs.size() != 0) { // Truncate existing vector - env.mixins.resize( + env.mixStack.resize( oldMixOffset); // Restore old frame address - env.mixFramePtr[idxs->mixFrame] = + env.mixStackPtr[idxs->framePtr] = oldMixFrame; } // Check if we had scoped functions if (idxs->fnIdxs.size() != 0) { // Truncate existing vector - env.functions.resize( + env.fnStack.resize( oldFnOffset); // Restore old frame address - env.fnFramePtr[idxs->fnFrame] = + env.fnStackPtr[idxs->framePtr] = oldFnFrame; } } - idxs->isCompiled = wasActive; - // Pop frame from stack env.stack.pop_back(); diff --git a/src/eval.cpp b/src/eval.cpp index 5e36c3c17f..de453e767f 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -311,7 +311,7 @@ namespace Sass { ValueObj value = getParameter(results, i, parameters[i]); // Set lexical variable on scope compiler.varRoot.setVariable( - idxs->varFrame, (uint32_t)i, + idxs->framePtr, (uint32_t)i, value->withoutSlash(), false); } @@ -343,7 +343,7 @@ namespace Sass { restargs = SASS_MEMORY_NEW(ArgumentList, pstate, separator, std::move(positional), std::move(results.named())); // Set last lexical variable on scope - compiler.varRoot.setVariable(idxs->varFrame, + compiler.varRoot.setVariable(idxs->framePtr, (uint32_t)parameters.size(), restargs.ptr(), false); @@ -610,18 +610,14 @@ namespace Sass { ValueFlatMap& named(results.named()); ValueVector& positional(results.positional()); - // Clear existing array, but - // preserve existing memory - named.clear(); positional.clear(); - // Allocate minimum expected size - // Collect positional args by evaluating input arguments - positional.reserve(arguments->positional().size()); + positional.reserve(arguments->positional().size() + 1); for (const auto& arg : arguments->positional()) { positional.emplace_back(arg->accept(this)); } // Collect named args by evaluating input arguments + // named.reserve(arguments->named().size() + 4); for (const auto& kv : arguments->named()) { named.insert(std::make_pair(kv.first, kv.second->accept(this))); } @@ -653,7 +649,7 @@ namespace Sass { } } else { - positional.emplace_back(rest); + positional.emplace_back(std::move(rest)); } if (arguments->kwdRest() == nullptr) { @@ -670,7 +666,7 @@ namespace Sass { return results; } else { - logger456.addFinalStackTrace(keywordRest->pstate()); + callStackFrame csf(logger456, keywordRest->pstate()); throw Exception::RuntimeException(traces, "Variable keyword arguments must be a map (was $keywordRest)."); } @@ -955,7 +951,7 @@ namespace Sass { } if (callable == nullptr) { - compiler.addFinalStackTrace(node->pstate()); + callStackFrame csf(logger456, node->pstate()); throw Exception::RuntimeException( logger456, "Undefined mixin."); } @@ -1284,7 +1280,7 @@ namespace Sass { Value* Eval::visitDebugRule(DebugRule* node) { ValueObj message = node->expression()->accept(this); - EnvIdx fidx = compiler.varRoot.findFnIdx(Keys::debugRule, ""); + EnvRef fidx = compiler.varRoot.findFnIdx(Keys::debugRule, ""); if (fidx.isValid()) { CallableObj& fn = compiler.varRoot.getFunction(fidx); callExternalMessageOverloadFunction(fn, message); @@ -1300,7 +1296,7 @@ namespace Sass { Value* Eval::visitWarnRule(WarnRule* node) { ValueObj message = node->expression()->accept(this); - EnvIdx fidx = compiler.varRoot.findFnIdx(Keys::debugRule, ""); + EnvRef fidx = compiler.varRoot.findFnIdx(Keys::debugRule, ""); if (fidx.isValid()) { CallableObj& fn = compiler.varRoot.getFunction(fidx); callExternalMessageOverloadFunction(fn, message); @@ -1316,7 +1312,7 @@ namespace Sass { Value* Eval::visitErrorRule(ErrorRule* node) { ValueObj message = node->expression()->accept(this); - EnvIdx fidx = compiler.varRoot.findFnIdx(Keys::errorRule, ""); + EnvRef fidx = compiler.varRoot.findFnIdx(Keys::errorRule, ""); if (fidx.isValid()) { CallableObj& fn = compiler.varRoot.getFunction(fidx); callExternalMessageOverloadFunction(fn, message); @@ -1865,7 +1861,7 @@ namespace Sass { for(const auto& kv : map->elements()) { if (String* str = kv.first->isaString()) { - values[str->value()] = kv.second; + values.insert(std::make_pair(str->value(), kv.second)); } else { callStackFrame frame(logger456, pstate); @@ -1883,8 +1879,8 @@ namespace Sass { for (const auto& kv : map->elements()) { if (String* str = kv.first->isaString()) { - values[str->value()] = SASS_MEMORY_NEW( - ValueExpression, map->pstate(), kv.second); + values.insert(std::make_pair(str->value(), SASS_MEMORY_NEW( + ValueExpression, map->pstate(), kv.second))); } else { callStackFrame frame(logger456, pstate); @@ -1917,7 +1913,7 @@ namespace Sass { { if (!isInStyleRule() && !inUnknownAtRule && !inKeyframes) { - logger456.addFinalStackTrace(node->pstate()); + callStackFrame csf(logger456, node->pstate()); throw Exception::RuntimeException(traces, "Declarations may only be used within style rules."); } @@ -2008,7 +2004,7 @@ namespace Sass { NumberObj sass_end = high->assertNumber(logger456, ""); // check if units are valid for sequence if (sass_start->unit() != sass_end->unit()) { - logger456.addFinalStackTrace(f->pstate()); + callStackFrame csf(logger456, f->pstate()); throw Exception::UnitMismatch( logger456, sass_start, sass_end); } @@ -2020,7 +2016,7 @@ namespace Sass { if (f->is_inclusive()) ++end; for (double i = start; i < end; ++i) { NumberObj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); - compiler.varRoot.setVariable(f->idxs->varFrame, 0, it.ptr(), false); + compiler.varRoot.setVariable(f->idxs->framePtr, 0, it.ptr(), false); val = acceptChildren(f); if (val) break; } @@ -2029,7 +2025,7 @@ namespace Sass { if (f->is_inclusive()) --end; for (double i = start; i > end; --i) { NumberObj it = SASS_MEMORY_NEW(Number, low->pstate(), i, sass_end->unit()); - compiler.varRoot.setVariable(f->idxs->varFrame, 0, it.ptr(), false); + compiler.varRoot.setVariable(f->idxs->framePtr, 0, it.ptr(), false); val = acceptChildren(f); if (val) break; } @@ -2041,7 +2037,7 @@ namespace Sass { { if (!isInStyleRule() /* || !declarationName.empty() */) { - logger456.addFinalStackTrace(e->pstate()); + callStackFrame csf(logger456, e->pstate()); throw Exception::RuntimeException(traces, "@extend may only be used within style rules."); } @@ -2054,7 +2050,7 @@ namespace Sass { for (const auto& complex : slist->elements()) { if (complex->size() != 1) { - logger456.addFinalStackTrace(complex->pstate()); + callStackFrame csf(logger456, complex->pstate()); throw Exception::RuntimeException(traces, "complex selectors may not be extended."); } @@ -2072,7 +2068,7 @@ namespace Sass { } sels << "` instead.\nSee http://bit.ly/ExtendCompound for details."; #ifdef SassRestrictCompoundExtending - logger456.addFinalStackTrace(compound->pstate()); + callStackFrame csf(logger456, compound->pstate()); throw Exception::RuntimeException(traces, sels.str()); #else logger456.addDeprecation(sels.str(), compound->pstate()); @@ -2092,7 +2088,7 @@ namespace Sass { } else { - logger456.addFinalStackTrace(complex->pstate()); + callStackFrame csf(logger456, complex->pstate()); throw Exception::RuntimeException(traces, "complex selectors may not be extended."); } @@ -2118,11 +2114,11 @@ namespace Sass { if (variables.size() == 1) { List* variable = SASS_MEMORY_NEW(List, map->pstate(), { key, value }, SASS_SPACE); - compiler.varRoot.setVariable(vidx->varFrame, 0, variable, false); + compiler.varRoot.setVariable(vidx->framePtr, 0, variable, false); } else { - compiler.varRoot.setVariable(vidx->varFrame, 0, key, false); - compiler.varRoot.setVariable(vidx->varFrame, 1, value, false); + compiler.varRoot.setVariable(vidx->framePtr, 0, key, false); + compiler.varRoot.setVariable(vidx->framePtr, 1, value, false); } ValueObj val = acceptChildren(e); if (val) return val.detach(); @@ -2145,11 +2141,11 @@ namespace Sass { // check if we got passed a list of args (investigate) if (List* scalars = item->isaList()) { // Ex if (variables.size() == 1) { - compiler.varRoot.setVariable(vidx->varFrame, 0, scalars, false); + compiler.varRoot.setVariable(vidx->framePtr, 0, scalars, false); } else { for (size_t j = 0, K = variables.size(); j < K; ++j) { - compiler.varRoot.setVariable(vidx->varFrame, (uint32_t)j, + compiler.varRoot.setVariable(vidx->framePtr, (uint32_t)j, j < scalars->size() ? scalars->get(j) : SASS_MEMORY_NEW(Null, expr->pstate()), false); } @@ -2157,10 +2153,10 @@ namespace Sass { } else { if (variables.size() > 0) { - compiler.varRoot.setVariable(vidx->varFrame, 0, item, false); + compiler.varRoot.setVariable(vidx->framePtr, 0, item, false); for (size_t j = 1, K = variables.size(); j < K; ++j) { Value* res = SASS_MEMORY_NEW(Null, expr->pstate()); - compiler.varRoot.setVariable(vidx->varFrame, (uint32_t)j, res, false); + compiler.varRoot.setVariable(vidx->framePtr, (uint32_t)j, res, false); } } } diff --git a/src/flat_map.hpp b/src/flat_map.hpp index 9e4f099038..a50b0b8b27 100644 --- a/src/flat_map.hpp +++ b/src/flat_map.hpp @@ -169,7 +169,22 @@ namespace Sass { { if (count(kv.first) == 0) { // Append the pair - items.push_back(kv); + items.emplace_back(kv); + // Returns success + return true; + } + // Nothing inserted + return false; + } + // EO insert + + // Insert passed key/value pair + // ToDo: should return pair + bool insert(const PAIR&& kv) + { + if (count(kv.first) == 0) { + // Append the pair + items.emplace_back(std::move(kv)); // Returns success return true; } diff --git a/src/fn_colors.cpp b/src/fn_colors.cpp index 58aed5b722..f2478e8ed0 100644 --- a/src/fn_colors.cpp +++ b/src/fn_colors.cpp @@ -153,12 +153,12 @@ namespace Sass { msg << " space-separated"; } msg << " list."; - compiler.addFinalStackTrace(list->pstate()); + callStackFrame csf(compiler, list->pstate()); throw Exception::RuntimeException(compiler, msg.str()); } // Check if we have too many arguments if (list->size() > 3) { - compiler.addFinalStackTrace(list->pstate()); + callStackFrame csf(compiler, list->pstate()); throw Exception::TooManyArguments(compiler, list->size(), 3); } // Check for not enough arguments @@ -297,7 +297,7 @@ namespace Sass { value = max * number->value() / 100; } else { - traces.addFinalStackTrace(number->pstate()); + callStackFrame csf(traces, number->pstate()); throw Exception::RuntimeException(traces, name + ": Expected " + number->inspect() + " to have no units or \"%\"."); @@ -1060,7 +1060,7 @@ namespace Sass { // Support the proprietary Microsoft alpha() function. return getFunctionString(Strings::alpha, pstate, arguments); } - compiler.addFinalStackTrace(arguments[0]->pstate()); + callStackFrame csf(compiler, arguments[0]->pstate()); throw Exception::TooManyArguments(compiler, size, 1); } diff --git a/src/fn_math.cpp b/src/fn_math.cpp index 5e799eb35a..19e5b09429 100644 --- a/src/fn_math.cpp +++ b/src/fn_math.cpp @@ -29,7 +29,7 @@ namespace Sass { if (double factor = number->getUnitConvertFactor(radiants)) { return number->value() * factor; } - compiler.addFinalStackTrace(number->pstate()); + callStackFrame csf(compiler, number->pstate()); throw Exception::RuntimeException(compiler, "$" + vname + ": Expected " + number->inspect() + " to be an angle."); } diff --git a/src/fn_meta.cpp b/src/fn_meta.cpp index 2a0e76609b..feb6d18b20 100644 --- a/src/fn_meta.cpp +++ b/src/fn_meta.cpp @@ -118,7 +118,7 @@ namespace Sass { } } if (hasVar) return SASS_MEMORY_NEW(Boolean, pstate, true); - EnvIdx vidx = compiler.varRoot.findVarIdx(variable->value(), "", true); + EnvRef vidx = compiler.varRoot.findVarIdx(variable->value(), "", true); if (!vidx.isValid()) return SASS_MEMORY_NEW(Boolean, pstate, false); auto& var = compiler.varRoot.getVariable(vidx); return SASS_MEMORY_NEW(Boolean, pstate, !var.isNull()); @@ -130,7 +130,7 @@ namespace Sass { BUILT_IN_FN(variableExists) { String* variable = arguments[0]->assertString(compiler, Sass::Strings::name); - EnvIdx vidx = compiler.varRoot.findVarIdx(variable->value(), ""); + EnvRef vidx = compiler.varRoot.findVarIdx(variable->value(), ""); bool hasVar = false; auto parent = compiler.getCurrentModule(); @@ -179,7 +179,7 @@ namespace Sass { } } if (hasFn) return SASS_MEMORY_NEW(Boolean, pstate, true); - EnvIdx fidx = compiler.varRoot.findFnIdx(variable->value(), ""); + EnvRef fidx = compiler.varRoot.findFnIdx(variable->value(), ""); return SASS_MEMORY_NEW(Boolean, pstate, fidx.isValid()); } @@ -245,7 +245,7 @@ namespace Sass { for (auto entry : refs->varIdxs) { auto name = SASS_MEMORY_NEW(String, pstate, sass::string(entry.first.norm()), true); - EnvIdx vidx(refs->varFrame, entry.second); + EnvRef vidx(refs->framePtr, entry.second); list->insert({ name, compiler. varRoot.getVariable(vidx) }); } @@ -253,7 +253,7 @@ namespace Sass { for (auto entry : root->mergedFwdVar) { auto name = SASS_MEMORY_NEW(String, pstate, sass::string(entry.first.norm()), true); - EnvIdx vidx(0xFFFFFFFF, entry.second); + EnvRef vidx(0xFFFFFFFF, entry.second); list->insert({ name, compiler. varRoot.getVariable(vidx) }); } @@ -281,7 +281,7 @@ namespace Sass { for (auto entry : refs->fnIdxs) { auto name = SASS_MEMORY_NEW(String, pstate, sass::string(entry.first.norm()), true); - EnvIdx fidx(refs->fnFrame, entry.second); + EnvRef fidx(refs->framePtr, entry.second); auto callable = compiler.varRoot.getFunction(fidx); auto fn = SASS_MEMORY_NEW(Function, pstate, callable); list->insert({ name, fn }); @@ -290,7 +290,7 @@ namespace Sass { for (auto entry : root->mergedFwdFn) { auto name = SASS_MEMORY_NEW(String, pstate, sass::string(entry.first.norm()), true); - EnvIdx fidx(0xFFFFFFFF, entry.second); + EnvRef fidx(0xFFFFFFFF, entry.second); auto callable = compiler.varRoot.getFunction(fidx); auto fn = SASS_MEMORY_NEW(Function, pstate, callable); list->insert({ name, fn }); @@ -306,7 +306,7 @@ namespace Sass { /// Like `_environment.findFunction`, but also returns built-in /// globally-available functions. Callable* _getFunction(const EnvKey& name, Compiler& ctx, const sass::string& ns = "") { - EnvIdx fidx = ctx.varRoot.findFnIdx(name, ""); + EnvRef fidx = ctx.varRoot.findFnIdx(name, ""); if (!fidx.isValid()) return nullptr; return ctx.varRoot.getFunction(fidx); } @@ -337,7 +337,7 @@ namespace Sass { EnvRefs* module = pp->second.first; auto it = module->fnIdxs.find(name->value()); if (it != module->fnIdxs.end()) { - EnvIdx fidx({ module->fnFrame, it->second }); + EnvRef fidx({ module->framePtr, it->second }); callable = compiler.varRoot.getFunction(fidx); } } @@ -359,7 +359,7 @@ namespace Sass { throw Exception::RuntimeException(compiler, "This function is available from multiple global modules."); } - EnvIdx fidx({ global->fnFrame, it->second }); + EnvRef fidx({ global->framePtr, it->second }); callable = compiler.varRoot.getFunction(fidx); if (callable) break; } diff --git a/src/fn_selectors.cpp b/src/fn_selectors.cpp index a83ffb6408..35133ed7a2 100644 --- a/src/fn_selectors.cpp +++ b/src/fn_selectors.cpp @@ -51,7 +51,7 @@ namespace Sass { // Iterate over the rest argument list for (Value* arg : arguments[0]->iterator()) { if (arg->isNull()) { - compiler.addFinalStackTrace(arg->pstate()); + callStackFrame csf(compiler, arg->pstate()); throw Exception::RuntimeException(compiler, // "$selectors: " "null is not a valid selector: it must be a string,\n" "a list of strings, or a list of lists of strings."); diff --git a/src/logger.cpp b/src/logger.cpp index f4e2dcf5a0..f114f9c564 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -596,6 +596,7 @@ namespace Sass { i_beg = traces.size() - 1; i_end = sass::string::npos; + const StackTrace* prev = nullptr; for (size_t i = i_beg; i != i_end; i--) { const StackTrace& trace = traces[i]; @@ -607,8 +608,10 @@ namespace Sass { // if (trace.caller.substr(0, 6) == ", in f") continue; if (amount == sass::string::npos || amount > 0) { + if (prev && *prev == trace) continue; printSourceSpan(trace.pstate, os, style); if (amount > 0) --amount; + prev = &trace; } if (showPos) { diff --git a/src/logger.hpp b/src/logger.hpp index 654091d9f8..f9f1839ecd 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -34,24 +34,6 @@ namespace Sass { // Flag for unicode and/or color enum SassLoggerStyle style; - // Append the pstate if not already there - // Only call this right before throwing errors - // DONT USE, CAN LEAD TO SEGFAULTS IF THE ERROR - // WE THROW RIGHT AFTER IS CAUGHT AND BACKTRACKED - void addFinalStackTrace(const SourceSpan& pstate) - { - if (callStack.empty()) { - callStack.push_back(pstate); - } - else { - BackTrace& trace(callStack.back()); - if (!(trace.pstate == pstate)) { - callStack.push_back(pstate); - } - } - } - // EO addFinalStackTrace - private: // Split the line to three parts for error reporting. diff --git a/src/memory/allocator.cpp b/src/memory/allocator.cpp index 0e3ee0f01d..b1f7dc1dce 100644 --- a/src/memory/allocator.cpp +++ b/src/memory/allocator.cpp @@ -5,12 +5,9 @@ #ifdef SASS_CUSTOM_ALLOCATOR #include "memory_pool.hpp" -#endif namespace Sass { -#ifdef SASS_CUSTOM_ALLOCATOR - // You must only use PODs for thread_local. // Objects get very unpredictable init order. static thread_local MemoryPool* pool; @@ -46,6 +43,6 @@ namespace Sass { } -#endif - } + +#endif diff --git a/src/memory/config.hpp b/src/memory/config.hpp index 9b9934cb7f..b9a481a71e 100644 --- a/src/memory/config.hpp +++ b/src/memory/config.hpp @@ -15,7 +15,7 @@ // Minimal alignment for memory fragments. Must be a multiple // of `SASS_MEM_ALIGN` and should not be too big (maybe 1 or 2) -#define SassAllocatorHeadSize sizeof(unsigned int) +// #define SassAllocatorHeadSize sizeof(unsigned int) // The number of bytes we use for our book-keeping before every // memory fragment. Needed to know to which bucket we belongs on @@ -34,12 +34,17 @@ #ifdef SASS_CUSTOM_ALLOCATOR // How many buckets should we have for the free-list - // Determines when allocations go directly to malloc/free - // For maximum size of managed items multiply by alignment - #define SassAllocatorBuckets 640 + // We have a bucket for every `SASS_MEM_ALIGN` * `SassAllocatorBuckets` + // When something requests x amount of memory, we will pad the request + // to be a multiple of `SASS_MEM_ALIGN` and then assign it either to + // an existing bucket or directly use to malloc/free. Otherwise we will + // chunk out a slice of the arena to store it in that memory. + #define SassAllocatorBuckets 960 // The size of the memory pool arenas in bytes. - #define SassAllocatorArenaSize (1024 * 640) + // This determines the minimum allocated memory chunk. + // Whenever we need more memory, we malloc that much. + #define SassAllocatorArenaSize (1024 * 1024) #endif // EO SASS_CUSTOM_ALLOCATOR diff --git a/src/modules.cpp b/src/modules.cpp index fdd1d2ff69..bc7f014d7f 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -22,9 +22,7 @@ namespace Sass { root, nullptr, 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF, - false, // isPermeable + false, // isSemiGlobal false)) // isImport { isBuiltIn = true; diff --git a/src/parser_stylesheet.cpp b/src/parser_stylesheet.cpp index e4ac2c9257..605215e785 100644 --- a/src/parser_stylesheet.cpp +++ b/src/parser_stylesheet.cpp @@ -1253,7 +1253,7 @@ namespace Sass { // Check if name is valid identifier if (url.empty() || isDigit(url[0])) { - context.addFinalStackTrace(state); + callStackFrame csf(context, state); throw Exception::InvalidSassIdentifier(context, url); } @@ -1263,7 +1263,7 @@ namespace Sass { expectStatementSeparator("@use rule"); if (isUseAllowed == false) { - context.addFinalStackTrace(state); + callStackFrame csf(context, state); throw Exception::TardyAtRule( context, Strings::useRule); } @@ -1282,7 +1282,7 @@ namespace Sass { if (startsWithIgnoreCase(url, "sass:", 5)) { if (hasWith) { - context.addFinalStackTrace(rule->pstate()); + callStackFrame csf(context, rule->pstate()); throw Exception::RuntimeException(context, "Built-in modules can't be configured."); } @@ -1294,7 +1294,7 @@ namespace Sass { BuiltInMod* module(context.getModule(name)); if (module == nullptr) { - context.addFinalStackTrace(rule->pstate()); + callStackFrame csf(context, rule->pstate()); throw Exception::RuntimeException(context, "Invalid internal module requested."); } @@ -1356,7 +1356,7 @@ namespace Sass { if (isUseAllowed == false) { SourceSpan state(scanner.relevantSpanFrom(start)); - context.addFinalStackTrace(state); + callStackFrame csf(context, state); throw Exception::ParserException(context, "@forward rules must be written before any other rules."); } @@ -1375,7 +1375,7 @@ namespace Sass { if (startsWithIgnoreCase(url, "sass:", 5)) { if (hasWith) { - context.addFinalStackTrace(rule->pstate()); + callStackFrame csf(context, rule->pstate()); throw Exception::RuntimeException(context, "Built-in modules can't be configured."); } @@ -1386,7 +1386,7 @@ namespace Sass { rule->root(nullptr); } else { - context.addFinalStackTrace(rule->pstate()); + callStackFrame csf(context, rule->pstate()); throw Exception::RuntimeException(context, "Invalid internal module requested."); } @@ -1431,7 +1431,7 @@ namespace Sass { std::move(pstate), {}, {}); } - sass::vector midxs; + sass::vector midxs; IncludeRuleObj rule = SASS_MEMORY_NEW(IncludeRule, scanner.relevantSpanFrom(start), name, ns, arguments); @@ -2740,7 +2740,7 @@ namespace Sass { if (!ns.empty()) { auto pstate(scanner.relevantSpanFrom(start)); - context.addFinalStackTrace(pstate); + callStackFrame csf(context, pstate); throw Exception::ParserException(context, "Variable namespaces not supported!"); } @@ -2911,14 +2911,14 @@ namespace Sass { if (scanner.peekChar() == $dollar) { sass::string name(variableName()); - sass::vector vidxs; + sass::vector vidxs; VariableExpressionObj expression = SASS_MEMORY_NEW(VariableExpression, scanner.relevantSpanFrom(start), name, inLoopDirective, plain); if (isPrivate(name)) { - context.addFinalStackTrace(expression->pstate()); + callStackFrame csf(context, expression->pstate()); throw Exception::ParserException(context, "Private members can't be accessed " "from outside their modules."); diff --git a/src/preloader.cpp b/src/preloader.cpp index bcd8d28d42..11fc1fbc2e 100644 --- a/src/preloader.cpp +++ b/src/preloader.cpp @@ -14,13 +14,14 @@ namespace Sass { Preloader::Preloader(Eval& eval, Root* root) : eval(eval), root(root), + compiler(eval.compiler), chroot77(eval.chroot77), wconfig(eval.wconfig), idxs(root->idxs) {} // During the whole parsing we should keep a big map of - // Variable name to EnvIdx vector with all alternatives + // Variable name to EnvRef vector with all alternatives void Preloader::process() { @@ -32,10 +33,10 @@ namespace Sass { if (sheet && !sheet->empty()) { LOCAL_PTR(Root, chroot77, sheet); LOCAL_PTR(EnvRefs, idxs, sheet->idxs); - ImportStackFrame isf(eval.compiler, sheet->import); - eval.compiler.varRoot.stack.push_back(sheet->idxs); + ImportStackFrame isf(compiler, sheet->import); + compiler.varRoot.stack.push_back(sheet->idxs); for (auto& it : sheet->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } } @@ -43,14 +44,14 @@ namespace Sass { { if (rule->empty()) return; LOCAL_PTR(EnvRefs, idxs, rule->idxs); - eval.compiler.varRoot.stack.push_back(rule->idxs); + compiler.varRoot.stack.push_back(rule->idxs); for (auto& it : rule->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } void Preloader::visitUseRule(UseRule* rule) { - callStackFrame frame(eval.compiler, { + callStackFrame frame(compiler, { rule->pstate(), Strings::useRule }); acceptRoot(eval.loadModRule(rule)); eval.exposeUseRule(rule); @@ -58,7 +59,7 @@ namespace Sass { void Preloader::visitForwardRule(ForwardRule* rule) { - callStackFrame frame(eval.compiler, { + callStackFrame frame(compiler, { rule->pstate(), Strings::forwardRule }); acceptRoot(eval.loadModRule(rule)); eval.exposeFwdRule(rule); @@ -66,7 +67,7 @@ namespace Sass { void Preloader::visitIncludeImport(IncludeImport* rule) { - callStackFrame frame(eval.compiler, { + callStackFrame frame(compiler, { rule->pstate(), Strings::importRule }); acceptRoot(eval.resolveIncludeImport(rule)); eval.exposeImpRule(rule); @@ -81,18 +82,18 @@ namespace Sass { { // const EnvKey& fname(rule->name()); LOCAL_PTR(EnvRefs, idxs, rule->idxs); - eval.compiler.varRoot.stack.push_back(rule->idxs); + compiler.varRoot.stack.push_back(rule->idxs); for (auto& it : rule->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } void Preloader::visitMixinRule(MixinRule* rule) { // const EnvKey& mname(rule->name()); LOCAL_PTR(EnvRefs, idxs, rule->idxs); - eval.compiler.varRoot.stack.push_back(rule->idxs); + compiler.varRoot.stack.push_back(rule->idxs); for (auto& it : rule->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } void Preloader::visitImportRule(ImportRule* rule) @@ -153,9 +154,9 @@ namespace Sass { { if (ContentBlock* content = rule->content()) { LOCAL_PTR(EnvRefs, idxs, content->idxs); - eval.compiler.varRoot.stack.push_back(content->idxs); + compiler.varRoot.stack.push_back(content->idxs); for (auto& it : content->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } } @@ -175,18 +176,18 @@ namespace Sass { for (size_t i = 0; i < vars.size(); i += 1) { idxs->varIdxs.insert({ vars[i], (uint32_t)i }); } - eval.compiler.varRoot.stack.push_back(rule->idxs); + compiler.varRoot.stack.push_back(rule->idxs); for (auto& it : rule->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } void Preloader::visitForRule(ForRule* rule) { LOCAL_PTR(EnvRefs, idxs, rule->idxs); idxs->varIdxs.insert({ rule->varname(), 0 }); - eval.compiler.varRoot.stack.push_back(rule->idxs); + compiler.varRoot.stack.push_back(rule->idxs); for (auto& it : rule->elements()) it->accept(this); - eval.compiler.varRoot.stack.pop_back(); + compiler.varRoot.stack.pop_back(); } void Preloader::visitReturnRule(ReturnRule* rule) diff --git a/src/preloader.hpp b/src/preloader.hpp index 60909e8b0b..027100c6c2 100644 --- a/src/preloader.hpp +++ b/src/preloader.hpp @@ -26,6 +26,7 @@ namespace Sass { Eval& eval; Root* root; + Compiler& compiler; // Alias into context Root*& chroot77; diff --git a/src/randomize.cpp b/src/randomize.cpp index 39081c5ace..8db8e5544a 100644 --- a/src/randomize.cpp +++ b/src/randomize.cpp @@ -41,6 +41,7 @@ namespace Sass { } // Use some sensible default if (seed == 0) { + // Fibonacci/Golden Ratio Hashing seed = 0x9e3779b9; } // Return the seed diff --git a/src/units.cpp b/src/units.cpp index 0430707363..ed73360427 100644 --- a/src/units.cpp +++ b/src/units.cpp @@ -95,7 +95,7 @@ namespace Sass { else if (s == "mm") return UnitType::MM; else if (s == "cm") return UnitType::CM; else if (s == "in") return UnitType::INCH; - else if (s == "q") return UnitType::QMM; + else if (s == "q") return UnitType::QMM; // angle units else if (s == "deg") return UnitType::DEG; else if (s == "grad") return UnitType::GRAD;