Unverified Commit 7ca324ac authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] support sound null-safety mode for the web (#60570)

In web debug mode, infer sound null safety by default. When sound null safety is enabled, provide a separate dill and precompiled Dart SDK. Release builds do not need this setting since we run dart2js from source.

Fixes #59873
parent 21881961
...@@ -39,9 +39,22 @@ enum Artifact { ...@@ -39,9 +39,22 @@ enum Artifact {
kernelWorkerSnapshot, kernelWorkerSnapshot,
/// The root of the web implementation of the dart SDK. /// The root of the web implementation of the dart SDK.
flutterWebSdk, flutterWebSdk,
/// The libraries JSON file for web release builds.
flutterWebLibrariesJson, flutterWebLibrariesJson,
/// The summary dill for the dartdevc target. /// The summary dill for the dartdevc target.
webPlatformKernelDill, webPlatformKernelDill,
/// The summary dill with null safety enabled for the dartdevc target.
webPlatformSoundKernelDill,
/// The precompiled SDKs and sourcemaps for web debug builds.
webPrecompiledSdk,
webPrecompiledSdkSourcemaps,
webPrecompiledCanvaskitSdk,
webPrecompiledCanvaskitSdkSourcemaps,
webPrecompiledSoundSdk,
webPrecompiledSoundSdkSourcemaps,
webPrecompiledCanvaskitSoundSdk,
webPrecompiledCanvaskitSoundSdkSourcemaps,
iosDeploy, iosDeploy,
idevicesyslog, idevicesyslog,
idevicescreenshot, idevicescreenshot,
...@@ -129,6 +142,8 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo ...@@ -129,6 +142,8 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo
return 'FlutterMacOS.podspec'; return 'FlutterMacOS.podspec';
case Artifact.webPlatformKernelDill: case Artifact.webPlatformKernelDill:
return 'flutter_ddc_sdk.dill'; return 'flutter_ddc_sdk.dill';
case Artifact.webPlatformSoundKernelDill:
return 'flutter_ddc_sdk_sound.dill';
case Artifact.fuchsiaKernelCompiler: case Artifact.fuchsiaKernelCompiler:
return 'kernel_compiler.snapshot'; return 'kernel_compiler.snapshot';
case Artifact.fuchsiaFlutterRunner: case Artifact.fuchsiaFlutterRunner:
...@@ -141,6 +156,16 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo ...@@ -141,6 +156,16 @@ String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMo
return 'const_finder.dart.snapshot'; return 'const_finder.dart.snapshot';
case Artifact.flutterWebLibrariesJson: case Artifact.flutterWebLibrariesJson:
return 'libraries.json'; return 'libraries.json';
case Artifact.webPrecompiledSdk:
case Artifact.webPrecompiledCanvaskitSdk:
case Artifact.webPrecompiledSoundSdk:
case Artifact.webPrecompiledCanvaskitSoundSdk:
return 'dart_sdk.js';
case Artifact.webPrecompiledSdkSourcemaps:
case Artifact.webPrecompiledCanvaskitSdkSourcemaps:
case Artifact.webPrecompiledSoundSdkSourcemaps:
case Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
return 'dart_sdk.js.map';
} }
assert(false, 'Invalid artifact $artifact.'); assert(false, 'Invalid artifact $artifact.');
return null; return null;
...@@ -348,6 +373,8 @@ class CachedArtifacts implements Artifacts { ...@@ -348,6 +373,8 @@ class CachedArtifacts implements Artifacts {
return _fileSystem.path.join(_getFlutterWebSdkPath(), _artifactToFileName(artifact)); return _fileSystem.path.join(_getFlutterWebSdkPath(), _artifactToFileName(artifact));
case Artifact.webPlatformKernelDill: case Artifact.webPlatformKernelDill:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact)); return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
case Artifact.webPlatformSoundKernelDill:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
case Artifact.dart2jsSnapshot: case Artifact.dart2jsSnapshot:
return _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin', 'snapshots', _artifactToFileName(artifact)); return _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin', 'snapshots', _artifactToFileName(artifact));
case Artifact.dartdevcSnapshot: case Artifact.dartdevcSnapshot:
...@@ -380,6 +407,22 @@ class CachedArtifacts implements Artifacts { ...@@ -380,6 +407,22 @@ class CachedArtifacts implements Artifacts {
.childDirectory(getNameForTargetPlatform(platform)) .childDirectory(getNameForTargetPlatform(platform))
.childFile(_artifactToFileName(artifact, platform, mode)) .childFile(_artifactToFileName(artifact, platform, mode))
.path; .path;
case Artifact.webPrecompiledSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledCanvaskitSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledCanvaskitSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledSoundSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledSoundSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledCanvaskitSoundSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _artifactToFileName(artifact, platform, mode));
case Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _artifactToFileName(artifact, platform, mode));
default: default:
assert(false, 'Artifact $artifact not available for platform $platform.'); assert(false, 'Artifact $artifact not available for platform $platform.');
return null; return null;
...@@ -542,6 +585,8 @@ class LocalEngineArtifacts implements Artifacts { ...@@ -542,6 +585,8 @@ class LocalEngineArtifacts implements Artifacts {
return _fileSystem.path.join(_hostEngineOutPath, _artifactToFileName(artifact)); return _fileSystem.path.join(_hostEngineOutPath, _artifactToFileName(artifact));
case Artifact.webPlatformKernelDill: case Artifact.webPlatformKernelDill:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact)); return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
case Artifact.webPlatformSoundKernelDill:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _artifactToFileName(artifact));
case Artifact.fuchsiaKernelCompiler: case Artifact.fuchsiaKernelCompiler:
final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform()); final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
final String modeName = mode.isRelease ? 'release' : mode.toString(); final String modeName = mode.isRelease ? 'release' : mode.toString();
...@@ -557,6 +602,22 @@ class LocalEngineArtifacts implements Artifacts { ...@@ -557,6 +602,22 @@ class LocalEngineArtifacts implements Artifacts {
return _fileSystem.path.join(_hostEngineOutPath, 'gen', artifactFileName); return _fileSystem.path.join(_hostEngineOutPath, 'gen', artifactFileName);
case Artifact.flutterWebLibrariesJson: case Artifact.flutterWebLibrariesJson:
return _fileSystem.path.join(_getFlutterWebSdkPath(), artifactFileName); return _fileSystem.path.join(_getFlutterWebSdkPath(), artifactFileName);
case Artifact.webPrecompiledSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', artifactFileName);
case Artifact.webPrecompiledSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', artifactFileName);
case Artifact.webPrecompiledCanvaskitSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', artifactFileName);
case Artifact.webPrecompiledCanvaskitSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', artifactFileName);
case Artifact.webPrecompiledSoundSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', artifactFileName);
case Artifact.webPrecompiledSoundSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', artifactFileName);
case Artifact.webPrecompiledCanvaskitSoundSdk:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', artifactFileName);
case Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
return _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', artifactFileName);
} }
assert(false, 'Invalid artifact $artifact.'); assert(false, 'Invalid artifact $artifact.');
return null; return null;
......
...@@ -30,10 +30,16 @@ class BuildInfo { ...@@ -30,10 +30,16 @@ class BuildInfo {
@required this.treeShakeIcons, @required this.treeShakeIcons,
this.performanceMeasurementFile, this.performanceMeasurementFile,
this.packagesPath = '.packages', this.packagesPath = '.packages',
this.nullSafetyMode = NullSafetyMode.autodetect,
}); });
final BuildMode mode; final BuildMode mode;
/// The null safety mode the application should be run in.
///
/// If not provided, defaults to [NullSafetyMode.autodetect].
final NullSafetyMode nullSafetyMode;
/// Whether the build should subdset icon fonts. /// Whether the build should subdset icon fonts.
final bool treeShakeIcons; final bool treeShakeIcons;
...@@ -688,3 +694,10 @@ List<String> decodeDartDefines(Map<String, String> environmentDefines, String ke ...@@ -688,3 +694,10 @@ List<String> decodeDartDefines(Map<String, String> environmentDefines, String ke
.cast<String>() .cast<String>()
.toList(); .toList();
} }
/// The null safety runtime mode the app should be built in.
enum NullSafetyMode {
sound,
unsound,
autodetect,
}
...@@ -103,6 +103,7 @@ class WebAssetServer implements AssetReader { ...@@ -103,6 +103,7 @@ class WebAssetServer implements AssetReader {
this.internetAddress, this.internetAddress,
this._modules, this._modules,
this._digests, this._digests,
this._buildInfo,
); );
// Fallback to "application/octet-stream" on null which // Fallback to "application/octet-stream" on null which
...@@ -167,6 +168,7 @@ class WebAssetServer implements AssetReader { ...@@ -167,6 +168,7 @@ class WebAssetServer implements AssetReader {
address, address,
modules, modules,
digests, digests,
buildInfo,
); );
if (testMode) { if (testMode) {
return server; return server;
...@@ -263,6 +265,7 @@ class WebAssetServer implements AssetReader { ...@@ -263,6 +265,7 @@ class WebAssetServer implements AssetReader {
return null; return null;
} }
final BuildInfo _buildInfo;
final HttpServer _httpServer; final HttpServer _httpServer;
// If holding these in memory is too much overhead, this can be switched to a // If holding these in memory is too much overhead, this can be switched to a
// RandomAccessFile and read on demand. // RandomAccessFile and read on demand.
...@@ -458,50 +461,31 @@ class WebAssetServer implements AssetReader { ...@@ -458,50 +461,31 @@ class WebAssetServer implements AssetReader {
/// Whether to use the cavaskit SDK for rendering. /// Whether to use the cavaskit SDK for rendering.
bool canvasKitRendering = false; bool canvasKitRendering = false;
@visibleForTesting
final File dartSdk = globals.fs.file(globals.fs.path.join(
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
'kernel',
'amd',
'dart_sdk.js',
));
@visibleForTesting
final File canvasKitDartSdk = globals.fs.file(globals.fs.path.join(
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
'kernel',
'amd-canvaskit',
'dart_sdk.js',
));
@visibleForTesting
final File dartSdkSourcemap = globals.fs.file(globals.fs.path.join(
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
'kernel',
'amd',
'dart_sdk.js.map',
));
@visibleForTesting
final File canvasKitDartSdkSourcemap = globals.fs.file(globals.fs.path.join(
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk),
'kernel',
'amd-canvaskit',
'dart_sdk.js.map',
));
// Attempt to resolve `path` to a dart file. // Attempt to resolve `path` to a dart file.
File _resolveDartFile(String path) { File _resolveDartFile(String path) {
// Return the actual file objects so that local engine changes are automatically picked up. // Return the actual file objects so that local engine changes are automatically picked up.
switch (path) { switch (path) {
case 'dart_sdk.js': case 'dart_sdk.js':
return canvasKitRendering if (_buildInfo.nullSafetyMode == NullSafetyMode.unsound) {
? canvasKitDartSdk return globals.fs.file(canvasKitRendering
: dartSdk; ? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdk)
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSdk));
} else {
return globals.fs.file(canvasKitRendering
? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdk)
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdk));
}
break;
case 'dart_sdk.js.map': case 'dart_sdk.js.map':
return canvasKitRendering if (_buildInfo.nullSafetyMode == NullSafetyMode.unsound) {
? canvasKitDartSdkSourcemap return globals.fs.file(canvasKitRendering
: dartSdkSourcemap; ? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdkSourcemaps)
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSdkSourcemaps));
} else {
return globals.fs.file(canvasKitRendering
? globals.artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps)
: globals.artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdkSourcemaps));
}
} }
// This is the special generated entrypoint. // This is the special generated entrypoint.
if (path == 'web_entrypoint.dart') { if (path == 'web_entrypoint.dart') {
......
...@@ -92,6 +92,20 @@ class FlutterDevice { ...@@ -92,6 +92,20 @@ class FlutterDevice {
// a warning message and dump some debug information which can be // a warning message and dump some debug information which can be
// used to file a bug, but the compiler will still start up correctly. // used to file a bug, but the compiler will still start up correctly.
if (targetPlatform == TargetPlatform.web_javascript) { if (targetPlatform == TargetPlatform.web_javascript) {
Artifact platformDillArtifact;
List<String> extraFrontEndOptions;
if (buildInfo.nullSafetyMode == NullSafetyMode.unsound) {
platformDillArtifact = Artifact.webPlatformKernelDill;
extraFrontEndOptions = buildInfo.extraFrontEndOptions;
} else {
platformDillArtifact = Artifact.webPlatformSoundKernelDill;
extraFrontEndOptions = <String>[
...?buildInfo?.extraFrontEndOptions,
if (!(buildInfo?.extraFrontEndOptions?.contains('--sound-null-safety') ?? false))
'--sound-null-safety'
];
}
generator = ResidentCompiler( generator = ResidentCompiler(
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: buildInfo.mode), globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: buildInfo.mode),
buildMode: buildInfo.mode, buildMode: buildInfo.mode,
...@@ -105,9 +119,9 @@ class FlutterDevice { ...@@ -105,9 +119,9 @@ class FlutterDevice {
dartDefines: buildInfo.dartDefines, dartDefines: buildInfo.dartDefines,
), ),
targetModel: TargetModel.dartdevc, targetModel: TargetModel.dartdevc,
extraFrontEndOptions: buildInfo.extraFrontEndOptions, extraFrontEndOptions: extraFrontEndOptions,
platformDill: globals.fs.file(globals.artifacts platformDill: globals.fs.file(globals.artifacts
.getArtifactPath(Artifact.webPlatformKernelDill, mode: buildInfo.mode)) .getArtifactPath(platformDillArtifact, mode: buildInfo.mode))
.absolute.uri.toString(), .absolute.uri.toString(),
dartDefines: buildInfo.dartDefines, dartDefines: buildInfo.dartDefines,
librariesSpec: globals.fs.file(globals.artifacts librariesSpec: globals.fs.file(globals.artifacts
......
...@@ -479,8 +479,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -479,8 +479,9 @@ abstract class FlutterCommand extends Command<void> {
help: help:
'Whether to override the inferred null safety mode. This allows null-safe ' 'Whether to override the inferred null safety mode. This allows null-safe '
'libraries to depend on un-migrated (non-null safe) libraries. By default, ' 'libraries to depend on un-migrated (non-null safe) libraries. By default, '
'Flutter applications will attempt to run at the null safety level of their ' 'Flutter mobile & desktop applications will attempt to run at the null safety '
'entrypoint library (usually lib/main.dart).', 'level of their entrypoint library (usually lib/main.dart). Flutter web '
'applications will default to sound null-safety, unless specifically configured.',
defaultsTo: null, defaultsTo: null,
hide: hide, hide: hide,
); );
...@@ -617,15 +618,20 @@ abstract class FlutterCommand extends Command<void> { ...@@ -617,15 +618,20 @@ abstract class FlutterCommand extends Command<void> {
} }
} }
NullSafetyMode nullSafetyMode = NullSafetyMode.unsound;
if (argParser.options.containsKey(FlutterOptions.kNullSafety)) { if (argParser.options.containsKey(FlutterOptions.kNullSafety)) {
final bool nullSafety = boolArg(FlutterOptions.kNullSafety); final bool nullSafety = boolArg(FlutterOptions.kNullSafety);
// Explicitly check for `true` and `false` so that `null` results in not // Explicitly check for `true` and `false` so that `null` results in not
// passing a flag. This will use the automatically detected null-safety // passing a flag. This will use the automatically detected null-safety
// value based on the entrypoint // value based on the entrypoint
if (nullSafety == true) { if (nullSafety == true) {
nullSafetyMode = NullSafetyMode.sound;
extraFrontEndOptions.add('--sound-null-safety'); extraFrontEndOptions.add('--sound-null-safety');
} else if (nullSafety == false) { } else if (nullSafety == false) {
nullSafetyMode = NullSafetyMode.unsound;
extraFrontEndOptions.add('--no-sound-null-safety'); extraFrontEndOptions.add('--no-sound-null-safety');
} else if (extraFrontEndOptions.contains('--enable-experiment=non-nullable')) {
nullSafetyMode = NullSafetyMode.autodetect;
} }
} }
...@@ -685,7 +691,8 @@ abstract class FlutterCommand extends Command<void> { ...@@ -685,7 +691,8 @@ abstract class FlutterCommand extends Command<void> {
bundleSkSLPath: bundleSkSLPath, bundleSkSLPath: bundleSkSLPath,
dartExperiments: experiments, dartExperiments: experiments,
performanceMeasurementFile: performanceMeasurementFile, performanceMeasurementFile: performanceMeasurementFile,
packagesPath: globalResults['packages'] as String ?? '.packages' packagesPath: globalResults['packages'] as String ?? '.packages',
nullSafetyMode: nullSafetyMode,
); );
} }
......
...@@ -53,7 +53,7 @@ Future<void> buildWeb( ...@@ -53,7 +53,7 @@ Future<void> buildWeb(
kCspMode: csp.toString(), kCspMode: csp.toString(),
kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(), kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(),
if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false) if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false)
kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(',') kExtraFrontEndOptions: encodeDartDefines(buildInfo.extraFrontEndOptions),
}, },
artifacts: globals.artifacts, artifacts: globals.artifacts,
fileSystem: globals.fs, fileSystem: globals.fs,
......
...@@ -52,6 +52,41 @@ void main() { ...@@ -52,6 +52,41 @@ void main() {
); );
}); });
testWithoutContext('precompiled web artifact paths are correct', () {
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledSdk),
'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledSdkSourcemaps),
'root/bin/cache/flutter_web_sdk/kernel/amd/dart_sdk.js.map',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdk),
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSdkSourcemaps),
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit/dart_sdk.js.map',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdk),
'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledSoundSdkSourcemaps),
'root/bin/cache/flutter_web_sdk/kernel/amd-sound/dart_sdk.js.map',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdk),
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js',
);
expect(
artifacts.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps),
'root/bin/cache/flutter_web_sdk/kernel/amd-canvaskit-sound/dart_sdk.js.map',
);
});
testWithoutContext('getEngineType', () { testWithoutContext('getEngineType', () {
expect( expect(
artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug), artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug),
......
...@@ -1540,7 +1540,7 @@ void main() { ...@@ -1540,7 +1540,7 @@ void main() {
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
})); }));
testUsingContext('FlutterDevice uses dartdevc configuration when targeting web', () => testbed.run(() async { testUsingContext('FlutterDevice uses dartdevc configuration when targeting web', () async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final MockDevice mockDevice = MockDevice(); final MockDevice mockDevice = MockDevice();
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async { when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
...@@ -1549,7 +1549,12 @@ void main() { ...@@ -1549,7 +1549,12 @@ void main() {
final DefaultResidentCompiler residentCompiler = (await FlutterDevice.create( final DefaultResidentCompiler residentCompiler = (await FlutterDevice.create(
mockDevice, mockDevice,
buildInfo: BuildInfo.debug, buildInfo: const BuildInfo(
BuildMode.debug,
'',
treeShakeIcons: false,
nullSafetyMode: NullSafetyMode.unsound,
),
flutterProject: FlutterProject.current(), flutterProject: FlutterProject.current(),
target: null, target: null,
)).generator as DefaultResidentCompiler; )).generator as DefaultResidentCompiler;
...@@ -1562,12 +1567,46 @@ void main() { ...@@ -1562,12 +1567,46 @@ void main() {
expect(residentCompiler.targetModel, TargetModel.dartdevc); expect(residentCompiler.targetModel, TargetModel.dartdevc);
expect(residentCompiler.sdkRoot, expect(residentCompiler.sdkRoot,
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: BuildMode.debug) + '/'); globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: BuildMode.debug) + '/');
expect( expect(residentCompiler.platformDill, 'file:///Artifact.webPlatformKernelDill.debug');
residentCompiler.platformDill, }, overrides: <Type, Generator>{
globals.fs.file(globals.artifacts.getArtifactPath(Artifact.webPlatformKernelDill, mode: BuildMode.debug)) Artifacts: () => Artifacts.test(),
.absolute.uri.toString(), FileSystem: () => MemoryFileSystem.test(),
); ProcessManager: () => FakeProcessManager.any(),
})); });
testUsingContext('FlutterDevice uses dartdevc configuration when targeting web with null-safety autodetected', () async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final MockDevice mockDevice = MockDevice();
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
return TargetPlatform.web_javascript;
});
final DefaultResidentCompiler residentCompiler = (await FlutterDevice.create(
mockDevice,
buildInfo: const BuildInfo(
BuildMode.debug,
'',
treeShakeIcons: false,
extraFrontEndOptions: <String>['--enable-experiment=non-nullable'],
),
flutterProject: FlutterProject.current(),
target: null,
)).generator as DefaultResidentCompiler;
expect(residentCompiler.initializeFromDill,
globals.fs.path.join(getBuildDirectory(), 'cache.dill'));
expect(residentCompiler.librariesSpec,
globals.fs.file(globals.artifacts.getArtifactPath(Artifact.flutterWebLibrariesJson))
.uri.toString());
expect(residentCompiler.targetModel, TargetModel.dartdevc);
expect(residentCompiler.sdkRoot,
globals.artifacts.getArtifactPath(Artifact.flutterWebSdk, mode: BuildMode.debug) + '/');
expect(residentCompiler.platformDill, 'file:///Artifact.webPlatformSoundKernelDill.debug');
}, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('connect sets up log reader', () => testbed.run(() async { testUsingContext('connect sets up log reader', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
......
...@@ -764,7 +764,7 @@ void main() { ...@@ -764,7 +764,7 @@ void main() {
testUsingContext('web resident runner can toggle CanvasKit', () async { testUsingContext('web resident runner can toggle CanvasKit', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice); final ResidentRunner residentWebRunner = setUpResidentRunner(mockFlutterDevice);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final WebAssetServer webAssetServer = WebAssetServer(null, null, null, null, null); final WebAssetServer webAssetServer = WebAssetServer(null, null, null, null, null, null);
when(mockWebDevFS.webAssetServer).thenReturn(webAssetServer); when(mockWebDevFS.webAssetServer).thenReturn(webAssetServer);
expect(residentWebRunner.supportsCanvasKit, true); expect(residentWebRunner.supportsCanvasKit, true);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:io'; import 'dart:io';
import 'package:dwds/dwds.dart'; import 'package:dwds/dwds.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -53,6 +54,8 @@ void main() { ...@@ -53,6 +54,8 @@ void main() {
InternetAddress.loopbackIPv4, InternetAddress.loopbackIPv4,
null, null,
null, null,
null,
); );
}); });
}); });
...@@ -416,7 +419,12 @@ void main() { ...@@ -416,7 +419,12 @@ void main() {
packagesFilePath: '.packages', packagesFilePath: '.packages',
urlTunneller: null, urlTunneller: null,
useSseForDebugProxy: true, useSseForDebugProxy: true,
buildInfo: BuildInfo.debug, buildInfo: const BuildInfo(
BuildMode.debug,
'',
treeShakeIcons: false,
nullSafetyMode: NullSafetyMode.unsound,
),
enableDwds: false, enableDwds: false,
entrypoint: Uri.base, entrypoint: Uri.base,
testMode: true, testMode: true,
...@@ -428,24 +436,31 @@ void main() { ...@@ -428,24 +436,31 @@ void main() {
final Uri uri = await webDevFS.create(); final Uri uri = await webDevFS.create();
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory; webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
final String webPrecompiledSdk = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledSdk);
final String webPrecompiledSdkSourcemaps = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledSdkSourcemaps);
final String webPrecompiledCanvaskitSdk = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledCanvaskitSdk);
final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledCanvaskitSdkSourcemaps);
globals.fs.currentDirectory globals.fs.currentDirectory
.childDirectory('lib') .childDirectory('lib')
.childFile('web_entrypoint.dart') .childFile('web_entrypoint.dart')
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('GENERATED'); ..writeAsStringSync('GENERATED');
webDevFS.webAssetServer.dartSdk globals.fs.file(webPrecompiledSdk)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('HELLO'); ..writeAsStringSync('HELLO');
webDevFS.webAssetServer.dartSdkSourcemap globals.fs.file(webPrecompiledSdkSourcemaps)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('THERE'); ..writeAsStringSync('THERE');
webDevFS.webAssetServer.canvasKitDartSdk globals.fs.file(webPrecompiledCanvaskitSdk)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('OL'); ..writeAsStringSync('OL');
webDevFS.webAssetServer.canvasKitDartSdkSourcemap globals.fs.file(webPrecompiledCanvaskitSdkSourcemaps)
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsStringSync('CHUM'); ..writeAsStringSync('CHUM');
webDevFS.webAssetServer.dartSdkSourcemap.createSync(recursive: true);
await webDevFS.update( await webDevFS.update(
mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri, mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri,
...@@ -465,7 +480,7 @@ void main() { ...@@ -465,7 +480,7 @@ void main() {
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE'); expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE');
// Update to the SDK. // Update to the SDK.
webDevFS.webAssetServer.dartSdk.writeAsStringSync('BELLOW'); globals.fs.file(webPrecompiledSdk).writeAsStringSync('BELLOW');
// New SDK should be visible.. // New SDK should be visible..
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW'); expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
...@@ -483,6 +498,116 @@ void main() { ...@@ -483,6 +498,116 @@ void main() {
expect(uri, Uri.http('localhost:0', '')); expect(uri, Uri.http('localhost:0', ''));
await webDevFS.destroy(); await webDevFS.destroy();
}, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(),
}));
test('Can start web server with specified assets in sound null safety mode', () => testbed.run(() async {
globals.fs.file('.packages').writeAsStringSync('\n');
final File outputFile = globals.fs.file(globals.fs.path.join('lib', 'main.dart'))
..createSync(recursive: true);
outputFile.parent.childFile('a.sources').writeAsStringSync('');
outputFile.parent.childFile('a.json').writeAsStringSync('{}');
outputFile.parent.childFile('a.map').writeAsStringSync('{}');
outputFile.parent.childFile('.packages').writeAsStringSync('\n');
final ResidentCompiler residentCompiler = MockResidentCompiler();
when(residentCompiler.recompile(
any,
any,
outputPath: anyNamed('outputPath'),
packageConfig: anyNamed('packageConfig'),
)).thenAnswer((Invocation invocation) async {
return const CompilerOutput('a', 0, <Uri>[]);
});
final WebDevFS webDevFS = WebDevFS(
hostname: 'localhost',
port: 0,
packagesFilePath: '.packages',
urlTunneller: null,
useSseForDebugProxy: true,
buildInfo: const BuildInfo(
BuildMode.debug,
'',
treeShakeIcons: false,
nullSafetyMode: NullSafetyMode.autodetect,
),
enableDwds: false,
entrypoint: Uri.base,
testMode: true,
expressionCompiler: null,
chromiumLauncher: null,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
final Uri uri = await webDevFS.create();
webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory;
globals.fs.currentDirectory
.childDirectory('lib')
.childFile('web_entrypoint.dart')
..createSync(recursive: true)
..writeAsStringSync('GENERATED');
final String webPrecompiledSoundSdk = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledSoundSdk);
final String webPrecompiledSoundSdkSourcemaps = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledSoundSdkSourcemaps);
final String webPrecompiledCanvaskitSoundSdk = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdk);
final String webPrecompiledCanvaskitSoundSdkSourcemaps = globals.artifacts
.getArtifactPath(Artifact.webPrecompiledCanvaskitSoundSdkSourcemaps);
globals.fs.file(webPrecompiledSoundSdk)
..createSync(recursive: true)
..writeAsStringSync('HELLO');
globals.fs.file(webPrecompiledSoundSdkSourcemaps)
..createSync(recursive: true)
..writeAsStringSync('THERE');
globals.fs.file(webPrecompiledCanvaskitSoundSdk)
..createSync(recursive: true)
..writeAsStringSync('OL');
globals.fs.file(webPrecompiledCanvaskitSoundSdkSourcemaps)
..createSync(recursive: true)
..writeAsStringSync('CHUM');
await webDevFS.update(
mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri,
generator: residentCompiler,
trackWidgetCreation: true,
bundleFirstUpload: true,
invalidatedFiles: <Uri>[],
packageConfig: PackageConfig.empty,
);
expect(webDevFS.webAssetServer.getFile('require.js'), isNotNull);
expect(webDevFS.webAssetServer.getFile('stack_trace_mapper.js'), isNotNull);
expect(webDevFS.webAssetServer.getFile('main.dart'), isNotNull);
expect(webDevFS.webAssetServer.getFile('manifest.json'), isNotNull);
expect(webDevFS.webAssetServer.getFile('flutter_service_worker.js'), isNotNull);
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'HELLO');
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE');
// Update to the SDK.
globals.fs.file(webPrecompiledSoundSdk).writeAsStringSync('BELLOW');
// New SDK should be visible..
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
// Toggle CanvasKit
webDevFS.webAssetServer.canvasKitRendering = true;
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'OL');
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'CHUM');
// Generated entrypoint.
expect(await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'),
contains('GENERATED'));
// served on localhost
expect(uri, Uri.http('localhost:0', ''));
await webDevFS.destroy();
}, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(),
})); }));
test('Can start web server with hostname any', () => testbed.run(() async { test('Can start web server with hostname any', () => testbed.run(() async {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment