blob: 174af31d73a22a10daed8747a89997e6a2745df0 [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
library instance_view_element;
import 'dart:async';
import 'dart:html';
import 'package:observatory/models.dart' as M;
import 'package:observatory/src/elements/class_ref.dart';
import 'package:observatory/src/elements/context_ref.dart';
import 'package:observatory/src/elements/curly_block.dart';
import 'package:observatory/src/elements/eval_box.dart';
import 'package:observatory/src/elements/field_ref.dart';
import 'package:observatory/src/elements/function_ref.dart';
import 'package:observatory/src/elements/helpers/any_ref.dart';
import 'package:observatory/src/elements/helpers/nav_bar.dart';
import 'package:observatory/src/elements/helpers/nav_menu.dart';
import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
import 'package:observatory/src/elements/helpers/tag.dart';
import 'package:observatory/src/elements/instance_ref.dart';
import 'package:observatory/src/elements/nav/class_menu.dart';
import 'package:observatory/src/elements/nav/isolate_menu.dart';
import 'package:observatory/src/elements/nav/library_menu.dart';
import 'package:observatory/src/elements/nav/notify.dart';
import 'package:observatory/src/elements/nav/refresh.dart';
import 'package:observatory/src/elements/nav/top_menu.dart';
import 'package:observatory/src/elements/nav/vm_menu.dart';
import 'package:observatory/src/elements/object_common.dart';
import 'package:observatory/src/elements/source_inset.dart';
import 'package:observatory/src/elements/source_link.dart';
import 'package:observatory/src/elements/view_footer.dart';
import 'package:observatory/utils.dart';
class InstanceViewElement extends HtmlElement implements Renderable {
static const tag =
const Tag<InstanceViewElement>('instance-view', dependencies: const [
ClassRefElement.tag,
ContextRefElement.tag,
CurlyBlockElement.tag,
FieldRefElement.tag,
FunctionRefElement.tag,
InstanceRefElement.tag,
NavClassMenuElement.tag,
NavLibraryMenuElement.tag,
NavTopMenuElement.tag,
NavVMMenuElement.tag,
NavIsolateMenuElement.tag,
NavRefreshElement.tag,
NavNotifyElement.tag,
ObjectCommonElement.tag,
SourceInsetElement.tag,
SourceLinkElement.tag,
ViewFooterElement.tag
]);
RenderingScheduler<InstanceViewElement> _r;
Stream<RenderedEvent<InstanceViewElement>> get onRendered => _r.onRendered;
M.VM _vm;
M.IsolateRef _isolate;
M.EventRepository _events;
M.NotificationRepository _notifications;
M.Instance _instance;
M.LibraryRef _library;
M.ObjectRepository _objects;
M.ClassRepository _classes;
M.RetainedSizeRepository _retainedSizes;
M.ReachableSizeRepository _reachableSizes;
M.InboundReferencesRepository _references;
M.RetainingPathRepository _retainingPaths;
M.ScriptRepository _scripts;
M.EvalRepository _eval;
M.TypeArguments _typeArguments;
M.TypeArgumentsRepository _arguments;
M.BreakpointRepository _breakpoints;
M.FunctionRepository _functions;
M.SourceLocation _location;
M.VMRef get vm => _vm;
M.IsolateRef get isolate => _isolate;
M.NotificationRepository get notifications => _notifications;
M.Instance get instance => _instance;
factory InstanceViewElement(
M.VM vm,
M.IsolateRef isolate,
M.Instance instance,
M.EventRepository events,
M.NotificationRepository notifications,
M.ObjectRepository objects,
M.ClassRepository classes,
M.RetainedSizeRepository retainedSizes,
M.ReachableSizeRepository reachableSizes,
M.InboundReferencesRepository references,
M.RetainingPathRepository retainingPaths,
M.ScriptRepository scripts,
M.EvalRepository eval,
M.TypeArgumentsRepository arguments,
M.BreakpointRepository breakpoints,
M.FunctionRepository functions,
{RenderingQueue queue}) {
assert(vm != null);
assert(isolate != null);
assert(events != null);
assert(notifications != null);
assert(instance != null);
assert(objects != null);
assert(classes != null);
assert(retainedSizes != null);
assert(reachableSizes != null);
assert(references != null);
assert(retainingPaths != null);
assert(scripts != null);
assert(eval != null);
assert(arguments != null);
assert(breakpoints != null);
assert(functions != null);
InstanceViewElement e = document.createElement(tag.name);
e._r = new RenderingScheduler<InstanceViewElement>(e, queue: queue);
e._vm = vm;
e._isolate = isolate;
e._events = events;
e._notifications = notifications;
e._instance = instance;
e._objects = objects;
e._classes = classes;
e._retainedSizes = retainedSizes;
e._reachableSizes = reachableSizes;
e._references = references;
e._retainingPaths = retainingPaths;
e._scripts = scripts;
e._eval = eval;
e._arguments = arguments;
e._breakpoints = breakpoints;
e._functions = functions;
return e;
}
InstanceViewElement.created() : super.created();
@override
attached() {
super.attached();
_r.enable();
_loadExtraData();
}
@override
detached() {
super.detached();
_r.disable(notify: true);
children = <Element>[];
}
void render() {
final content = <Element>[
new HeadingElement.h2()
..text = M.isAbstractType(_instance.kind)
? 'type ${_instance.name}'
: 'instance of ${_instance.clazz.name}',
new HRElement(),
new ObjectCommonElement(_isolate, _instance, _retainedSizes,
_reachableSizes, _references, _retainingPaths, _objects,
queue: _r.queue),
new BRElement(),
new DivElement()
..classes = ['memberList']
..children = _createMembers(),
new HRElement(),
new EvalBoxElement(_isolate, _instance, _objects, _eval,
quickExpressions: const ['toString()', 'runtimeType'],
queue: _r.queue)
];
if (_location != null) {
content.addAll([
new HRElement(),
new SourceInsetElement(_isolate, _location, _scripts, _objects, _events,
queue: _r.queue)
]);
}
content.addAll([new HRElement(), new ViewFooterElement(queue: _r.queue)]);
children = <Element>[
navBar(_createMenu()),
new DivElement()
..classes = ['content-centered-big']
..children = content
];
}
List<Element> _createMenu() {
final menu = <Element>[
new NavTopMenuElement(queue: _r.queue),
new NavVMMenuElement(_vm, _events, queue: _r.queue),
new NavIsolateMenuElement(_isolate, _events, queue: _r.queue)
];
if (_library != null) {
menu.add(new NavLibraryMenuElement(_isolate, _library, queue: _r.queue));
}
menu.addAll(<Element>[
new NavClassMenuElement(_isolate, _instance.clazz, queue: _r.queue),
navMenu('instance'),
new NavRefreshElement(queue: _r.queue)
..onRefresh.listen((e) {
e.element.disabled = true;
_refresh();
}),
new NavNotifyElement(_notifications, queue: _r.queue)
]);
return menu;
}
List<Element> _createMembers() {
final members = <Element>[];
if (_instance.valueAsString != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = _instance.kind == M.InstanceKind.string
? 'value as literal'
: 'value',
new DivElement()
..classes = ['memberValue']
..text = _instance.kind == M.InstanceKind.string
? Utils.formatStringAsLiteral(
_instance.valueAsString, _instance.valueAsStringIsTruncated)
: _instance.valueAsString
]);
}
if (_instance.typeClass != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'type class',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new ClassRefElement(_isolate, _instance.typeClass,
queue: _r.queue)
]
]);
}
if (_typeArguments != null && _typeArguments.types.isNotEmpty) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'type arguments',
new DivElement()
..classes = ['memberValue']
..children = ([new SpanElement()..text = '< ']
..addAll(_typeArguments.types.expand((type) => [
new InstanceRefElement(_isolate, type, _objects,
queue: _r.queue),
new SpanElement()..text = ', '
]))
..removeLast()
..add(new SpanElement()..text = ' >'))
]);
}
if (_instance.parameterizedClass != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'parameterized class',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new ClassRefElement(_isolate, _instance.parameterizedClass,
queue: _r.queue)
]
]);
}
if (_instance.parameterIndex != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'parameter index',
new DivElement()
..classes = ['memberValue']
..text = '${_instance.parameterIndex}'
]);
}
if (_instance.targetType != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'target type',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new InstanceRefElement(_isolate, _instance.targetType, _objects,
queue: _r.queue)
]
]);
}
if (_instance.bound != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'bound',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new InstanceRefElement(_isolate, _instance.bound, _objects,
queue: _r.queue)
]
]);
}
if (_instance.closureFunction != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'closure function',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new FunctionRefElement(_isolate, _instance.closureFunction,
queue: _r.queue)
]
]);
}
if (_instance.closureContext != null) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'closure context',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new ContextRefElement(
_isolate, _instance.closureContext, _objects,
queue: _r.queue)
]
]);
}
if (_instance.kind == M.InstanceKind.closure) {
ButtonElement btn;
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'closure breakpoint',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
btn = new ButtonElement()
..text = _instance.activationBreakpoint == null
? 'break on activation'
: 'remove'
..onClick.listen((_) {
btn.disabled = true;
_toggleBreakpoint();
})
]
]);
}
if (_instance.nativeFields != null && _instance.nativeFields.isNotEmpty) {
int i = 0;
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'native fields (${_instance.nativeFields.length})',
new DivElement()
..classes = ['memberName']
..children = <Element>[
new CurlyBlockElement(
expanded: _instance.nativeFields.length <= 100,
queue: _r.queue)
..content = <Element>[
new DivElement()
..classes = ['memberList']
..children = _instance.nativeFields
.map<Element>((f) => new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = '[ ${i++} ]',
new DivElement()
..classes = ['memberValue']
..text = '[ ${f.value} ]'
])
.toList()
]
]
]);
}
if (_instance.fields != null && _instance.fields.isNotEmpty) {
final fields = _instance.fields.toList();
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'fields (${fields.length})',
new DivElement()
..classes = ['memberName']
..children = <Element>[
new CurlyBlockElement(
expanded: fields.length <= 100, queue: _r.queue)
..content = <Element>[
new DivElement()
..classes = ['memberList']
..children = fields
.map<Element>((f) => new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..children = <Element>[
new FieldRefElement(_isolate, f.decl, _objects,
queue: _r.queue)
],
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new SpanElement()..text = ' = ',
anyRef(_isolate, f.value, _objects,
queue: _r.queue)
]
])
.toList()
]
]
]);
}
if (_instance.elements != null && _instance.elements.isNotEmpty) {
final elements = _instance.elements.toList();
int i = 0;
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'elements (${_instance.length})',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new CurlyBlockElement(
expanded: elements.length <= 100, queue: _r.queue)
..content = <Element>[
new DivElement()
..classes = ['memberList']
..children = elements
.map<Element>((element) => new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = '[ ${i++} ]',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
anyRef(_isolate, element, _objects,
queue: _r.queue)
]
])
.toList()
]
]
]);
if (_instance.length != elements.length) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = '...',
new DivElement()
..classes = ['memberValue']
..text = '${_instance.length - elements.length} omitted elements'
]);
}
}
if (_instance.associations != null && _instance.associations.isNotEmpty) {
final associations = _instance.associations.toList();
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'associations (${_instance.length})',
new DivElement()
..classes = ['memberName']
..children = <Element>[
new CurlyBlockElement(
expanded: associations.length <= 100, queue: _r.queue)
..content = <Element>[
new DivElement()
..classes = ['memberList']
..children = associations
.map<Element>((a) => new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..children = <Element>[
new SpanElement()..text = '[ ',
anyRef(_isolate, a.key, _objects,
queue: _r.queue),
new SpanElement()..text = ' ]',
],
new DivElement()
..classes = ['memberValue']
..children = <Element>[
anyRef(_isolate, a.value, _objects,
queue: _r.queue)
]
])
.toList()
]
]
]);
if (_instance.length != associations.length) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = '...',
new DivElement()
..classes = ['memberValue']
..text = '${_instance.length - associations.length} '
'omitted elements'
]);
}
}
if (_instance.typedElements != null && _instance.typedElements.isNotEmpty) {
final typedElements = _instance.typedElements.toList();
int i = 0;
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'elements (${_instance.length})',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new CurlyBlockElement(
expanded: typedElements.length <= 100, queue: _r.queue)
..content = <Element>[
new DivElement()
..classes = ['memberList']
..children = typedElements
.map<Element>((e) => new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = '[ ${i++} ]',
new DivElement()
..classes = ['memberValue']
..text = '$e'
])
.toList()
]
]
]);
if (_instance.length != typedElements.length) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = '...',
new DivElement()
..classes = ['memberValue']
..text = '${_instance.length - typedElements.length} '
'omitted elements'
]);
}
}
if (_instance.kind == M.InstanceKind.regExp) {
members.addAll([
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'pattern',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
anyRef(_isolate, _instance.pattern, _objects, queue: _r.queue)
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'isCaseSensitive',
new DivElement()
..classes = ['memberValue']
..text = _instance.isCaseSensitive ? 'yes' : 'no'
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'isMultiLine',
new DivElement()
..classes = ['memberValue']
..text = _instance.isMultiLine ? 'yes' : 'no'
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'oneByteFunction',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new FunctionRefElement(_isolate, _instance.oneByteFunction,
queue: _r.queue)
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'twoByteFunction',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new FunctionRefElement(_isolate, _instance.twoByteFunction,
queue: _r.queue)
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'externalOneByteFunction',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new FunctionRefElement(
_isolate, _instance.externalOneByteFunction,
queue: _r.queue)
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'externalTwoByteFunction',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new FunctionRefElement(
_isolate, _instance.externalTwoByteFunction,
queue: _r.queue)
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'oneByteBytecode',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new InstanceRefElement(
_isolate, _instance.oneByteBytecode, _objects,
queue: _r.queue)
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'twoByteBytecode',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new InstanceRefElement(
_isolate, _instance.twoByteBytecode, _objects,
queue: _r.queue)
]
]
]);
}
if (_instance.kind == M.InstanceKind.mirrorReference) {
members.add(new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'referent',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
anyRef(_isolate, _instance.referent, _objects, queue: _r.queue)
]
]);
}
if (_instance.kind == M.InstanceKind.weakProperty) {
members.addAll([
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'key',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new InstanceRefElement(_isolate, _instance.key, _objects,
queue: _r.queue),
]
],
new DivElement()
..classes = ['memberItem']
..children = <Element>[
new DivElement()
..classes = ['memberName']
..text = 'value',
new DivElement()
..classes = ['memberValue']
..children = <Element>[
new InstanceRefElement(_isolate, _instance.value, _objects,
queue: _r.queue),
]
]
]);
}
return members;
}
Future _refresh() async {
_instance = await _objects.get(_isolate, _instance.id);
await _loadExtraData();
_r.dirty();
}
Future _loadExtraData() async {
_library = (await _classes.get(_isolate, _instance.clazz.id)).library;
if (_instance.typeArguments != null) {
_typeArguments =
await _arguments.get(_isolate, _instance.typeArguments.id);
} else {
_typeArguments = null;
}
if (_instance.closureFunction != null) {
_location = (await _functions.get(_isolate, _instance.closureFunction.id))
.location;
} else if (_instance.typeClass != null) {
_location =
(await _classes.get(_isolate, _instance.typeClass.id)).location;
}
_r.dirty();
}
Future _toggleBreakpoint() async {
if (_instance.activationBreakpoint == null) {
await _breakpoints.addOnActivation(_isolate, _instance);
} else {
await _breakpoints.remove(_isolate, _instance.activationBreakpoint);
}
await _refresh();
}
}