| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library persitent_handles_page; | 5 library persitent_handles_page; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'observatory_element.dart'; | 9 import 'package:observatory/models.dart' as M; |
| 10 import 'package:observatory/app.dart'; | 10 import 'package:observatory/src/elements/containers/virtual_collection.dart'; |
| 11 import 'package:observatory/elements.dart'; | 11 import 'package:observatory/src/elements/helpers/any_ref.dart'; |
| 12 import 'package:observatory/service.dart'; | 12 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| 13 import 'package:polymer/polymer.dart'; | 13 import 'package:observatory/src/elements/helpers/tag.dart'; |
| 14 | 14 import 'package:observatory/src/elements/helpers/uris.dart'; |
| 15 class WeakPersistentHandlesSortedTable extends SortedTable { | 15 import 'package:observatory/src/elements/instance_ref.dart'; |
| 16 factory WeakPersistentHandlesSortedTable() { | 16 import 'package:observatory/src/elements/nav/bar.dart'; |
| 17 var columns = [ | 17 import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
| 18 new SortedTableColumn.withFormatter('External Size', | 18 import 'package:observatory/src/elements/nav/menu.dart'; |
| 19 Utils.formatSize), | 19 import 'package:observatory/src/elements/nav/notify.dart'; |
| 20 new SortedTableColumn('Peer'), | 20 import 'package:observatory/src/elements/nav/refresh.dart'; |
| 21 new SortedTableColumn('Finalizer Callback'), | 21 import 'package:observatory/src/elements/nav/top_menu.dart'; |
| 22 new SortedTableColumn(''), // Spacer column. | 22 import 'package:observatory/src/elements/nav/vm_menu.dart'; |
| 23 new SortedTableColumn('Object'), | 23 import 'package:observatory/utils.dart'; |
| 24 |
| 25 enum _SortingField { |
| 26 externalSize, |
| 27 peer, |
| 28 finalizerCallback |
| 29 } |
| 30 |
| 31 enum _SortingDirection { |
| 32 ascending, |
| 33 descending |
| 34 } |
| 35 |
| 36 class PersistentHandlesPageElement extends HtmlElement implements Renderable { |
| 37 static const tag = |
| 38 const Tag<PersistentHandlesPageElement>('persistent-handles-page', |
| 39 dependencies: const [ |
| 40 InstanceRefElement.tag, |
| 41 NavBarElement.tag, |
| 42 NavTopMenuElement.tag, |
| 43 NavVMMenuElement.tag, |
| 44 NavIsolateMenuElement.tag, |
| 45 NavMenuElement.tag, |
| 46 NavRefreshElement.tag, |
| 47 NavNotifyElement.tag, |
| 48 VirtualCollectionElement.tag |
| 49 ]); |
| 50 |
| 51 RenderingScheduler<PersistentHandlesPageElement> _r; |
| 52 |
| 53 Stream<RenderedEvent<PersistentHandlesPageElement>> get onRendered => |
| 54 _r.onRendered; |
| 55 |
| 56 M.VM _vm; |
| 57 M.IsolateRef _isolate; |
| 58 M.EventRepository _events; |
| 59 M.NotificationRepository _notifications; |
| 60 M.PersistentHandlesRepository _repository; |
| 61 M.InstanceRepository _instances; |
| 62 M.PersistentHandles _handles; |
| 63 _SortingField _sortingField = |
| 64 _SortingField.externalSize; |
| 65 _SortingDirection _sortingDirection = |
| 66 _SortingDirection.descending; |
| 67 |
| 68 M.VMRef get vm => _vm; |
| 69 M.IsolateRef get isolate => _isolate; |
| 70 M.NotificationRepository get notifications => _notifications; |
| 71 |
| 72 factory PersistentHandlesPageElement(M.VM vm, M.IsolateRef isolate, |
| 73 M.EventRepository events, |
| 74 M.NotificationRepository notifications, |
| 75 M.PersistentHandlesRepository repository, |
| 76 M.InstanceRepository instances, |
| 77 {RenderingQueue queue}) { |
| 78 assert(vm != null); |
| 79 assert(isolate != null); |
| 80 assert(events != null); |
| 81 assert(notifications != null); |
| 82 assert(repository != null); |
| 83 assert(instances != null); |
| 84 PersistentHandlesPageElement e = document.createElement(tag.name); |
| 85 e._r = new RenderingScheduler(e, queue: queue); |
| 86 e._vm = vm; |
| 87 e._isolate = isolate; |
| 88 e._events = events; |
| 89 e._notifications = notifications; |
| 90 e._repository = repository; |
| 91 e._instances = instances; |
| 92 return e; |
| 93 } |
| 94 |
| 95 PersistentHandlesPageElement.created() : super.created(); |
| 96 |
| 97 @override |
| 98 attached() { |
| 99 super.attached(); |
| 100 _r.enable(); |
| 101 _refresh(); |
| 102 } |
| 103 |
| 104 @override |
| 105 detached() { |
| 106 super.detached(); |
| 107 _r.disable(notify: true); |
| 108 children = []; |
| 109 } |
| 110 |
| 111 void render() { |
| 112 children = [ |
| 113 new NavBarElement(queue: _r.queue) |
| 114 ..children = [ |
| 115 new NavTopMenuElement(queue: _r.queue), |
| 116 new NavVMMenuElement(_vm, _events, queue: _r.queue), |
| 117 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), |
| 118 new NavMenuElement('persistent handles', last: true, |
| 119 link: Uris.persistentHandles(_isolate), queue: _r.queue), |
| 120 new NavRefreshElement(queue: _r.queue) |
| 121 ..onRefresh.listen((_) => _refresh()), |
| 122 new NavNotifyElement(_notifications, queue: _r.queue) |
| 123 ] |
| 124 ]..addAll(_createHandlers('Persistent Handles', |
| 125 _handles?.elements?.toList(), |
| 126 _createLine, |
| 127 _updateLine)) |
| 128 ..add(new BRElement()) |
| 129 ..addAll(_createHandlers('Weak Persistent Handles', |
| 130 _handles == null |
| 131 ? null |
| 132 : (_handles.weakElements.toList() |
| 133 ..sort(_createSorter())), |
| 134 _createWeakLine, |
| 135 _updateWeakLine, |
| 136 createHeader: _createWeakHeader)); |
| 137 } |
| 138 |
| 139 List<Element> _createHandlers(String name, List items, create, update, |
| 140 {createHeader}) { |
| 141 return [ |
| 142 new DivElement()..classes = const ['content-centered-big'] |
| 143 ..children = [ |
| 144 new HeadingElement.h1() |
| 145 ..text = items == null ? '$name' |
| 146 : '$name (${items.length})', |
| 147 new HRElement(), |
| 148 ], |
| 149 new DivElement()..classes = const ['persistent-handles'] |
| 150 ..children = [ |
| 151 items == null |
| 152 ? (new HeadingElement.h2()..classes = const ['content-centered-big'] |
| 153 ..text = 'Loading...') |
| 154 : new VirtualCollectionElement(create, update, items: items, |
| 155 createHeader: createHeader, |
| 156 queue: _r.queue) |
| 157 ] |
| 24 ]; | 158 ]; |
| 25 WeakPersistentHandlesSortedTable result = | 159 } |
| 26 new WeakPersistentHandlesSortedTable._(columns); | 160 |
| 27 // Sort by external size. | 161 _createSorter() { |
| 28 result.sortColumnIndex = 0; | 162 var getter; |
| 29 return result; | 163 switch (_sortingField) { |
| 30 } | 164 case _SortingField.externalSize: |
| 31 | 165 getter = _getExternalSize; |
| 32 WeakPersistentHandlesSortedTable._(columns) : super(columns); | 166 break; |
| 33 | 167 case _SortingField.peer: |
| 34 @override | 168 getter = _getPeer; |
| 35 dynamic getSortKeyFor(int row, int col) { | 169 break; |
| 36 return super.getSortKeyFor(row, col); | 170 case _SortingField.finalizerCallback: |
| 37 } | 171 getter = _getFinalizerCallback; |
| 38 | 172 break; |
| 39 void update(List<ServiceMap> handles, HtmlElement tableBody) { | |
| 40 clearRows(); | |
| 41 for (ServiceMap handle in handles) { | |
| 42 var row = [int.parse(handle['externalSize'], onError: (_) => 0), | |
| 43 handle['peer'], | |
| 44 handle['callbackSymbolName'] + | |
| 45 '( ${handle['callbackAddress']} )', | |
| 46 '', // Spacer column. | |
| 47 handle['object']]; | |
| 48 addRow(new SortedTableRow(row)); | |
| 49 print(row); | |
| 50 } | 173 } |
| 51 sort(); | 174 switch (_sortingDirection) { |
| 52 _updateTableInDom(tableBody); | 175 case _SortingDirection.ascending: |
| 53 } | 176 return (a, b) => getter(a).compareTo(getter(b)); |
| 54 | 177 case _SortingDirection.descending: |
| 55 void sortAndDisplay(HtmlElement tableBody) { | 178 return (a, b) => getter(b).compareTo(getter(a)); |
| 56 sort(); | 179 } |
| 57 _updateTableInDom(tableBody); | 180 } |
| 58 } | 181 |
| 59 | 182 static Element _createLine() => |
| 60 | 183 new DivElement()..classes = const ['collection-item'] |
| 61 void _updateTableInDom(HtmlElement tableBody) { | 184 ..text = 'object'; |
| 62 assert(tableBody != null); | 185 |
| 63 // Resize DOM table. | 186 static Element _createWeakLine() => |
| 64 if (tableBody.children.length > sortedRows.length) { | 187 new DivElement() |
| 65 // Shrink the table. | 188 ..classes = const ['weak-item'] |
| 66 var deadRows = | 189 ..children = [ |
| 67 tableBody.children.length - sortedRows.length; | 190 new SpanElement()..classes = const ['external-size'] |
| 68 for (var i = 0; i < deadRows; i++) { | 191 ..text = '0B', |
| 69 tableBody.children.removeLast(); | 192 new SpanElement()..classes = const ['peer'] |
| 193 ..text = '0x00000', |
| 194 new SpanElement()..classes = const ['object'], |
| 195 new SpanElement()..classes = const ['finalizer'] |
| 196 ..text = 'dart::Class::Method()' |
| 197 ]; |
| 198 |
| 199 Element _createWeakHeader() => |
| 200 new DivElement() |
| 201 ..classes = const ['weak-item'] |
| 202 ..children = [ |
| 203 _createHeaderButton(const ['external-size'], 'External Size', |
| 204 _SortingField.externalSize, |
| 205 _SortingDirection.descending), |
| 206 _createHeaderButton(const ['peer'], 'Peer', |
| 207 _SortingField.peer, |
| 208 _SortingDirection.descending), |
| 209 new SpanElement()..classes = const ['object'] |
| 210 ..text = 'Object', |
| 211 _createHeaderButton(const ['finalizer'], 'Finalizer Callback', |
| 212 _SortingField.finalizerCallback, |
| 213 _SortingDirection.ascending) |
| 214 ]; |
| 215 |
| 216 ButtonElement _createHeaderButton(List<String> classes, |
| 217 String text, |
| 218 _SortingField field, |
| 219 _SortingDirection direction) => |
| 220 new ButtonElement()..classes = classes |
| 221 ..text = _sortingField != field ? text : |
| 222 _sortingDirection == _SortingDirection.ascending |
| 223 ? '$textâ–¼' : '$textâ–²' |
| 224 ..onClick.listen((_) => _setSorting(field, direction)); |
| 225 |
| 226 |
| 227 void _setSorting(_SortingField field, |
| 228 _SortingDirection defaultDirection) { |
| 229 if (_sortingField == field) { |
| 230 switch (_sortingDirection) { |
| 231 case _SortingDirection.descending: |
| 232 _sortingDirection = _SortingDirection.ascending; |
| 233 break; |
| 234 case _SortingDirection.ascending: |
| 235 _sortingDirection = _SortingDirection.descending; |
| 236 break; |
| 70 } | 237 } |
| 71 } else if (tableBody.children.length < sortedRows.length) { | 238 } else { |
| 72 // Grow table. | 239 _sortingDirection = defaultDirection; |
| 73 var newRows = sortedRows.length - tableBody.children.length; | 240 _sortingField = field; |
| 74 for (var i = 0; i < newRows; i++) { | |
| 75 _addDomRow(tableBody); | |
| 76 } | |
| 77 } | 241 } |
| 78 assert(tableBody.children.length == sortedRows.length); | 242 _r.dirty(); |
| 79 // Fill table. | 243 } |
| 80 for (var i = 0; i < sortedRows.length; i++) { | 244 |
| 81 var rowIndex = sortedRows[i]; | 245 void _updateWeakLine(Element e, M.WeakPersistentHandle item, |
| 82 var tr = tableBody.children[i]; | 246 index) { |
| 83 _fillDomRow(tr, rowIndex); | 247 e.children[0].text = Utils.formatSize(_getExternalSize(item)); |
| 84 } | 248 e.children[1].text = '${_getPeer(item)}'; |
| 85 } | 249 e.children[2] = anyRef(_isolate, item.object, _instances, queue: _r.queue) |
| 86 | 250 ..classes = const ['object']; |
| 87 void _addDomRow(HtmlElement tableBody) { | 251 e.children[3]..text = '${_getFinalizerCallback(item)}' |
| 88 // Add empty dom row. | 252 ..title = '${_getFinalizerCallback(item)}'; |
| 89 var tr = new TableRowElement(); | 253 } |
| 90 | 254 |
| 91 var cell; | 255 void _updateLine(Element e, M.PersistentHandle item, |
| 92 | 256 index) { |
| 93 cell = tr.insertCell(-1); | 257 e.children = [ |
| 94 cell = tr.insertCell(-1); | 258 anyRef(_isolate, item.object, _instances, queue: _r.queue) |
| 95 cell = tr.insertCell(-1); | 259 ..classes = const ['object'] |
| 96 | 260 ]; |
| 97 // Add spacer. | 261 } |
| 98 cell = tr.insertCell(-1); | 262 |
| 99 cell.classes.add('left-border-spacer'); | 263 Future _refresh({bool gc: false, bool reset: false}) async { |
| 100 | 264 _handles = null; |
| 101 // Add class ref. | 265 _r.dirty(); |
| 102 cell = tr.insertCell(-1); | 266 _handles = await _repository.get(_isolate); |
| 103 AnyServiceRefElement objectRef = new Element.tag('any-service-ref'); | 267 _r.dirty(); |
| 104 cell.children.add(objectRef); | 268 } |
| 105 | 269 |
| 106 // Add row to table. | 270 static int _getExternalSize(M.WeakPersistentHandle h) => h.externalSize; |
| 107 tableBody.children.add(tr); | 271 static String _getPeer(M.WeakPersistentHandle h) => h.peer; |
| 108 } | 272 static String _getFinalizerCallback(M.WeakPersistentHandle h) => |
| 109 | 273 '${h.callbackSymbolName} (${h.callbackAddress})'; |
| 110 void _fillDomRow(TableRowElement tr, int rowIndex) { | |
| 111 var row = rows[rowIndex]; | |
| 112 | |
| 113 for (var i = 0; i < row.values.length - 2; i++) { | |
| 114 var cell = tr.children[i]; | |
| 115 cell.title = row.values[i].toString(); | |
| 116 cell.text = getFormattedValue(rowIndex, i); | |
| 117 cell.style.paddingLeft = '1em'; | |
| 118 cell.style.paddingRight = '1em'; | |
| 119 } | |
| 120 | |
| 121 final int objectIndex = row.values.length - 1; | |
| 122 AnyServiceRefElement objectRef = tr.children[objectIndex].children[0]; | |
| 123 objectRef.ref = row.values[objectIndex]; | |
| 124 } | |
| 125 } | 274 } |
| 126 | |
| 127 | |
| 128 @CustomTag('persistent-handles-page') | |
| 129 class PersistentHandlesPageElement extends ObservatoryElement { | |
| 130 PersistentHandlesPageElement.created() : super.created(); | |
| 131 | |
| 132 @observable Isolate isolate; | |
| 133 @observable var /*ObservableList | ServiceObject*/ persistentHandles; | |
| 134 @observable var /*ObservableList | ServiceObject*/ weakPersistentHandles; | |
| 135 @observable WeakPersistentHandlesSortedTable weakPersistentHandlesTable; | |
| 136 var _weakPersistentHandlesTableBody; | |
| 137 | |
| 138 void isolateChanged(oldValue) { | |
| 139 if (isolate != null) { | |
| 140 refresh(); | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 @override | |
| 145 void attached() { | |
| 146 super.attached(); | |
| 147 _weakPersistentHandlesTableBody = | |
| 148 shadowRoot.querySelector('#weakPersistentHandlesTableBody'); | |
| 149 weakPersistentHandlesTable = | |
| 150 new WeakPersistentHandlesSortedTable(); | |
| 151 } | |
| 152 | |
| 153 Future refresh() { | |
| 154 return isolate.getPersistentHandles().then(_refreshView); | |
| 155 } | |
| 156 | |
| 157 _refreshView(/*ObservableList | ServiceObject*/ object) { | |
| 158 persistentHandles = object['persistentHandles']; | |
| 159 weakPersistentHandles = object['weakPersistentHandles']; | |
| 160 weakPersistentHandlesTable.update( | |
| 161 weakPersistentHandles, | |
| 162 _weakPersistentHandlesTableBody); | |
| 163 } | |
| 164 | |
| 165 @observable void changeSort(Event e, var detail, Element target) { | |
| 166 if (target is TableCellElement) { | |
| 167 if (weakPersistentHandlesTable.sortColumnIndex != target.cellIndex) { | |
| 168 weakPersistentHandlesTable.sortColumnIndex = target.cellIndex; | |
| 169 weakPersistentHandlesTable.sortDescending = true; | |
| 170 } else { | |
| 171 weakPersistentHandlesTable.sortDescending = | |
| 172 !weakPersistentHandlesTable.sortDescending; | |
| 173 } | |
| 174 weakPersistentHandlesTable.sortAndDisplay( | |
| 175 _weakPersistentHandlesTableBody); | |
| 176 } | |
| 177 } | |
| 178 } | |
| OLD | NEW |