@@ -8,8 +8,9 @@ import 'dart:async' show Future;
88
99import 'dart:convert' show jsonDecode;
1010
11- import 'dart:io' show File, Platform;
11+ import 'dart:io' show Directory, File, Platform;
1212
13+ import 'package:front_end/src/api_prototype/compiler_options.dart' ;
1314import 'package:kernel/ast.dart'
1415 show AwaitExpression, Component, Library, Node, Visitor;
1516
@@ -130,6 +131,14 @@ String generateExpectationName(bool legacyMode) {
130131 return legacyMode ? "legacy" : "strong" ;
131132}
132133
134+ const String experimentalFlagOptions = '--enable-experiment=' ;
135+
136+ class TestOptions {
137+ final Map <ExperimentalFlag , bool > experimentalFlags;
138+
139+ TestOptions (this .experimentalFlags);
140+ }
141+
133142class FastaContext extends ChainContext with MatchContext {
134143 final UriTranslator uriTranslator;
135144 final List <Step > steps;
@@ -143,6 +152,7 @@ class FastaContext extends ChainContext with MatchContext {
143152 final Map <Component , StringBuffer > componentToDiagnostics =
144153 < Component , StringBuffer > {};
145154 final Uri platformBinaries;
155+ final Map <Uri , TestOptions > _testOptions = {};
146156
147157 @override
148158 final bool updateExpectations;
@@ -203,6 +213,41 @@ class FastaContext extends ChainContext with MatchContext {
203213 }
204214 }
205215
216+ /// Computes the experimental flag for [description] .
217+ ///
218+ /// [forcedExperimentalFlags] is used to override the default flags for
219+ /// [description] .
220+ Map <ExperimentalFlag , bool > computeExperimentalFlags (
221+ TestDescription description,
222+ Map <ExperimentalFlag , bool > forcedExperimentalFlags) {
223+ Directory directory = new File .fromUri (description.uri).parent;
224+ // TODO(johnniwinther): Support nested test folders?
225+ TestOptions testOptions = _testOptions[directory.uri];
226+ if (testOptions == null ) {
227+ List <String > experimentalFlagsArguments = [];
228+ File optionsFile =
229+ new File .fromUri (directory.uri.resolve ('test.options' ));
230+ if (optionsFile.existsSync ()) {
231+ for (String line in optionsFile.readAsStringSync ().split ('\n ' )) {
232+ // TODO(johnniwinther): Support more options if need.
233+ if (line.startsWith (experimentalFlagOptions)) {
234+ experimentalFlagsArguments =
235+ line.substring (experimentalFlagOptions.length).split ('\n ' );
236+ }
237+ }
238+ }
239+ testOptions = new TestOptions (parseExperimentalFlags (
240+ parseExperimentalArguments (experimentalFlagsArguments),
241+ onError: (String message) => throw new ArgumentError (message),
242+ onWarning: (String message) => throw new ArgumentError (message)));
243+ _testOptions[directory.uri] = testOptions;
244+ }
245+ Map <ExperimentalFlag , bool > flags =
246+ new Map .from (testOptions.experimentalFlags);
247+ flags.addAll (forcedExperimentalFlags);
248+ return flags;
249+ }
250+
206251 Expectation get verificationError => expectationSet["VerificationError" ];
207252
208253 Future ensurePlatformUris () async {
@@ -249,16 +294,19 @@ class FastaContext extends ChainContext with MatchContext {
249294 Uri vm = Uri .base .resolveUri (new Uri .file (Platform .resolvedExecutable));
250295 Uri packages = Uri .base .resolve (".packages" );
251296 bool legacyMode = environment.containsKey (LEGACY_MODE );
252- Map <ExperimentalFlag , bool > experimentalFlags = < ExperimentalFlag , bool > {
253- ExperimentalFlag .controlFlowCollections:
254- environment["enableControlFlowCollections" ] != "false" && ! legacyMode,
255- ExperimentalFlag .spreadCollections:
256- environment["enableSpreadCollections" ] != "false" && ! legacyMode,
257- ExperimentalFlag .extensionMethods:
258- environment["enableExtensionMethods" ] != "false" && ! legacyMode,
259- ExperimentalFlag .nonNullable:
260- environment["enableNonNullable" ] != "false" && ! legacyMode,
261- };
297+ Map <ExperimentalFlag , bool > experimentalFlags = < ExperimentalFlag , bool > {};
298+
299+ void addForcedExperimentalFlag (String name, ExperimentalFlag flag) {
300+ if (environment.containsKey (name)) {
301+ experimentalFlags[flag] = environment[name] == "true" ;
302+ }
303+ }
304+
305+ addForcedExperimentalFlag (
306+ "enableExtensionMethods" , ExperimentalFlag .extensionMethods);
307+ addForcedExperimentalFlag (
308+ "enableNonNullable" , ExperimentalFlag .nonNullable);
309+
262310 var options = new ProcessedOptions (
263311 options: new CompilerOptions ()
264312 ..onDiagnostic = (DiagnosticMessage message) {
@@ -354,7 +402,8 @@ class Outline extends Step<TestDescription, Component, FastaContext> {
354402 errors.writeAll (message.plainTextFormatted, "\n " );
355403 }
356404 ..environmentDefines = {}
357- ..experimentalFlags = context.experimentalFlags,
405+ ..experimentalFlags = context.computeExperimentalFlags (
406+ description, context.experimentalFlags),
358407 inputs: < Uri > [description.uri]);
359408 return await CompilerContext .runWithOptions (options, (_) async {
360409 // Disable colors to ensure that expectation files are the same across
0 commit comments