Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow passing functions/mixins across compilations #2544

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion lib/src/value/function.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:meta/meta.dart';

import '../callable.dart';
import '../exception.dart';
import '../visitor/interface/value.dart';
import '../value.dart';

Expand All @@ -23,14 +24,31 @@ 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
T accept<T>(ValueVisitor<T> visitor) => visitor.visitFunction(this);

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;

Expand Down
20 changes: 19 additions & 1 deletion lib/src/value/mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:meta/meta.dart';

import '../callable.dart';
import '../exception.dart';
import '../visitor/interface/value.dart';
import '../value.dart';

Expand All @@ -25,14 +26,31 @@ 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
T accept<T>(ValueVisitor<T> visitor) => visitor.visitMixin(this);

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;

Expand Down
27 changes: 20 additions & 7 deletions lib/src/visitor/async_evaluate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ final class _EvaluateVisitor
StatementVisitor<Future<Value?>>,
ExpressionVisitor<Future<Value>>,
CssVisitor<Future<void>> {
/// 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;

Expand Down Expand Up @@ -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"),

Expand All @@ -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"),

Expand All @@ -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!, () {
Expand All @@ -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",
),
Expand All @@ -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...", (
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
29 changes: 21 additions & 8 deletions lib/src/visitor/evaluate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -144,6 +144,10 @@ final class _EvaluateVisitor
StatementVisitor<Value?>,
ExpressionVisitor<Value>,
CssVisitor<void> {
/// 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;

Expand Down Expand Up @@ -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"),

Expand All @@ -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"),

Expand All @@ -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!, () {
Expand All @@ -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",
),
Expand All @@ -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...", (
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down