OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 class_view_element; | 5 library class_view_element; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'observatory_element.dart'; | 8 import 'dart:html'; |
9 import 'sample_buffer_control.dart'; | |
10 import 'stack_trace_tree_config.dart'; | |
11 import 'cpu_profile/virtual_tree.dart'; | |
12 import 'package:observatory/heap_snapshot.dart'; | |
13 import 'package:observatory/elements.dart'; | |
14 import 'package:observatory/models.dart' as M; | 9 import 'package:observatory/models.dart' as M; |
15 import 'package:observatory/service.dart'; | 10 import 'package:observatory/src/elements/class_allocation_profile.dart'; |
16 import 'package:observatory/repositories.dart'; | 11 import 'package:observatory/src/elements/class_instances.dart'; |
17 import 'package:polymer/polymer.dart'; | 12 import 'package:observatory/src/elements/class_ref.dart'; |
18 | 13 import 'package:observatory/src/elements/curly_block.dart'; |
19 @CustomTag('class-view') | 14 import 'package:observatory/src/elements/error_ref.dart'; |
20 class ClassViewElement extends ObservatoryElement { | 15 import 'package:observatory/src/elements/eval_box.dart'; |
21 @published Class cls; | 16 import 'package:observatory/src/elements/field_ref.dart'; |
22 @observable ServiceMap instances; | 17 import 'package:observatory/src/elements/function_ref.dart'; |
23 @observable int reachableBytes; | 18 import 'package:observatory/src/elements/helpers/any_ref.dart'; |
24 @observable int retainedBytes; | 19 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
25 @observable ObservableList mostRetained; | 20 import 'package:observatory/src/elements/helpers/tag.dart'; |
26 SampleBufferControlElement sampleBufferControlElement; | 21 import 'package:observatory/src/elements/instance_ref.dart'; |
27 StackTraceTreeConfigElement stackTraceTreeConfigElement; | 22 import 'package:observatory/src/elements/library_ref.dart'; |
28 CpuProfileVirtualTreeElement cpuProfileTreeElement; | 23 import 'package:observatory/src/elements/nav/bar.dart'; |
29 ClassSampleProfileRepository repository = new ClassSampleProfileRepository(); | 24 import 'package:observatory/src/elements/nav/class_menu.dart'; |
30 | 25 import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
| 26 import 'package:observatory/src/elements/nav/menu.dart'; |
| 27 import 'package:observatory/src/elements/nav/notify.dart'; |
| 28 import 'package:observatory/src/elements/nav/refresh.dart'; |
| 29 import 'package:observatory/src/elements/nav/top_menu.dart'; |
| 30 import 'package:observatory/src/elements/nav/vm_menu.dart'; |
| 31 import 'package:observatory/src/elements/object_common.dart'; |
| 32 import 'package:observatory/src/elements/source_inset.dart'; |
| 33 import 'package:observatory/src/elements/source_link.dart'; |
| 34 import 'package:observatory/src/elements/view_footer.dart'; |
| 35 |
| 36 class ClassViewElement extends HtmlElement implements Renderable { |
| 37 static const tag = const Tag<ClassViewElement>('class-view', |
| 38 dependencies: const [ |
| 39 ClassInstancesElement.tag, |
| 40 ClassRefElement.tag, |
| 41 CurlyBlockElement.tag, |
| 42 ErrorRefElement.tag, |
| 43 EvalBoxElement.tag, |
| 44 FieldRefElement.tag, |
| 45 FunctionRefElement.tag, |
| 46 InstanceRefElement.tag, |
| 47 LibraryRefElement.tag, |
| 48 NavBarElement.tag, |
| 49 NavClassMenuElement.tag, |
| 50 NavTopMenuElement.tag, |
| 51 NavVMMenuElement.tag, |
| 52 NavIsolateMenuElement.tag, |
| 53 NavMenuElement.tag, |
| 54 NavRefreshElement.tag, |
| 55 NavNotifyElement.tag, |
| 56 ObjectCommonElement.tag, |
| 57 SourceInsetElement.tag, |
| 58 SourceLinkElement.tag, |
| 59 ViewFooterElement.tag |
| 60 ]); |
| 61 |
| 62 RenderingScheduler<ClassViewElement> _r; |
| 63 |
| 64 Stream<RenderedEvent<ClassViewElement>> get onRendered => _r.onRendered; |
| 65 |
| 66 M.VM _vm; |
| 67 M.IsolateRef _isolate; |
| 68 M.EventRepository _events; |
| 69 M.NotificationRepository _notifications; |
| 70 M.Class _cls; |
| 71 M.ClassRepository _classes; |
| 72 M.RetainedSizeRepository _retainedSizes; |
| 73 M.ReachableSizeRepository _reachableSizes; |
| 74 M.InboundReferencesRepository _references; |
| 75 M.RetainingPathRepository _retainingPaths; |
| 76 M.StronglyReachableInstancesRepository _stronglyReachableInstances; |
| 77 M.TopRetainingInstancesRepository _topRetainedInstances; |
| 78 M.FieldRepository _fields; |
| 79 M.ScriptRepository _scripts; |
| 80 M.InstanceRepository _instances; |
| 81 M.EvalRepository _eval; |
| 82 M.ClassSampleProfileRepository _profiles; |
| 83 Iterable<M.Field> _classFields; |
| 84 |
| 85 |
| 86 M.VMRef get vm => _vm; |
| 87 M.IsolateRef get isolate => _isolate; |
| 88 M.NotificationRepository get notifications => _notifications; |
| 89 M.Class get cls => _cls; |
| 90 |
| 91 factory ClassViewElement(M.VM vm, M.IsolateRef isolate, M.Class cls, |
| 92 M.EventRepository events, |
| 93 M.NotificationRepository notifications, |
| 94 M.ClassRepository classes, |
| 95 M.RetainedSizeRepository retainedSizes, |
| 96 M.ReachableSizeRepository reachableSizes, |
| 97 M.InboundReferencesRepository references, |
| 98 M.RetainingPathRepository retainingPaths, |
| 99 M.FieldRepository fields, |
| 100 M.ScriptRepository scripts, |
| 101 M.InstanceRepository instances, |
| 102 M.EvalRepository eval, |
| 103 M.StronglyReachableInstancesRepository stronglyReachable, |
| 104 M.TopRetainingInstancesRepository topRetained, |
| 105 M.ClassSampleProfileRepository profiles, |
| 106 {RenderingQueue queue}) { |
| 107 assert(vm != null); |
| 108 assert(isolate != null); |
| 109 assert(events != null); |
| 110 assert(notifications != null); |
| 111 assert(cls != null); |
| 112 assert(classes != null); |
| 113 assert(retainedSizes != null); |
| 114 assert(reachableSizes != null); |
| 115 assert(references != null); |
| 116 assert(retainingPaths != null); |
| 117 assert(fields != null); |
| 118 assert(scripts != null); |
| 119 assert(instances != null); |
| 120 assert(eval != null); |
| 121 assert(stronglyReachable != null); |
| 122 assert(topRetained != null); |
| 123 assert(profiles != null); |
| 124 ClassViewElement e = document.createElement(tag.name); |
| 125 e._r = new RenderingScheduler(e, queue: queue); |
| 126 e._vm = vm; |
| 127 e._isolate = isolate; |
| 128 e._events = events; |
| 129 e._notifications = notifications; |
| 130 e._cls = cls; |
| 131 e._classes = classes; |
| 132 e._retainedSizes = retainedSizes; |
| 133 e._reachableSizes = reachableSizes; |
| 134 e._references = references; |
| 135 e._retainingPaths = retainingPaths; |
| 136 e._fields = fields; |
| 137 e._scripts = scripts; |
| 138 e._instances = instances; |
| 139 e._eval = eval; |
| 140 e._stronglyReachableInstances = stronglyReachable; |
| 141 e._topRetainedInstances = topRetained; |
| 142 e._profiles = profiles; |
| 143 return e; |
| 144 } |
31 | 145 |
32 ClassViewElement.created() : super.created(); | 146 ClassViewElement.created() : super.created(); |
33 | 147 |
34 Future<ServiceObject> evaluate(String expression) { | 148 @override |
35 return cls.evaluate(expression); | 149 attached() { |
36 } | |
37 | |
38 Future<ServiceObject> reachable(var limit) { | |
39 return cls.isolate.getInstances(cls, limit).then((ServiceMap obj) { | |
40 instances = obj; | |
41 }); | |
42 } | |
43 | |
44 Future retainedToplist(var limit) async { | |
45 final raw = await cls.isolate.fetchHeapSnapshot(true).last; | |
46 final snapshot = new HeapSnapshot(); | |
47 await snapshot.loadProgress(cls.isolate, raw).last; | |
48 final most = await Future.wait(snapshot.getMostRetained(cls.isolate, | |
49 classId: cls.vmCid
, | |
50 limit: 10)); | |
51 mostRetained = new ObservableList.from(most); | |
52 } | |
53 | |
54 // TODO(koda): Add no-arg "calculate-link" instead of reusing "eval-link". | |
55 Future<ServiceObject> reachableSize(var dummy) { | |
56 return cls.isolate.getReachableSize(cls).then((Instance obj) { | |
57 reachableBytes = int.parse(obj.valueAsString); | |
58 }); | |
59 } | |
60 | |
61 Future<ServiceObject> retainedSize(var dummy) { | |
62 return cls.isolate.getRetainedSize(cls).then((Instance obj) { | |
63 retainedBytes = int.parse(obj.valueAsString); | |
64 }); | |
65 } | |
66 | |
67 void attached() { | |
68 super.attached(); | 150 super.attached(); |
69 cls.fields.forEach((field) => field.reload()); | 151 _r.enable(); |
70 } | 152 _loadAdditionalData(); |
71 | 153 } |
72 Future refresh() async { | 154 |
73 instances = null; | 155 @override |
74 retainedBytes = null; | 156 detached() { |
75 mostRetained = null; | 157 super.detached(); |
76 await cls.reload(); | 158 _r.disable(notify: true); |
77 await Future.wait(cls.fields.map((field) => field.reload())); | 159 children = []; |
78 } | 160 } |
79 | 161 |
80 M.SampleProfileTag _tag = M.SampleProfileTag.none; | 162 ObjectCommonElement _common; |
81 | 163 ClassInstancesElement _classInstances; |
82 Future refreshAllocationProfile() async { | 164 bool _loadProfile = false; |
83 shadowRoot.querySelector('#sampleBufferControl').children = const []; | 165 |
84 shadowRoot.querySelector('#stackTraceTreeConfig').children = const []; | 166 void render() { |
85 shadowRoot.querySelector('#cpuProfileTree').children = const []; | 167 _common = _common ?? new ObjectCommonElement(_isolate, _cls, _retainedSizes, |
86 final stream = repository.get(cls, _tag); | 168 _reachableSizes, _references, _retainingPaths, _instances, |
87 var progress = (await stream.first).progress; | 169 queue: _r.queue); |
88 shadowRoot.querySelector('#sampleBufferControl')..children = [ | 170 _classInstances = _classInstances ?? new ClassInstancesElement(_isolate, |
89 new SampleBufferControlElement(progress, stream, queue: app.queue, | 171 _cls, _retainedSizes, _reachableSizes, _stronglyReachableInstances, |
90 selectedTag: _tag) | 172 _topRetainedInstances, _instances, queue: _r.queue); |
91 ..onTagChange.listen((e) { | 173 var header = ''; |
92 _tag = e.element.selectedTag; | 174 if (_cls.isAbstract) { |
93 refreshAllocationProfile(); | 175 header += 'abstract '; |
94 }) | 176 } |
| 177 if (_cls.isPatch) { |
| 178 header += 'patch '; |
| 179 } |
| 180 children = [ |
| 181 new NavBarElement(queue: _r.queue) |
| 182 ..children = [ |
| 183 new NavTopMenuElement(queue: _r.queue), |
| 184 new NavVMMenuElement(_vm, _events, queue: _r.queue), |
| 185 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), |
| 186 new NavClassMenuElement(_isolate, _cls, queue: _r.queue), |
| 187 new NavRefreshElement(label: 'Refresh Allocation Profile', |
| 188 queue: _r.queue) |
| 189 ..onRefresh.listen((e) { |
| 190 e.element.disabled = true; |
| 191 _loadProfile = true; |
| 192 _r.dirty(); |
| 193 }), |
| 194 new NavRefreshElement(queue: _r.queue) |
| 195 ..onRefresh.listen((e) { |
| 196 e.element.disabled = true; |
| 197 _common = null; |
| 198 _classInstances = null; |
| 199 _fieldsExpanded = null; |
| 200 _functionsExpanded = null; |
| 201 _refresh(); |
| 202 }), |
| 203 new NavNotifyElement(_notifications, queue: _r.queue) |
| 204 ], |
| 205 new DivElement()..classes = ['content-centered-big'] |
| 206 ..children = [ |
| 207 new HeadingElement.h2()..text = '$header class ${_cls.name}', |
| 208 new HRElement(), |
| 209 _common, |
| 210 new BRElement(), |
| 211 new DivElement()..classes = ['memberList'] |
| 212 ..children = _createMembers(), |
| 213 new DivElement() |
| 214 ..children = _cls.error == null |
| 215 ? const [] |
| 216 : [ |
| 217 new HRElement(), |
| 218 new ErrorRefElement(_cls.error, queue: _r.queue) |
| 219 ], |
| 220 new HRElement(), |
| 221 new EvalBoxElement(_isolate, _cls, _instances, _eval, |
| 222 queue: _r.queue), |
| 223 new HRElement(), |
| 224 new HeadingElement.h2()..text = 'Fields & Functions', |
| 225 new DivElement()..classes = ['memberList'] |
| 226 ..children = _createElements(), |
| 227 new HRElement(), |
| 228 new HeadingElement.h2()..text = 'Instances', |
| 229 new DivElement() |
| 230 ..children = _cls.hasAllocations |
| 231 ? [_classInstances] |
| 232 : const [], |
| 233 new HRElement(), |
| 234 new HeadingElement.h2()..text = 'Allocations', |
| 235 new DivElement()..classes = ['memberList'] |
| 236 ..children = [ |
| 237 new DivElement()..classes = ['memberName'] |
| 238 ..text = 'Tracing allocations?» ', |
| 239 new DivElement()..classes = ['memberValue'] |
| 240 ..children = _cls.traceAllocations |
| 241 ? [ |
| 242 new SpanElement()..text = 'Yes ', |
| 243 new ButtonElement()..text = 'disable' |
| 244 ..onClick.listen((e) async { |
| 245 e.target.disabled = true; |
| 246 await _profiles.disable(_isolate, _cls); |
| 247 _loadProfile = true; |
| 248 _refresh(); |
| 249 }) |
| 250 ] |
| 251 : [ |
| 252 new SpanElement()..text = 'No ', |
| 253 new ButtonElement()..text = 'enable' |
| 254 ..onClick.listen((e) async { |
| 255 e.target.disabled = true; |
| 256 await _profiles.enable(_isolate, _cls); |
| 257 _refresh(); |
| 258 }) |
| 259 ] |
| 260 ], |
| 261 new DivElement() |
| 262 ..children = _loadProfile |
| 263 ? [new ClassAllocationProfileElement(_isolate, _cls, _profiles, |
| 264 queue: _r.queue)] |
| 265 : const [], |
| 266 new DivElement() |
| 267 ..children = _cls.location != null |
| 268 ? [new HRElement(), |
| 269 new SourceInsetElement(_isolate, _cls.location, _scripts, |
| 270 _instances, _events, queue: _r.queue)] |
| 271 : const [], |
| 272 new HRElement(), |
| 273 new ViewFooterElement(queue: _r.queue) |
| 274 ] |
95 ]; | 275 ]; |
96 if (M.isSampleProcessRunning(progress.status)) { | 276 } |
97 progress = (await stream.last).progress; | 277 |
98 } | 278 bool _fieldsExpanded; |
99 if (progress.status == M.SampleProfileLoadingStatus.loaded) { | 279 bool _functionsExpanded; |
100 shadowRoot.querySelector('#stackTraceTreeConfig')..children = [ | 280 |
101 new StackTraceTreeConfigElement( | 281 List<Element> _createMembers() { |
102 queue: app.queue) | 282 final members = <Element>[]; |
103 ..showFilter = false | 283 if (_cls.library != null) { |
104 ..onModeChange.listen((e) { | 284 members.add( |
105 cpuProfileTreeElement.mode = e.element.mode; | 285 new DivElement()..classes = ['memberItem'] |
106 }) | 286 ..children = [ |
107 ..onDirectionChange.listen((e) { | 287 new DivElement()..classes = ['memberName'] |
108 cpuProfileTreeElement.direction = e.element.direction; | 288 ..text = 'library', |
109 }) | 289 new DivElement()..classes = ['memberValue'] |
110 ]; | 290 ..children = [ |
111 shadowRoot.querySelector('#cpuProfileTree')..children = [ | 291 new LibraryRefElement(_isolate, _cls.library, queue: _r.queue) |
112 cpuProfileTreeElement = new CpuProfileVirtualTreeElement(cls.isolate, | 292 ] |
113 progress.profile, queue: app.queue) | 293 ] |
114 ]; | 294 ); |
115 } | 295 } |
116 } | 296 if (_cls.location != null) { |
117 | 297 members.add( |
118 Future toggleAllocationTrace() { | 298 new DivElement()..classes = ['memberItem'] |
119 if (cls == null) { | 299 ..children = [ |
120 return new Future(refresh); | 300 new DivElement()..classes = ['memberName'] |
121 } | 301 ..text = 'script', |
122 if (cls.traceAllocations) { | 302 new DivElement()..classes = ['memberValue'] |
123 refreshAllocationProfile(); | 303 ..children = [ |
124 } | 304 new SourceLinkElement(_isolate, _cls.location, _scripts, |
125 return cls.setTraceAllocations(!cls.traceAllocations).whenComplete(refresh); | 305 queue: _r.queue) |
| 306 ] |
| 307 ] |
| 308 ); |
| 309 } |
| 310 if (_cls.superclass != null) { |
| 311 members.add( |
| 312 new DivElement()..classes = ['memberItem'] |
| 313 ..children = [ |
| 314 new DivElement()..classes = ['memberName'] |
| 315 ..text = 'superclass', |
| 316 new DivElement()..classes = ['memberValue'] |
| 317 ..children = [ |
| 318 new ClassRefElement(_isolate, _cls.superclass, queue: _r.queue) |
| 319 ] |
| 320 ] |
| 321 ); |
| 322 } |
| 323 if (_cls.superType != null) { |
| 324 members.add( |
| 325 new DivElement()..classes = ['memberItem'] |
| 326 ..children = [ |
| 327 new DivElement()..classes = ['memberName'] |
| 328 ..text = 'supertype', |
| 329 new DivElement()..classes = ['memberValue'] |
| 330 ..children = [ |
| 331 new InstanceRefElement(_isolate, _cls.superType, _instances, |
| 332 queue: _r.queue) |
| 333 ] |
| 334 ] |
| 335 ); |
| 336 } |
| 337 if (cls.mixin != null) { |
| 338 members.add( |
| 339 new DivElement()..classes = ['memberItem'] |
| 340 ..children = [ |
| 341 new DivElement()..classes = ['memberName'] |
| 342 ..text = 'mixin', |
| 343 new DivElement()..classes = ['memberValue'] |
| 344 ..children = [ |
| 345 new InstanceRefElement(_isolate, _cls.mixin, _instances, |
| 346 queue: _r.queue) |
| 347 ] |
| 348 ] |
| 349 ); |
| 350 } |
| 351 if (_cls.subclasses.length > 0) { |
| 352 members.add( |
| 353 new DivElement()..classes = ['memberItem'] |
| 354 ..children = [ |
| 355 new DivElement()..classes = ['memberName'] |
| 356 ..text = 'extended by', |
| 357 new DivElement()..classes = ['memberValue'] |
| 358 ..children = (_cls.subclasses.expand((subcls) => [ |
| 359 new ClassRefElement(_isolate, subcls, queue: _r.queue), |
| 360 new SpanElement()..text = ', ' |
| 361 ]).toList()..removeLast()) |
| 362 ] |
| 363 ); |
| 364 } |
| 365 |
| 366 members.add(new BRElement()); |
| 367 |
| 368 if (_cls.interfaces.length > 0) { |
| 369 members.add( |
| 370 new DivElement()..classes = ['memberItem'] |
| 371 ..children = [ |
| 372 new DivElement()..classes = ['memberName'] |
| 373 ..text = 'implements', |
| 374 new DivElement()..classes = ['memberValue'] |
| 375 ..children = (_cls.interfaces.expand((interf) => [ |
| 376 new InstanceRefElement(_isolate, interf, _instances, |
| 377 queue: _r.queue), |
| 378 new SpanElement()..text = ', ' |
| 379 ]).toList()..removeLast()) |
| 380 ] |
| 381 ); |
| 382 } |
| 383 if (_cls.name != _cls.vmName) { |
| 384 members.add( |
| 385 new DivElement()..classes = ['memberItem'] |
| 386 ..children = [ |
| 387 new DivElement()..classes = ['memberName'] |
| 388 ..text = 'vm name', |
| 389 new DivElement()..classes = ['memberValue'] |
| 390 ..text = '${_cls.vmName}' |
| 391 ] |
| 392 ); |
| 393 } |
| 394 return members; |
| 395 } |
| 396 |
| 397 List<Element> _createElements() { |
| 398 final members = <Element>[]; |
| 399 if (_classFields != null && _classFields.isNotEmpty) { |
| 400 final fields = _classFields.toList(); |
| 401 _fieldsExpanded = _fieldsExpanded ?? (fields.length <= 8); |
| 402 members.add( |
| 403 new DivElement()..classes = ['memberItem'] |
| 404 ..children = [ |
| 405 new DivElement()..classes = ['memberName'] |
| 406 ..text = 'fields ${fields.length}', |
| 407 new DivElement()..classes = ['memberValue'] |
| 408 ..children =[ |
| 409 new CurlyBlockElement(expanded: _fieldsExpanded) |
| 410 ..onToggle.listen((e) => _fieldsExpanded = e.control.expanded) |
| 411 ..children = [ |
| 412 new DivElement()..classes = ['memberList'] |
| 413 ..children = (fields.map((f) => |
| 414 new DivElement()..classes = ['memberItem'] |
| 415 ..children = [ |
| 416 new DivElement()..classes = ['memberName'] |
| 417 ..children =[ |
| 418 new FieldRefElement(_isolate, f, _instances, |
| 419 queue: _r.queue) |
| 420 ], |
| 421 new DivElement()..classes = ['memberValue'] |
| 422 ..children = [ |
| 423 anyRef(_isolate, f.staticValue, _instances, |
| 424 queue: _r.queue) |
| 425 ] |
| 426 ] |
| 427 ).toList()) |
| 428 ] |
| 429 ] |
| 430 ] |
| 431 ); |
| 432 } |
| 433 |
| 434 if (_cls.functions.isNotEmpty) { |
| 435 final functions = _cls.functions.toList(); |
| 436 _functionsExpanded = _functionsExpanded ?? (functions.length <= 8); |
| 437 members.add( |
| 438 new DivElement()..classes = ['memberItem'] |
| 439 ..children = [ |
| 440 new DivElement()..classes = ['memberName'] |
| 441 ..text = 'functions (${functions.length})', |
| 442 new DivElement()..classes = ['memberValue'] |
| 443 ..children =[ |
| 444 new CurlyBlockElement(expanded: _functionsExpanded) |
| 445 ..onToggle.listen((e) => |
| 446 _functionsExpanded = e.control.expanded) |
| 447 ..children = (functions.map((f) => |
| 448 new DivElement()..classes = ['indent'] |
| 449 ..children = [ |
| 450 new FunctionRefElement(_isolate, f, queue: _r.queue) |
| 451 ] |
| 452 ).toList()) |
| 453 ] |
| 454 ] |
| 455 ); |
| 456 } |
| 457 return members; |
| 458 } |
| 459 |
| 460 Future _refresh() async { |
| 461 _cls = await _classes.get(_isolate, _cls.id); |
| 462 await _loadAdditionalData(); |
| 463 _r.dirty(); |
| 464 } |
| 465 |
| 466 Future _loadAdditionalData() async { |
| 467 _classFields = await Future.wait(_cls.fields.map((f) |
| 468 => _fields.get(_isolate, f.id))); |
| 469 _r.dirty(); |
126 } | 470 } |
127 } | 471 } |
OLD | NEW |