diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java index e991d826..a68534e8 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionFilter.java @@ -5,5 +5,7 @@ public interface ExpressionFilter { Expression apply(Expression input); + + boolean accepts(String name, Expression value); } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionsEvaluator.java b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionsEvaluator.java index 3d72e6d5..b13a79ae 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionsEvaluator.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/expressions/ExpressionsEvaluator.java @@ -117,6 +117,11 @@ private ExpressionFilter toEvaluationFilter() { public Expression apply(Expression input) { return evaluate(input); } + + @Override + public boolean accepts(String name, Expression value) { + return true; + } }; } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/KeyValueStorage.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/KeyValueStorage.java index b63d16e4..ffbc5ee3 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/KeyValueStorage.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/KeyValueStorage.java @@ -50,6 +50,15 @@ public T getValue(M key) { return null; } + public void remove(M key) { + Iterator> di = levels.descendingIterator(); + while (di.hasNext()) { + Level level = di.next(); + if (level.contains(key)) + level.remove(key); + } + } + public Set> getAllEntries() { Set> result = new HashSet>(); Iterator> iterator = levels.descendingIterator(); @@ -151,6 +160,10 @@ public T getValue(M key) { return storage.get(key); } + public void remove(M key) { + storage.remove(key); + } + public boolean contains(M key) { return storage.containsKey(key); } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java index 6cd3fc7f..1a3aad44 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/scopes/local/VariablesDeclarationsStorage.java @@ -19,6 +19,10 @@ public Expression getValue(String name) { return coolStorage.getValue(name); } + public void remove(String name) { + coolStorage.remove(name); + } + public void store(AbstractVariableDeclaration node) { store(node.getVariable().getName(), node.getValue()); } @@ -48,7 +52,8 @@ public void addFilteredVariables(ExpressionFilter filter, VariablesDeclarationsS for (Entry entry : variablesSource.coolStorage.getAllEntries()) { String name = entry.getKey(); Expression value = entry.getValue(); - store(name, filter.apply(value)); + if (filter.accepts(name, value)) + store(name, filter.apply(value)); } } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsRulesetsSolver.java similarity index 91% rename from src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java rename to src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsRulesetsSolver.java index 27b2e58b..19f642ec 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsSolver.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/MixinsRulesetsSolver.java @@ -26,7 +26,7 @@ import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory; import com.github.sommeri.less4j.core.problems.ProblemsHandler; -class MixinsSolver { +class MixinsRulesetsSolver { private final ProblemsHandler problemsHandler; private final ReferencesSolver parentSolver; @@ -35,7 +35,7 @@ class MixinsSolver { private final DefaultGuardHelper defaultGuardHelper; private final CallerCalleeScopeJoiner scopeManipulation = new CallerCalleeScopeJoiner(); - public MixinsSolver(ReferencesSolver parentSolver, AstNodesStack semiCompiledNodes, ProblemsHandler problemsHandler, Configuration configuration) { + public MixinsRulesetsSolver(ReferencesSolver parentSolver, AstNodesStack semiCompiledNodes, ProblemsHandler problemsHandler, Configuration configuration) { this.parentSolver = parentSolver; this.semiCompiledNodes = semiCompiledNodes; this.problemsHandler = problemsHandler; @@ -43,7 +43,7 @@ public MixinsSolver(ReferencesSolver parentSolver, AstNodesStack semiCompiledNod this.defaultGuardHelper = new DefaultGuardHelper(problemsHandler); } - private BodyCompilationResult resolveCalledBody(final IScope callerScope, final BodyOwner bodyOwner, final IScope bodyWorkingScope) { + private BodyCompilationResult resolveCalledBody(final IScope callerScope, final BodyOwner bodyOwner, final IScope bodyWorkingScope, final ScopeProtection protectionLevel) { final ExpressionsEvaluator expressionEvaluator = new ExpressionsEvaluator(bodyWorkingScope, problemsHandler, configuration); final IScope referencedMixinScope = bodyWorkingScope; @@ -57,7 +57,7 @@ public BodyCompilationResult run() { // collect variables and mixins to be imported IScope returnValues = ScopeFactory.createDummyScope(); - returnValues.addFilteredVariables(new ImportedScopeFilter(expressionEvaluator, callerScope), referencedMixinScope); + returnValues.addFilteredVariables(new ImportedScopeFilter(expressionEvaluator, callerScope, protectionLevel), referencedMixinScope); List unmodifiedMixinsToImport = referencedMixinScope.getAllMixins(); List allMixinsToImport = scopeManipulation.mixinsToImport(callerScope, referencedMixinScope, unmodifiedMixinsToImport); @@ -121,7 +121,7 @@ public void run() { if (guardValue != GuardValue.DO_NOT_USE) { //OPTIMIZATION POSSIBLE: there is no need to compile mixins at this point, some of them are not going to be //used and create snapshot operation is cheap now. It should be done later on. - BodyCompilationResult compiled = resolveCalledBody(callerScope, fullMixin.getMixin(), mixinWorkingScope); + BodyCompilationResult compiled = resolveCalledBody(callerScope, fullMixin.getMixin(), mixinWorkingScope, ScopeProtection.LOCAL_ONLY); //mark the mixin according to its default() function use compiled.setGuardValue(guardValue); //store the mixin as candidate @@ -149,7 +149,7 @@ public void run() { public GeneralBody buildDetachedRulesetReplacement(DetachedRulesetReference reference, IScope callerScope, DetachedRuleset detachedRuleset, IScope detachedRulesetScope) { IScope mixinWorkingScope = scopeManipulation.joinIfIndependent(callerScope, detachedRulesetScope); - BodyCompilationResult compiled = resolveCalledBody(callerScope, detachedRuleset, mixinWorkingScope); + BodyCompilationResult compiled = resolveCalledBody(callerScope, detachedRuleset, mixinWorkingScope, ScopeProtection.FULL); GeneralBody result = new GeneralBody(reference.getUnderlyingStructure()); result.addMembers(compiled.getReplacement()); @@ -186,11 +186,13 @@ class ImportedScopeFilter implements ExpressionFilter { private final ExpressionsEvaluator expressionEvaluator; private final IScope importTargetScope; private final CallerCalleeScopeJoiner scopeManipulation = new CallerCalleeScopeJoiner(); + private final ScopeProtection protectionLevel; - public ImportedScopeFilter(ExpressionsEvaluator expressionEvaluator, IScope importTargetScope) { + public ImportedScopeFilter(ExpressionsEvaluator expressionEvaluator, IScope importTargetScope, ScopeProtection protectionLevel) { super(); this.expressionEvaluator = expressionEvaluator; this.importTargetScope = importTargetScope; + this.protectionLevel = protectionLevel; } public Expression apply(Expression input) { @@ -207,6 +209,14 @@ private IScope apply(IScope input) { return scopeManipulation.joinIfIndependentAndPreserveContent(importTargetScope, input); } + @Override + public boolean accepts(String name, Expression value) { + if (protectionLevel==ScopeProtection.LOCAL_ONLY) + return true; + + return importTargetScope.getValue(name)==null; + } + } } diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java index f9c8b79e..60ad65d4 100644 --- a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ReferencesSolver.java @@ -42,7 +42,7 @@ public class ReferencesSolver { public static final String ALL_ARGUMENTS = "@arguments"; private ASTManipulator manipulator = new ASTManipulator(); - private final MixinsSolver mixinsSolver; + private final MixinsRulesetsSolver mixinsSolver; private final ProblemsHandler problemsHandler; private final Configuration configuration; private final AstNodesStack semiCompiledNodes = new AstNodesStack(); @@ -52,7 +52,7 @@ public ReferencesSolver(ProblemsHandler problemsHandler, Configuration configura this.problemsHandler = problemsHandler; this.configuration = configuration; this.stringInterpolator = new StringInterpolator(problemsHandler); - this.mixinsSolver = new MixinsSolver(this, semiCompiledNodes, problemsHandler, configuration); + this.mixinsSolver = new MixinsRulesetsSolver(this, semiCompiledNodes, problemsHandler, configuration); } public void solveReferences(final ASTCssNode node, final IScope scope) { diff --git a/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ScopeProtection.java b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ScopeProtection.java new file mode 100644 index 00000000..1c371f4f --- /dev/null +++ b/src/main/java/com/github/sommeri/less4j/core/compiler/stages/ScopeProtection.java @@ -0,0 +1,5 @@ +package com.github.sommeri.less4j.core.compiler.stages; + +public enum ScopeProtection { + LOCAL_ONLY, FULL +} diff --git a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.css b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.css index 0b0c2b2e..5b08b582 100644 --- a/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.css +++ b/src/test/resources/compile-basic-features/detached-rulesets/detached-rulesets-lessjs.css @@ -2,7 +2,7 @@ color: black; one: 1px; four: magic-frame; - visible-one: hidden and if you see this in the output its a bug; + visible-one: visible; visible-two: visible; } .wrap-selector {