Skip to content

Commit 7f54226

Browse files
ntkmenex3
andauthored
Disallow passing functions/mixins across compilations (#2544)
Co-authored-by: Natalie Weizenbaum <[email protected]>
1 parent 90b6b39 commit 7f54226

File tree

8 files changed

+100
-27
lines changed

8 files changed

+100
-27
lines changed

CHANGELOG.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
## 1.88.1-dev
1+
## 1.88.0-dev
22

33
* Fix a bug when calculating source spans for interpolations.
44

5-
## 1.88.0
5+
### Dart and JS APIs
6+
7+
* **Potentially breaking bug fix:** Throw an error when passing a function or
8+
mixin object from one compilation to another.
9+
10+
### Dart API
611

712
* Deprecate passing a relative URL to `compileString()` and related functions.
813

lib/src/value/function.dart

+25-1
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,37 @@ 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 this [SassFunction] belongs to
28+
/// the current compilation or not.
29+
///
30+
/// This is `null` for functions defined in plugins' Dart code.
31+
final Object? _compileContext;
32+
33+
SassFunction(this.callable) : _compileContext = null;
34+
35+
@internal
36+
SassFunction.withCompileContext(this.callable, this._compileContext);
2737

2838
/// @nodoc
2939
@internal
3040
T accept<T>(ValueVisitor<T> visitor) => visitor.visitFunction(this);
3141

3242
SassFunction assertFunction([String? name]) => this;
3343

44+
/// Asserts that this SassFunction belongs to [compileContext] and returns it.
45+
///
46+
/// It's checked before evaluating a SassFunction to prevent execution of
47+
/// SassFunction across different compilations.
48+
@internal
49+
SassFunction assertCompileContext(Object compileContext) {
50+
if (_compileContext != null && _compileContext != compileContext) {
51+
throw SassScriptException(
52+
"$this does not belong to current compilation.");
53+
}
54+
55+
return this;
56+
}
57+
3458
bool operator ==(Object other) =>
3559
other is SassFunction && callable == other.callable;
3660

lib/src/value/mixin.dart

+23-1
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,35 @@ final class SassMixin extends Value {
2526
@internal
2627
final AsyncCallable callable;
2728

28-
SassMixin(this.callable);
29+
/// The unique compile context for tracking if this [SassMixin] belongs to the
30+
/// 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+
/// Asserts that this SassMixin belongs to [compileContext] and returns it.
45+
///
46+
/// It's checked before evaluating a SassMixin to prevent execution of
47+
/// SassMixin across different compilations.
48+
@internal
49+
SassMixin assertCompileContext(Object compileContext) {
50+
if (_compileContext != null && _compileContext != compileContext) {
51+
throw SassScriptException(
52+
"$this does not belong to current compilation.");
53+
}
54+
55+
return this;
56+
}
57+
3658
bool operator ==(Object other) =>
3759
other is SassMixin && callable == other.callable;
3860

lib/src/visitor/async_evaluate.dart

+20-7
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ final class _EvaluateVisitor
179179
/// Whether to track source map information.
180180
final bool _sourceMap;
181181

182+
/// The unique compile context for tracking if [SassFunction]s and
183+
/// [SassMixin]s belongs to the current compilation or not.
184+
final Object _compileContext = Object();
185+
182186
/// The current lexical environment.
183187
AsyncEnvironment _environment;
184188

@@ -433,7 +437,8 @@ final class _EvaluateVisitor
433437

434438
return SassMap({
435439
for (var (name, value) in module.functions.pairs)
436-
SassString(name): SassFunction(value),
440+
SassString(name):
441+
SassFunction.withCompileContext(value, _compileContext),
437442
});
438443
}, url: "sass:meta"),
439444

@@ -446,7 +451,8 @@ final class _EvaluateVisitor
446451

447452
return SassMap({
448453
for (var (name, value) in module.mixins.pairs)
449-
SassString(name): SassMixin(value),
454+
SassString(name):
455+
SassMixin.withCompileContext(value, _compileContext),
450456
});
451457
}, url: "sass:meta"),
452458

@@ -462,7 +468,8 @@ final class _EvaluateVisitor
462468
if (module != null) {
463469
throw r"$css and $module may not both be passed at once.";
464470
}
465-
return SassFunction(PlainCssCallable(name.text));
471+
return SassFunction.withCompileContext(
472+
PlainCssCallable(name.text), _compileContext);
466473
}
467474

468475
var callable = _addExceptionSpan(_callableNode!, () {
@@ -477,7 +484,7 @@ final class _EvaluateVisitor
477484
});
478485
if (callable == null) throw "Function not found: $name";
479486

480-
return SassFunction(callable);
487+
return SassFunction.withCompileContext(callable, _compileContext);
481488
},
482489
url: "sass:meta",
483490
),
@@ -497,7 +504,7 @@ final class _EvaluateVisitor
497504
);
498505
if (callable == null) throw "Mixin not found: $name";
499506

500-
return SassMixin(callable);
507+
return SassMixin.withCompileContext(callable, _compileContext);
501508
}, url: "sass:meta"),
502509

503510
AsyncBuiltInCallable.function("call", r"$function, $args...", (
@@ -541,7 +548,10 @@ final class _EvaluateVisitor
541548
return await expression.accept(this);
542549
}
543550

544-
var callable = function.assertFunction("function").callable;
551+
var callable = function
552+
.assertFunction("function")
553+
.assertCompileContext(_compileContext)
554+
.callable;
545555
// ignore: unnecessary_type_check
546556
if (callable is AsyncCallable) {
547557
return await _runFunctionCallable(
@@ -608,7 +618,10 @@ final class _EvaluateVisitor
608618
rest: ValueExpression(args, callableNode.span),
609619
);
610620

611-
var callable = mixin.assertMixin("mixin").callable;
621+
var callable = mixin
622+
.assertMixin("mixin")
623+
.assertCompileContext(_compileContext)
624+
.callable;
612625
var content = _environment.content;
613626

614627
// ignore: unnecessary_type_check

lib/src/visitor/evaluate.dart

+21-8
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: 05d8589b401932198e1f52434066ea4d6cbf3756
8+
// Checksum: 6e5710daa106ed0b9b684af8bc61ce9cc233a10b
99
//
1010
// ignore_for_file: unused_import
1111

@@ -187,6 +187,10 @@ final class _EvaluateVisitor
187187
/// Whether to track source map information.
188188
final bool _sourceMap;
189189

190+
/// The unique compile context for tracking if [SassFunction]s and
191+
/// [SassMixin]s belongs to the current compilation or not.
192+
final Object _compileContext = Object();
193+
190194
/// The current lexical environment.
191195
Environment _environment;
192196

@@ -441,7 +445,8 @@ final class _EvaluateVisitor
441445

442446
return SassMap({
443447
for (var (name, value) in module.functions.pairs)
444-
SassString(name): SassFunction(value),
448+
SassString(name):
449+
SassFunction.withCompileContext(value, _compileContext),
445450
});
446451
}, url: "sass:meta"),
447452

@@ -454,7 +459,8 @@ final class _EvaluateVisitor
454459

455460
return SassMap({
456461
for (var (name, value) in module.mixins.pairs)
457-
SassString(name): SassMixin(value),
462+
SassString(name):
463+
SassMixin.withCompileContext(value, _compileContext),
458464
});
459465
}, url: "sass:meta"),
460466

@@ -470,7 +476,8 @@ final class _EvaluateVisitor
470476
if (module != null) {
471477
throw r"$css and $module may not both be passed at once.";
472478
}
473-
return SassFunction(PlainCssCallable(name.text));
479+
return SassFunction.withCompileContext(
480+
PlainCssCallable(name.text), _compileContext);
474481
}
475482

476483
var callable = _addExceptionSpan(_callableNode!, () {
@@ -485,7 +492,7 @@ final class _EvaluateVisitor
485492
});
486493
if (callable == null) throw "Function not found: $name";
487494

488-
return SassFunction(callable);
495+
return SassFunction.withCompileContext(callable, _compileContext);
489496
},
490497
url: "sass:meta",
491498
),
@@ -505,7 +512,7 @@ final class _EvaluateVisitor
505512
);
506513
if (callable == null) throw "Mixin not found: $name";
507514

508-
return SassMixin(callable);
515+
return SassMixin.withCompileContext(callable, _compileContext);
509516
}, url: "sass:meta"),
510517

511518
BuiltInCallable.function("call", r"$function, $args...", (
@@ -549,7 +556,10 @@ final class _EvaluateVisitor
549556
return expression.accept(this);
550557
}
551558

552-
var callable = function.assertFunction("function").callable;
559+
var callable = function
560+
.assertFunction("function")
561+
.assertCompileContext(_compileContext)
562+
.callable;
553563
// ignore: unnecessary_type_check
554564
if (callable is Callable) {
555565
return _runFunctionCallable(
@@ -616,7 +626,10 @@ final class _EvaluateVisitor
616626
rest: ValueExpression(args, callableNode.span),
617627
);
618628

619-
var callable = mixin.assertMixin("mixin").callable;
629+
var callable = mixin
630+
.assertMixin("mixin")
631+
.assertCompileContext(_compileContext)
632+
.callable;
620633
var content = _environment.content;
621634

622635
// ignore: unnecessary_type_check

pkg/sass_api/CHANGELOG.md

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
## 15.5.1-dev
2-
3-
* No user-visible changes.
4-
5-
## 15.5.0
1+
## 15.5.0-dev
62

73
* No user-visible changes.
84

pkg/sass_api/pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ name: sass_api
22
# Note: Every time we add a new Sass AST node, we need to bump the *major*
33
# version because it's a breaking change for anyone who's implementing the
44
# visitor interface(s).
5-
version: 15.5.1-dev
5+
version: 15.5.0-dev
66
description: Additional APIs for Dart Sass.
77
homepage: https://github.com/sass/dart-sass
88

99
environment:
1010
sdk: ">=3.6.0 <4.0.0"
1111

1212
dependencies:
13-
sass: 1.88.1
13+
sass: 1.88.0
1414

1515
dev_dependencies:
1616
dartdoc: ^8.0.14

pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: sass
2-
version: 1.88.1-dev
2+
version: 1.88.0-dev
33
description: A Sass implementation in Dart.
44
homepage: https://github.com/sass/dart-sass
55

0 commit comments

Comments
 (0)