Skip to content

Commit fc95268

Browse files
derekxu16Commit Queue
authored andcommitted
[ResidentFrontendServer] Cache the compiler options that were last used
to compile every entrypoint This is necessary to ensure that the options used when performing compilation during expression evaluation or hot reload match the options that were used to compile the running program before it was started. TEST=test case added to pkg/dartdev/test/commands/run_test.dart, CI CoreLibraryReviewExempt: This CL does not include any core library API changes, only VM Service implementation changes in sdk/lib/vmservice/running_isolates.dart. Change-Id: If11207e090d9b02d5a6a8396e09dc90d0c874f58 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403240 Reviewed-by: Ben Konyi <[email protected]> Commit-Queue: Derek Xu <[email protected]>
1 parent 337e07f commit fc95268

File tree

6 files changed

+217
-45
lines changed

6 files changed

+217
-45
lines changed

pkg/dartdev/lib/src/generate_kernel.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import 'dart:io';
66

77
import 'package:args/args.dart';
88
import 'package:frontend_server/resident_frontend_server_utils.dart'
9-
show computeCachedDillPath, ResidentCompilerInfo, sendAndReceiveResponse;
9+
show
10+
computeCachedDillAndCompilerOptionsPaths,
11+
ResidentCompilerInfo,
12+
sendAndReceiveResponse;
1013
import 'package:kernel/binary/tag.dart' show isValidSdkHash;
1114
import 'package:path/path.dart' as p;
1215
import 'package:pub/pub.dart';
@@ -57,7 +60,9 @@ Future<DartExecutableWithPackageConfig> generateKernel(
5760
packageRoot != null ? p.join(packageRoot, packageConfigName) : null;
5861

5962
final canonicalizedExecutablePath = p.canonicalize(executable.executable);
60-
final cachedDillPath = computeCachedDillPath(canonicalizedExecutablePath);
63+
final cachedDillPath =
64+
computeCachedDillAndCompilerOptionsPaths(canonicalizedExecutablePath)
65+
.cachedDillPath;
6166

6267
Map<String, dynamic> result;
6368
try {

pkg/dartdev/test/commands/run_test.dart

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import 'dart:io';
99
import 'package:dartdev/src/resident_frontend_constants.dart';
1010
import 'package:dartdev/src/resident_frontend_utils.dart';
1111
import 'package:frontend_server/resident_frontend_server_utils.dart'
12-
show computeCachedDillPath, ResidentCompilerInfo, sendAndReceiveResponse;
12+
show
13+
computeCachedDillAndCompilerOptionsPaths,
14+
ResidentCompilerInfo,
15+
sendAndReceiveResponse;
1316
import 'package:kernel/binary/tag.dart' show sdkHashNull;
1417
import 'package:path/path.dart' as path;
1518
import 'package:pub_semver/pub_semver.dart';
@@ -987,8 +990,10 @@ void residentRun() {
987990
serverInfoDirectory = project(mainSrc: 'void main() {}');
988991
serverInfoFile = path.join(serverInfoDirectory.dirPath, 'info');
989992

990-
final cachedDillFile =
991-
File(computeCachedDillPath(serverInfoDirectory.mainPath));
993+
final cachedDillFile = File(
994+
computeCachedDillAndCompilerOptionsPaths(serverInfoDirectory.mainPath)
995+
.cachedDillPath,
996+
);
992997
expect(cachedDillFile.existsSync(), false);
993998

994999
final result = await serverInfoDirectory.run([
@@ -1054,7 +1059,9 @@ void residentRun() {
10541059
test("'Hello World'", () async {
10551060
p = project(mainSrc: "void main() { print('Hello World'); }");
10561061

1057-
final cachedDillFile = File(computeCachedDillPath(p.mainPath));
1062+
final cachedDillFile = File(
1063+
computeCachedDillAndCompilerOptionsPaths(p.mainPath).cachedDillPath,
1064+
);
10581065
expect(cachedDillFile.existsSync(), false);
10591066

10601067
final result = await p.run([
@@ -1081,7 +1088,9 @@ void residentRun() {
10811088
p = project(mainSrc: "void main() { print('Hello World'); }");
10821089
p.deleteFile('.dart_tool/package_config.json');
10831090

1084-
final cachedDillFile = File(computeCachedDillPath(p.mainPath));
1091+
final cachedDillFile = File(
1092+
computeCachedDillAndCompilerOptionsPaths(p.mainPath).cachedDillPath,
1093+
);
10851094
expect(cachedDillFile.existsSync(), false);
10861095

10871096
final result = await p.run([
@@ -1108,7 +1117,9 @@ void residentRun() {
11081117
() async {
11091118
p = project(mainSrc: "void main() { print('Hello World'); }");
11101119

1111-
final cachedDillFile = File(computeCachedDillPath(p.mainPath));
1120+
final cachedDillFile = File(
1121+
computeCachedDillAndCompilerOptionsPaths(p.mainPath).cachedDillPath,
1122+
);
11121123
expect(cachedDillFile.existsSync(), false);
11131124

11141125
final result = await p.run([
@@ -1135,7 +1146,9 @@ void residentRun() {
11351146
() async {
11361147
p = project(mainSrc: "void main() { print('Hello World'); }");
11371148

1138-
final cachedDillFile = File(computeCachedDillPath(p.mainPath));
1149+
final cachedDillFile = File(
1150+
computeCachedDillAndCompilerOptionsPaths(p.mainPath).cachedDillPath,
1151+
);
11391152
expect(cachedDillFile.existsSync(), false);
11401153

11411154
final result = await p.run([
@@ -1257,8 +1270,12 @@ void residentRun() {
12571270
),
12581271
);
12591272

1260-
final cachedDillFile = File(computeCachedDillPath(p.mainPath));
1273+
final (:cachedDillPath, :cachedCompilerOptionsPath) =
1274+
computeCachedDillAndCompilerOptionsPaths(p.mainPath);
1275+
final cachedDillFile = File(cachedDillPath);
12611276
expect(cachedDillFile.existsSync(), false);
1277+
final cachedCompilerOptionsFile = File(cachedCompilerOptionsPath);
1278+
expect(cachedCompilerOptionsFile.existsSync(), false);
12621279

12631280
final result = await p.run([
12641281
'run',
@@ -1277,8 +1294,24 @@ void residentRun() {
12771294
),
12781295
);
12791296
expect(result.exitCode, 0);
1297+
12801298
expect(cachedDillFile.existsSync(), true);
12811299
cachedDillFile.deleteSync();
1300+
1301+
expect(cachedCompilerOptionsFile.existsSync(), true);
1302+
final cachedCompilerOptionsFileContents =
1303+
cachedCompilerOptionsFile.readAsStringSync();
1304+
cachedCompilerOptionsFile.deleteSync();
1305+
1306+
// [cachedCompilerOptionsFileContents] should be a valid JSON list that
1307+
// contains the enabled experiment.
1308+
final cachedCompilerOptionsAsList =
1309+
(jsonDecode(cachedCompilerOptionsFileContents) as List<dynamic>)
1310+
.cast<String>();
1311+
expect(
1312+
cachedCompilerOptionsAsList,
1313+
contains('--enable-experiment=test-experiment'),
1314+
);
12821315
});
12831316

12841317
test('same server used from different directories', () async {
@@ -1320,12 +1353,18 @@ void residentRun() {
13201353
p.file('lib/main.dart', 'void main() {}');
13211354
p.file('bin/main.dart', 'void main() {}');
13221355

1323-
final cachedDillForLibMain =
1324-
File(computeCachedDillPath(path.join(p.dirPath, 'lib/main.dart')));
1356+
final cachedDillForLibMain = File(
1357+
computeCachedDillAndCompilerOptionsPaths(
1358+
path.join(p.dirPath, 'lib/main.dart'),
1359+
).cachedDillPath,
1360+
);
13251361
expect(cachedDillForLibMain.existsSync(), false);
13261362

1327-
final cachedDillForBinMain =
1328-
File(computeCachedDillPath(path.join(p.dirPath, 'bin/main.dart')));
1363+
final cachedDillForBinMain = File(
1364+
computeCachedDillAndCompilerOptionsPaths(
1365+
path.join(p.dirPath, 'bin/main.dart'),
1366+
).cachedDillPath,
1367+
);
13291368
expect(cachedDillForBinMain.existsSync(), false);
13301369

13311370
final runResult1 = await p.run([

pkg/frontend_server/lib/resident_frontend_server_utils.dart

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,14 @@ final class ResidentCompilerInfo {
4949
});
5050
}
5151

52-
/// Returns the absolute path to the cached kernel file associated with
53-
/// [canonicalizedLibraryPath].
54-
String computeCachedDillPath(
52+
typedef CachedDillAndCompilerOptionsPaths = ({
53+
String cachedDillPath,
54+
String cachedCompilerOptionsPath
55+
});
56+
57+
/// Returns the absolute paths to the cached kernel file and the cached compiler
58+
/// options file associated with [canonicalizedLibraryPath].
59+
CachedDillAndCompilerOptionsPaths computeCachedDillAndCompilerOptionsPaths(
5560
final String canonicalizedLibraryPath,
5661
) {
5762
final String dirname = path.dirname(canonicalizedLibraryPath);
@@ -73,7 +78,14 @@ String computeCachedDillPath(
7378
);
7479
}
7580

76-
return path.join(cachedKernelDirectoryPath, '$basename.dill');
81+
final String cachedDillPath =
82+
path.join(cachedKernelDirectoryPath, '$basename.dill');
83+
final String cachedCompilerOptionsPath =
84+
path.join(cachedKernelDirectoryPath, '${basename}_options.json');
85+
return (
86+
cachedDillPath: cachedDillPath,
87+
cachedCompilerOptionsPath: cachedCompilerOptionsPath
88+
);
7789
}
7890

7991
/// Sends a compilation [request] to the resident frontend compiler associated

pkg/frontend_server/lib/src/resident_frontend_server.dart

Lines changed: 98 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import 'package:kernel/kernel.dart' show Component, loadComponentFromBytes;
2222
import 'package:path/path.dart' as path;
2323

2424
import '../frontend_server.dart';
25-
import '../resident_frontend_server_utils.dart' show computeCachedDillPath;
25+
import '../resident_frontend_server_utils.dart'
26+
show
27+
CachedDillAndCompilerOptionsPaths,
28+
computeCachedDillAndCompilerOptionsPaths;
2629

2730
/// Floor the system time by this amount in order to correctly detect modified
2831
/// source files on all platforms. This has no effect on correctness,
@@ -321,6 +324,8 @@ class ResidentFrontendServer {
321324
static const String _packageString = 'packages';
322325
static const String _successString = 'success';
323326
static const String _outputString = 'output-dill';
327+
static const String _useCachedCompilerOptionsAsBaseString =
328+
'useCachedCompilerOptionsAsBase';
324329
static const String _compileExpressionString = 'compileExpression';
325330
static const String _libraryUriString = 'libraryUri';
326331
static const String _rootLibraryUriString = 'rootLibraryUri';
@@ -351,11 +356,18 @@ class ResidentFrontendServer {
351356

352357
/// Returns a [ResidentCompiler] that has been configured with
353358
/// [compileOptions] and prepared to compile the [canonicalizedLibraryPath]
354-
/// entrypoint.
355-
static ResidentCompiler _getResidentCompilerForEntrypoint(
356-
String canonicalizedLibraryPath,
357-
ArgResults compileOptions,
358-
) {
359+
/// entrypoint. This function also writes [compileOptions.arguments] to
360+
/// [cachedCompilerOptions] as a JSON list.
361+
static ResidentCompiler _getResidentCompilerForEntrypoint({
362+
required final String canonicalizedLibraryPath,
363+
required final ArgResults compileOptions,
364+
required final File cachedCompilerOptions,
365+
}) {
366+
cachedCompilerOptions.createSync();
367+
cachedCompilerOptions.writeAsStringSync(
368+
compileOptions.arguments.map(jsonEncode).toList().toString(),
369+
);
370+
359371
late final ResidentCompiler residentCompiler;
360372
if (compilers[canonicalizedLibraryPath] == null) {
361373
// Avoids using too much memory.
@@ -398,8 +410,14 @@ class ResidentFrontendServer {
398410
component.mainMethod!.enclosingLibrary.fileUri.toFilePath(),
399411
);
400412

401-
final String cachedDillPath =
402-
computeCachedDillPath(canonicalizedLibraryPath);
413+
final String cachedDillPath;
414+
try {
415+
cachedDillPath =
416+
computeCachedDillAndCompilerOptionsPaths(canonicalizedLibraryPath)
417+
.cachedDillPath;
418+
} on Exception catch (e) {
419+
return _encodeErrorMessage(e.toString());
420+
}
403421
replacementDillFile.copySync(cachedDillPath);
404422
} catch (e) {
405423
return _encodeErrorMessage('Failed to replace cached dill');
@@ -427,21 +445,29 @@ class ResidentFrontendServer {
427445
final String canonicalizedExecutablePath =
428446
path.canonicalize(request[_executableString]);
429447

430-
late final String cachedDillPath;
448+
final String cachedDillPath;
449+
final File cachedCompilerOptions;
431450
try {
432-
cachedDillPath = computeCachedDillPath(canonicalizedExecutablePath);
451+
final CachedDillAndCompilerOptionsPaths computationResult =
452+
computeCachedDillAndCompilerOptionsPaths(canonicalizedExecutablePath);
453+
cachedDillPath = computationResult.cachedDillPath;
454+
cachedCompilerOptions =
455+
new File(computationResult.cachedCompilerOptionsPath);
433456
} on Exception catch (e) {
434457
return _encodeErrorMessage(e.toString());
435458
}
436459

437460
final ArgResults options = _generateCompilerOptions(
438461
request: request,
462+
cachedCompilerOptions: cachedCompilerOptions,
439463
outputDillOverride: cachedDillPath,
440464
initializeFromDillPath: cachedDillPath,
441465
);
466+
442467
final ResidentCompiler residentCompiler = _getResidentCompilerForEntrypoint(
443-
canonicalizedExecutablePath,
444-
options,
468+
canonicalizedLibraryPath: canonicalizedExecutablePath,
469+
compileOptions: options,
470+
cachedCompilerOptions: cachedCompilerOptions,
445471
);
446472
final Map<String, dynamic> response = await residentCompiler.compile();
447473

@@ -488,8 +514,17 @@ class ResidentFrontendServer {
488514
);
489515
}
490516

491-
final String cachedDillPath =
492-
computeCachedDillPath(canonicalizedLibraryPath);
517+
final String cachedDillPath;
518+
final File cachedCompilerOptions;
519+
try {
520+
final CachedDillAndCompilerOptionsPaths computationResult =
521+
computeCachedDillAndCompilerOptionsPaths(canonicalizedLibraryPath);
522+
cachedDillPath = computationResult.cachedDillPath;
523+
cachedCompilerOptions =
524+
new File(computationResult.cachedCompilerOptionsPath);
525+
} on Exception catch (e) {
526+
return _encodeErrorMessage(e.toString());
527+
}
493528
// Make the [ResidentCompiler] output the compiled expression to
494529
// [compiledExpressionDillPath] to prevent it from overwriting the
495530
// cached program dill.
@@ -499,14 +534,19 @@ class ResidentFrontendServer {
499534
null,
500535
'.expr.dill',
501536
);
537+
502538
final ArgResults options = _generateCompilerOptions(
503539
request: request,
540+
cachedCompilerOptions: cachedCompilerOptions,
504541
outputDillOverride: compiledExpressionDillPath,
505542
initializeFromDillPath: cachedDillPath,
506543
);
507544

508-
final ResidentCompiler residentCompiler =
509-
_getResidentCompilerForEntrypoint(canonicalizedLibraryPath, options);
545+
final ResidentCompiler residentCompiler = _getResidentCompilerForEntrypoint(
546+
canonicalizedLibraryPath: canonicalizedLibraryPath,
547+
compileOptions: options,
548+
cachedCompilerOptions: cachedCompilerOptions,
549+
);
510550

511551
final String expression = request[_expressionString];
512552
final List<String> definitions =
@@ -580,13 +620,32 @@ class ResidentFrontendServer {
580620
/// Generates the compiler options needed to handle the [request].
581621
static ArgResults _generateCompilerOptions({
582622
required Map<String, dynamic> request,
623+
required File cachedCompilerOptions,
583624

584625
/// The compiled kernel file will be stored at this path, and not at
585626
/// [request['--output-dill']].
586627
required String outputDillOverride,
587628
required String initializeFromDillPath,
588629
}) {
589-
return argParser.parse(<String>[
630+
final Map<String, dynamic> options = {};
631+
if (request[_useCachedCompilerOptionsAsBaseString] == true &&
632+
cachedCompilerOptions.existsSync()) {
633+
// If [request[_useCachedCompilerOptionsAsBaseString]] is true, then we
634+
// start with the cached options and apply any options specified in
635+
// [request] as overrides.
636+
final String cachedCompilerOptionsContents =
637+
cachedCompilerOptions.readAsStringSync();
638+
final List<String> cachedCompilerOptionsAsList =
639+
(jsonDecode(cachedCompilerOptionsContents) as List<dynamic>)
640+
.cast<String>();
641+
final ArgResults cachedOptions =
642+
argParser.parse(cachedCompilerOptionsAsList);
643+
for (final String option in cachedOptions.options) {
644+
options[option] = cachedOptions[option];
645+
}
646+
}
647+
648+
final ArgResults overrides = argParser.parse(<String>[
590649
'--sdk-root=${_sdkUri.toFilePath()}',
591650
if (!(request['aot'] ?? false)) '--incremental',
592651
'--platform=${_platformKernelUri.path}',
@@ -623,6 +682,28 @@ class ResidentFrontendServer {
623682
if (request['enable-experiment'] != null)
624683
for (String experiment in request['enable-experiment']) experiment,
625684
]);
685+
686+
for (final String option in overrides.options) {
687+
options[option] = overrides[option];
688+
}
689+
690+
// Transform [options] into a list that can be passed to [argParser.parse].
691+
final List<String> optionsAsList = <String>[];
692+
for (final MapEntry<String, dynamic>(:key, :value) in options.entries) {
693+
if (value is List<dynamic>) {
694+
for (final Object multiOptionValue in value) {
695+
optionsAsList.add('--$key=$multiOptionValue');
696+
}
697+
} else if (value is bool) {
698+
if (value) {
699+
optionsAsList.add('--$key');
700+
}
701+
} else {
702+
optionsAsList.add('--$key=$value');
703+
}
704+
}
705+
706+
return argParser.parse(optionsAsList);
626707
}
627708

628709
/// Encodes the [message] in JSON to be sent over the socket.

0 commit comments

Comments
 (0)