blob: 5f1b5a19560a66ae3dcba01745f9537e87a83fb5 [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.scope_information_visitor;
import 'package:compiler/src/elements/modelx.dart' as modelx;
import 'package:compiler/src/elements/modelx.dart' show
CompilationUnitElementX,
FieldElementX;
import 'package:compiler/src/elements/visitor.dart' show
BaseElementVisitor;
import 'package:compiler/src/compiler.dart' show
Compiler;
import 'package:compiler/src/elements/elements.dart' show
AbstractFieldElement,
ClassElement,
CompilationUnitElement,
Element,
ElementCategory,
FunctionElement,
LibraryElement,
ScopeContainerElement;
import 'package:compiler/src/dart_types.dart' show
DartType;
/**
* Serializes scope information about an element. This is accomplished by
* calling the [serialize] method on each element. Some elements need special
* treatment, as their enclosing scope must also be serialized.
*/
class ScopeInformationVisitor extends BaseElementVisitor/* <void> */ {
// TODO(ahe): Include function parameters and local variables.
final Compiler compiler;
final Element currentElement;
final int position;
final StringBuffer buffer = new StringBuffer();
int indentationLevel = 0;
ClassElement currentClass;
bool sortMembers = false;
bool ignoreImports = false;
ScopeInformationVisitor(this.compiler, this.currentElement, 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;
forEach(Element member) {
if (!isFirst) {
buffer.write(',');
}
buffer.write('\n');
indented;
serialize(member);
isFirst = false;
}
serialize(
e,
// TODO(ahe): We omit the import scope if there is no current
// class. That's wrong.
omitEnclosing: ignoreImports || currentClass == null,
name: e.libraryName,
serializeEnclosing: () {
// The enclosing scope of a library is a scope which contains all the
// imported names.
isFirst = true;
buffer.write('{\n');
indentationLevel++;
indented.write('"kind": "imports",\n');
indented.write('"members": [');
indentationLevel++;
sortElements(importScope(e).importScope.values).forEach(forEach);
indentationLevel--;
buffer.write('\n');
indented.write('],\n');
// The enclosing scope of the imported names scope is the superclass
// scope of the current class.
indented.write('"enclosing": ');
serializeClassSide(
currentClass.superclass, isStatic: false, includeSuper: true);
buffer.write('\n');
indentationLevel--;
indented.write('}');
},
serializeMembers: () {
isFirst = true;
sortElements(localScope(e).values).forEach(forEach);
});
}
void visitClassElement(ClassElement e, _) {
currentClass = e;
serializeClassSide(e, isStatic: true);
}
/// Serializes one of the "sides" a class. The sides of a class are "instance
/// side" and "class side". These terms are from Smalltalk. The instance side
/// is all the local instance members of the class (the members of the
/// mixin), and the class side is the equivalent for static members and
/// constructors.
/// The scope chain is ordered so that the "class side" is searched before
/// the "instance side".
void serializeClassSide(
ClassElement e,
{bool isStatic: false,
bool omitEnclosing: false,
bool includeSuper: false}) {
e.ensureResolved(compiler.resolution);
bool isFirst = true;
var serializeEnclosing;
String kind;
if (isStatic) {
kind = 'class side';
serializeEnclosing = () {
serializeClassSide(e, isStatic: false, omitEnclosing: omitEnclosing);
};
} else {
kind = 'instance side';
}
if (includeSuper) {
assert(!omitEnclosing && !isStatic);
if (e.superclass == null) {
omitEnclosing = true;
} else {
// Members of the superclass are represented as a separate scope.
serializeEnclosing = () {
serializeClassSide(
e.superclass, isStatic: false, omitEnclosing: false,
includeSuper: true);
};
}
}
serialize(
e, omitEnclosing: omitEnclosing, serializeEnclosing: serializeEnclosing,
kind: kind, serializeMembers: () {
localMembersSorted(e).forEach((Element member) {
// Filter out members that don't belong to this "side".
if (member.isConstructor) {
// In dart2js, some constructors aren't static, but that isn't
// convenient here.
if (!isStatic) return;
} else if (member.isStatic != isStatic) {
return;
}
if (!isFirst) {
buffer.write(',');
}
buffer.write('\n');
indented;
serialize(member);
isFirst = false;
});
});
}
void visitScopeContainerElement(ScopeContainerElement e, _) {
bool isFirst = true;
serialize(e, omitEnclosing: false, serializeMembers: () {
localMembersSorted(e).forEach((Element member) {
if (!isFirst) {
buffer.write(',');
}
buffer.write('\n');
indented;
serialize(member);
isFirst = false;
});
});
}
void visitCompilationUnitElement(CompilationUnitElement e, _) {
e.enclosingElement.accept(this, _);
}
void visitAbstractFieldElement(AbstractFieldElement e, _) {
throw new UnsupportedError('AbstractFieldElement cannot be serialized.');
}
void serialize(
Element element,
{bool omitEnclosing: true,
void serializeMembers(),
void serializeEnclosing(),
String kind,
String name}) {
if (element.isAbstractField) {
AbstractFieldElement field = element;
FunctionElement getter = field.getter;
FunctionElement setter = field.setter;
if (getter != null) {
serialize(
getter,
omitEnclosing: omitEnclosing,
serializeMembers: serializeMembers,
serializeEnclosing: serializeEnclosing,
kind: kind,
name: name);
}
if (setter != null) {
if (getter != null) {
buffer.write(',\n');
indented;
}
serialize(
getter,
omitEnclosing: omitEnclosing,
serializeMembers: serializeMembers,
serializeEnclosing: serializeEnclosing,
kind: kind,
name: name);
}
return;
}
DartType type;
int category = element.kind.category;
if (category == ElementCategory.FUNCTION ||
category == ElementCategory.VARIABLE ||
element.isConstructor) {
type = element.computeType(compiler.resolution);
}
if (name == null) {
name = element.name;
}
if (kind == null) {
kind = '${element.kind}';
}
buffer.write('{\n');
indentationLevel++;
if (name != '') {
indented
..write('"name": "')
..write(name)
..write('",\n');
}
indented
..write('"kind": "')
..write(kind)
..write('"');
if (type != null) {
buffer.write(',\n');
indented
..write('"type": "')
..write(type)
..write('"');
}
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": ');
if (serializeEnclosing != null) {
serializeEnclosing();
} else {
element.enclosingElement.accept(this, null);
}
}
indentationLevel--;
buffer.write('\n');
indented.write('}');
}
List<Element> localMembersSorted(ScopeContainerElement element) {
List<Element> result = <Element>[];
element.forEachLocalMember((Element member) {
result.add(member);
});
return sortElements(result);
}
List<Element> sortElements(Iterable<Element> elements) {
List<Element> result = new List<Element>.from(elements);
if (sortMembers) {
result.sort((Element a, Element b) => a.name.compareTo(b.name));
}
return result;
}
}
modelx.ScopeX localScope(modelx.LibraryElementX element) => element.localScope;
modelx.ImportScope importScope(modelx.LibraryElementX element) {
return element.importScope;
}