Skip to content

Disallow passing functions/mixins across compilations #2544

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

Open
wants to merge 3 commits 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
22 changes: 21 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,33 @@ 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 this [SassFunction] belongs to
/// the current compilation or not.
///
/// This is `null` for functions defined in plugins' Dart code.
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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document this.

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 this [SassMixin] belongs to the
/// 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 @@ -179,6 +179,10 @@ final class _EvaluateVisitor
/// Whether to track source map information.
final bool _sourceMap;

/// The unique compile context for tracking if [SassFunction]s and
/// [SassMixin]s belongs to the current compilation or not.
final Object _compileContext = Object();

/// The current lexical environment.
AsyncEnvironment _environment;

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: 607745b48d0737b3be112d0a8753dd87492fcc31
// Checksum: 303cd172dc9de38a8051a4ad70fa321d63b2669e
//
// ignore_for_file: unused_import

Expand Down Expand Up @@ -187,6 +187,10 @@ final class _EvaluateVisitor
/// Whether to track source map information.
final bool _sourceMap;

/// The unique compile context for tracking if [SassFunction]s and
/// [SassMixin]s belongs to the current compilation or not.
final Object _compileContext = Object();

/// The current lexical environment.
Environment _environment;

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