From ce1ea0d4a30ab289e8d3cdcfb7344746eb77f2fc Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Sat, 2 Dec 2017 14:30:47 -0800 Subject: [PATCH 1/3] Add a ParentStatement class for Sass statements with children --- lib/src/ast/sass/statement/at_root_rule.dart | 8 +++----- lib/src/ast/sass/statement/at_rule.dart | 11 +++-------- lib/src/ast/sass/statement/declaration.dart | 11 ++++------- lib/src/ast/sass/statement/each_rule.dart | 8 +++----- lib/src/ast/sass/statement/for_rule.dart | 10 ++++------ lib/src/ast/sass/statement/media_rule.dart | 8 +++----- lib/src/ast/sass/statement/parent.dart | 13 +++++++++++++ lib/src/ast/sass/statement/style_rule.dart | 8 +++----- lib/src/ast/sass/statement/stylesheet.dart | 8 +++----- lib/src/ast/sass/statement/supports_rule.dart | 8 +++----- lib/src/ast/sass/statement/while_rule.dart | 8 +++----- 11 files changed, 45 insertions(+), 56 deletions(-) create mode 100644 lib/src/ast/sass/statement/parent.dart diff --git a/lib/src/ast/sass/statement/at_root_rule.dart b/lib/src/ast/sass/statement/at_root_rule.dart index 3df4ce0b..1335510b 100644 --- a/lib/src/ast/sass/statement/at_root_rule.dart +++ b/lib/src/ast/sass/statement/at_root_rule.dart @@ -7,22 +7,20 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../interpolation.dart'; import '../statement.dart'; +import 'parent.dart'; /// An `@at-root` rule. /// /// This moves it contents "up" the tree through parent nodes. -class AtRootRule implements Statement { +class AtRootRule extends ParentStatement { /// The query specifying which statements this should move its contents /// through. final Interpolation query; - /// The statements contained in [this]. - final List children; - final FileSpan span; AtRootRule(Iterable children, this.span, {this.query}) - : children = new List.from(children); + : super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitAtRootRule(this); diff --git a/lib/src/ast/sass/statement/at_rule.dart b/lib/src/ast/sass/statement/at_rule.dart index c7e00a53..7ea3ff33 100644 --- a/lib/src/ast/sass/statement/at_rule.dart +++ b/lib/src/ast/sass/statement/at_rule.dart @@ -8,9 +8,10 @@ import '../../../utils.dart'; import '../../../visitor/interface/statement.dart'; import '../interpolation.dart'; import '../statement.dart'; +import 'parent.dart'; /// An unknown at-rule. -class AtRule implements Statement { +class AtRule extends ParentStatement { /// The name of this rule. final String name; @@ -20,18 +21,12 @@ class AtRule implements Statement { /// The value of this rule. final Interpolation value; - /// The children of this rule. - /// - /// If [children] is empty, [this] was declared with empty brackets. If - /// [children] is null, it was declared without brackets. - final List children; - final FileSpan span; AtRule(String name, this.span, {this.value, Iterable children}) : name = name, normalizedName = unvendor(name), - children = children == null ? null : new List.unmodifiable(children); + super(children == null ? null : new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitAtRule(this); diff --git a/lib/src/ast/sass/statement/declaration.dart b/lib/src/ast/sass/statement/declaration.dart index c00a1bac..0feaf841 100644 --- a/lib/src/ast/sass/statement/declaration.dart +++ b/lib/src/ast/sass/statement/declaration.dart @@ -8,24 +8,21 @@ import '../../../visitor/interface/statement.dart'; import '../expression.dart'; import '../interpolation.dart'; import '../statement.dart'; +import 'parent.dart'; /// A declaration (that is, a `name: value` pair). -class Declaration implements Statement { +class Declaration extends ParentStatement { /// The name of this declaration. final Interpolation name; /// The value of this declaration. final Expression value; - /// The children of this declaration. - /// - /// This is `null` if the declaration has no children. - final List children; - final FileSpan span; Declaration(this.name, this.span, {this.value, Iterable children}) - : children = children == null ? null : new List.unmodifiable(children); + : super(children = + children == null ? null : new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitDeclaration(this); diff --git a/lib/src/ast/sass/statement/each_rule.dart b/lib/src/ast/sass/statement/each_rule.dart index 94a2a227..fe5566ed 100644 --- a/lib/src/ast/sass/statement/each_rule.dart +++ b/lib/src/ast/sass/statement/each_rule.dart @@ -7,26 +7,24 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../expression.dart'; import '../statement.dart'; +import 'parent.dart'; /// An `@each` rule. /// /// This iterates over values in a list or map. -class EachRule implements Statement { +class EachRule extends ParentStatement { /// The variables assigned for each iteration. final List variables; /// The expression whose value this iterates through. final Expression list; - /// The child statements executed for each iteration. - final List children; - final FileSpan span; EachRule(Iterable variables, this.list, Iterable children, this.span) : variables = new List.unmodifiable(variables), - children = new List.unmodifiable(children); + super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitEachRule(this); diff --git a/lib/src/ast/sass/statement/for_rule.dart b/lib/src/ast/sass/statement/for_rule.dart index eb936096..e3661ee5 100644 --- a/lib/src/ast/sass/statement/for_rule.dart +++ b/lib/src/ast/sass/statement/for_rule.dart @@ -7,11 +7,12 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../expression.dart'; import '../statement.dart'; +import 'parent.dart'; /// A `@for` rule. /// /// This iterates a set number of times. -class ForRule implements Statement { +class ForRule extends ParentStatement { /// The name of the variable that will contain the index value. final String variable; @@ -24,16 +25,13 @@ class ForRule implements Statement { /// Whether [to] is exclusive. final bool isExclusive; - /// The child statements executed for each iteration. - final List children; - final FileSpan span; ForRule(this.variable, this.from, this.to, Iterable children, this.span, {bool exclusive: true}) - : children = new List.unmodifiable(children), - isExclusive = exclusive; + : isExclusive = exclusive, + super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitForRule(this); diff --git a/lib/src/ast/sass/statement/media_rule.dart b/lib/src/ast/sass/statement/media_rule.dart index 52263833..2bb63bda 100644 --- a/lib/src/ast/sass/statement/media_rule.dart +++ b/lib/src/ast/sass/statement/media_rule.dart @@ -7,21 +7,19 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../interpolation.dart'; import '../statement.dart'; +import 'parent.dart'; /// A `@media` rule. -class MediaRule implements Statement { +class MediaRule extends ParentStatement { /// The query that determines on which platforms the styles will be in effect. /// /// This is only parsed after the interpolation has been resolved. final Interpolation query; - /// The contents of this rule. - final List children; - final FileSpan span; MediaRule(this.query, Iterable children, this.span) - : children = new List.unmodifiable(children); + : super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitMediaRule(this); diff --git a/lib/src/ast/sass/statement/parent.dart b/lib/src/ast/sass/statement/parent.dart new file mode 100644 index 00000000..84d3f2ad --- /dev/null +++ b/lib/src/ast/sass/statement/parent.dart @@ -0,0 +1,13 @@ +// Copyright 2017 Google Inc. Use of this source code is governed by an +// MIT-style license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import '../statement.dart'; + +/// A [Statement] that can have child statements. +abstract class ParentStatement implements Statement { + /// The child statements of this statement. + final List children; + + ParentStatement(this.children); +} diff --git a/lib/src/ast/sass/statement/style_rule.dart b/lib/src/ast/sass/statement/style_rule.dart index afdde4d2..7732aff7 100644 --- a/lib/src/ast/sass/statement/style_rule.dart +++ b/lib/src/ast/sass/statement/style_rule.dart @@ -7,23 +7,21 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../interpolation.dart'; import '../statement.dart'; +import 'parent.dart'; /// A style rule. /// /// This applies style declarations to elements that match a given selector. -class StyleRule implements Statement { +class StyleRule extends ParentStatement { /// The selector to which the declaration will be applied. /// /// This is only parsed after the interpolation has been resolved. final Interpolation selector; - /// The declarations associated with the selector. - final List children; - final FileSpan span; StyleRule(this.selector, Iterable children, this.span) - : children = new List.unmodifiable(children); + : super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitStyleRule(this); diff --git a/lib/src/ast/sass/statement/stylesheet.dart b/lib/src/ast/sass/statement/stylesheet.dart index 4abc7c9e..f3bbfefd 100644 --- a/lib/src/ast/sass/statement/stylesheet.dart +++ b/lib/src/ast/sass/statement/stylesheet.dart @@ -8,18 +8,16 @@ import '../../../visitor/interface/statement.dart'; import '../../../parse/sass.dart'; import '../../../parse/scss.dart'; import '../statement.dart'; +import 'parent.dart'; /// A Sass stylesheet. /// /// This is the root Sass node. It contains top-level statements. -class Stylesheet implements Statement { - /// The top-level statements of this Sass stylesheet. - final List children; - +class Stylesheet extends ParentStatement { final FileSpan span; Stylesheet(Iterable children, this.span) - : children = new List.unmodifiable(children); + : super(new List.unmodifiable(children)); /// Parses an indented-syntax stylesheet from [contents]. /// diff --git a/lib/src/ast/sass/statement/supports_rule.dart b/lib/src/ast/sass/statement/supports_rule.dart index 98d1c60e..e065c866 100644 --- a/lib/src/ast/sass/statement/supports_rule.dart +++ b/lib/src/ast/sass/statement/supports_rule.dart @@ -7,19 +7,17 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../statement.dart'; import '../supports_condition.dart'; +import 'parent.dart'; /// A `@supports` rule. -class SupportsRule implements Statement { +class SupportsRule extends ParentStatement { /// The condition that selects what browsers this rule targets. final SupportsCondition condition; - /// The contents of this rule. - final List children; - final FileSpan span; SupportsRule(this.condition, Iterable children, this.span) - : children = new List.from(children); + : super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitSupportsRule(this); diff --git a/lib/src/ast/sass/statement/while_rule.dart b/lib/src/ast/sass/statement/while_rule.dart index c496aaf7..302c4a6f 100644 --- a/lib/src/ast/sass/statement/while_rule.dart +++ b/lib/src/ast/sass/statement/while_rule.dart @@ -7,22 +7,20 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../expression.dart'; import '../statement.dart'; +import 'parent.dart'; /// A `@while` rule. /// /// This repeatedly executes a block of code as long as a statement evaluates to /// `true`. -class WhileRule implements Statement { +class WhileRule extends ParentStatement { /// The condition that determines whether the block will be executed. final Expression condition; - /// The code to execute repeatedly. - final List children; - final FileSpan span; WhileRule(this.condition, Iterable children, this.span) - : children = new List.unmodifiable(children); + : super(new List.unmodifiable(children)); T accept(StatementVisitor visitor) => visitor.visitWhileRule(this); From 3de6680bcb12951f3573adc1a650a05e92887175 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Sat, 2 Dec 2017 14:34:03 -0800 Subject: [PATCH 2/3] Add an explicit IfClause class --- lib/src/ast/sass/statement/if_rule.dart | 41 ++++++++++++++++++------- lib/src/parse/stylesheet.dart | 8 ++--- lib/src/visitor/async_evaluate.dart | 9 +++--- lib/src/visitor/evaluate.dart | 11 ++++--- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/lib/src/ast/sass/statement/if_rule.dart b/lib/src/ast/sass/statement/if_rule.dart index e8ef6839..945b5f6a 100644 --- a/lib/src/ast/sass/statement/if_rule.dart +++ b/lib/src/ast/sass/statement/if_rule.dart @@ -3,7 +3,6 @@ // https://opensource.org/licenses/MIT. import 'package:source_span/source_span.dart'; -import 'package:tuple/tuple.dart'; import '../../../visitor/interface/statement.dart'; import '../expression.dart'; @@ -18,30 +17,50 @@ class IfRule implements Statement { /// The first clause whose expression evaluates to `true` will have its /// statements executed. If no expression evaluates to `true`, `lastClause` /// will be executed if it's not `null`. - final List>> clauses; + final List clauses; /// The final, unconditional `@else` clause. /// /// This is `null` if there is no unconditional `@else`. - final List lastClause; + final IfClause lastClause; final FileSpan span; - IfRule(Iterable>> clauses, this.span, - {Iterable lastClause}) - : clauses = new List.unmodifiable(clauses.map((pair) => - new Tuple2(pair.item1, new List.unmodifiable(pair.item2)))), - lastClause = - lastClause == null ? null : new List.unmodifiable(lastClause); + IfRule(Iterable clauses, this.span, {this.lastClause}) + : clauses = new List.unmodifiable(clauses) { + assert(clauses.every((clause) => clause.expression != null)); + assert(lastClause?.expression == null); + } T accept(StatementVisitor visitor) => visitor.visitIfRule(this); String toString() { var first = true; - return clauses.map((pair) { + return clauses.map((clause) { var name = first ? 'if' : 'else'; first = false; - return '@$name ${pair.item1} {${pair.item2.join(" ")}}'; + return '@$name ${clause.expression} {${clause.children.join(" ")}}'; }).join(' '); } } + +/// A single clause in an `@if` rule. +class IfClause { + /// The expression to evaluate to determine whether to run this rule, or + /// `null` if this is the final unconditional `@else` clause. + final Expression expression; + + /// The statements to evaluate if this clause matches. + final List children; + + IfClause(this.expression, Iterable children) + : children = new List.unmodifiable(children); + + IfClause.last(Iterable children) + : expression = null, + children = new List.unmodifiable(children); + + String toString() => + (expression == null ? "@else" : "@if $expression") + + " {${children.join(' ')}}"; +} diff --git a/lib/src/parse/stylesheet.dart b/lib/src/parse/stylesheet.dart index 25445e4d..824f786f 100644 --- a/lib/src/parse/stylesheet.dart +++ b/lib/src/parse/stylesheet.dart @@ -671,16 +671,16 @@ abstract class StylesheetParser extends Parser { var expression = _expression(); var children = this.children(child); - var clauses = [new Tuple2(expression, children)]; - List lastClause; + var clauses = [new IfClause(expression, children)]; + IfClause lastClause; while (scanElse(ifIndentation)) { whitespace(); if (scanIdentifier("if")) { whitespace(); - clauses.add(new Tuple2(_expression(), this.children(child))); + clauses.add(new IfClause(_expression(), this.children(child))); } else { - lastClause = this.children(child); + lastClause = new IfClause.last(this.children(child)); break; } } diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 432a2277..113c22d6 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -634,16 +634,17 @@ class _EvaluateVisitor Future visitIfRule(IfRule node) async { var clause = node.lastClause; - for (var pair in node.clauses) { - if ((await pair.item1.accept(this)).isTruthy) { - clause = pair.item2; + for (var clauseToCheck in node.clauses) { + if ((await clauseToCheck.expression.accept(this)).isTruthy) { + clause = clauseToCheck; break; } } if (clause == null) return null; return await _environment.scope( - () => _handleReturn(clause, (child) => child.accept(this)), + () => _handleReturn( + clause.children, (child) => child.accept(this)), semiGlobal: true); } diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index 3dd4d288..aadfaebb 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_evaluate.dart. // See tool/synchronize.dart for details. // -// Checksum: ae64ba442752642066f0e9e038f5f2c1bbfa866b +// Checksum: 33e96540f3e5999ed172d168221b27910e8672b1 import 'dart:math' as math; @@ -629,16 +629,17 @@ class _EvaluateVisitor Value visitIfRule(IfRule node) { var clause = node.lastClause; - for (var pair in node.clauses) { - if (pair.item1.accept(this).isTruthy) { - clause = pair.item2; + for (var clauseToCheck in node.clauses) { + if (clauseToCheck.expression.accept(this).isTruthy) { + clause = clauseToCheck; break; } } if (clause == null) return null; return _environment.scope( - () => _handleReturn(clause, (child) => child.accept(this)), + () => _handleReturn( + clause.children, (child) => child.accept(this)), semiGlobal: true); } From 3de531b9bc5eaf6729233977086c4354a0ed277f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Sat, 2 Dec 2017 14:36:52 -0800 Subject: [PATCH 3/3] Don't create scopes when nothing is declared This seems to provide a fairly minimal speed boost, but it's more than nothing. It's also a pretty easy change now that we have ParentStatement. --- lib/src/ast/sass/statement/if_rule.dart | 19 +++++++-- lib/src/ast/sass/statement/parent.dart | 13 ++++++- lib/src/async_environment.dart | 32 ++++++++++++--- lib/src/environment.dart | 33 +++++++++++++--- lib/src/visitor/async_evaluate.dart | 50 ++++++++++++++---------- lib/src/visitor/evaluate.dart | 52 +++++++++++++++---------- 6 files changed, 142 insertions(+), 57 deletions(-) diff --git a/lib/src/ast/sass/statement/if_rule.dart b/lib/src/ast/sass/statement/if_rule.dart index 945b5f6a..23063843 100644 --- a/lib/src/ast/sass/statement/if_rule.dart +++ b/lib/src/ast/sass/statement/if_rule.dart @@ -7,6 +7,9 @@ import 'package:source_span/source_span.dart'; import '../../../visitor/interface/statement.dart'; import '../expression.dart'; import '../statement.dart'; +import 'function_rule.dart'; +import 'mixin_rule.dart'; +import 'variable_declaration.dart'; /// An `@if` rule. /// @@ -53,12 +56,20 @@ class IfClause { /// The statements to evaluate if this clause matches. final List children; - IfClause(this.expression, Iterable children) - : children = new List.unmodifiable(children); + /// Whether any of [children] is a variable, function, or mixin declaration. + final bool hasDeclarations; + + IfClause(Expression expression, Iterable children) + : this._(expression, new List.unmodifiable(children)); IfClause.last(Iterable children) - : expression = null, - children = new List.unmodifiable(children); + : this._(null, new List.unmodifiable(children)); + + IfClause._(this.expression, this.children) + : hasDeclarations = children.any((child) => + child is VariableDeclaration || + child is FunctionRule || + child is MixinRule); String toString() => (expression == null ? "@else" : "@if $expression") + diff --git a/lib/src/ast/sass/statement/parent.dart b/lib/src/ast/sass/statement/parent.dart index 84d3f2ad..98b8641a 100644 --- a/lib/src/ast/sass/statement/parent.dart +++ b/lib/src/ast/sass/statement/parent.dart @@ -3,11 +3,22 @@ // https://opensource.org/licenses/MIT. import '../statement.dart'; +import 'function_rule.dart'; +import 'mixin_rule.dart'; +import 'variable_declaration.dart'; /// A [Statement] that can have child statements. abstract class ParentStatement implements Statement { /// The child statements of this statement. final List children; - ParentStatement(this.children); + /// Whether any of [children] is a variable, function, or mixin declaration. + final bool hasDeclarations; + + ParentStatement(this.children) + : hasDeclarations = children?.any((child) => + child is VariableDeclaration || + child is FunctionRule || + child is MixinRule) ?? + false; } diff --git a/lib/src/async_environment.dart b/lib/src/async_environment.dart index 57b172c2..9ab42679 100644 --- a/lib/src/async_environment.dart +++ b/lib/src/async_environment.dart @@ -82,11 +82,11 @@ class AsyncEnvironment { bool get inMixin => _inMixin; var _inMixin = false; - /// Whether the environment is currently in a semi-global scope. + /// Whether the environment is currently in a global or semi-global scope. /// /// A semi-global scope can assign to global variables, but it doesn't declare /// them by default. - var _inSemiGlobalScope = false; + var _inSemiGlobalScope = true; AsyncEnvironment() : _variables = [normalizedMap()], @@ -279,10 +279,32 @@ class AsyncEnvironment { /// Variables, functions, and mixins declared in a given scope are /// inaccessible outside of it. If [semiGlobal] is passed, this scope can /// assign to global variables without a `!global` declaration. - Future scope(Future callback(), {bool semiGlobal: false}) async { - semiGlobal = semiGlobal && (_inSemiGlobalScope || _variables.length == 1); + /// + /// If [when] is false, this doesn't create a new scope and instead just + /// executes [callback] and returns its result. + Future scope(Future callback(), + {bool semiGlobal: false, bool when: true}) async { + if (!when) { + // We still have to track semi-globalness so that + // + // div { + // @if ... { + // $x: y; + // } + // } + // + // doesn't assign to the global scope. + var wasInSemiGlobalScope = _inSemiGlobalScope; + _inSemiGlobalScope = semiGlobal; + try { + return await callback(); + } finally { + _inSemiGlobalScope = wasInSemiGlobalScope; + } + } + + semiGlobal = semiGlobal && _inSemiGlobalScope; - // TODO: avoid creating a new scope if no variables are declared. var wasInSemiGlobalScope = _inSemiGlobalScope; _inSemiGlobalScope = semiGlobal; _variables.add(normalizedMap()); diff --git a/lib/src/environment.dart b/lib/src/environment.dart index f4fa839c..7e4dcde9 100644 --- a/lib/src/environment.dart +++ b/lib/src/environment.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_environment.dart. // See tool/synchronize.dart for details. // -// Checksum: 97410abbd78c3bbc9899f3ac460cc0736218bfe3 +// Checksum: 4669f41a70664bd5f391c6b8627264a5d0ad8f6c import 'ast/sass.dart'; import 'callable.dart'; @@ -85,11 +85,11 @@ class Environment { bool get inMixin => _inMixin; var _inMixin = false; - /// Whether the environment is currently in a semi-global scope. + /// Whether the environment is currently in a global or semi-global scope. /// /// A semi-global scope can assign to global variables, but it doesn't declare /// them by default. - var _inSemiGlobalScope = false; + var _inSemiGlobalScope = true; Environment() : _variables = [normalizedMap()], @@ -282,10 +282,31 @@ class Environment { /// Variables, functions, and mixins declared in a given scope are /// inaccessible outside of it. If [semiGlobal] is passed, this scope can /// assign to global variables without a `!global` declaration. - T scope(T callback(), {bool semiGlobal: false}) { - semiGlobal = semiGlobal && (_inSemiGlobalScope || _variables.length == 1); + /// + /// If [when] is false, this doesn't create a new scope and instead just + /// executes [callback] and returns its result. + T scope(T callback(), {bool semiGlobal: false, bool when: true}) { + if (!when) { + // We still have to track semi-globalness so that + // + // div { + // @if ... { + // $x: y; + // } + // } + // + // doesn't assign to the global scope. + var wasInSemiGlobalScope = _inSemiGlobalScope; + _inSemiGlobalScope = semiGlobal; + try { + return callback(); + } finally { + _inSemiGlobalScope = wasInSemiGlobalScope; + } + } + + semiGlobal = semiGlobal && _inSemiGlobalScope; - // TODO: avoid creating a new scope if no variables are declared. var wasInSemiGlobalScope = _inSemiGlobalScope; _inSemiGlobalScope = semiGlobal; _variables.add(normalizedMap()); diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 113c22d6..13828776 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -327,7 +327,7 @@ class _EvaluateVisitor for (var child in node.children) { await child.accept(this); } - }); + }, when: node.hasDeclarations); return null; } @@ -341,7 +341,7 @@ class _EvaluateVisitor } if (outerCopy != null) root.addChild(outerCopy); - await _scopeForAtRoot(innerCopy ?? root, query, included)(() async { + await _scopeForAtRoot(node, innerCopy ?? root, query, included)(() async { for (var child in node.children) { await child.accept(this); } @@ -386,14 +386,14 @@ class _EvaluateVisitor /// This returns a callback that adjusts various instance variables for its /// duration, based on which rules are excluded by [query]. It always assigns /// [_parent] to [newParent]. - _ScopeCallback _scopeForAtRoot(CssParentNode newParent, AtRootQuery query, - List included) { + _ScopeCallback _scopeForAtRoot(AtRootRule node, CssParentNode newParent, + AtRootQuery query, List included) { var scope = (Future callback()) async { // We can't use [_withParent] here because it'll add the node to the tree // in the wrong place. var oldParent = _parent; _parent = newParent; - await _environment.scope(callback); + await _environment.scope(callback, when: node.hasDeclarations); _parent = oldParent; }; @@ -488,7 +488,7 @@ class _EvaluateVisitor for (var child in node.children) { await child.accept(this); } - }); + }, when: node.hasDeclarations); _declarationName = oldDeclarationName; } @@ -588,9 +588,11 @@ class _EvaluateVisitor for (var child in node.children) { await child.accept(this); } - }); + }, scopeWhen: false); } - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); _inUnknownAtRule = wasInUnknownAtRule; _inKeyframes = wasInKeyframes; @@ -645,7 +647,8 @@ class _EvaluateVisitor return await _environment.scope( () => _handleReturn( clause.children, (child) => child.accept(this)), - semiGlobal: true); + semiGlobal: true, + when: clause.hasDeclarations); } Future visitImportRule(ImportRule node) async { @@ -878,10 +881,12 @@ class _EvaluateVisitor for (var child in node.children) { await child.accept(this); } - }); + }, scopeWhen: false); } }); - }, through: (node) => node is CssStyleRule || node is CssMediaRule); + }, + through: (node) => node is CssStyleRule || node is CssMediaRule, + scopeWhen: node.hasDeclarations); return null; } @@ -931,7 +936,9 @@ class _EvaluateVisitor for (var child in node.children) { await child.accept(this); } - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); return null; } @@ -955,7 +962,9 @@ class _EvaluateVisitor await child.accept(this); } }); - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; if (!_inStyleRule) { @@ -992,7 +1001,9 @@ class _EvaluateVisitor } }); } - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); return null; } @@ -1068,7 +1079,7 @@ class _EvaluateVisitor if (result != null) return result; } return null; - }, semiGlobal: true); + }, semiGlobal: true, when: node.hasDeclarations); } // ## Expressions @@ -1250,9 +1261,6 @@ class _EvaluateVisitor _verifyArguments( positional.length, named, callable.declaration.arguments, span); - // TODO: if we get here and there are no rest params involved, mark - // the callable as fast-path and don't do error checking or extra - // allocations for future calls. var declaredArguments = callable.declaration.arguments.arguments; var minLength = math.min(positional.length, declaredArguments.length); for (var i = 0; i < minLength; i++) { @@ -1633,9 +1641,11 @@ class _EvaluateVisitor /// If [through] is passed, [node] is added as a child of the first parent for /// which [through] returns `false`. That parent is copied unless it's the /// lattermost child of its parent. + /// + /// Runs [callback] in a new environment scope unless [scopeWhen] is false. Future _withParent( S node, Future callback(), - {bool through(CssNode node)}) async { + {bool through(CssNode node), bool scopeWhen: true}) async { var oldParent = _parent; // Go up through parents that match [through]. @@ -1657,7 +1667,7 @@ class _EvaluateVisitor parent.addChild(node); _parent = node; - var result = await _environment.scope(callback); + var result = await _environment.scope(callback, when: scopeWhen); _parent = oldParent; return result; diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index aadfaebb..0b51614e 100644 --- a/lib/src/visitor/evaluate.dart +++ b/lib/src/visitor/evaluate.dart @@ -5,7 +5,7 @@ // DO NOT EDIT. This file was generated from async_evaluate.dart. // See tool/synchronize.dart for details. // -// Checksum: 33e96540f3e5999ed172d168221b27910e8672b1 +// Checksum: cdeeec2634c97638aef571043c0be1e99f22d7d5 import 'dart:math' as math; @@ -328,7 +328,7 @@ class _EvaluateVisitor for (var child in node.children) { child.accept(this); } - }); + }, when: node.hasDeclarations); return null; } @@ -342,7 +342,7 @@ class _EvaluateVisitor } if (outerCopy != null) root.addChild(outerCopy); - _scopeForAtRoot(innerCopy ?? root, query, included)(() { + _scopeForAtRoot(node, innerCopy ?? root, query, included)(() { for (var child in node.children) { child.accept(this); } @@ -387,14 +387,14 @@ class _EvaluateVisitor /// This returns a callback that adjusts various instance variables for its /// duration, based on which rules are excluded by [query]. It always assigns /// [_parent] to [newParent]. - _ScopeCallback _scopeForAtRoot(CssParentNode newParent, AtRootQuery query, - List included) { + _ScopeCallback _scopeForAtRoot(AtRootRule node, CssParentNode newParent, + AtRootQuery query, List included) { var scope = (void callback()) { // We can't use [_withParent] here because it'll add the node to the tree // in the wrong place. var oldParent = _parent; _parent = newParent; - _environment.scope(callback); + _environment.scope(callback, when: node.hasDeclarations); _parent = oldParent; }; @@ -487,7 +487,7 @@ class _EvaluateVisitor for (var child in node.children) { child.accept(this); } - }); + }, when: node.hasDeclarations); _declarationName = oldDeclarationName; } @@ -583,9 +583,11 @@ class _EvaluateVisitor for (var child in node.children) { child.accept(this); } - }); + }, scopeWhen: false); } - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); _inUnknownAtRule = wasInUnknownAtRule; _inKeyframes = wasInKeyframes; @@ -640,7 +642,8 @@ class _EvaluateVisitor return _environment.scope( () => _handleReturn( clause.children, (child) => child.accept(this)), - semiGlobal: true); + semiGlobal: true, + when: clause.hasDeclarations); } Value visitImportRule(ImportRule node) { @@ -872,10 +875,12 @@ class _EvaluateVisitor for (var child in node.children) { child.accept(this); } - }); + }, scopeWhen: false); } }); - }, through: (node) => node is CssStyleRule || node is CssMediaRule); + }, + through: (node) => node is CssStyleRule || node is CssMediaRule, + scopeWhen: node.hasDeclarations); return null; } @@ -922,7 +927,9 @@ class _EvaluateVisitor for (var child in node.children) { child.accept(this); } - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); return null; } @@ -946,7 +953,9 @@ class _EvaluateVisitor child.accept(this); } }); - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); _atRootExcludingStyleRule = oldAtRootExcludingStyleRule; if (!_inStyleRule) { @@ -983,7 +992,9 @@ class _EvaluateVisitor } }); } - }, through: (node) => node is CssStyleRule); + }, + through: (node) => node is CssStyleRule, + scopeWhen: node.hasDeclarations); return null; } @@ -1058,7 +1069,7 @@ class _EvaluateVisitor if (result != null) return result; } return null; - }, semiGlobal: true); + }, semiGlobal: true, when: node.hasDeclarations); } // ## Expressions @@ -1231,9 +1242,6 @@ class _EvaluateVisitor _verifyArguments( positional.length, named, callable.declaration.arguments, span); - // TODO: if we get here and there are no rest params involved, mark - // the callable as fast-path and don't do error checking or extra - // allocations for future calls. var declaredArguments = callable.declaration.arguments.arguments; var minLength = math.min(positional.length, declaredArguments.length); for (var i = 0; i < minLength; i++) { @@ -1604,8 +1612,10 @@ class _EvaluateVisitor /// If [through] is passed, [node] is added as a child of the first parent for /// which [through] returns `false`. That parent is copied unless it's the /// lattermost child of its parent. + /// + /// Runs [callback] in a new environment scope unless [scopeWhen] is false. T _withParent(S node, T callback(), - {bool through(CssNode node)}) { + {bool through(CssNode node), bool scopeWhen: true}) { var oldParent = _parent; // Go up through parents that match [through]. @@ -1627,7 +1637,7 @@ class _EvaluateVisitor parent.addChild(node); _parent = node; - var result = _environment.scope(callback); + var result = _environment.scope(callback, when: scopeWhen); _parent = oldParent; return result;