blob: e93e6c888f61f09039313684003bf06c1d6321a9 [file] [log] [blame]
// Copyright (c) 2014, 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 trydart.poi;
import 'dart:async' show
Future;
import 'dart:io' show
Platform;
import 'package:dart2js_incremental/dart2js_incremental.dart' show
reuseCompiler;
import 'package:compiler/implementation/source_file_provider.dart' show
FormattingDiagnosticHandler,
SourceFileProvider;
import 'package:compiler/compiler.dart' as api;
import 'package:compiler/implementation/dart2jslib.dart' show
Compiler,
Enqueuer,
QueueFilter,
WorkItem;
import 'package:compiler/implementation/elements/visitor.dart' show
ElementVisitor;
import 'package:compiler/implementation/elements/elements.dart' show
ClassElement,
CompilationUnitElement,
Element,
LibraryElement,
ScopeContainerElement;
import 'package:compiler/implementation/scanner/scannerlib.dart' show
PartialClassElement,
PartialElement;
import 'package:compiler/implementation/util/uri_extras.dart' show
relativize;
main(List<String> arguments) {
Uri script = Uri.base.resolve(arguments.first);
int position = int.parse(arguments[1]);
FormattingDiagnosticHandler handler = new FormattingDiagnosticHandler();
handler
..verbose = true
..enableColors = true;
api.CompilerInputProvider inputProvider = handler.provider;
inputProvider(script);
handler(
script, position, position + 1,
'Point of interest.', api.Diagnostic.HINT);
Future future = runPoi(script, position, inputProvider, handler);
return future.then((Element element) {
print(scopeInformation(element, position));
});
}
Future<Element> runPoi(
Uri script, int position,
api.CompilerInputProvider inputProvider,
api.DiagnosticHandler handler) {
Uri libraryRoot = Uri.base.resolve('sdk/');
Uri packageRoot = Uri.base.resolveUri(
new Uri.file('${Platform.packageRoot}/'));
var options = [
'--analyze-main',
'--analyze-only',
'--no-source-maps',
'--verbose',
'--categories=Client,Server',
];
Compiler cachedCompiler = null;
cachedCompiler = reuseCompiler(
diagnosticHandler: handler,
inputProvider: inputProvider,
options: options,
cachedCompiler: cachedCompiler,
libraryRoot: libraryRoot,
packageRoot: packageRoot,
packagesAreImmutable: true);
cachedCompiler.enqueuerFilter = new ScriptOnlyFilter(script);
return cachedCompiler.run(script).then((success) {
if (success != true) {
throw 'Compilation failed';
}
return findPosition(position, cachedCompiler.mainApp);
});
}
Element findPosition(int position, Element element) {
FindPositionVisitor visitor = new FindPositionVisitor(position, element);
element.accept(visitor);
return visitor.element;
}
String scopeInformation(Element element, int position) {
ScopeInformationVisitor visitor =
new ScopeInformationVisitor(element, position);
element.accept(visitor);
return '${visitor.buffer}';
}
class FindPositionVisitor extends ElementVisitor {
final int position;
Element element;
FindPositionVisitor(this.position, this.element);
visitElement(Element e) {
if (e is PartialElement) {
if (e.beginToken.charOffset <= position &&
position < e.endToken.next.charOffset) {
element = e;
}
}
}
visitClassElement(ClassElement e) {
if (e is PartialClassElement) {
if (e.beginToken.charOffset <= position &&
position < e.endToken.next.charOffset) {
element = e;
visitScopeContainerElement(e);
}
}
}
visitScopeContainerElement(ScopeContainerElement e) {
e.forEachLocalMember((Element element) => element.accept(this));
}
}
class ScriptOnlyFilter implements QueueFilter {
final Uri script;
ScriptOnlyFilter(this.script);
bool checkNoEnqueuedInvokedInstanceMethods(Enqueuer enqueuer) => true;
void processWorkItem(void f(WorkItem work), WorkItem work) {
if (work.element.library.canonicalUri == script) {
f(work);
}
}
}
class ScopeInformationVisitor extends ElementVisitor/* <void> */ {
// TODO(ahe): Include function parameters and local variables.
final Element element;
final int position;
final StringBuffer buffer = new StringBuffer();
int indentationLevel = 0;
ScopeInformationVisitor(this.element, this.position);
String get indentation => ' ' * indentationLevel;
StringBuffer get indented => buffer..write(indentation);
void visitElement(Element e) {
serialize(e, omitEnclosing: false);
}
void visitLibraryElement(LibraryElement e) {
bool isFirst = true;
serialize(
e, omitEnclosing: true,
name: relativize(Uri.base, e.canonicalUri, false),
serializeMembers: () {
// TODO(ahe): Include imported elements in libraries.
e.forEachLocalMember((Element member) {
if (!isFirst) {
buffer.write(',');
}
buffer.write('\n');
indented;
serialize(member);
isFirst = false;
});
});
}
void visitScopeContainerElement(ScopeContainerElement e) {
bool isFirst = true;
serialize(e, omitEnclosing: false, serializeMembers: () {
// TODO(ahe): Include inherited members in classes.
e.forEachLocalMember((Element member) {
if (!isFirst) {
buffer.write(',');
}
buffer.write('\n');
indented;
serialize(member);
isFirst = false;
});
});
}
void visitCompilationUnitElement(CompilationUnitElement e) {
e.enclosingElement.accept(this);
}
void serialize(
Element element,
{bool omitEnclosing: true,
void serializeMembers(),
String name}) {
if (name == null) {
name = element.name;
}
buffer.write('{\n');
indentationLevel++;
indented
..write('"name": "')
..write(name)
..write('",\n');
indented
..write('"kind": "')
..write(element.kind)
..write('"');
// TODO(ahe): Add a type/signature field.
if (serializeMembers != null) {
buffer.write(',\n');
indented.write('"members": [');
indentationLevel++;
serializeMembers();
indentationLevel--;
buffer.write('\n');
indented.write(']');
}
if (!omitEnclosing) {
buffer.write(',\n');
indented.write('"enclosing": ');
element.enclosingElement.accept(this);
}
indentationLevel--;
buffer.write('\n');
indented.write('}');
}
}