Skip to content

Commit 29d7ef1

Browse files
committed
Disallow passing functions/mixins across compilations
1 parent cee843b commit 29d7ef1

File tree

4 files changed

+79
-17
lines changed

4 files changed

+79
-17
lines changed

lib/src/value/function.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:meta/meta.dart';
66

77
import '../callable.dart';
8+
import '../exception.dart';
89
import '../visitor/interface/value.dart';
910
import '../value.dart';
1011

@@ -23,14 +24,31 @@ class SassFunction extends Value {
2324
/// synchronous evaluate visitor will crash if this isn't a [Callable].
2425
final AsyncCallable callable;
2526

26-
SassFunction(this.callable);
27+
/// The unique compile context for tracking if SassFunction and SassMixin
28+
/// belongs to current compilation or not.
29+
final Object? _compileContext;
30+
31+
SassFunction(this.callable) : _compileContext = null;
32+
33+
@internal
34+
SassFunction.withCompileContext(this.callable, this._compileContext);
2735

2836
/// @nodoc
2937
@internal
3038
T accept<T>(ValueVisitor<T> visitor) => visitor.visitFunction(this);
3139

3240
SassFunction assertFunction([String? name]) => this;
3341

42+
@internal
43+
SassFunction assertCompileContext(Object compileContext) {
44+
if (_compileContext != null && _compileContext != compileContext) {
45+
throw SassScriptException(
46+
"$this does not belong to current compilation.");
47+
}
48+
49+
return this;
50+
}
51+
3452
bool operator ==(Object other) =>
3553
other is SassFunction && callable == other.callable;
3654

lib/src/value/mixin.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:meta/meta.dart';
66

77
import '../callable.dart';
8+
import '../exception.dart';
89
import '../visitor/interface/value.dart';
910
import '../value.dart';
1011

@@ -25,14 +26,31 @@ final class SassMixin extends Value {
2526
@internal
2627
final AsyncCallable callable;
2728

28-
SassMixin(this.callable);
29+
/// The unique compile context for tracking if SassFunction and SassMixin
30+
/// belongs to current compilation or not.
31+
final Object? _compileContext;
32+
33+
SassMixin(this.callable) : _compileContext = null;
34+
35+
@internal
36+
SassMixin.withCompileContext(this.callable, this._compileContext);
2937

3038
/// @nodoc
3139
@internal
3240
T accept<T>(ValueVisitor<T> visitor) => visitor.visitMixin(this);
3341

3442
SassMixin assertMixin([String? name]) => this;
3543

44+
@internal
45+
SassMixin assertCompileContext(Object compileContext) {
46+
if (_compileContext != null && _compileContext != compileContext) {
47+
throw SassScriptException(
48+
"$this does not belong to current compilation.");
49+
}
50+
51+
return this;
52+
}
53+
3654
bool operator ==(Object other) =>
3755
other is SassMixin && callable == other.callable;
3856

lib/src/visitor/async_evaluate.dart

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ final class _EvaluateVisitor
136136
StatementVisitor<Future<Value?>>,
137137
ExpressionVisitor<Future<Value>>,
138138
CssVisitor<Future<void>> {
139+
/// The unique compile context for tracking if SassFunction and SassMixin
140+
/// belongs to current compilation or not.
141+
final Object _compileContext = Object();
142+
139143
/// The import cache used to import other stylesheets.
140144
final AsyncImportCache? _importCache;
141145

@@ -436,7 +440,8 @@ final class _EvaluateVisitor
436440

437441
return SassMap({
438442
for (var (name, value) in module.functions.pairs)
439-
SassString(name): SassFunction(value),
443+
SassString(name):
444+
SassFunction.withCompileContext(value, _compileContext),
440445
});
441446
}, url: "sass:meta"),
442447

@@ -449,7 +454,8 @@ final class _EvaluateVisitor
449454

450455
return SassMap({
451456
for (var (name, value) in module.mixins.pairs)
452-
SassString(name): SassMixin(value),
457+
SassString(name):
458+
SassMixin.withCompileContext(value, _compileContext),
453459
});
454460
}, url: "sass:meta"),
455461

@@ -465,7 +471,8 @@ final class _EvaluateVisitor
465471
if (module != null) {
466472
throw r"$css and $module may not both be passed at once.";
467473
}
468-
return SassFunction(PlainCssCallable(name.text));
474+
return SassFunction.withCompileContext(
475+
PlainCssCallable(name.text), _compileContext);
469476
}
470477

471478
var callable = _addExceptionSpan(_callableNode!, () {
@@ -480,7 +487,7 @@ final class _EvaluateVisitor
480487
});
481488
if (callable == null) throw "Function not found: $name";
482489

483-
return SassFunction(callable);
490+
return SassFunction.withCompileContext(callable, _compileContext);
484491
},
485492
url: "sass:meta",
486493
),
@@ -500,7 +507,7 @@ final class _EvaluateVisitor
500507
);
501508
if (callable == null) throw "Mixin not found: $name";
502509

503-
return SassMixin(callable);
510+
return SassMixin.withCompileContext(callable, _compileContext);
504511
}, url: "sass:meta"),
505512

506513
AsyncBuiltInCallable.function("call", r"$function, $args...", (
@@ -544,7 +551,10 @@ final class _EvaluateVisitor
544551
return await expression.accept(this);
545552
}
546553

547-
var callable = function.assertFunction("function").callable;
554+
var callable = function
555+
.assertFunction("function")
556+
.assertCompileContext(_compileContext)
557+
.callable;
548558
// ignore: unnecessary_type_check
549559
if (callable is AsyncCallable) {
550560
return await _runFunctionCallable(
@@ -611,7 +621,10 @@ final class _EvaluateVisitor
611621
rest: ValueExpression(args, callableNode.span),
612622
);
613623

614-
var callable = mixin.assertMixin("mixin").callable;
624+
var callable = mixin
625+
.assertMixin("mixin")
626+
.assertCompileContext(_compileContext)
627+
.callable;
615628
var content = _environment.content;
616629

617630
// ignore: unnecessary_type_check

lib/src/visitor/evaluate.dart

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// DO NOT EDIT. This file was generated from async_evaluate.dart.
66
// See tool/grind/synchronize.dart for details.
77
//
8-
// Checksum: 25aa2d050126950ea37dc1c53539f0b041356e8e
8+
// Checksum: 66ae6c443d9f6024ab41f6726cacc44a92a76ae0
99
//
1010
// ignore_for_file: unused_import
1111

@@ -144,6 +144,10 @@ final class _EvaluateVisitor
144144
StatementVisitor<Value?>,
145145
ExpressionVisitor<Value>,
146146
CssVisitor<void> {
147+
/// The unique compile context for tracking if SassFunction and SassMixin
148+
/// belongs to current compilation or not.
149+
final Object _compileContext = Object();
150+
147151
/// The import cache used to import other stylesheets.
148152
final ImportCache? _importCache;
149153

@@ -444,7 +448,8 @@ final class _EvaluateVisitor
444448

445449
return SassMap({
446450
for (var (name, value) in module.functions.pairs)
447-
SassString(name): SassFunction(value),
451+
SassString(name):
452+
SassFunction.withCompileContext(value, _compileContext),
448453
});
449454
}, url: "sass:meta"),
450455

@@ -457,7 +462,8 @@ final class _EvaluateVisitor
457462

458463
return SassMap({
459464
for (var (name, value) in module.mixins.pairs)
460-
SassString(name): SassMixin(value),
465+
SassString(name):
466+
SassMixin.withCompileContext(value, _compileContext),
461467
});
462468
}, url: "sass:meta"),
463469

@@ -473,7 +479,8 @@ final class _EvaluateVisitor
473479
if (module != null) {
474480
throw r"$css and $module may not both be passed at once.";
475481
}
476-
return SassFunction(PlainCssCallable(name.text));
482+
return SassFunction.withCompileContext(
483+
PlainCssCallable(name.text), _compileContext);
477484
}
478485

479486
var callable = _addExceptionSpan(_callableNode!, () {
@@ -488,7 +495,7 @@ final class _EvaluateVisitor
488495
});
489496
if (callable == null) throw "Function not found: $name";
490497

491-
return SassFunction(callable);
498+
return SassFunction.withCompileContext(callable, _compileContext);
492499
},
493500
url: "sass:meta",
494501
),
@@ -508,7 +515,7 @@ final class _EvaluateVisitor
508515
);
509516
if (callable == null) throw "Mixin not found: $name";
510517

511-
return SassMixin(callable);
518+
return SassMixin.withCompileContext(callable, _compileContext);
512519
}, url: "sass:meta"),
513520

514521
BuiltInCallable.function("call", r"$function, $args...", (
@@ -552,7 +559,10 @@ final class _EvaluateVisitor
552559
return expression.accept(this);
553560
}
554561

555-
var callable = function.assertFunction("function").callable;
562+
var callable = function
563+
.assertFunction("function")
564+
.assertCompileContext(_compileContext)
565+
.callable;
556566
// ignore: unnecessary_type_check
557567
if (callable is Callable) {
558568
return _runFunctionCallable(
@@ -619,7 +629,10 @@ final class _EvaluateVisitor
619629
rest: ValueExpression(args, callableNode.span),
620630
);
621631

622-
var callable = mixin.assertMixin("mixin").callable;
632+
var callable = mixin
633+
.assertMixin("mixin")
634+
.assertCompileContext(_compileContext)
635+
.callable;
623636
var content = _environment.content;
624637

625638
// ignore: unnecessary_type_check

0 commit comments

Comments
 (0)