Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
3a012b32
Commit
3a012b32
authored
Aug 17, 2016
by
John McCutchan
Committed by
GitHub
Aug 17, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
vmservice redux (#5437)
parent
53dd5dbd
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
825 additions
and
368 deletions
+825
-368
run.dart
packages/flutter_tools/lib/src/commands/run.dart
+1
-1
trace.dart
packages/flutter_tools/lib/src/commands/trace.dart
+12
-13
devfs.dart
packages/flutter_tools/lib/src/devfs.dart
+24
-21
hot.dart
packages/flutter_tools/lib/src/hot.dart
+23
-25
resident_runner.dart
packages/flutter_tools/lib/src/resident_runner.dart
+17
-16
run.dart
packages/flutter_tools/lib/src/run.dart
+13
-9
view.dart
packages/flutter_tools/lib/src/view.dart
+0
-89
vmservice.dart
packages/flutter_tools/lib/src/vmservice.dart
+735
-194
No files found.
packages/flutter_tools/lib/src/commands/run.dart
View file @
3a012b32
...
...
@@ -143,7 +143,7 @@ class RunCommand extends RunCommandBase {
return
1
;
}
}
else
{
if
(
argResults
[
'control-pipe'
])
{
if
(
argResults
[
'control-pipe'
]
!=
null
)
{
printError
(
'--control-pipe requires --hot'
);
return
1
;
}
...
...
packages/flutter_tools/lib/src/commands/trace.dart
View file @
3a012b32
...
...
@@ -95,40 +95,39 @@ class TraceCommand extends FlutterCommand {
}
class
Tracing
{
Tracing
(
this
.
observatory
);
Tracing
(
this
.
vmService
);
static
Future
<
Tracing
>
connect
(
int
port
)
{
return
VMService
.
connect
(
port
).
then
((
VMService
observatory
)
=>
new
Tracing
(
observatory
));
}
final
VMService
observatory
;
final
VMService
vmService
;
Future
<
Null
>
startTracing
()
async
{
await
observatory
.
setVMTimelineFlags
(<
String
>[
'Compiler'
,
'Dart'
,
'Embedder'
,
'GC'
]);
await
observatory
.
clearVMTimeline
();
await
vmService
.
vm
.
setVMTimelineFlags
(<
String
>[
'Compiler'
,
'Dart'
,
'Embedder'
,
'GC'
]);
await
vmService
.
vm
.
clearVMTimeline
();
}
/// Stops tracing; optionally wait for first frame.
Future
<
Map
<
String
,
dynamic
>>
stopTracingAndDownloadTimeline
({
bool
waitForFirstFrame:
false
})
async
{
Response
timeline
;
Map
<
String
,
dynamic
>
timeline
;
if
(!
waitForFirstFrame
)
{
// Stop tracing immediately and get the timeline
await
observatory
.
setVMTimelineFlags
(<
String
>[]);
timeline
=
await
observatory
.
getVMTimeline
();
await
vmService
.
vm
.
setVMTimelineFlags
(<
String
>[]);
timeline
=
await
vmService
.
vm
.
getVMTimeline
();
}
else
{
Completer
<
Null
>
whenFirstFrameRendered
=
new
Completer
<
Null
>();
observatory
.
onTimelineEvent
.
listen
((
Event
timelineEvent
)
{
List
<
Map
<
String
,
dynamic
>>
events
=
timelineEvent
[
'timelineEvents'
]
;
vmService
.
onTimelineEvent
.
listen
((
Service
Event
timelineEvent
)
{
List
<
Map
<
String
,
dynamic
>>
events
=
timelineEvent
.
timelineEvents
;
for
(
Map
<
String
,
dynamic
>
event
in
events
)
{
if
(
event
[
'name'
]
==
kFirstUsefulFrameEventName
)
whenFirstFrameRendered
.
complete
();
}
});
await
observatory
.
streamListen
(
'Timeline'
);
await
whenFirstFrameRendered
.
future
.
timeout
(
const
Duration
(
seconds:
10
),
...
...
@@ -142,12 +141,12 @@ class Tracing {
}
);
timeline
=
await
observatory
.
getVMTimeline
();
timeline
=
await
vmService
.
vm
.
getVMTimeline
();
await
observatory
.
setVMTimelineFlags
(<
String
>[]);
await
vmService
.
vm
.
setVMTimelineFlags
(<
String
>[]);
}
return
timeline
.
response
;
return
timeline
;
}
}
...
...
packages/flutter_tools/lib/src/devfs.dart
View file @
3a012b32
...
...
@@ -106,21 +106,21 @@ abstract class DevFSOperations {
}
/// An implementation of [DevFSOperations] that speaks to the
///
service protocol
.
///
vm service
.
class
ServiceProtocolDevFSOperations
implements
DevFSOperations
{
final
VMService
serviceProtocol
;
final
VMService
vmService
;
ServiceProtocolDevFSOperations
(
this
.
serviceProtocol
);
ServiceProtocolDevFSOperations
(
this
.
vmService
);
@override
Future
<
Uri
>
create
(
String
fsName
)
async
{
Response
response
=
await
serviceProtocol
.
createDevFS
(
fsName
);
Map
<
String
,
dynamic
>
response
=
await
vmService
.
vm
.
createDevFS
(
fsName
);
return
Uri
.
parse
(
response
[
'uri'
]);
}
@override
Future
<
dynamic
>
destroy
(
String
fsName
)
async
{
await
serviceProtocol
.
sendRequest
(
'_deleteDevFS'
,
await
vmService
.
vm
.
invokeRpcRaw
(
'_deleteDevFS'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
});
}
...
...
@@ -134,7 +134,7 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
}
String
fileContents
=
BASE64
.
encode
(
bytes
);
try
{
return
await
serviceProtocol
.
sendRequest
(
'_writeDevFSFile'
,
return
await
vmService
.
vm
.
invokeRpcRaw
(
'_writeDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'path'
:
entry
.
devicePath
,
...
...
@@ -155,7 +155,7 @@ class ServiceProtocolDevFSOperations implements DevFSOperations {
String
devicePath
,
String
contents
)
async
{
String
fileContents
=
BASE64
.
encode
(
UTF8
.
encode
(
contents
));
return
await
serviceProtocol
.
sendRequest
(
'_writeDevFSFile'
,
return
await
vmService
.
vm
.
invokeRpcRaw
(
'_writeDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'path'
:
devicePath
,
...
...
@@ -251,7 +251,8 @@ class DevFS {
final
Set
<
DevFSEntry
>
_deletedEntries
=
new
Set
<
DevFSEntry
>();
final
Set
<
DevFSEntry
>
dirtyAssetEntries
=
new
Set
<
DevFSEntry
>();
final
List
<
Future
<
Response
>>
_pendingOperations
=
new
List
<
Future
<
Response
>>();
final
List
<
Future
<
Map
<
String
,
dynamic
>>>
_pendingOperations
=
new
List
<
Future
<
Map
<
String
,
dynamic
>>>();
int
_bytes
=
0
;
int
get
bytes
=>
_bytes
;
...
...
@@ -358,7 +359,8 @@ class DevFS {
if
(
_deletedEntries
.
length
>
0
)
{
status
=
logger
.
startProgress
(
'Removing deleted files...'
);
for
(
DevFSEntry
entry
in
_deletedEntries
)
{
Future
<
Response
>
operation
=
_operations
.
deleteFile
(
fsName
,
entry
);
Future
<
Map
<
String
,
dynamic
>>
operation
=
_operations
.
deleteFile
(
fsName
,
entry
);
if
(
operation
!=
null
)
_pendingOperations
.
add
(
operation
);
}
...
...
@@ -382,7 +384,8 @@ class DevFS {
}
else
{
// Make service protocol requests for each.
for
(
DevFSEntry
entry
in
_dirtyEntries
)
{
Future
<
Response
>
operation
=
_operations
.
writeFile
(
fsName
,
entry
);
Future
<
Map
<
String
,
dynamic
>>
operation
=
_operations
.
writeFile
(
fsName
,
entry
);
if
(
operation
!=
null
)
_pendingOperations
.
add
(
operation
);
}
...
...
packages/flutter_tools/lib/src/hot.dart
View file @
3a012b32
...
...
@@ -23,7 +23,6 @@ import 'devfs.dart';
import
'vmservice.dart'
;
import
'resident_runner.dart'
;
import
'toolchain.dart'
;
import
'view.dart'
;
String
getDevFSLoaderScript
(
)
{
return
path
.
absolute
(
path
.
join
(
Cache
.
flutterRoot
,
...
...
@@ -80,18 +79,18 @@ class StartupDependencySetBuilder {
class
FirstFrameTimer
{
FirstFrameTimer
(
this
.
serviceProtocol
);
FirstFrameTimer
(
this
.
vmService
);
void
start
()
{
stopwatch
.
reset
();
stopwatch
.
start
();
_subscription
=
serviceProtocol
.
onExtensionEvent
.
listen
(
_onExtensionEvent
);
_subscription
=
vmService
.
onExtensionEvent
.
listen
(
_onExtensionEvent
);
}
/// Returns a Future which completes after the first frame event is received.
Future
<
Null
>
firstFrame
()
=>
_completer
.
future
;
void
_onExtensionEvent
(
Event
event
)
{
void
_onExtensionEvent
(
Service
Event
event
)
{
if
(
event
.
extensionKind
==
'Flutter.FirstFrame'
)
_stop
();
}
...
...
@@ -108,10 +107,10 @@ class FirstFrameTimer {
return
stopwatch
.
elapsed
;
}
final
VMService
serviceProtocol
;
final
VMService
vmService
;
final
Stopwatch
stopwatch
=
new
Stopwatch
();
final
Completer
<
Null
>
_completer
=
new
Completer
<
Null
>();
StreamSubscription
<
Event
>
_subscription
;
StreamSubscription
<
Service
Event
>
_subscription
;
}
class
HotRunner
extends
ResidentRunner
{
...
...
@@ -294,8 +293,8 @@ class HotRunner extends ResidentRunner {
return
3
;
}
await
v
iewManager
.
refresh
();
printStatus
(
'Connected to view
\'
${v
iewManager
.mainView}
\'
.'
);
await
v
mService
.
vm
.
refreshViews
();
printStatus
(
'Connected to view
\'
${v
mService.vm
.mainView}
\'
.'
);
printStatus
(
'Running
${getDisplayPath(_mainPath)}
on
${device.name}
...'
);
_loaderShowMessage
(
'Launching...'
);
...
...
@@ -332,13 +331,13 @@ class HotRunner extends ResidentRunner {
}
void
_loaderShowMessage
(
String
message
,
{
int
progress
,
int
max
})
{
serviceProtocol
.
flutterLoaderShowMessage
(
serviceProtocol
.
firstIsolateId
,
message
);
currentView
.
uiIsolate
.
flutterLoaderShowMessage
(
message
);
if
(
progress
!=
null
)
{
serviceProtocol
.
flutterLoaderSetProgress
(
serviceProtocol
.
firstIsolateId
,
progress
.
toDouble
());
serviceProtocol
.
flutterLoaderSetProgressMax
(
serviceProtocol
.
firstIsolateId
,
max
?.
toDouble
()
??
0.0
);
currentView
.
uiIsolate
.
flutterLoaderSetProgress
(
progress
.
toDouble
());
currentView
.
uiIsolate
.
flutterLoaderSetProgressMax
(
max
?.
toDouble
()
??
0.0
);
}
else
{
serviceProtocol
.
flutterLoaderSetProgress
(
serviceProtocol
.
firstIsolateId
,
0.0
);
serviceProtocol
.
flutterLoaderSetProgressMax
(
serviceProtocol
.
firstIsolateId
,
-
1.0
);
currentView
.
uiIsolate
.
flutterLoaderSetProgress
(
0.0
);
currentView
.
uiIsolate
.
flutterLoaderSetProgressMax
(
-
1.0
);
}
}
...
...
@@ -346,7 +345,7 @@ class HotRunner extends ResidentRunner {
Future
<
Uri
>
_initDevFS
()
{
String
fsName
=
path
.
basename
(
_projectRootPath
);
_devFS
=
new
DevFS
(
serviceProtocol
,
_devFS
=
new
DevFS
(
vmService
,
fsName
,
new
Directory
(
_projectRootPath
));
return
_devFS
.
create
();
...
...
@@ -377,11 +376,10 @@ class HotRunner extends ResidentRunner {
Future
<
Null
>
_evictDirtyAssets
()
async
{
if
(
_devFS
.
dirtyAssetEntries
.
length
==
0
)
return
;
if
(
serviceProtocol
.
firstIsolateId
==
null
)
if
(
currentView
.
uiIsolate
==
null
)
throw
'Application isolate not found'
;
for
(
DevFSEntry
entry
in
_devFS
.
dirtyAssetEntries
)
{
await
serviceProtocol
.
flutterEvictAsset
(
serviceProtocol
.
firstIsolateId
,
entry
.
assetPath
);
await
currentView
.
uiIsolate
.
flutterEvictAsset
(
entry
.
assetPath
);
}
}
...
...
@@ -400,7 +398,7 @@ class HotRunner extends ResidentRunner {
Future
<
Null
>
_launchInView
(
String
entryPath
,
String
packagesPath
,
String
assetsDirectoryPath
)
async
{
FlutterView
view
=
v
iewManager
.
mainView
;
FlutterView
view
=
v
mService
.
vm
.
mainView
;
return
view
.
runFromSource
(
entryPath
,
packagesPath
,
assetsDirectoryPath
);
}
...
...
@@ -419,7 +417,7 @@ class HotRunner extends ResidentRunner {
}
Future
<
Null
>
_restartFromSources
()
async
{
FirstFrameTimer
firstFrameTimer
=
new
FirstFrameTimer
(
serviceProtocol
);
FirstFrameTimer
firstFrameTimer
=
new
FirstFrameTimer
(
vmService
);
firstFrameTimer
.
start
();
await
_updateDevFS
();
await
_launchFromDevFS
(
_package
,
_mainPath
);
...
...
@@ -459,16 +457,16 @@ class HotRunner extends ResidentRunner {
}
Future
<
bool
>
_reloadSources
()
async
{
if
(
serviceProtocol
.
firstIsolateId
==
null
)
if
(
currentView
.
uiIsolate
==
null
)
throw
'Application isolate not found'
;
FirstFrameTimer
firstFrameTimer
=
new
FirstFrameTimer
(
serviceProtocol
);
FirstFrameTimer
firstFrameTimer
=
new
FirstFrameTimer
(
vmService
);
firstFrameTimer
.
start
();
if
(
_devFS
!=
null
)
await
_updateDevFS
();
Status
reloadStatus
=
logger
.
startProgress
(
'Performing hot reload...'
);
try
{
Map
<
String
,
dynamic
>
reloadReport
=
await
serviceProtocol
.
reloadSources
(
serviceProtocol
.
firstIsolateId
);
await
currentView
.
uiIsolate
.
reloadSources
(
);
reloadStatus
.
stop
(
showElapsedTime:
true
);
if
(!
_printReloadReport
(
reloadReport
))
{
// Reload failed.
...
...
@@ -477,16 +475,16 @@ class HotRunner extends ResidentRunner {
}
else
{
flutterUsage
.
sendEvent
(
'hot'
,
'reload'
);
}
}
catch
(
errorMessage
)
{
}
catch
(
errorMessage
,
st
)
{
reloadStatus
.
stop
(
showElapsedTime:
true
);
printError
(
'Hot reload failed:
\n
$errorMessage
'
);
printError
(
'Hot reload failed:
\n
$errorMessage
\n
$st
'
);
return
false
;
}
await
_evictDirtyAssets
();
Status
reassembleStatus
=
logger
.
startProgress
(
'Reassembling application...'
);
try
{
await
serviceProtocol
.
flutterReassemble
(
serviceProtocol
.
firstIsolateId
);
await
currentView
.
uiIsolate
.
flutterReassemble
(
);
}
catch
(
_
)
{
reassembleStatus
.
stop
(
showElapsedTime:
true
);
printError
(
'Reassembling application failed.'
);
...
...
packages/flutter_tools/lib/src/resident_runner.dart
View file @
3a012b32
...
...
@@ -12,7 +12,6 @@ import 'build_info.dart';
import
'device.dart'
;
import
'globals.dart'
;
import
'vmservice.dart'
;
import
'view.dart'
;
// Shared code between different resident application runners.
abstract
class
ResidentRunner
{
...
...
@@ -28,8 +27,8 @@ abstract class ResidentRunner {
final
bool
usesTerminalUI
;
final
Completer
<
int
>
_finished
=
new
Completer
<
int
>();
VMService
serviceProtocol
;
ViewManager
viewManager
;
VMService
vmService
;
FlutterView
currentView
;
StreamSubscription
<
String
>
_loggingSubscription
;
/// Start the app and keep the process running during its lifetime.
...
...
@@ -48,11 +47,11 @@ abstract class ResidentRunner {
}
Future
<
Null
>
_debugDumpApp
()
async
{
await
serviceProtocol
.
flutterDebugDumpApp
(
serviceProtocol
.
firstIsolateId
);
await
currentView
.
uiIsolate
.
flutterDebugDumpApp
(
);
}
Future
<
Null
>
_debugDumpRenderTree
()
async
{
await
serviceProtocol
.
flutterDebugDumpRenderTree
(
serviceProtocol
.
firstIsolateId
);
await
currentView
.
uiIsolate
.
flutterDebugDumpRenderTree
(
);
}
void
registerSignalHandlers
()
{
...
...
@@ -90,22 +89,23 @@ abstract class ResidentRunner {
if
(!
debuggingOptions
.
debuggingEnabled
)
{
return
new
Future
<
Null
>.
error
(
'Error the service protocol is not enabled.'
);
}
serviceProtocol
=
await
VMService
.
connect
(
port
);
vmService
=
await
VMService
.
connect
(
port
);
printTrace
(
'Connected to service protocol on port
$port
'
);
serviceProtocol
.
populateIsolateInfo
();
serviceProtocol
.
onExtensionEvent
.
listen
((
Event
event
)
{
await
vmService
.
getVM
();
vmService
.
onExtensionEvent
.
listen
((
Service
Event
event
)
{
printTrace
(
event
.
toString
());
});
serviceProtocol
.
onIsolateEvent
.
listen
((
Event
event
)
{
vmService
.
onIsolateEvent
.
listen
((
Service
Event
event
)
{
printTrace
(
event
.
toString
());
});
// Setup view manager and refresh the view list.
viewManager
=
new
ViewManager
(
serviceProtocol
);
await
viewManager
.
refresh
();
// Refresh the view list.
await
vmService
.
vm
.
refreshViews
();
currentView
=
vmService
.
vm
.
mainView
;
assert
(
currentView
!=
null
);
// Listen for service protocol connection to close.
serviceProtocol
.
done
.
whenComplete
(()
{
vmService
.
done
.
whenComplete
(()
{
appFinished
();
});
}
...
...
@@ -175,9 +175,10 @@ abstract class ResidentRunner {
Future
<
Null
>
preStop
()
async
{
}
Future
<
Null
>
stopApp
()
async
{
if
(
serviceProtocol
!=
null
&&
!
serviceProtocol
.
isClosed
)
{
if
(
serviceProtocol
.
isolates
.
isNotEmpty
)
{
serviceProtocol
.
flutterExit
(
serviceProtocol
.
firstIsolateId
);
if
(
vmService
!=
null
&&
!
vmService
.
isClosed
)
{
if
((
currentView
!=
null
)
&&
(
currentView
.
uiIsolate
!=
null
))
{
// TODO(johnmccutchan): Wait for the exit command to complete.
currentView
.
uiIsolate
.
flutterExit
();
await
new
Future
<
Null
>.
delayed
(
new
Duration
(
milliseconds:
100
));
}
}
...
...
packages/flutter_tools/lib/src/run.dart
View file @
3a012b32
...
...
@@ -59,17 +59,17 @@ class RunAndStayResident extends ResidentRunner {
@override
Future
<
bool
>
restart
({
bool
fullRestart:
false
})
async
{
if
(
serviceProtocol
==
null
)
{
if
(
vmService
==
null
)
{
printError
(
'Debugging is not enabled.'
);
return
false
;
}
else
{
Status
status
=
logger
.
startProgress
(
'Re-starting application...'
);
Future
<
Event
>
extensionAddedEvent
;
Future
<
Service
Event
>
extensionAddedEvent
;
if
(
device
.
restartSendsFrameworkInitEvent
)
{
extensionAddedEvent
=
serviceProtocol
.
onExtensionEvent
.
where
((
Event
event
)
=>
event
.
extensionKind
==
'Flutter.FrameworkInitialization'
)
extensionAddedEvent
=
vmService
.
onExtensionEvent
.
where
((
Service
Event
event
)
=>
event
.
extensionKind
==
'Flutter.FrameworkInitialization'
)
.
first
;
}
...
...
@@ -77,7 +77,7 @@ class RunAndStayResident extends ResidentRunner {
_package
,
_result
,
mainPath:
_mainPath
,
observatory:
serviceProtocol
observatory:
vmService
);
status
.
stop
(
showElapsedTime:
true
);
...
...
@@ -178,16 +178,20 @@ class RunAndStayResident extends ResidentRunner {
if
(
debuggingOptions
.
debuggingEnabled
)
{
await
connectToServiceProtocol
(
_result
.
observatoryPort
);
if
(
benchmark
)
await
serviceProtocol
.
waitFirstIsolate
;
if
(
benchmark
)
{
await
vmService
.
getVM
();
}
}
printStatus
(
'Application running.'
);
if
(
serviceProtocol
!=
null
&&
traceStartup
)
{
await
vmService
.
vm
.
refreshViews
();
printStatus
(
'Connected to view
\'
${vmService.vm.mainView}
\'
.'
);
if
(
vmService
!=
null
&&
traceStartup
)
{
printStatus
(
'Downloading startup trace info...'
);
try
{
await
downloadStartupTrace
(
serviceProtocol
);
await
downloadStartupTrace
(
vmService
);
}
catch
(
error
)
{
printError
(
error
);
return
2
;
...
...
packages/flutter_tools/lib/src/view.dart
deleted
100644 → 0
View file @
53dd5dbd
// 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
'globals.dart'
;
import
'vmservice.dart'
;
/// Peered to a Android/iOS FlutterView widget on a device.
class
FlutterView
{
FlutterView
(
this
.
viewId
,
this
.
viewManager
);
final
String
viewId
;
final
ViewManager
viewManager
;
String
_uiIsolateId
;
String
get
uiIsolateId
=>
_uiIsolateId
;
Future
<
Null
>
runFromSource
(
String
entryPath
,
String
packagesPath
,
String
assetsDirectoryPath
)
async
{
return
viewManager
.
_runFromSource
(
this
,
entryPath
,
packagesPath
,
assetsDirectoryPath
);
}
@override
String
toString
()
=>
viewId
;
@override
bool
operator
==(
FlutterView
other
)
{
return
other
.
viewId
==
viewId
;
}
@override
int
get
hashCode
=>
viewId
.
hashCode
;
}
/// Manager of FlutterViews.
class
ViewManager
{
ViewManager
(
this
.
serviceProtocol
);
final
VMService
serviceProtocol
;
Future
<
Null
>
refresh
()
async
{
List
<
Map
<
String
,
String
>>
viewList
=
await
serviceProtocol
.
getViewList
();
for
(
Map
<
String
,
String
>
viewDescription
in
viewList
)
{
FlutterView
view
=
new
FlutterView
(
viewDescription
[
'id'
],
this
);
if
(!
views
.
contains
(
view
))
{
// Canonicalize views against the view set.
views
.
add
(
view
);
}
}
}
// TODO(johnmccutchan): Report errors when running failed.
Future
<
Null
>
_runFromSource
(
FlutterView
view
,
String
entryPath
,
String
packagesPath
,
String
assetsDirectoryPath
)
async
{
final
String
viewId
=
await
serviceProtocol
.
getFirstViewId
();
// When this completer completes the isolate is running.
final
Completer
<
Null
>
completer
=
new
Completer
<
Null
>();
final
StreamSubscription
<
Event
>
subscription
=
serviceProtocol
.
onIsolateEvent
.
listen
((
Event
event
)
{
// TODO(johnmccutchan): Listen to the debug stream and catch initial
// launch errors.
if
(
event
.
kind
==
'IsolateRunnable'
)
{
printTrace
(
'Isolate is runnable.'
);
completer
.
complete
(
null
);
}
});
await
serviceProtocol
.
runInView
(
viewId
,
entryPath
,
packagesPath
,
assetsDirectoryPath
);
await
completer
.
future
;
await
subscription
.
cancel
();
}
// TODO(johnmccutchan): Remove this accessor and make the runner multi-view
// aware.
FlutterView
get
mainView
{
return
views
.
first
;
}
final
Set
<
FlutterView
>
views
=
new
Set
<
FlutterView
>();
}
packages/flutter_tools/lib/src/vmservice.dart
View file @
3a012b32
...
...
@@ -11,23 +11,17 @@ import 'package:web_socket_channel/io.dart';
import
'globals.dart'
;
//
TODO(johnmccutchan): Rename this class to ServiceProtocol or Vm
Service.
//
/ A connection to the Dart VM
Service.
class
VMService
{
VMService
.
_
(
this
.
peer
,
this
.
port
,
this
.
httpAddress
)
{
_vm
=
new
VM
.
_empty
(
this
);
peer
.
registerMethod
(
'streamNotify'
,
(
rpc
.
Parameters
event
)
{
_handleStreamNotify
(
event
.
asMap
);
});
onIsolateEvent
.
listen
((
Event
event
)
{
if
(
event
.
kind
==
'IsolateStart'
)
{
_addIsolate
(
event
.
isolate
);
}
else
if
(
event
.
kind
==
'IsolateExit'
)
{
String
removedId
=
event
.
isolate
.
id
;
isolates
.
removeWhere
((
IsolateRef
ref
)
=>
ref
.
id
==
removedId
);
}
});
}
/// Connect to '127.0.0.1' at [port].
static
Future
<
VMService
>
connect
(
int
port
)
async
{
Uri
uri
=
new
Uri
(
scheme:
'ws'
,
host:
'127.0.0.1'
,
port:
port
,
path:
'ws'
);
WebSocket
ws
=
await
WebSocket
.
connect
(
uri
.
toString
());
...
...
@@ -37,158 +31,572 @@ class VMService {
return
new
VMService
.
_
(
peer
,
port
,
httpAddress
);
}
final
Uri
httpAddress
;
final
rpc
.
Peer
peer
;
final
int
port
;
final
rpc
.
Peer
peer
;
List
<
IsolateRef
>
isolates
=
<
IsolateRef
>[];
Completer
<
IsolateRef
>
_waitFirstIsolateCompleter
;
VM
_vm
;
/// The singleton [VM] object. Owns [Isolate] and [FlutterView] objects.
VM
get
vm
=>
_vm
;
Map
<
String
,
StreamController
<
Event
>>
_eventControllers
=
<
String
,
StreamController
<
Event
>>{};
final
Map
<
String
,
StreamController
<
ServiceEvent
>>
_eventControllers
=
<
String
,
StreamController
<
ServiceEvent
>>{};
Set
<
String
>
_listeningFor
=
new
Set
<
String
>();
bool
get
isClosed
=>
peer
.
isClosed
;
Future
<
Null
>
get
done
=>
peer
.
done
;
String
get
firstIsolateId
=>
isolates
.
isEmpty
?
null
:
isolates
.
first
.
id
;
// Events
Stream
<
Event
>
get
onExtensionEvent
=>
onEvent
(
'Extension'
);
Stream
<
ServiceEvent
>
get
onDebugEvent
=>
onEvent
(
'Debug'
);
Stream
<
Service
Event
>
get
onExtensionEvent
=>
onEvent
(
'Extension'
);
// IsolateStart, IsolateRunnable, IsolateExit, IsolateUpdate, ServiceExtensionAdded
Stream
<
Event
>
get
onIsolateEvent
=>
onEvent
(
'Isolate'
);
Stream
<
Event
>
get
onTimelineEvent
=>
onEvent
(
'Timeline'
);
Stream
<
ServiceEvent
>
get
onIsolateEvent
=>
onEvent
(
'Isolate'
);
Stream
<
ServiceEvent
>
get
onTimelineEvent
=>
onEvent
(
'Timeline'
);
// TODO(johnmccutchan): Add FlutterView events.
// Listen for a specific event name.
Stream
<
Event
>
onEvent
(
String
streamId
)
{
streamListen
(
streamId
);
Stream
<
Service
Event
>
onEvent
(
String
streamId
)
{
_
streamListen
(
streamId
);
return
_getEventController
(
streamId
).
stream
;
}
StreamController
<
Event
>
_getEventController
(
String
eventName
)
{
StreamController
<
Event
>
controller
=
_eventControllers
[
eventName
];
StreamController
<
Service
Event
>
_getEventController
(
String
eventName
)
{
StreamController
<
Service
Event
>
controller
=
_eventControllers
[
eventName
];
if
(
controller
==
null
)
{
controller
=
new
StreamController
<
Event
>.
broadcast
();
controller
=
new
StreamController
<
Service
Event
>.
broadcast
();
_eventControllers
[
eventName
]
=
controller
;
}
return
controller
;
}
void
_handleStreamNotify
(
Map
<
String
,
dynamic
>
data
)
{
Event
event
=
new
Event
(
data
[
'event'
]);
_getEventController
(
data
[
'streamId'
]).
add
(
event
);
final
String
streamId
=
data
[
'streamId'
];
final
Map
<
String
,
dynamic
>
eventData
=
data
[
'event'
];
final
Map
<
String
,
dynamic
>
eventIsolate
=
eventData
[
'isolate'
];
ServiceEvent
event
;
if
(
eventIsolate
!=
null
)
{
// getFromMap creates the Isolate if necessary.
Isolate
isolate
=
vm
.
getFromMap
(
eventIsolate
);
event
=
new
ServiceObject
.
_fromMap
(
isolate
,
eventData
);
if
(
event
.
kind
==
ServiceEvent
.
kIsolateExit
)
{
vm
.
_isolateCache
.
remove
(
isolate
.
id
);
vm
.
_buildIsolateList
();
}
else
if
(
event
.
kind
==
ServiceEvent
.
kIsolateRunnable
)
{
// Force reload once the isolate becomes runnable so that we
// update the root library.
isolate
.
reload
();
}
}
else
{
// The event doesn't have an isolate, so it is owned by the VM.
event
=
new
ServiceObject
.
_fromMap
(
vm
,
eventData
);
}
_getEventController
(
streamId
).
add
(
event
);
}
Future
<
Null
>
_streamListen
(
String
streamId
)
async
{
if
(!
_listeningFor
.
contains
(
streamId
))
{
_listeningFor
.
add
(
streamId
);
await
peer
.
sendRequest
(
'streamListen'
,
<
String
,
dynamic
>{
'streamId'
:
streamId
});
}
}
Future
<
Null
>
populateIsolateInfo
()
async
{
// Calling this has the side effect of populating the isolate information.
await
waitFirstIsolate
;
/// Reloads the VM.
Future
<
VM
>
getVM
()
{
return
_vm
.
reload
()
;
}
}
Future
<
IsolateRef
>
get
waitFirstIsolate
async
{
if
(
isolates
.
isNotEmpty
)
return
isolates
.
first
;
/// An error that is thrown when constructing/updating a service object.
class
VMServiceObjectLoadError
{
VMServiceObjectLoadError
(
this
.
message
,
this
.
map
);
final
String
message
;
final
Map
<
String
,
dynamic
>
map
;
}
_waitFirstIsolateCompleter
??=
new
Completer
<
IsolateRef
>();
bool
_isServiceMap
(
Map
<
String
,
dynamic
>
m
)
{
return
(
m
!=
null
)
&&
(
m
[
'type'
]
!=
null
);
}
bool
_hasRef
(
String
type
)
=>
(
type
!=
null
)
&&
type
.
startsWith
(
'@'
);
String
_stripRef
(
String
type
)
=>
(
_hasRef
(
type
)
?
type
.
substring
(
1
)
:
type
);
/// Given a raw response from the service protocol and a [ServiceObjectOwner],
/// recursively walk the response and replace values that are service maps with
/// actual [ServiceObject]s. During the upgrade the owner is given a chance
/// to return a cached / canonicalized object.
void
_upgradeCollection
(
dynamic
collection
,
ServiceObjectOwner
owner
)
{
if
(
collection
is
ServiceMap
)
{
return
;
}
if
(
collection
is
Map
)
{
_upgradeMap
(
collection
,
owner
);
}
else
if
(
collection
is
List
)
{
_upgradeList
(
collection
,
owner
);
}
}
getVM
().
then
((
VM
vm
)
{
for
(
IsolateRef
isolate
in
vm
.
isolates
)
_addIsolate
(
isolate
);
void
_upgradeMap
(
Map
<
String
,
dynamic
>
map
,
ServiceObjectOwner
owner
)
{
map
.
forEach
((
String
k
,
dynamic
v
)
{
if
((
v
is
Map
)
&&
_isServiceMap
(
v
))
{
map
[
k
]
=
owner
.
getFromMap
(
v
);
}
else
if
(
v
is
List
)
{
_upgradeList
(
v
,
owner
);
}
else
if
(
v
is
Map
)
{
_upgradeMap
(
v
,
owner
);
}
});
}
return
_waitFirstIsolateCompleter
.
future
;
void
_upgradeList
(
List
<
dynamic
>
list
,
ServiceObjectOwner
owner
)
{
for
(
int
i
=
0
;
i
<
list
.
length
;
i
++)
{
dynamic
v
=
list
[
i
];
if
((
v
is
Map
)
&&
_isServiceMap
(
v
))
{
list
[
i
]
=
owner
.
getFromMap
(
v
);
}
else
if
(
v
is
List
)
{
_upgradeList
(
v
,
owner
);
}
else
if
(
v
is
Map
)
{
_upgradeMap
(
v
,
owner
);
}
}
}
// Requests
/// Base class of all objects received over the service protocol.
abstract
class
ServiceObject
{
ServiceObject
.
_empty
(
this
.
_owner
);
Future
<
Response
>
sendRequest
(
String
method
,
[
Map
<
String
,
dynamic
>
args
])
{
return
peer
.
sendRequest
(
method
,
args
).
then
((
dynamic
result
)
=>
new
Response
(
result
));
/// Factory constructor given a [ServiceObjectOwner] and a service map,
/// upgrade the map into a proper [ServiceObject]. This function always
/// returns a new instance and does not interact with caches.
factory
ServiceObject
.
_fromMap
(
ServiceObjectOwner
owner
,
Map
<
String
,
dynamic
>
map
)
{
if
(
map
==
null
)
return
null
;
if
(!
_isServiceMap
(
map
))
throw
new
VMServiceObjectLoadError
(
"Expected a service map"
,
map
);
String
type
=
_stripRef
(
map
[
'type'
]);
ServiceObject
serviceObject
;
switch
(
type
)
{
case
'Event'
:
serviceObject
=
new
ServiceEvent
.
_empty
(
owner
);
break
;
case
'FlutterView'
:
serviceObject
=
new
FlutterView
.
_empty
(
owner
.
vm
);
break
;
case
'Isolate'
:
serviceObject
=
new
Isolate
.
_empty
(
owner
.
vm
);
break
;
default
:
printTrace
(
"Unsupported service object type:
$type
"
);
}
if
(
serviceObject
==
null
)
{
// If we don't have a model object for this service object type, as a
// fallback return a ServiceMap object.
serviceObject
=
new
ServiceMap
.
_empty
(
owner
);
}
// We have now constructed an emtpy service object, call update to
// populate it.
serviceObject
.
update
(
map
);
return
serviceObject
;
}
Future
<
Null
>
streamListen
(
String
streamId
)
async
{
if
(!
_listeningFor
.
contains
(
streamId
))
{
_listeningFor
.
add
(
streamId
);
sendRequest
(
'streamListen'
,
<
String
,
dynamic
>{
'streamId'
:
streamId
});
final
ServiceObjectOwner
_owner
;
ServiceObjectOwner
get
owner
=>
_owner
;
/// The id of this object.
String
get
id
=>
_id
;
String
_id
;
/// The user-level type of this object.
String
get
type
=>
_type
;
String
_type
;
/// The vm-level type of this object. Usually the same as [type].
String
get
vmType
=>
_vmType
;
String
_vmType
;
/// Is it safe to cache this object?
bool
_canCache
=
false
;
bool
get
canCache
=>
_canCache
;
/// Has this object been fully loaded?
bool
get
loaded
=>
_loaded
;
bool
_loaded
=
false
;
/// Is this object immutable after it is [loaded]?
bool
get
immutable
=>
false
;
String
get
name
=>
_name
;
String
_name
;
String
get
vmName
=>
_vmName
;
String
_vmName
;
/// If this is not already loaded, load it. Otherwise reload.
Future
<
ServiceObject
>
load
()
async
{
if
(
loaded
)
{
return
this
;
}
return
reload
();
}
Future
<
VM
>
getVM
()
{
return
peer
.
sendRequest
(
'getVM'
).
then
((
dynamic
result
)
{
return
new
VM
(
result
);
});
/// Fetch this object from vmService and return the response directly.
Future
<
Map
<
String
,
dynamic
>>
_fetchDirect
()
{
Map
<
String
,
dynamic
>
params
=
<
String
,
dynamic
>{
'objectId'
:
id
,
};
return
_owner
.
isolate
.
invokeRpcRaw
(
'getObject'
,
params
);
}
Future
<
Map
<
String
,
dynamic
>>
reloadSources
(
String
isolateId
)
async
{
Future
<
ServiceObject
>
_inProgressReload
;
/// Reload the service object (if possible).
Future
<
ServiceObject
>
reload
()
async
{
bool
hasId
=
(
id
!=
null
)
&&
(
id
!=
''
);
bool
isVM
=
this
is
VM
;
// We should always reload the VM.
// We can't reload objects without an id.
// We shouldn't reload an immutable and already loaded object.
bool
skipLoad
=
!
isVM
&&
(!
hasId
||
(
immutable
&&
loaded
));
if
(
skipLoad
)
{
return
this
;
}
if
(
_inProgressReload
==
null
)
{
Completer
<
ServiceObject
>
completer
=
new
Completer
<
ServiceObject
>();
_inProgressReload
=
completer
.
future
;
try
{
Response
response
=
await
sendRequest
(
'_reloadSources'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
});
return
response
.
response
;
}
catch
(
e
)
{
return
new
Future
<
Map
<
String
,
dynamic
>>.
error
(
e
.
data
[
'details'
]);
Map
<
String
,
dynamic
>
response
=
await
_fetchDirect
();
if
(
_stripRef
(
response
[
'type'
])
==
'Sentinel'
)
{
// An object may have been collected.
completer
.
complete
(
new
ServiceObject
.
_fromMap
(
owner
,
response
));
}
else
{
update
(
response
);
completer
.
complete
(
this
);
}
}
catch
(
e
,
st
)
{
completer
.
completeError
(
e
,
st
);
}
_inProgressReload
=
null
;
}
Future
<
List
<
Map
<
String
,
String
>>>
getViewList
()
async
{
Map
<
String
,
dynamic
>
response
=
await
peer
.
sendRequest
(
'_flutter.listViews'
);
List
<
Map
<
String
,
String
>>
views
=
response
[
'views'
];
return
views
;
return
_inProgressReload
;
}
Future
<
String
>
getFirstViewId
()
async
{
List
<
Map
<
String
,
String
>>
views
=
await
getViewList
();
return
views
[
0
][
'id'
];
/// Update [this] using [map] as a source. [map] can be a service reference.
void
update
(
Map
<
String
,
dynamic
>
map
)
{
// Don't allow the type to change on an object update.
final
bool
mapIsRef
=
_hasRef
(
map
[
'type'
]);
final
String
mapType
=
_stripRef
(
map
[
'type'
]);
if
((
_type
!=
null
)
&&
(
_type
!=
mapType
))
{
throw
new
VMServiceObjectLoadError
(
"ServiceObject types must not change"
,
map
);
}
_type
=
mapType
;
_vmType
=
map
.
containsKey
(
'_vmType'
)
?
_stripRef
(
map
[
'_vmType'
])
:
_type
;
Future
<
Null
>
runInView
(
String
viewId
,
String
main
,
String
packages
,
String
assetsDirectory
)
async
{
await
peer
.
sendRequest
(
'_flutter.runInView'
,
<
String
,
dynamic
>
{
'viewId'
:
viewId
,
'mainScript'
:
main
,
'packagesFile'
:
packages
,
'assetDirectory'
:
assetsDirectory
_canCache
=
map
[
'fixedId'
]
==
true
;
if
((
_id
!=
null
)
&&
(
_id
!=
map
[
'id'
])
&&
_canCache
)
{
throw
new
VMServiceObjectLoadError
(
"ServiceObject id changed"
,
map
);
}
_id
=
map
[
'id'
];
// Copy name properties.
_name
=
map
[
'name'
];
_vmName
=
map
.
containsKey
(
'_vmName'
)
?
map
[
'_vmName'
]
:
_name
;
// We have now updated all common properties, let the subclasses update
// their specific properties.
_update
(
map
,
mapIsRef
);
}
/// Implemented by subclasses to populate their model.
void
_update
(
Map
<
String
,
dynamic
>
map
,
bool
mapIsRef
);
}
class
ServiceEvent
extends
ServiceObject
{
/// The possible 'kind' values.
static
const
String
kVMUpdate
=
'VMUpdate'
;
static
const
String
kIsolateStart
=
'IsolateStart'
;
static
const
String
kIsolateRunnable
=
'IsolateRunnable'
;
static
const
String
kIsolateExit
=
'IsolateExit'
;
static
const
String
kIsolateUpdate
=
'IsolateUpdate'
;
static
const
String
kIsolateReload
=
'IsolateReload'
;
static
const
String
kIsolateSpawn
=
'IsolateSpawn'
;
static
const
String
kServiceExtensionAdded
=
'ServiceExtensionAdded'
;
static
const
String
kPauseStart
=
'PauseStart'
;
static
const
String
kPauseExit
=
'PauseExit'
;
static
const
String
kPauseBreakpoint
=
'PauseBreakpoint'
;
static
const
String
kPauseInterrupted
=
'PauseInterrupted'
;
static
const
String
kPauseException
=
'PauseException'
;
static
const
String
kNone
=
'None'
;
static
const
String
kResume
=
'Resume'
;
static
const
String
kBreakpointAdded
=
'BreakpointAdded'
;
static
const
String
kBreakpointResolved
=
'BreakpointResolved'
;
static
const
String
kBreakpointRemoved
=
'BreakpointRemoved'
;
static
const
String
kGraph
=
'_Graph'
;
static
const
String
kGC
=
'GC'
;
static
const
String
kInspect
=
'Inspect'
;
static
const
String
kDebuggerSettingsUpdate
=
'_DebuggerSettingsUpdate'
;
static
const
String
kConnectionClosed
=
'ConnectionClosed'
;
static
const
String
kLogging
=
'_Logging'
;
static
const
String
kExtension
=
'Extension'
;
ServiceEvent
.
_empty
(
ServiceObjectOwner
owner
)
:
super
.
_empty
(
owner
);
String
_kind
;
String
get
kind
=>
_kind
;
DateTime
_timestamp
;
DateTime
get
timestmap
=>
_timestamp
;
String
_extensionKind
;
String
get
extensionKind
=>
_extensionKind
;
Map
<
String
,
dynamic
>
_extensionData
;
Map
<
String
,
dynamic
>
get
extensionData
=>
_extensionData
;
List
<
Map
<
String
,
dynamic
>>
_timelineEvents
;
List
<
Map
<
String
,
dynamic
>>
get
timelineEvents
=>
_timelineEvents
;
@override
void
_update
(
Map
<
String
,
dynamic
>
map
,
bool
mapIsRef
)
{
_loaded
=
true
;
_upgradeCollection
(
map
,
owner
);
_kind
=
map
[
'kind'
];
assert
(
map
[
'isolate'
]
==
null
||
owner
==
map
[
'isolate'
]);
_timestamp
=
new
DateTime
.
fromMillisecondsSinceEpoch
(
map
[
'timestamp'
]);
if
(
map
[
'extensionKind'
]
!=
null
)
{
_extensionKind
=
map
[
'extensionKind'
];
_extensionData
=
map
[
'extensionData'
];
}
_timelineEvents
=
map
[
'timelineEvents'
];
}
bool
get
isPauseEvent
{
return
(
kind
==
kPauseStart
||
kind
==
kPauseExit
||
kind
==
kPauseBreakpoint
||
kind
==
kPauseInterrupted
||
kind
==
kPauseException
||
kind
==
kNone
);
}
}
/// A ServiceObjectOwner is either a [VM] or an [Isolate]. Owners can cache
/// and/or canonicalize service objets received over the wire.
abstract
class
ServiceObjectOwner
extends
ServiceObject
{
ServiceObjectOwner
.
_empty
(
ServiceObjectOwner
owner
)
:
super
.
_empty
(
owner
);
/// Returns the owning VM.
VM
get
vm
=>
null
;
/// Returns the owning isolate (if any).
Isolate
get
isolate
=>
null
;
/// Returns the vmService connection.
VMService
get
vmService
=>
null
;
/// Builds a [ServiceObject] corresponding to the [id] from [map].
/// The result may come from the cache. The result will not necessarily
/// be [loaded].
ServiceObject
getFromMap
(
Map
<
String
,
dynamic
>
map
);
}
/// There is only one instance of the VM class. The VM class owns [Isolate]
/// and [FlutterView] objects.
class
VM
extends
ServiceObjectOwner
{
VM
.
_empty
(
this
.
_vmService
)
:
super
.
_empty
(
null
);
/// Connection to the VMService.
final
VMService
_vmService
;
@override
VMService
get
vmService
=>
_vmService
;
@override
VM
get
vm
=>
this
;
@override
Future
<
Map
<
String
,
dynamic
>>
_fetchDirect
()
async
{
return
invokeRpcRaw
(
'getVM'
,
<
String
,
dynamic
>
{});
}
@override
void
_update
(
Map
<
String
,
dynamic
>
map
,
bool
mapIsRef
)
{
if
(
mapIsRef
)
return
;
// Upgrade the collection. A side effect of this call is that any new
// isolates in the map are created and added to the isolate cache.
_upgradeCollection
(
map
,
this
);
_loaded
=
true
;
// TODO(johnmccutchan): Extract any properties we care about here.
// Remove any isolates which are now dead from the isolate cache.
_removeDeadIsolates
(
map
[
'isolates'
]);
}
final
Map
<
String
,
ServiceObject
>
_cache
=
new
Map
<
String
,
ServiceObject
>();
final
Map
<
String
,
Isolate
>
_isolateCache
=
new
Map
<
String
,
Isolate
>();
/// The list of live isolates, ordered by isolate start time.
final
List
<
Isolate
>
isolates
=
new
List
<
Isolate
>();
/// The set of live views.
final
Map
<
String
,
FlutterView
>
_viewCache
=
new
Map
<
String
,
FlutterView
>();
int
_compareIsolates
(
Isolate
a
,
Isolate
b
)
{
DateTime
aStart
=
a
.
startTime
;
DateTime
bStart
=
b
.
startTime
;
if
(
aStart
==
null
)
{
if
(
bStart
==
null
)
{
return
0
;
}
else
{
return
1
;
}
}
if
(
bStart
==
null
)
{
return
-
1
;
}
return
aStart
.
compareTo
(
bStart
);
}
void
_buildIsolateList
()
{
List
<
Isolate
>
isolateList
=
_isolateCache
.
values
.
toList
();
isolateList
.
sort
(
_compareIsolates
);
isolates
.
clear
();
isolates
.
addAll
(
isolateList
);
}
void
_removeDeadIsolates
(
List
<
Isolate
>
newIsolates
)
{
// Build a set of new isolates.
Set
<
String
>
newIsolateSet
=
new
Set
<
String
>();
newIsolates
.
forEach
((
Isolate
iso
)
=>
newIsolateSet
.
add
(
iso
.
id
));
// Remove any old isolates which no longer exist.
List
<
String
>
toRemove
=
<
String
>[];
_isolateCache
.
forEach
((
String
id
,
_
)
{
if
(!
newIsolateSet
.
contains
(
id
))
{
toRemove
.
add
(
id
);
}
});
toRemove
.
forEach
((
String
id
)
=>
_isolateCache
.
remove
(
id
));
_buildIsolateList
();
}
@override
ServiceObject
getFromMap
(
Map
<
String
,
dynamic
>
map
)
{
if
(
map
==
null
)
{
return
null
;
}
String
type
=
_stripRef
(
map
[
'type'
]);
if
(
type
==
'VM'
)
{
// Update this VM object.
update
(
map
);
return
this
;
}
Future
<
Response
>
clearVMTimeline
()
=>
sendRequest
(
'_clearVMTimeline'
)
;
String
mapId
=
map
[
'id'
]
;
Future
<
Response
>
setVMTimelineFlags
(
List
<
String
>
recordedStreams
)
{
assert
(
recordedStreams
!=
null
);
switch
(
type
)
{
case
'Isolate'
:
{
// Check cache.
Isolate
isolate
=
_isolateCache
[
mapId
];
if
(
isolate
==
null
)
{
// Add new isolate to the cache.
isolate
=
new
ServiceObject
.
_fromMap
(
this
,
map
);
_isolateCache
[
mapId
]
=
isolate
;
_buildIsolateList
();
return
sendRequest
(
'_setVMTimelineFlags'
,
<
String
,
dynamic
>
{
'recordedStreams'
:
recordedStreams
// Eagerly load the isolate.
isolate
.
load
().
catchError
((
dynamic
e
,
StackTrace
stack
)
{
printTrace
(
'Eagerly loading an isolate failed:
$e
\n
$stack
'
);
});
}
else
{
// Existing isolate, update data.
isolate
.
update
(
map
);
}
return
isolate
;
}
break
;
case
'FlutterView'
:
{
FlutterView
view
=
_viewCache
[
mapId
];
if
(
view
==
null
)
{
// Add new view to the cache.
view
=
new
ServiceObject
.
_fromMap
(
this
,
map
);
_viewCache
[
mapId
]
=
view
;
}
else
{
view
.
update
(
map
);
}
return
view
;
}
break
;
default
:
throw
new
VMServiceObjectLoadError
(
'VM.getFromMap called for something other than an isolate'
,
map
);
}
}
Future
<
Response
>
getVMTimeline
()
=>
sendRequest
(
'_getVMTimeline'
);
// Note that this function does not reload the isolate if it found
// in the cache.
Future
<
Isolate
>
getIsolate
(
String
isolateId
)
{
if
(!
loaded
)
{
// Trigger a VM load, then get the isolate. Ignore any errors.
return
load
().
then
((
_
)
=>
getIsolate
(
isolateId
)).
catchError
((
_
)
=>
null
);
}
return
new
Future
<
Isolate
>.
value
(
_isolateCache
[
isolateId
]);
}
// DevFS / VM virtual file system methods
/// Invoke the RPC and return the raw response.
Future
<
Map
<
String
,
dynamic
>>
invokeRpcRaw
(
String
method
,
[
Map
<
String
,
dynamic
>
params
])
async
{
if
(
params
==
null
)
{
params
=
<
String
,
dynamic
>{};
}
Map
<
String
,
dynamic
>
result
=
await
_vmService
.
peer
.
sendRequest
(
method
,
params
);
return
result
;
}
/// Create a new file system.
Future
<
CreateDevFSResponse
>
createDevFS
(
String
fsName
)
async
{
Response
response
=
await
sendRequest
(
'_createDevFS'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
});
return
new
CreateDevFSResponse
(
response
.
response
);
/// Invoke the RPC and return a ServiceObject response.
Future
<
ServiceObject
>
invokeRpc
(
String
method
,
[
Map
<
String
,
dynamic
>
params
])
async
{
Map
<
String
,
dynamic
>
response
=
await
invokeRpcRaw
(
method
,
params
);
ServiceObject
serviceObject
=
new
ServiceObject
.
_fromMap
(
this
,
response
);
if
((
serviceObject
!=
null
)
&&
(
serviceObject
.
_canCache
))
{
String
serviceObjectId
=
serviceObject
.
id
;
_cache
.
putIfAbsent
(
serviceObjectId
,
()
=>
serviceObject
);
}
return
serviceObject
;
}
/// List the available file systems.
Future
<
List
<
String
>>
listDevFS
()
{
return
sendRequest
(
'_listDevFS'
).
then
((
Response
response
)
{
return
response
.
response
[
'fsNames'
];
/// Create a new development file system on the device.
Future
<
Map
<
String
,
dynamic
>>
createDevFS
(
String
fsName
)
async
{
Map
<
String
,
dynamic
>
response
=
await
invokeRpcRaw
(
'_createDevFS'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
});
return
response
;
}
/// List the development file system son the device.
Future
<
List
<
String
>>
listDevFS
()
async
{
Map
<
String
,
dynamic
>
response
=
await
invokeRpcRaw
(
'_listDevFS'
,
<
String
,
dynamic
>{});
return
response
[
'fsNames'
];
}
// Write one file into a file system.
Future
<
Response
>
writeDevFSFile
(
String
fsName
,
{
Future
<
Map
<
String
,
dynamic
>
>
writeDevFSFile
(
String
fsName
,
{
String
path
,
List
<
int
>
fileContents
})
{
assert
(
path
!=
null
);
assert
(
fileContents
!=
null
);
return
sendRequest
(
'_writeDevFSFile'
,
<
String
,
dynamic
>
{
return
invokeRpcRaw
(
'_writeDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'path'
:
path
,
'fileContents'
:
BASE64
.
encode
(
fileContents
)
...
...
@@ -197,159 +605,292 @@ class VMService {
// Read one file from a file system.
Future
<
List
<
int
>>
readDevFSFile
(
String
fsName
,
String
path
)
{
return
sendRequest
(
'_readDevFSFile'
,
<
String
,
dynamic
>
{
return
invokeRpcRaw
(
'_readDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'path'
:
path
}).
then
((
Response
response
)
{
return
BASE64
.
decode
(
response
.
response
[
'fileContents'
]);
}).
then
((
Map
<
String
,
dynamic
>
response
)
{
return
BASE64
.
decode
(
response
[
'fileContents'
]);
});
}
/// The complete list of a file system.
Future
<
List
<
String
>>
listDevFSFiles
(
String
fsName
)
{
return
sendRequest
(
'_listDevFSFiles'
,
<
String
,
dynamic
>
{
return
invokeRpcRaw
(
'_listDevFSFiles'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
}).
then
((
Response
response
)
{
return
response
.
response
[
'files'
];
}).
then
((
Map
<
String
,
dynamic
>
response
)
{
return
response
[
'files'
];
});
}
/// Delete an existing file system.
Future
<
Response
>
deleteDevFS
(
String
fsName
)
{
return
sendRequest
(
'_deleteDevFS'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
});
Future
<
Map
<
String
,
dynamic
>
>
deleteDevFS
(
String
fsName
)
{
return
invokeRpcRaw
(
'_deleteDevFS'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
});
}
// Flutter extension methods.
Future
<
ServiceMap
>
runInView
(
String
viewId
,
String
main
,
String
packages
,
String
assetsDirectory
)
{
return
invokeRpc
(
'_flutter.runInView'
,
<
String
,
dynamic
>
{
'viewId'
:
viewId
,
'mainScript'
:
main
,
'packagesFile'
:
packages
,
'assetDirectory'
:
assetsDirectory
});
}
Future
<
Map
<
String
,
dynamic
>>
clearVMTimeline
()
{
return
invokeRpcRaw
(
'_clearVMTimeline'
,
<
String
,
dynamic
>{});
}
Future
<
Map
<
String
,
dynamic
>>
setVMTimelineFlags
(
List
<
String
>
recordedStreams
)
{
assert
(
recordedStreams
!=
null
);
Future
<
Response
>
flutterDebugDumpApp
(
String
isolateId
)
{
return
peer
.
sendRequest
(
'ext.flutter.debugDumpApp'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
}).
then
((
dynamic
result
)
=>
new
Response
(
result
));
return
invokeRpcRaw
(
'_setVMTimelineFlags'
,
<
String
,
dynamic
>
{
'recordedStreams'
:
recordedStreams
});
}
Future
<
Response
>
flutterDebugDumpRenderTree
(
String
isolateId
)
{
return
peer
.
sendRequest
(
'ext.flutter.debugDumpRenderTree'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
}).
then
((
dynamic
result
)
=>
new
Response
(
result
));
Future
<
Map
<
String
,
dynamic
>>
getVMTimeline
()
{
return
invokeRpcRaw
(
'_getVMTimeline'
,
<
String
,
dynamic
>
{});
}
// Loader page extension methods.
Future
<
Null
>
refreshViews
()
async
{
await
vmService
.
vm
.
invokeRpc
(
'_flutter.listViews'
);
}
Future
<
Response
>
flutterLoaderShowMessage
(
String
isolateId
,
String
message
)
{
return
peer
.
sendRequest
(
'ext.flutter.loaderShowMessage'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
,
'value'
:
message
}).
then
(
(
dynamic
result
)
=>
new
Response
(
result
),
onError:
(
dynamic
exception
)
{
printTrace
(
'ext.flutter.loaderShowMessage:
$exception
'
);
}
);
FlutterView
get
mainView
{
return
_viewCache
.
values
.
first
;
}
}
Future
<
Response
>
flutterLoaderSetProgress
(
String
isolateId
,
double
progress
)
{
return
peer
.
sendRequest
(
'ext.flutter.loaderSetProgress'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
,
'loaderSetProgress'
:
progress
}).
then
(
(
dynamic
result
)
=>
new
Response
(
result
),
onError:
(
dynamic
exception
)
{
printTrace
(
'ext.flutter.loaderSetProgress:
$exception
'
);
}
);
/// An isolate running inside the VM. Instances of the Isolate class are always
/// canonicalized.
class
Isolate
extends
ServiceObjectOwner
{
Isolate
.
_empty
(
ServiceObjectOwner
owner
)
:
super
.
_empty
(
owner
);
@override
VM
get
vm
=>
owner
;
@override
VMService
get
vmService
=>
vm
.
vmService
;
@override
Isolate
get
isolate
=>
this
;
DateTime
startTime
;
final
Map
<
String
,
ServiceObject
>
_cache
=
new
Map
<
String
,
ServiceObject
>();
@override
ServiceObject
getFromMap
(
Map
<
String
,
dynamic
>
map
)
{
if
(
map
==
null
)
{
return
null
;
}
String
mapType
=
_stripRef
(
map
[
'type'
]);
if
(
mapType
==
'Isolate'
)
{
// There are sometimes isolate refs in ServiceEvents.
return
vm
.
getFromMap
(
map
);
}
Future
<
Response
>
flutterLoaderSetProgressMax
(
String
isolateId
,
double
max
)
{
return
peer
.
sendRequest
(
'ext.flutter.loaderSetProgressMax'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
,
'loaderSetProgressMax'
:
max
}).
then
(
(
dynamic
result
)
=>
new
Response
(
result
),
onError:
(
dynamic
exception
)
{
printTrace
(
'ext.flutter.loaderSetProgressMax:
$exception
'
);
}
);
String
mapId
=
map
[
'id'
];
ServiceObject
serviceObject
=
(
mapId
!=
null
)
?
_cache
[
mapId
]
:
null
;
if
(
serviceObject
!=
null
)
{
serviceObject
.
update
(
map
);
return
serviceObject
;
}
// Build the object from the map directly.
serviceObject
=
new
ServiceObject
.
_fromMap
(
this
,
map
);
if
((
serviceObject
!=
null
)
&&
serviceObject
.
canCache
)
{
_cache
[
mapId
]
=
serviceObject
;
}
return
serviceObject
;
}
/// Causes the application to pick up any changed code.
Future
<
Response
>
flutterReassemble
(
String
isolateId
)
{
return
peer
.
sendRequest
(
'ext.flutter.reassemble'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
}).
then
((
dynamic
result
)
=>
new
Response
(
result
));
@override
Future
<
Map
<
String
,
dynamic
>>
_fetchDirect
()
{
return
invokeRpcRaw
(
'getIsolate'
,
<
String
,
dynamic
>{});
}
Future
<
Response
>
flutterEvictAsset
(
String
isolateId
,
String
assetPath
)
{
return
peer
.
sendRequest
(
'ext.flutter.evict'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
,
'value'
:
assetPath
}).
then
((
dynamic
result
)
=>
new
Response
(
result
));
/// Invoke the RPC and return the raw response.
Future
<
Map
<
String
,
dynamic
>>
invokeRpcRaw
(
String
method
,
[
Map
<
String
,
dynamic
>
params
])
{
// Inject the 'isolateId' parameter.
if
(
params
==
null
)
{
params
=
<
String
,
dynamic
>{
'isolateId'
:
id
};
}
else
{
params
[
'isolateId'
]
=
id
;
}
return
vm
.
invokeRpcRaw
(
method
,
params
);
}
Future
<
Response
>
flutterExit
(
String
isolateId
)
{
return
peer
.
sendRequest
(
'ext.flutter.exit'
,
<
String
,
dynamic
>{
'isolateId'
:
isolateId
})
.
then
((
dynamic
result
)
=>
new
Response
(
result
))
.
timeout
(
new
Duration
(
seconds:
2
),
onTimeout:
()
=>
null
);
/// Invoke the RPC and return a ServiceObject response.
Future
<
ServiceObject
>
invokeRpc
(
String
method
,
Map
<
String
,
dynamic
>
params
)
async
{
Map
<
String
,
dynamic
>
response
=
await
invokeRpcRaw
(
method
,
params
);
return
getFromMap
(
response
);
}
void
_addIsolate
(
IsolateRef
isolate
)
{
if
(!
isolates
.
contains
(
isolate
))
{
isolates
.
add
(
isolate
);
@override
void
_update
(
Map
<
String
,
dynamic
>
map
,
bool
mapIsRef
)
{
if
(
mapIsRef
)
{
return
;
}
_loaded
=
true
;
if
(
_waitFirstIsolateCompleter
!=
null
)
{
_waitFirstIsolateCompleter
.
complete
(
isolate
);
_waitFirstIsolateCompleter
=
null
;
int
startTimeMillis
=
map
[
'startTime'
];
startTime
=
new
DateTime
.
fromMillisecondsSinceEpoch
(
startTimeMillis
);
// TODO(johnmccutchan): Extract any properties we care about here.
_upgradeCollection
(
map
,
this
);
}
Future
<
Map
<
String
,
dynamic
>>
reloadSources
()
async
{
try
{
Map
<
String
,
dynamic
>
response
=
await
invokeRpcRaw
(
'_reloadSources'
);
return
response
;
}
catch
(
e
)
{
return
new
Future
<
Map
<
String
,
dynamic
>>.
error
(
e
.
data
[
'details'
]);
}
}
}
class
Response
{
Response
(
this
.
response
);
// Flutter extension methods.
final
Map
<
String
,
dynamic
>
response
;
Future
<
Map
<
String
,
dynamic
>>
flutterDebugDumpApp
()
{
return
invokeRpcRaw
(
'ext.flutter.debugDumpApp'
);
}
String
get
type
=>
response
[
'type'
];
Future
<
Map
<
String
,
dynamic
>>
flutterDebugDumpRenderTree
()
{
return
invokeRpcRaw
(
'ext.flutter.debugDumpRenderTree'
);
}
dynamic
operator
[](
String
key
)
=>
response
[
key
];
// Loader page extension methods.
@override
String
toString
()
=>
response
.
toString
();
}
Future
<
Map
<
String
,
dynamic
>>
flutterLoaderShowMessage
(
String
message
)
{
return
invokeRpcRaw
(
'ext.flutter.loaderShowMessage'
,
<
String
,
dynamic
>
{
'value'
:
message
});
}
class
CreateDevFSResponse
extends
Response
{
CreateDevFSResponse
(
Map
<
String
,
dynamic
>
response
)
:
super
(
response
);
Future
<
Map
<
String
,
dynamic
>>
flutterLoaderSetProgress
(
double
progress
)
{
return
invokeRpcRaw
(
'ext.flutter.loaderSetProgress'
,
<
String
,
dynamic
>{
'loaderSetProgress'
:
progress
});
}
String
get
name
=>
response
[
'name'
];
String
get
uri
=>
response
[
'uri'
];
}
Future
<
Map
<
String
,
dynamic
>>
flutterLoaderSetProgressMax
(
double
max
)
{
return
invokeRpcRaw
(
'ext.flutter.loaderSetProgressMax'
,
<
String
,
dynamic
>{
'loaderSetProgressMax'
:
max
});
}
/// Causes the application to pick up any changed code.
Future
<
Map
<
String
,
dynamic
>>
flutterReassemble
()
{
return
invokeRpcRaw
(
'ext.flutter.reassemble'
);
}
class
VM
extends
Response
{
VM
(
Map
<
String
,
dynamic
>
response
)
:
super
(
response
);
Future
<
Map
<
String
,
dynamic
>>
flutterEvictAsset
(
String
assetPath
)
{
return
invokeRpcRaw
(
'ext.flutter.evict'
,
<
String
,
dynamic
>{
'value'
:
assetPath
});
}
List
<
IsolateRef
>
get
isolates
=>
response
[
'isolates'
].
map
((
dynamic
ref
)
=>
new
IsolateRef
(
ref
)).
toList
();
Future
<
Map
<
String
,
dynamic
>>
flutterExit
()
{
return
invokeRpcRaw
(
'ext.flutter.exit'
).
timeout
(
const
Duration
(
seconds:
2
),
onTimeout:
()
=>
null
);
}
}
class
Event
extends
Response
{
Event
(
Map
<
String
,
dynamic
>
response
)
:
super
(
response
);
class
ServiceMap
extends
ServiceObject
implements
Map
<
String
,
dynamic
>
{
ServiceMap
.
_empty
(
ServiceObjectOwner
owner
)
:
super
.
_empty
(
owner
);
String
get
kind
=>
response
[
'kind'
];
IsolateRef
get
isolate
=>
new
IsolateRef
.
from
(
response
[
'isolate'
]);
final
Map
<
String
,
dynamic
>
_map
=
new
Map
<
String
,
dynamic
>();
/// Only valid for [kind] == `Extension`.
String
get
extensionKind
=>
response
[
'extensionKind'
];
@override
void
_update
(
Map
<
String
,
dynamic
>
map
,
bool
mapIsRef
)
{
_loaded
=
!
mapIsRef
;
_upgradeCollection
(
map
,
owner
);
_map
.
clear
();
_map
.
addAll
(
map
);
}
// Forward Map interface calls.
@override
void
addAll
(
Map
<
String
,
dynamic
>
other
)
=>
_map
.
addAll
(
other
);
@override
void
clear
()
=>
_map
.
clear
();
@override
bool
containsValue
(
dynamic
v
)
=>
_map
.
containsValue
(
v
);
@override
bool
containsKey
(
String
k
)
=>
_map
.
containsKey
(
k
);
@override
void
forEach
(
Function
f
)
=>
_map
.
forEach
(
f
);
@override
dynamic
putIfAbsent
(
String
key
,
Function
ifAbsent
)
=>
_map
.
putIfAbsent
(
key
,
ifAbsent
);
@override
void
remove
(
String
key
)
=>
_map
.
remove
(
key
);
@override
dynamic
operator
[](
String
k
)
=>
_map
[
k
];
@override
void
operator
[]=(
String
k
,
dynamic
v
)
=>
_map
[
k
]
=
v
;
@override
bool
get
isEmpty
=>
_map
.
isEmpty
;
@override
bool
get
isNotEmpty
=>
_map
.
isNotEmpty
;
@override
Iterable
<
String
>
get
keys
=>
_map
.
keys
;
@override
Iterable
<
dynamic
>
get
values
=>
_map
.
values
;
@override
int
get
length
=>
_map
.
length
;
@override
String
toString
()
=>
_map
.
toString
();
}
class
IsolateRef
extends
Response
{
IsolateRef
(
Map
<
String
,
dynamic
>
response
)
:
super
(
response
);
factory
IsolateRef
.
from
(
dynamic
ref
)
=>
ref
==
null
?
null
:
new
IsolateRef
(
ref
);
/// Peered to a Android/iOS FlutterView widget on a device.
class
FlutterView
extends
ServiceObject
{
FlutterView
.
_empty
(
ServiceObjectOwner
owner
)
:
super
.
_empty
(
owner
);
String
get
id
=>
response
[
'id'
];
Isolate
_uiIsolate
;
Isolate
get
uiIsolate
=>
_uiIsolate
;
@override
bool
operator
==(
dynamic
other
)
{
if
(
identical
(
this
,
other
))
return
true
;
if
(
other
is
!
IsolateRef
)
return
false
;
final
IsolateRef
typedOther
=
other
;
return
id
==
typedOther
.
id
;
void
_update
(
Map
<
String
,
dynamic
>
map
,
bool
mapIsRef
)
{
_loaded
=
!
mapIsRef
;
_upgradeCollection
(
map
,
owner
);
_uiIsolate
=
map
[
'isolate'
];
}
// TODO(johnmccutchan): Report errors when running failed.
Future
<
Null
>
runFromSource
(
String
entryPath
,
String
packagesPath
,
String
assetsDirectoryPath
)
async
{
final
String
viewId
=
id
;
// When this completer completes the isolate is running.
final
Completer
<
Null
>
completer
=
new
Completer
<
Null
>();
final
StreamSubscription
<
ServiceEvent
>
subscription
=
owner
.
vm
.
vmService
.
onIsolateEvent
.
listen
((
ServiceEvent
event
)
{
// TODO(johnmccutchan): Listen to the debug stream and catch initial
// launch errors.
if
(
event
.
kind
==
ServiceEvent
.
kIsolateRunnable
)
{
printTrace
(
'Isolate is runnable.'
);
completer
.
complete
(
null
);
}
});
await
owner
.
vm
.
runInView
(
viewId
,
entryPath
,
packagesPath
,
assetsDirectoryPath
);
await
completer
.
future
;
await
subscription
.
cancel
();
}
bool
get
hasIsolate
=>
_uiIsolate
!=
null
;
@override
int
get
hashCode
=>
id
.
hashCode
;
String
toString
()
=>
id
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment