diff --git a/lib/src/value/function.dart b/lib/src/value/function.dart index a5f949550..2018c2a68 100644 --- a/lib/src/value/function.dart +++ b/lib/src/value/function.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import '../callable.dart'; +import '../exception.dart'; import '../visitor/interface/value.dart'; import '../value.dart'; @@ -23,7 +24,14 @@ class SassFunction extends Value { /// synchronous evaluate visitor will crash if this isn't a [Callable]. final AsyncCallable callable; - SassFunction(this.callable); + /// The unique compile context for tracking if SassFunction and SassMixin + /// belongs to current compilation or not. + final Object? _compileContext; + + SassFunction(this.callable) : _compileContext = null; + + @internal + SassFunction.withCompileContext(this.callable, this._compileContext); /// @nodoc @internal @@ -31,6 +39,16 @@ class SassFunction extends Value { SassFunction assertFunction([String? name]) => this; + @internal + SassFunction assertCompileContext(Object compileContext) { + if (_compileContext != null && _compileContext != compileContext) { + throw SassScriptException( + "$this does not belong to current compilation."); + } + + return this; + } + bool operator ==(Object other) => other is SassFunction && callable == other.callable; diff --git a/lib/src/value/mixin.dart b/lib/src/value/mixin.dart index 79091579d..747a6fe76 100644 --- a/lib/src/value/mixin.dart +++ b/lib/src/value/mixin.dart @@ -5,6 +5,7 @@ import 'package:meta/meta.dart'; import '../callable.dart'; +import '../exception.dart'; import '../visitor/interface/value.dart'; import '../value.dart'; @@ -25,7 +26,14 @@ final class SassMixin extends Value { @internal final AsyncCallable callable; - SassMixin(this.callable); + /// The unique compile context for tracking if SassFunction and SassMixin + /// belongs to current compilation or not. + final Object? _compileContext; + + SassMixin(this.callable) : _compileContext = null; + + @internal + SassMixin.withCompileContext(this.callable, this._compileContext); /// @nodoc @internal @@ -33,6 +41,16 @@ final class SassMixin extends Value { SassMixin assertMixin([String? name]) => this; + @internal + SassMixin assertCompileContext(Object compileContext) { + if (_compileContext != null && _compileContext != compileContext) { + throw SassScriptException( + "$this does not belong to current compilation."); + } + + return this; + } + bool operator ==(Object other) => other is SassMixin && callable == other.callable; diff --git a/lib/src/visitor/async_evaluate.dart b/lib/src/visitor/async_evaluate.dart index 70dfb415a..6f6a2d0d8 100644 --- a/lib/src/visitor/async_evaluate.dart +++ b/lib/src/visitor/async_evaluate.dart @@ -136,6 +136,10 @@ final class _EvaluateVisitor StatementVisitor>, ExpressionVisitor>, CssVisitor> { + /// The unique compile context for tracking if SassFunction and SassMixin + /// belongs to current compilation or not. + final Object _compileContext = Object(); + /// The import cache used to import other stylesheets. final AsyncImportCache? _importCache; @@ -436,7 +440,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.functions.pairs) - SassString(name): SassFunction(value), + SassString(name): + SassFunction.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -449,7 +454,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.mixins.pairs) - SassString(name): SassMixin(value), + SassString(name): + SassMixin.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -465,7 +471,8 @@ final class _EvaluateVisitor if (module != null) { throw r"$css and $module may not both be passed at once."; } - return SassFunction(PlainCssCallable(name.text)); + return SassFunction.withCompileContext( + PlainCssCallable(name.text), _compileContext); } var callable = _addExceptionSpan(_callableNode!, () { @@ -480,7 +487,7 @@ final class _EvaluateVisitor }); if (callable == null) throw "Function not found: $name"; - return SassFunction(callable); + return SassFunction.withCompileContext(callable, _compileContext); }, url: "sass:meta", ), @@ -500,7 +507,7 @@ final class _EvaluateVisitor ); if (callable == null) throw "Mixin not found: $name"; - return SassMixin(callable); + return SassMixin.withCompileContext(callable, _compileContext); }, url: "sass:meta"), AsyncBuiltInCallable.function("call", r"$function, $args...", ( @@ -544,7 +551,10 @@ final class _EvaluateVisitor return await expression.accept(this); } - var callable = function.assertFunction("function").callable; + var callable = function + .assertFunction("function") + .assertCompileContext(_compileContext) + .callable; // ignore: unnecessary_type_check if (callable is AsyncCallable) { return await _runFunctionCallable( @@ -611,7 +621,10 @@ final class _EvaluateVisitor rest: ValueExpression(args, callableNode.span), ); - var callable = mixin.assertMixin("mixin").callable; + var callable = mixin + .assertMixin("mixin") + .assertCompileContext(_compileContext) + .callable; var content = _environment.content; // ignore: unnecessary_type_check diff --git a/lib/src/visitor/evaluate.dart b/lib/src/visitor/evaluate.dart index be4707f2b..cd8810ef7 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/grind/synchronize.dart for details. // -// Checksum: 25aa2d050126950ea37dc1c53539f0b041356e8e +// Checksum: 66ae6c443d9f6024ab41f6726cacc44a92a76ae0 // // ignore_for_file: unused_import @@ -144,6 +144,10 @@ final class _EvaluateVisitor StatementVisitor, ExpressionVisitor, CssVisitor { + /// The unique compile context for tracking if SassFunction and SassMixin + /// belongs to current compilation or not. + final Object _compileContext = Object(); + /// The import cache used to import other stylesheets. final ImportCache? _importCache; @@ -444,7 +448,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.functions.pairs) - SassString(name): SassFunction(value), + SassString(name): + SassFunction.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -457,7 +462,8 @@ final class _EvaluateVisitor return SassMap({ for (var (name, value) in module.mixins.pairs) - SassString(name): SassMixin(value), + SassString(name): + SassMixin.withCompileContext(value, _compileContext), }); }, url: "sass:meta"), @@ -473,7 +479,8 @@ final class _EvaluateVisitor if (module != null) { throw r"$css and $module may not both be passed at once."; } - return SassFunction(PlainCssCallable(name.text)); + return SassFunction.withCompileContext( + PlainCssCallable(name.text), _compileContext); } var callable = _addExceptionSpan(_callableNode!, () { @@ -488,7 +495,7 @@ final class _EvaluateVisitor }); if (callable == null) throw "Function not found: $name"; - return SassFunction(callable); + return SassFunction.withCompileContext(callable, _compileContext); }, url: "sass:meta", ), @@ -508,7 +515,7 @@ final class _EvaluateVisitor ); if (callable == null) throw "Mixin not found: $name"; - return SassMixin(callable); + return SassMixin.withCompileContext(callable, _compileContext); }, url: "sass:meta"), BuiltInCallable.function("call", r"$function, $args...", ( @@ -552,7 +559,10 @@ final class _EvaluateVisitor return expression.accept(this); } - var callable = function.assertFunction("function").callable; + var callable = function + .assertFunction("function") + .assertCompileContext(_compileContext) + .callable; // ignore: unnecessary_type_check if (callable is Callable) { return _runFunctionCallable( @@ -619,7 +629,10 @@ final class _EvaluateVisitor rest: ValueExpression(args, callableNode.span), ); - var callable = mixin.assertMixin("mixin").callable; + var callable = mixin + .assertMixin("mixin") + .assertCompileContext(_compileContext) + .callable; var content = _environment.content; // ignore: unnecessary_type_check