Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8e2a6fc

Browse files
authored
Implement hot reload using the DDC library bundle format (#162498)
dart-lang/webdev#2516- Updates restart/reload code to accept a resetCompiler boolean todisambiguate between whether this is a full restart and whether to resetthe resident compiler.- Adds code to call reloadSources in DWDS and handle the response(including any errors).- Adds code to invoke reassemble.- Adds code to emit a script that DWDS can later consume that containsthe changed sources and their associated libraries. This is used to hotreload. The bootstrapper puts this in the global window. DWDS should beupdated to accept it in the provider itself. Seedart-lang/webdev#2584.- Adds code to parse module metadata from the frontend server. This isidentical to the implementation in DWDS % addressing type-related lints.- Adds tests that run the existing hot reload tests but with web. Somemodifications are mode, including waiting for Flutter runs to finishexecuting, and skipping a test that's not possible on the web.Needs DWDS 24.3.4 to be published first and used before we can land.## Pre-launch Checklist- [x] I read the [Contributor Guide] and followed the process outlinedthere for submitting PRs.- [x] I read the [Tree Hygiene] wiki page, which explains myresponsibilities.- [x] I read and followed the [Flutter Style Guide], including [Featureswe expect every widget to implement].- [x] I signed the [CLA].- [x] I listed at least one issue that this PR fixes in the descriptionabove.- [x] I updated/added relevant documentation (doc comments with `///`).- [x] I added new tests to check the change I am making, or this PR is[test-exempt].- [x] I followed the [breaking change policy] and added [Data DrivenFixes] where supported.- [ ] All existing and new tests are passing.
1 parent4d08217 commit8e2a6fc

21 files changed

+866
-360
lines changed

‎packages/flutter_tools/lib/src/devfs.dart‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,10 @@ class DevFS {
551551
/// Updates files on the device.
552552
///
553553
/// Returns the number of bytes synced.
554+
///
555+
/// If[fullRestart] is true, assumes this is a hot restart instead of a hot
556+
/// reload. If[resetCompiler] is true, sends a`reset` instruction to the
557+
/// frontend server.
554558
Future<UpdateFSReport>update({
555559
requiredUri mainUri,
556560
requiredResidentCompiler generator,
@@ -566,6 +570,7 @@ class DevFS {
566570
AssetBundle? bundle,
567571
bool bundleFirstUpload=false,
568572
bool fullRestart=false,
573+
bool resetCompiler=false,
569574
File? dartPluginRegistrant,
570575
})async {
571576
finalDateTime candidateCompileTime=DateTime.now();
@@ -577,7 +582,7 @@ class DevFS {
577582
finalList<Future<void>> pendingAssetBuilds=<Future<void>>[];
578583
bool assetBuildFailed=false;
579584
int syncedBytes=0;
580-
if (fullRestart) {
585+
if (resetCompiler) {
581586
generator.reset();
582587
}
583588
// On a full restart, or on an initial compile for the attach based workflow,

‎packages/flutter_tools/lib/src/isolated/devfs_web.dart‎

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import '../web/bootstrap.dart';
4040
import'../web/chrome.dart';
4141
import'../web/compile.dart';
4242
import'../web/memory_fs.dart';
43+
import'../web/module_metadata.dart';
4344
import'../web_template.dart';
4445

4546
typedefDwdsLauncher=
@@ -158,6 +159,16 @@ class WebAssetServer implements AssetReader {
158159
/// If[writeRestartScripts] is true, writes a list of sources mapped to their
159160
/// ids to the file system that can then be consumed by the hot restart
160161
/// callback.
162+
///
163+
/// For example:
164+
/// ```json
165+
///[
166+
///{
167+
///"src": "<file_name>",
168+
///"id": "<id>",
169+
///},
170+
///]
171+
/// ```
161172
voidperformRestart(List<String> modules, {requiredbool writeRestartScripts}) {
162173
for (finalString modulein modules) {
163174
// We skip computing the digest by using the hashCode of the underlying buffer.
@@ -174,11 +185,44 @@ class WebAssetServer implements AssetReader {
174185
for (finalString srcin modules) {
175186
srcIdsList.add(<String,String>{'src':'$src?gen=$_hotRestartGeneration','id': src});
176187
}
177-
writeFile('main.dart.js.restartScripts', json.encode(srcIdsList));
188+
writeFile('restart_scripts.json', json.encode(srcIdsList));
178189
}
179190
_hotRestartGeneration++;
180191
}
181192

193+
/// Given a list of[modules] that need to be reloaded, writes a file that
194+
/// contains a list of objects each with two fields:
195+
///
196+
///`src`: A string that corresponds to the file path containing a DDC library
197+
/// bundle.
198+
///`libraries`: An array of strings containing the libraries that were
199+
/// compiled in`src`.
200+
///
201+
/// For example:
202+
/// ```json
203+
///[
204+
///{
205+
///"src": "<file_name>",
206+
///"libraries": ["<lib1>", "<lib2>"],
207+
///},
208+
///]
209+
/// ```
210+
///
211+
/// The path of the output file should stay consistent across the lifetime of
212+
/// the app.
213+
voidperformReload(List<String> modules) {
214+
finalList<Map<String,Object>> moduleToLibrary=<Map<String,Object>>[];
215+
for (finalString modulein modules) {
216+
finalModuleMetadata metadata=ModuleMetadata.fromJson(
217+
json.decode(utf8.decode(_webMemoryFS.metadataFiles['$module.metadata']!.toList()))
218+
asMap<String,dynamic>,
219+
);
220+
finalList<String> libraries= metadata.libraries.keys.toList();
221+
moduleToLibrary.add(<String,Object>{'src': module,'libraries': libraries});
222+
}
223+
writeFile('reload_scripts.json', json.encode(moduleToLibrary));
224+
}
225+
182226
@visibleForTesting
183227
List<String>write(File codeFile,File manifestFile,File sourcemapFile,File metadataFile) {
184228
return _webMemoryFS.write(codeFile, manifestFile, sourcemapFile, metadataFile);
@@ -1001,6 +1045,7 @@ class WebDevFS implements DevFS {
10011045
AssetBundle? bundle,
10021046
bool bundleFirstUpload=false,
10031047
bool fullRestart=false,
1048+
bool resetCompiler=false,
10041049
String? projectRootPath,
10051050
File? dartPluginRegistrant,
10061051
})async {
@@ -1077,7 +1122,7 @@ class WebDevFS implements DevFS {
10771122
await_validateTemplateFile('index.html');
10781123
await_validateTemplateFile('flutter_bootstrap.js');
10791124
finalDateTime candidateCompileTime=DateTime.now();
1080-
if (fullRestart) {
1125+
if (resetCompiler) {
10811126
generator.reset();
10821127
}
10831128

@@ -1122,8 +1167,11 @@ class WebDevFS implements DevFS {
11221167
}onFileSystemExceptioncatch (err) {
11231168
throwToolExit('Failed to load recompiled sources:\n$err');
11241169
}
1125-
1126-
webAssetServer.performRestart(modules, writeRestartScripts: ddcModuleSystem);
1170+
if (fullRestart) {
1171+
webAssetServer.performRestart(modules, writeRestartScripts: ddcModuleSystem);
1172+
}else {
1173+
webAssetServer.performReload(modules);
1174+
}
11271175
returnUpdateFSReport(
11281176
success:true,
11291177
syncedBytes: codeFile.lengthSync(),

‎packages/flutter_tools/lib/src/isolated/resident_web_runner.dart‎

Lines changed: 112 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
322322
}
323323
if (debuggingOptions.buildInfo.isDebug&&!debuggingOptions.webUseWasm) {
324324
awaitrunSourceGenerators();
325-
finalUpdateFSReport report=await_updateDevFS(fullRestart:true);
325+
finalUpdateFSReport report=await_updateDevFS(fullRestart:true, resetCompiler:true);
326326
if (!report.success) {
327327
_logger.printError('Failed to compile application.');
328328
appFailedToStart();
@@ -406,15 +406,28 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
406406
bool benchmarkMode=false,
407407
})async {
408408
finalDateTime start= _systemClock.now();
409-
finalStatus status= _logger.startProgress(
410-
'Performing hot restart...',
411-
progressId:'hot.restart',
412-
);
409+
finalStatus status;
410+
if (debuggingOptions.buildInfo.ddcModuleFormat!=DdcModuleFormat.ddc||
411+
debuggingOptions.buildInfo.canaryFeatures==false) {
412+
// Triggering hot reload performed hot restart for the old module formats
413+
// historically. Keep that behavior and only perform hot reload when the
414+
// new module format is used.
415+
fullRestart=true;
416+
}
417+
if (fullRestart) {
418+
status= _logger.startProgress('Performing hot restart...', progressId:'hot.restart');
419+
}else {
420+
status= _logger.startProgress('Performing hot reload...', progressId:'hot.reload');
421+
}
413422

414423
if (debuggingOptions.buildInfo.isDebug&&!debuggingOptions.webUseWasm) {
415424
awaitrunSourceGenerators();
416-
// Full restart is always false for web, since the extra recompile is wasteful.
417-
finalUpdateFSReport report=await_updateDevFS();
425+
// Don't reset the resident compiler for web, since the extra recompile is
426+
// wasteful.
427+
finalUpdateFSReport report=await_updateDevFS(
428+
fullRestart: fullRestart,
429+
resetCompiler:false,
430+
);
418431
if (report.success) {
419432
device!.generator!.accept();
420433
}else {
@@ -448,10 +461,32 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
448461
if (!deviceIsDebuggable) {
449462
_logger.printStatus('Recompile complete. Page requires refresh.');
450463
}elseif (isRunningDebug) {
451-
// If the hot-restart service extension method is registered, then use
452-
// it. Otherwise, default to calling "hotRestart" without a namespace.
453-
finalString hotRestartMethod= _registeredMethodsForService['hotRestart']??'hotRestart';
454-
await _vmService.service.callMethod(hotRestartMethod);
464+
if (fullRestart) {
465+
// If the hot-restart service extension method is registered, then use
466+
// it. Otherwise, default to calling "hotRestart" without a namespace.
467+
finalString hotRestartMethod=
468+
_registeredMethodsForService['hotRestart']??'hotRestart';
469+
await _vmService.service.callMethod(hotRestartMethod);
470+
}else {
471+
// Isolates don't work on web. For lack of a better value, pass an
472+
// empty string for the isolate id.
473+
final vmservice.ReloadReport report=await _vmService.service.reloadSources('');
474+
finalReloadReportContents contents=ReloadReportContents.fromReloadReport(report);
475+
finalbool success= contents.success??false;
476+
if (!success) {
477+
// Rejections happen at compile-time for the web, so in theory,
478+
// nothing should go wrong here. However, if DWDS or the DDC runtime
479+
// has some internal error, we should still surface it to make
480+
// debugging easier.
481+
String reloadFailedMessage='Hot reload failed:';
482+
globals.printError(reloadFailedMessage);
483+
for (finalReasonForCancelling reasonin contents.notices) {
484+
reloadFailedMessage+= reason.toString();
485+
globals.printError(reason.toString());
486+
}
487+
returnOperationResult(1, reloadFailedMessage);
488+
}
489+
}
455490
}else {
456491
// On non-debug builds, a hard refresh is required to ensure the
457492
// up to date sources are loaded.
@@ -467,40 +502,76 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
467502

468503
finalDuration elapsed= _systemClock.now().difference(start);
469504
finalString elapsedMS=getElapsedAsMilliseconds(elapsed);
470-
_logger.printStatus('Restarted application in $elapsedMS.');
505+
_logger.printStatus('${fullRestart ?'Restarted' :'Reloaded'} application in $elapsedMS.');
471506

472-
unawaited(residentDevtoolsHandler!.hotRestart(flutterDevices));
507+
if (fullRestart) {
508+
unawaited(residentDevtoolsHandler!.hotRestart(flutterDevices));
509+
}
473510

474511
// Don't track restart times for dart2js builds or web-server devices.
475512
if (debuggingOptions.buildInfo.isDebug&& deviceIsDebuggable) {
476-
_analytics.send(
477-
Event.timing(
478-
workflow:'hot',
479-
variableName:'web-incremental-restart',
480-
elapsedMilliseconds: elapsed.inMilliseconds,
481-
),
482-
);
513+
// TODO(srujzs): There are a number of fields that the VM tracks in the
514+
// analytics that we do not for both hot restart and reload. We should
515+
// unify that.
516+
finalString targetPlatform=getNameForTargetPlatform(TargetPlatform.web_javascript);
483517
finalString sdkName=await device!.device!.sdkNameAndVersion;
484-
HotEvent(
485-
'restart',
486-
targetPlatform:getNameForTargetPlatform(TargetPlatform.web_javascript),
487-
sdkName: sdkName,
488-
emulator:false,
489-
fullRestart:true,
490-
reason: reason,
491-
overallTimeInMs: elapsed.inMilliseconds,
492-
).send();
493-
_analytics.send(
494-
Event.hotRunnerInfo(
495-
label:'restart',
496-
targetPlatform:getNameForTargetPlatform(TargetPlatform.web_javascript),
518+
if (fullRestart) {
519+
_analytics.send(
520+
Event.timing(
521+
workflow:'hot',
522+
variableName:'web-incremental-restart',
523+
elapsedMilliseconds: elapsed.inMilliseconds,
524+
),
525+
);
526+
HotEvent(
527+
'restart',
528+
targetPlatform: targetPlatform,
497529
sdkName: sdkName,
498530
emulator:false,
499531
fullRestart:true,
500532
reason: reason,
501533
overallTimeInMs: elapsed.inMilliseconds,
502-
),
503-
);
534+
).send();
535+
_analytics.send(
536+
Event.hotRunnerInfo(
537+
label:'restart',
538+
targetPlatform: targetPlatform,
539+
sdkName: sdkName,
540+
emulator:false,
541+
fullRestart:true,
542+
reason: reason,
543+
overallTimeInMs: elapsed.inMilliseconds,
544+
),
545+
);
546+
}else {
547+
_analytics.send(
548+
Event.timing(
549+
workflow:'hot',
550+
variableName:'reload',
551+
elapsedMilliseconds: elapsed.inMilliseconds,
552+
),
553+
);
554+
HotEvent(
555+
'reload',
556+
targetPlatform: targetPlatform,
557+
sdkName: sdkName,
558+
emulator:false,
559+
fullRestart:false,
560+
reason: reason,
561+
overallTimeInMs: elapsed.inMilliseconds,
562+
).send();
563+
_analytics.send(
564+
Event.hotRunnerInfo(
565+
label:'reload',
566+
targetPlatform: targetPlatform,
567+
sdkName: sdkName,
568+
emulator:false,
569+
fullRestart:false,
570+
reason: reason,
571+
overallTimeInMs: elapsed.inMilliseconds,
572+
),
573+
);
574+
}
504575
}
505576
returnOperationResult.ok;
506577
}
@@ -551,7 +622,10 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
551622
return result!.absolute.uri;
552623
}
553624

554-
Future<UpdateFSReport>_updateDevFS({bool fullRestart=false})async {
625+
Future<UpdateFSReport>_updateDevFS({
626+
requiredbool fullRestart,
627+
requiredbool resetCompiler,
628+
})async {
555629
finalbool isFirstUpload=!assetBundle.wasBuiltOnce();
556630
finalbool rebuildBundle= assetBundle.needsBuild();
557631
if (rebuildBundle) {
@@ -584,8 +658,9 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
584658
bundleFirstUpload: isFirstUpload,
585659
generator: device!.generator!,
586660
fullRestart: fullRestart,
661+
resetCompiler: resetCompiler,
587662
dillOutputPath: dillOutputPath,
588-
pathToReload:getReloadPath(fullRestart: fullRestart, swap:false),
663+
pathToReload:getReloadPath(resetCompiler: resetCompiler, swap:false),
589664
invalidatedFiles: invalidationResult.uris!,
590665
packageConfig: invalidationResult.packageConfig!,
591666
trackWidgetCreation: debuggingOptions.buildInfo.trackWidgetCreation,

‎packages/flutter_tools/lib/src/resident_runner.dart‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ class FlutterDevice {
570570
bundleFirstUpload: bundleFirstUpload,
571571
generator: generator!,
572572
fullRestart: fullRestart,
573+
resetCompiler: fullRestart,
573574
dillOutputPath: dillOutputPath,
574575
trackWidgetCreation: buildInfo.trackWidgetCreation,
575576
pathToReload: pathToReload,
@@ -1112,8 +1113,8 @@ abstract class ResidentRunner extends ResidentHandlers {
11121113

11131114
Stringget dillOutputPath=>
11141115
_dillOutputPath?? globals.fs.path.join(artifactDirectory.path,'app.dill');
1115-
StringgetReloadPath({boolfullRestart=false,requiredbool swap}) {
1116-
if (!fullRestart) {
1116+
StringgetReloadPath({boolresetCompiler=false,requiredbool swap}) {
1117+
if (!resetCompiler) {
11171118
return'main.dart.incremental.dill';
11181119
}
11191120
return'main.dart${swap ?'.swap' :''}.dill';

‎packages/flutter_tools/lib/src/run_hot.dart‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ class HotRunner extends ResidentRunner {
536536
bundleFirstUpload: isFirstUpload,
537537
bundleDirty:!isFirstUpload&& rebuildBundle,
538538
fullRestart: fullRestart,
539-
pathToReload:getReloadPath(fullRestart: fullRestart, swap: _swap),
539+
pathToReload:getReloadPath(resetCompiler: fullRestart, swap: _swap),
540540
invalidatedFiles: invalidationResult.uris!,
541541
packageConfig: invalidationResult.packageConfig!,
542542
dillOutputPath: dillOutputPath,

‎packages/flutter_tools/lib/src/web/bootstrap.dart‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,15 @@ $_simpleLoaderScript
252252
// We should have written a file containing all the scripts that need to be
253253
// reloaded into the page. This is then read when a hot restart is triggered
254254
// in DDC via the `\$dartReloadModifiedModules` callback.
255-
let restartScripts = currentUri + '.restartScripts';
255+
let restartScripts = _currentDirectory + 'restart_scripts.json';
256+
// Flutter tools should write a file containing the scripts and libraries
257+
// that need to be hot reloaded. This is read in DWDS when a hot reload is
258+
// triggered.
259+
// TODO(srujzs): Ideally, this should be passed to the
260+
// `FrontendServerDdcLibraryBundleStrategyProvider` instead. See
261+
// https://github.com/dart-lang/webdev/issues/2584 for more details.
262+
let reloadScripts = _currentDirectory + 'reload_scripts.json';
263+
window.\$reloadScriptsPath = reloadScripts;
256264
257265
if (!window.\$dartReloadModifiedModules) {
258266
window.\$dartReloadModifiedModules = (function(appName, callback) {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2026 Movatter.jp