// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // part of "common_patch.dart"; @patch class _File { @patch static _exists(_Namespace namespace, Uint8List rawPath) native "File_Exists"; @patch static _create(_Namespace namespace, Uint8List rawPath) native "File_Create"; @patch static _createLink(_Namespace namespace, Uint8List rawPath, String target) native "File_CreateLink"; @patch static _linkTarget(_Namespace namespace, Uint8List rawPath) native "File_LinkTarget"; @patch static _deleteNative(_Namespace namespace, Uint8List rawPath) native "File_Delete"; @patch static _deleteLinkNative(_Namespace namespace, Uint8List rawPath) native "File_DeleteLink"; @patch static _rename(_Namespace namespace, Uint8List oldPath, String newPath) native "File_Rename"; @patch static _renameLink(_Namespace namespace, Uint8List oldPath, String newPath) native "File_RenameLink"; @patch static _copy(_Namespace namespace, Uint8List oldPath, String newPath) native "File_Copy"; @patch static _lengthFromPath(_Namespace namespace, Uint8List rawPath) native "File_LengthFromPath"; @patch static _lastModified(_Namespace namespace, Uint8List rawPath) native "File_LastModified"; @patch static _setLastModified(_Namespace namespace, Uint8List rawPath, int millis) native "File_SetLastModified"; @patch static _lastAccessed(_Namespace namespace, Uint8List rawPath) native "File_LastAccessed"; @patch static _setLastAccessed(_Namespace namespace, Uint8List rawPath, int millis) native "File_SetLastAccessed"; @patch static _open(_Namespace namespace, Uint8List rawPath, int mode) native "File_Open"; @patch static int _openStdio(int fd) native "File_OpenStdio"; } @patch class _RandomAccessFileOps { @patch factory _RandomAccessFileOps(int pointer) => new _RandomAccessFileOpsImpl(pointer); } @pragma("vm:entry-point") class _RandomAccessFileOpsImpl extends NativeFieldWrapperClass1 implements _RandomAccessFileOps { _RandomAccessFileOpsImpl._(); factory _RandomAccessFileOpsImpl(int pointer) => new _RandomAccessFileOpsImpl._().._setPointer(pointer); void _setPointer(int pointer) native "File_SetPointer"; int getPointer() native "File_GetPointer"; int close() native "File_Close"; readByte() native "File_ReadByte"; read(int bytes) native "File_Read"; readInto(List buffer, int start, int end) native "File_ReadInto"; writeByte(int value) native "File_WriteByte"; writeFrom(List buffer, int start, int end) native "File_WriteFrom"; position() native "File_Position"; setPosition(int position) native "File_SetPosition"; truncate(int length) native "File_Truncate"; length() native "File_Length"; flush() native "File_Flush"; lock(int lock, int start, int end) native "File_Lock"; } class _WatcherPath { final int pathId; final String path; final int events; int count = 0; _WatcherPath(this.pathId, this.path, this.events); } @patch class _FileSystemWatcher { void _pathWatchedEnd(); static int _id; static final Map _idMap = {}; final String _path; final int _events; final bool _recursive; _WatcherPath _watcherPath; StreamController _broadcastController; @patch static Stream _watch( String path, int events, bool recursive) { if (Platform.isLinux) { return new _InotifyFileSystemWatcher(path, events, recursive)._stream; } if (Platform.isWindows) { return new _Win32FileSystemWatcher(path, events, recursive)._stream; } if (Platform.isMacOS) { return new _FSEventStreamFileSystemWatcher(path, events, recursive) ._stream; } throw new FileSystemException( "File system watching is not supported on this platform"); } _FileSystemWatcher._(this._path, this._events, this._recursive) { if (!isSupported) { throw new FileSystemException( "File system watching is not supported on this platform", _path); } _broadcastController = new StreamController.broadcast( onListen: _listen, onCancel: _cancel); } Stream get _stream => _broadcastController.stream; void _listen() { if (_id == null) { try { _id = _initWatcher(); _newWatcher(); } catch (e) { _broadcastController.addError(new FileSystemException( "Failed to initialize file system entity watcher", null, e)); _broadcastController.close(); return; } } var pathId; try { pathId = _watchPath(_id, _Namespace._namespace, _path, _events, _recursive); } catch (e) { _broadcastController .addError(new FileSystemException("Failed to watch path", _path, e)); _broadcastController.close(); return; } if (!_idMap.containsKey(pathId)) { _idMap[pathId] = new _WatcherPath(pathId, _path, _events); } _watcherPath = _idMap[pathId]; _watcherPath.count++; _pathWatched().pipe(_broadcastController); } void _cancel() { if (_watcherPath != null) { assert(_watcherPath.count > 0); _watcherPath.count--; if (_watcherPath.count == 0) { _unwatchPath(_id, _watcherPath.pathId); _pathWatchedEnd(); _idMap.remove(_watcherPath.pathId); } _watcherPath = null; } if (_idMap.isEmpty && _id != null) { _closeWatcher(_id); _doneWatcher(); _id = null; } } // Called when (and after) a new watcher instance is created and available. void _newWatcher() {} // Called when a watcher is no longer needed. void _doneWatcher() {} // Called when a new path is being watched. Stream _pathWatched() {} // Called when a path is no longer being watched. void _donePathWatched() {} static _WatcherPath _pathFromPathId(int pathId) { return _idMap[pathId]; } static Stream _listenOnSocket(int socketId, int id, int pathId) { var native = new _NativeSocket.watch(socketId); var socket = new _RawSocket(native); return socket.expand((event) { var stops = []; var events = []; var pair = {}; if (event == RawSocketEvent.read) { String getPath(event) { var path = _pathFromPathId(event[4]).path; if (event[2] != null && event[2].isNotEmpty) { path += Platform.pathSeparator; path += event[2]; } return path; } bool getIsDir(event) { if (Platform.isWindows) { // Windows does not get 'isDir' as part of the event. return FileSystemEntity.isDirectorySync(getPath(event)); } return (event[0] & FileSystemEvent._isDir) != 0; } void add(id, event) { if ((event.type & _pathFromPathId(id).events) == 0) return; events.add([id, event]); } void rewriteMove(event, isDir) { if (event[3]) { add(event[4], new FileSystemCreateEvent._(getPath(event), isDir)); } else { add(event[4], new FileSystemDeleteEvent._(getPath(event), isDir)); } } int eventCount; do { eventCount = 0; for (var event in _readEvents(id, pathId)) { if (event == null) continue; eventCount++; int pathId = event[4]; if (!_idMap.containsKey(pathId)) { // Path is no longer being wathed. continue; } bool isDir = getIsDir(event); var path = getPath(event); if ((event[0] & FileSystemEvent.create) != 0) { add(event[4], new FileSystemCreateEvent._(path, isDir)); } if ((event[0] & FileSystemEvent.modify) != 0) { add(event[4], new FileSystemModifyEvent._(path, isDir, true)); } if ((event[0] & FileSystemEvent._modifyAttributes) != 0) { add(event[4], new FileSystemModifyEvent._(path, isDir, false)); } if ((event[0] & FileSystemEvent.move) != 0) { int link = event[1]; if (link > 0) { pair.putIfAbsent(pathId, () => {}); if (pair[pathId].containsKey(link)) { add( event[4], new FileSystemMoveEvent._( getPath(pair[pathId][link]), isDir, path)); pair[pathId].remove(link); } else { pair[pathId][link] = event; } } else { rewriteMove(event, isDir); } } if ((event[0] & FileSystemEvent.delete) != 0) { add(event[4], new FileSystemDeleteEvent._(path, isDir)); } if ((event[0] & FileSystemEvent._deleteSelf) != 0) { add(event[4], new FileSystemDeleteEvent._(path, isDir)); // Signal done event. stops.add([event[4], null]); } } } while (eventCount > 0); // Be sure to clear this manually, as the sockets are not read through // the _NativeSocket interface. native.available = 0; for (var map in pair.values) { for (var event in map.values) { rewriteMove(event, getIsDir(event)); } } } else if (event == RawSocketEvent.closed) { } else if (event == RawSocketEvent.readClosed) { } else { assert(false); } events.addAll(stops); return events; }); } @patch static bool get isSupported native "FileSystemWatcher_IsSupported"; static int _initWatcher() native "FileSystemWatcher_InitWatcher"; static void _closeWatcher(int id) native "FileSystemWatcher_CloseWatcher"; static int _watchPath(int id, _Namespace namespace, String path, int events, bool recursive) native "FileSystemWatcher_WatchPath"; static void _unwatchPath(int id, int path_id) native "FileSystemWatcher_UnwatchPath"; static List _readEvents(int id, int path_id) native "FileSystemWatcher_ReadEvents"; static int _getSocketId(int id, int path_id) native "FileSystemWatcher_GetSocketId"; } class _InotifyFileSystemWatcher extends _FileSystemWatcher { static final Map _idMap = {}; static StreamSubscription _subscription; _InotifyFileSystemWatcher(path, events, recursive) : super._(path, events, recursive); void _newWatcher() { int id = _FileSystemWatcher._id; _subscription = _FileSystemWatcher._listenOnSocket(id, id, 0).listen((event) { if (_idMap.containsKey(event[0])) { if (event[1] != null) { _idMap[event[0]].add(event[1]); } else { _idMap[event[0]].close(); } } }); } void _doneWatcher() { _subscription.cancel(); } Stream _pathWatched() { var pathId = _watcherPath.pathId; if (!_idMap.containsKey(pathId)) { _idMap[pathId] = new StreamController.broadcast(); } return _idMap[pathId].stream; } void _pathWatchedEnd() { var pathId = _watcherPath.pathId; if (!_idMap.containsKey(pathId)) return; _idMap[pathId].close(); _idMap.remove(pathId); } } class _Win32FileSystemWatcher extends _FileSystemWatcher { StreamSubscription _subscription; StreamController _controller; _Win32FileSystemWatcher(path, events, recursive) : super._(path, events, recursive); Stream _pathWatched() { var pathId = _watcherPath.pathId; _controller = new StreamController(); _subscription = _FileSystemWatcher._listenOnSocket(pathId, 0, pathId).listen((event) { assert(event[0] == pathId); if (event[1] != null) { _controller.add(event[1]); } else { _controller.close(); } }); return _controller.stream; } void _pathWatchedEnd() { _subscription.cancel(); _controller.close(); } } class _FSEventStreamFileSystemWatcher extends _FileSystemWatcher { StreamSubscription _subscription; StreamController _controller; _FSEventStreamFileSystemWatcher(path, events, recursive) : super._(path, events, recursive); Stream _pathWatched() { var pathId = _watcherPath.pathId; var socketId = _FileSystemWatcher._getSocketId(0, pathId); _controller = new StreamController(); _subscription = _FileSystemWatcher._listenOnSocket(socketId, 0, pathId).listen((event) { if (event[1] != null) { _controller.add(event[1]); } else { _controller.close(); } }); return _controller.stream; } void _pathWatchedEnd() { _subscription.cancel(); _controller.close(); } } @pragma("vm:entry-point", "call") Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { return new Uint8List.view(source.buffer, offsetInBytes, length); }