Add --hot mode for flutter run
diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart
new file mode 100644
index 0000000..09b7f8e
--- /dev/null
+++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -0,0 +1,237 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert' show BASE64, UTF8;
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'dart/package_map.dart';
+import 'globals.dart';
+import 'observatory.dart';
+
+// A file that has been added to a DevFS.
+class DevFSEntry {
+  DevFSEntry(this.devicePath, this.file);
+
+  final String devicePath;
+  final File file;
+  FileStat _fileStat;
+
+  DateTime get lastModified => _fileStat?.modified;
+  bool get stillExists {
+    _stat();
+    return _fileStat.type != FileSystemEntityType.NOT_FOUND;
+  }
+  bool get isModified {
+    if (_fileStat == null) {
+      _stat();
+      return true;
+    }
+    FileStat _oldFileStat = _fileStat;
+    _stat();
+    return _fileStat.modified.isAfter(_oldFileStat.modified);
+  }
+
+  void _stat() {
+    _fileStat = file.statSync();
+  }
+}
+
+
+/// Abstract DevFS operations interface.
+abstract class DevFSOperations {
+  Future<Uri> create(String fsName);
+  Future<dynamic> destroy(String fsName);
+  Future<dynamic> writeFile(String fsName, DevFSEntry entry);
+  Future<dynamic> writeSource(String fsName,
+                              String devicePath,
+                              String contents);
+}
+
+/// An implementation of [DevFSOperations] that speaks to the
+/// service protocol.
+class ServiceProtocolDevFSOperations implements DevFSOperations {
+  final Observatory  serviceProtocol;
+
+  ServiceProtocolDevFSOperations(this.serviceProtocol);
+
+  @override
+  Future<Uri> create(String fsName) async {
+    Response response = await serviceProtocol.createDevFS(fsName);
+    return Uri.parse(response['uri']);
+  }
+
+  @override
+  Future<dynamic> destroy(String fsName) async {
+    await serviceProtocol.sendRequest('_deleteDevFS',
+                                      <String, dynamic> { 'fsName': fsName });
+  }
+
+  @override
+  Future<dynamic> writeFile(String fsName, DevFSEntry entry) async {
+    List<int> bytes;
+    try {
+      bytes = await entry.file.readAsBytes();
+    } catch (e) {
+      return e;
+    }
+    String fileContents = BASE64.encode(bytes);
+    return await serviceProtocol.sendRequest('_writeDevFSFile',
+                                             <String, dynamic> {
+                                                'fsName': fsName,
+                                                'path': entry.devicePath,
+                                                'fileContents': fileContents
+                                             });
+  }
+
+  @override
+  Future<dynamic> writeSource(String fsName,
+                              String devicePath,
+                              String contents) async {
+    String fileContents = BASE64.encode(UTF8.encode(contents));
+    return await serviceProtocol.sendRequest('_writeDevFSFile',
+                                             <String, dynamic> {
+                                                'fsName': fsName,
+                                                'path': devicePath,
+                                                'fileContents': fileContents
+                                             });
+  }
+}
+
+class DevFS {
+  /// Create a [DevFS] named [fsName] for the local files in [directory].
+  DevFS(Observatory serviceProtocol,
+        this.fsName,
+        this.rootDirectory)
+    : _operations = new ServiceProtocolDevFSOperations(serviceProtocol);
+
+  DevFS.operations(this._operations,
+                   this.fsName,
+                   this.rootDirectory);
+
+  final DevFSOperations _operations;
+  final String fsName;
+  final Directory rootDirectory;
+  final Map<String, DevFSEntry> _entries = <String, DevFSEntry>{};
+  final List<Future<Response>> _pendingWrites = new List<Future<Response>>();
+  Uri _baseUri;
+  Uri get baseUri => _baseUri;
+
+  Future<Uri> create() async {
+    _baseUri = await _operations.create(fsName);
+    printTrace('DevFS: Created new filesystem on the device ($_baseUri)');
+    return _baseUri;
+  }
+
+  Future<dynamic> destroy() async {
+    printTrace('DevFS: Deleted filesystem on the device ($_baseUri)');
+    return await _operations.destroy(fsName);
+  }
+
+  Future<dynamic> update() async {
+    printTrace('DevFS: Starting sync from $rootDirectory');
+    // Send the root and lib directories.
+    Directory directory = rootDirectory;
+    _syncDirectory(directory, recursive: true);
+    String packagesFilePath = path.join(rootDirectory.path, kPackagesFileName);
+    StringBuffer sb;
+    // Send the packages.
+    if (FileSystemEntity.isFileSync(packagesFilePath)) {
+      PackageMap packageMap = new PackageMap(kPackagesFileName);
+
+      for (String packageName in packageMap.map.keys) {
+        Uri uri = packageMap.map[packageName];
+        // Ignore self-references.
+        if (uri.toString() == 'lib/')
+          continue;
+        Directory directory = new Directory.fromUri(uri);
+        if (_syncDirectory(directory,
+                           directoryName: 'packages/$packageName',
+                           recursive: true)) {
+          if (sb == null) {
+            sb = new StringBuffer();
+          }
+          sb.writeln('$packageName:packages/$packageName');
+        }
+      }
+    }
+    printTrace('DevFS: Waiting for sync of ${_pendingWrites.length} files '
+               'to finish');
+    await Future.wait(_pendingWrites);
+    _pendingWrites.clear();
+    if (sb != null) {
+      await _operations.writeSource(fsName, '.packages', sb.toString());
+    }
+    printTrace('DevFS: Sync finished');
+    // NB: You must call flush after a printTrace if you want to be printed
+    // immediately.
+    logger.flush();
+  }
+
+  void _syncFile(String devicePath, File file) {
+    DevFSEntry entry = _entries[devicePath];
+    if (entry == null) {
+      // New file.
+      entry = new DevFSEntry(devicePath, file);
+      _entries[devicePath] = entry;
+    }
+    bool needsWrite = entry.isModified;
+    if (needsWrite) {
+      Future<dynamic> pendingWrite = _operations.writeFile(fsName, entry);
+      if (pendingWrite != null) {
+        _pendingWrites.add(pendingWrite);
+      } else {
+        printTrace('DevFS: Failed to sync "$devicePath"');
+      }
+    }
+  }
+
+  bool _shouldIgnore(String path) {
+    List<String> ignoredPrefixes = <String>['android/',
+                                            'build/',
+                                            'ios/',
+                                            'packages/analyzer'];
+    for (String ignoredPrefix in ignoredPrefixes) {
+      if (path.startsWith(ignoredPrefix))
+        return true;
+    }
+    return false;
+  }
+
+  bool _syncDirectory(Directory directory,
+                      {String directoryName,
+                       bool recursive: false,
+                       bool ignoreDotFiles: true}) {
+    String prefix = directoryName;
+    if (prefix == null) {
+      prefix = path.relative(directory.path, from: rootDirectory.path);
+      if (prefix == '.')
+        prefix = '';
+    }
+    try {
+      List<FileSystemEntity> files =
+          directory.listSync(recursive: recursive, followLinks: false);
+      for (FileSystemEntity file in files) {
+        if (file is! File) {
+          // Skip non-files.
+          continue;
+        }
+        if (ignoreDotFiles && path.basename(file.path).startsWith('.')) {
+          // Skip dot files.
+          continue;
+        }
+        final String devicePath =
+            path.join(prefix, path.relative(file.path, from: directory.path));
+        if (!_shouldIgnore(devicePath))
+          _syncFile(devicePath, file);
+      }
+    } catch (e) {
+      // Ignore directory and error.
+      return false;
+    }
+    return true;
+  }
+}