More AppContext cleanups (#7073)

diff --git a/packages/flutter_tools/bin/fuchsia_builder.dart b/packages/flutter_tools/bin/fuchsia_builder.dart
index 3fae218..8dafd3a 100644
--- a/packages/flutter_tools/bin/fuchsia_builder.dart
+++ b/packages/flutter_tools/bin/fuchsia_builder.dart
@@ -8,11 +8,14 @@
 import 'package:args/args.dart';
 
 import '../lib/src/base/common.dart';
+import '../lib/src/base/config.dart';
 import '../lib/src/base/context.dart';
 import '../lib/src/base/logger.dart';
+import '../lib/src/base/os.dart';
 import '../lib/src/cache.dart';
 import '../lib/src/flx.dart';
 import '../lib/src/globals.dart';
+import '../lib/src/usage.dart';
 
 const String _kOptionPackages = 'packages';
 const String _kOptionOutput = 'output-file';
@@ -28,10 +31,16 @@
   _kOptionWorking,
 ];
 
-void main(List<String> args) async {
+Future<Null> main(List<String> args) async {
   AppContext executableContext = new AppContext();
+  executableContext.setVariable(Logger, new StdoutLogger());
   executableContext.runInZone(() {
-    context[Logger] = new StdoutLogger();
+    // Initialize the context with some defaults.
+    context.putIfAbsent(Logger, () => new StdoutLogger());
+    context.putIfAbsent(Cache, () => new Cache());
+    context.putIfAbsent(Config, () => new Config());
+    context.putIfAbsent(OperatingSystemUtils, () => new OperatingSystemUtils());
+    context.putIfAbsent(Usage, () => new Usage());
     return run(args);
   });
 }
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 90e20cd..6508b3a 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -9,10 +9,13 @@
 import 'package:stack_trace/stack_trace.dart';
 
 import 'src/base/common.dart';
+import 'src/base/config.dart';
 import 'src/base/context.dart';
 import 'src/base/logger.dart';
+import 'src/base/os.dart';
 import 'src/base/process.dart';
 import 'src/base/utils.dart';
+import 'src/cache.dart';
 import 'src/commands/analyze.dart';
 import 'src/commands/build.dart';
 import 'src/commands/channel.dart';
@@ -40,8 +43,12 @@
 import 'src/doctor.dart';
 import 'src/globals.dart';
 import 'src/hot.dart';
-import 'src/usage.dart';
+import 'src/ios/mac.dart';
+import 'src/ios/simulators.dart';
 import 'src/runner/flutter_command_runner.dart';
+import 'src/toolchain.dart';
+import 'src/usage.dart';
+
 
 /// Main entry point for commands.
 ///
@@ -88,16 +95,19 @@
   // Make the context current.
   _executableContext.runInZone(() {
     // Initialize the context with some defaults.
-    if (context[Logger] == null)
-      context[Logger] = new StdoutLogger();
-    if (context[DeviceManager] == null)
-      context[DeviceManager] = new DeviceManager();
-    if (context[DevFSConfig] == null)
-      context[DevFSConfig] = new DevFSConfig();
-    if (context[Doctor] == null)
-      context[Doctor] = new Doctor();
-    if (context[HotRunnerConfig] == null)
-      context[HotRunnerConfig] = new HotRunnerConfig();
+    context.putIfAbsent(Logger, () => new StdoutLogger());
+    context.putIfAbsent(DeviceManager, () => new DeviceManager());
+    context.putIfAbsent(DevFSConfig, () => new DevFSConfig());
+    context.putIfAbsent(Doctor, () => new Doctor());
+    context.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
+    context.putIfAbsent(Cache, () => new Cache());
+    context.putIfAbsent(ToolConfiguration, () => new ToolConfiguration());
+    context.putIfAbsent(Config, () => new Config());
+    context.putIfAbsent(OperatingSystemUtils, () => new OperatingSystemUtils());
+    context.putIfAbsent(XCode, () => new XCode());
+    context.putIfAbsent(IOSSimulatorUtils, () => new IOSSimulatorUtils());
+    context.putIfAbsent(SimControl, () => new SimControl());
+    context.putIfAbsent(Usage, () => new Usage());
 
     return Chain.capture/*<Future<Null>>*/(() async {
       await runner.run(args);
@@ -192,7 +202,7 @@
     BufferLogger logger = new BufferLogger();
     AppContext appContext = new AppContext();
 
-    appContext[Logger] = logger;
+    appContext.setVariable(Logger, logger);
 
     await appContext.runInZone(() => doctor.diagnose());
 
diff --git a/packages/flutter_tools/lib/src/base/config.dart b/packages/flutter_tools/lib/src/base/config.dart
index c92d42f..6a53013 100644
--- a/packages/flutter_tools/lib/src/base/config.dart
+++ b/packages/flutter_tools/lib/src/base/config.dart
@@ -16,7 +16,7 @@
       _values = JSON.decode(_configFile.readAsStringSync());
   }
 
-  static Config get instance => context[Config] ?? (context[Config] = new Config());
+  static Config get instance => context[Config];
 
   File _configFile;
   String get configPath => _configFile.path;
diff --git a/packages/flutter_tools/lib/src/base/context.dart b/packages/flutter_tools/lib/src/base/context.dart
index 3cb1189..96a21f1 100644
--- a/packages/flutter_tools/lib/src/base/context.dart
+++ b/packages/flutter_tools/lib/src/base/context.dart
@@ -36,7 +36,15 @@
 
   dynamic operator[](Type type) => getVariable(type);
 
-  void operator[]=(Type type, dynamic instance) => setVariable(type, instance);
+  dynamic putIfAbsent(Type type, dynamic ifAbsent()) {
+    dynamic value = getVariable(type);
+    if (value != null) {
+      return value;
+    }
+    value = ifAbsent();
+    setVariable(type, value);
+    return value;
+  }
 
   AppContext _calcParent(Zone zone) {
     Zone parentZone = zone.parent;
diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart
index 9a4290a..f709d4d 100644
--- a/packages/flutter_tools/lib/src/base/os.dart
+++ b/packages/flutter_tools/lib/src/base/os.dart
@@ -12,9 +12,7 @@
 import 'process.dart';
 
 /// Returns [OperatingSystemUtils] active in the current app context (i.e. zone).
-OperatingSystemUtils get os {
-  return context[OperatingSystemUtils] ?? (context[OperatingSystemUtils] = new OperatingSystemUtils());
-}
+OperatingSystemUtils get os => context[OperatingSystemUtils];
 
 abstract class OperatingSystemUtils {
   factory OperatingSystemUtils() {
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index 6b58cd8..7aee1d5 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -99,7 +99,7 @@
     return _engineRevision;
   }
 
-  static Cache get instance => context[Cache] ?? (context[Cache] = new Cache());
+  static Cache get instance => context[Cache];
 
   /// Return the top-level directory in the cache; this is `bin/cache`.
   Directory getRoot() {
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 9b1c847..a1a868c 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -48,7 +48,7 @@
 
     AppContext appContext = new AppContext();
     NotifyingLogger notifyingLogger = new NotifyingLogger();
-    appContext[Logger] = notifyingLogger;
+    appContext.setVariable(Logger, notifyingLogger);
 
     Cache.releaseLockEarly();
 
@@ -674,7 +674,7 @@
       _logger = new _AppRunLogger(domain, this);
 
     AppContext appContext = new AppContext();
-    appContext[Logger] = _logger;
+    appContext.setVariable(Logger, _logger);
     return appContext.runInZone(method);
   }
 }
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index da726d5..f727f38 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -56,7 +56,7 @@
   }
 
   /// Returns [XCode] active in the current app context.
-  static XCode get instance => context[XCode] ?? (context[XCode] = new XCode());
+  static XCode get instance => context[XCode];
 
   bool get isInstalledAndMeetsVersionCheck => isInstalled && xcodeVersionSatisfactory;
 
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 6d405c8..d0bae71 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -37,9 +37,7 @@
 
 class IOSSimulatorUtils {
   /// Returns [IOSSimulatorUtils] active in the current app context (i.e. zone).
-  static IOSSimulatorUtils get instance {
-    return context[IOSSimulatorUtils] ?? (context[IOSSimulatorUtils] = new IOSSimulatorUtils());
-  }
+  static IOSSimulatorUtils get instance => context[IOSSimulatorUtils];
 
   List<IOSSimulator> getAttachedDevices() {
     if (!XCode.instance.isInstalledAndMeetsVersionCheck)
@@ -54,7 +52,7 @@
 /// A wrapper around the `simctl` command line tool.
 class SimControl {
   /// Returns [SimControl] active in the current app context (i.e. zone).
-  static SimControl get instance => context[SimControl] ?? (context[SimControl] = new SimControl());
+  static SimControl get instance => context[SimControl];
 
   Future<bool> boot({ String deviceName }) async {
     if (_isAnyConnected())
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index f023058..bc79962 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -137,8 +137,10 @@
   @override
   Future<Null> runCommand(ArgResults globalResults) async {
     // Check for verbose.
-    if (globalResults['verbose'])
-      context[Logger] = new VerboseLogger();
+    if (globalResults['verbose']) {
+      // Override the logger.
+      context.setVariable(Logger, new VerboseLogger());
+    }
 
     logger.quiet = globalResults['quiet'];
 
@@ -171,8 +173,7 @@
     }
 
     // The Android SDK could already have been set by tests.
-    if (!context.isSet(AndroidSdk))
-      context[AndroidSdk] = AndroidSdk.locateAndroidSdk();
+    context.putIfAbsent(AndroidSdk, () => AndroidSdk.locateAndroidSdk());
 
     if (globalResults['version']) {
       flutterUsage.sendCommand('version');
diff --git a/packages/flutter_tools/lib/src/toolchain.dart b/packages/flutter_tools/lib/src/toolchain.dart
index 61539e6..3145377 100644
--- a/packages/flutter_tools/lib/src/toolchain.dart
+++ b/packages/flutter_tools/lib/src/toolchain.dart
@@ -25,18 +25,11 @@
 /// and the engine artifact directory for a given target platform. It is configurable
 /// via command-line arguments in order to support local engine builds.
 class ToolConfiguration {
-  /// [overrideCache] is configurable for testing.
-  ToolConfiguration({ Cache overrideCache }) {
-    _cache = overrideCache ?? cache;
-  }
+  ToolConfiguration();
 
-  Cache _cache;
+  Cache get cache => context[Cache];
 
-  static ToolConfiguration get instance {
-    if (context[ToolConfiguration] == null)
-      context[ToolConfiguration] = new ToolConfiguration();
-    return context[ToolConfiguration];
-  }
+  static ToolConfiguration get instance => context[ToolConfiguration];
 
   /// Override using the artifacts from the cache directory (--engine-src-path).
   String engineSrcPath;
@@ -63,14 +56,14 @@
 
       // Create something like `android-arm` or `android-arm-release`.
       String dirName = getNameForTargetPlatform(platform) + suffix;
-      Directory engineDir = _cache.getArtifactDirectory('engine');
+      Directory engineDir = cache.getArtifactDirectory('engine');
       return new Directory(path.join(engineDir.path, dirName));
     }
   }
 
   String getHostToolPath(HostTool tool) {
     if (engineBuildPath == null) {
-      return path.join(_cache.getArtifactDirectory('engine').path,
+      return path.join(cache.getArtifactDirectory('engine').path,
                        getNameForHostPlatform(getCurrentHostPlatform()),
                        _kHostToolFileName[tool]);
     }
diff --git a/packages/flutter_tools/lib/src/usage.dart b/packages/flutter_tools/lib/src/usage.dart
index 503a0b7..934522d 100644
--- a/packages/flutter_tools/lib/src/usage.dart
+++ b/packages/flutter_tools/lib/src/usage.dart
@@ -39,7 +39,7 @@
   }
 
   /// Returns [Usage] active in the current app context.
-  static Usage get instance => context[Usage] ?? (context[Usage] = new Usage());
+  static Usage get instance => context[Usage];
 
   Analytics _analytics;
 
diff --git a/packages/flutter_tools/test/context_test.dart b/packages/flutter_tools/test/context_test.dart
index 51e5464..8411058 100644
--- a/packages/flutter_tools/test/context_test.dart
+++ b/packages/flutter_tools/test/context_test.dart
@@ -12,7 +12,7 @@
     test('error', () async {
       AppContext context = new AppContext();
       BufferLogger mockLogger = new BufferLogger();
-      context[Logger] = mockLogger;
+      context.setVariable(Logger, mockLogger);
 
       await context.runInZone(() {
         printError('foo bar');
@@ -26,7 +26,7 @@
     test('status', () async {
       AppContext context = new AppContext();
       BufferLogger mockLogger = new BufferLogger();
-      context[Logger] = mockLogger;
+      context.setVariable(Logger, mockLogger);
 
       await context.runInZone(() {
         printStatus('foo bar');
@@ -40,7 +40,7 @@
     test('trace', () async {
       AppContext context = new AppContext();
       BufferLogger mockLogger = new BufferLogger();
-      context[Logger] = mockLogger;
+      context.setVariable(Logger, mockLogger);
 
       await context.runInZone(() {
         printTrace('foo bar');
diff --git a/packages/flutter_tools/test/daemon_test.dart b/packages/flutter_tools/test/daemon_test.dart
index 79ea9e6..9154b6c 100644
--- a/packages/flutter_tools/test/daemon_test.dart
+++ b/packages/flutter_tools/test/daemon_test.dart
@@ -32,11 +32,11 @@
     setUp(() {
       appContext = new AppContext();
       notifyingLogger = new NotifyingLogger();
-      appContext[Logger] = notifyingLogger;
-      appContext[Doctor] = new Doctor();
+      appContext.setVariable(Logger, notifyingLogger);
+      appContext.setVariable(Doctor, new Doctor());
       if (Platform.isMacOS)
-        appContext[XCode] = new XCode();
-      appContext[DeviceManager] = new MockDeviceManager();
+        appContext.setVariable(XCode, new XCode());
+      appContext.setVariable(DeviceManager, new MockDeviceManager());
     });
 
     tearDown(() {
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index e5bcad2..71af551 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -3,17 +3,19 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:io';
 
+import 'package:flutter_tools/src/base/config.dart';
 import 'package:flutter_tools/src/base/context.dart';
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/base/os.dart';
+import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/device.dart';
 import 'package:flutter_tools/src/devfs.dart';
 import 'package:flutter_tools/src/doctor.dart';
 import 'package:flutter_tools/src/hot.dart';
 import 'package:flutter_tools/src/ios/mac.dart';
 import 'package:flutter_tools/src/ios/simulators.dart';
+import 'package:flutter_tools/src/toolchain.dart';
 import 'package:flutter_tools/src/usage.dart';
 
 import 'package:mockito/mockito.dart';
@@ -32,49 +34,33 @@
   test(description, () async {
     AppContext testContext = new AppContext();
 
+    // Apply all overrides to the test context.
     overrides.forEach((Type type, dynamic value) {
-      testContext[type] = value;
+      testContext.setVariable(type, value);
     });
 
-    if (!overrides.containsKey(Logger))
-      testContext[Logger] = new BufferLogger();
-
-    if (!overrides.containsKey(DeviceManager))
-      testContext[DeviceManager] = new MockDeviceManager();
-
-    if (!overrides.containsKey(Doctor))
-      testContext[Doctor] = new MockDoctor();
-
-    if (!overrides.containsKey(SimControl))
-      testContext[SimControl] = new MockSimControl();
-
-    if (!overrides.containsKey(Usage))
-      testContext[Usage] = new MockUsage();
-
-    if (!overrides.containsKey(OperatingSystemUtils)) {
+    // Initialize the test context with some default mocks.
+    testContext.putIfAbsent(Logger, () => new BufferLogger());
+    testContext.putIfAbsent(DeviceManager, () => new MockDeviceManager());
+    testContext.putIfAbsent(DevFSConfig, () => new DevFSConfig());
+    testContext.putIfAbsent(Doctor, () => new MockDoctor());
+    testContext.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
+    testContext.putIfAbsent(Cache, () => new Cache());
+    testContext.putIfAbsent(ToolConfiguration, () => new ToolConfiguration());
+    testContext.putIfAbsent(Config, () => new Config());
+    testContext.putIfAbsent(OperatingSystemUtils, () {
       MockOperatingSystemUtils os = new MockOperatingSystemUtils();
       when(os.isWindows).thenReturn(false);
-      testContext[OperatingSystemUtils] = os;
-    }
-
-    if (!overrides.containsKey(DevFSConfig)) {
-      testContext[DevFSConfig] = new DevFSConfig();
-    }
-
-    if (!overrides.containsKey(HotRunnerConfig)) {
-      testContext[HotRunnerConfig] = new HotRunnerConfig();
-    }
-
-    if (!overrides.containsKey(IOSSimulatorUtils)) {
+      return os;
+    });
+    testContext.putIfAbsent(XCode, () => new XCode());
+    testContext.putIfAbsent(IOSSimulatorUtils, () {
       MockIOSSimulatorUtils mock = new MockIOSSimulatorUtils();
       when(mock.getAttachedDevices()).thenReturn(<IOSSimulator>[]);
-      testContext[IOSSimulatorUtils] = mock;
-    }
-
-    if (Platform.isMacOS) {
-      if (!overrides.containsKey(XCode))
-        testContext[XCode] = new XCode();
-    }
+      return mock;
+    });
+    testContext.putIfAbsent(SimControl, () => new MockSimControl());
+    testContext.putIfAbsent(Usage, () => new MockUsage());
 
     try {
       return await testContext.runInZone(testMethod);
diff --git a/packages/flutter_tools/test/toolchain_test.dart b/packages/flutter_tools/test/toolchain_test.dart
index dd5b153..f54b906 100644
--- a/packages/flutter_tools/test/toolchain_test.dart
+++ b/packages/flutter_tools/test/toolchain_test.dart
@@ -14,19 +14,10 @@
 void main() {
   group('ToolConfiguration', () {
     Directory tempDir;
-
-    setUp(() {
-      tempDir = Directory.systemTemp.createTempSync('flutter_temp');
-    });
-
-    tearDown(() {
-      tempDir.deleteSync(recursive: true);
-    });
+    tempDir = Directory.systemTemp.createTempSync('flutter_temp');
 
     testUsingContext('using cache', () {
-      ToolConfiguration toolConfig = new ToolConfiguration(
-        overrideCache: new Cache(rootOverride: tempDir)
-      );
+      ToolConfiguration toolConfig = new ToolConfiguration();
 
       expect(
         toolConfig.getEngineArtifactsDirectory(TargetPlatform.android_arm, BuildMode.debug).path,
@@ -36,6 +27,10 @@
         toolConfig.getEngineArtifactsDirectory(TargetPlatform.android_arm, BuildMode.release).path,
         endsWith('cache/artifacts/engine/android-arm-release')
       );
+      expect(tempDir, isNotNull);
+      tempDir.deleteSync(recursive: true);
+    }, overrides: <Type, dynamic> {
+      Cache: new Cache(rootOverride: tempDir)
     });
 
     testUsingContext('using enginePath', () {