blob: 5aa71debeb7f74c3ee64e8ff0c09d3ee8f0bcad7 [file] [log] [blame]
// Copyright (c) 2012, 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 dart2js.mirrors;
import 'dart:collection' show UnmodifiableListView, UnmodifiableMapView;
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../elements/elements.dart';
import '../scanner/scannerlib.dart';
import '../resolution/resolution.dart' show Scope;
import '../dart2jslib.dart';
import '../dart_types.dart';
import '../tree/tree.dart';
import '../util/util.dart'
show Link,
LinkBuilder;
import '../util/characters.dart' show $CR, $LF;
import 'source_mirrors.dart';
import 'mirrors_util.dart';
part 'dart2js_library_mirror.dart';
part 'dart2js_type_mirrors.dart';
part 'dart2js_member_mirrors.dart';
part 'dart2js_instance_mirrors.dart';
//------------------------------------------------------------------------------
// Utility types and functions for the dart2js mirror system
//------------------------------------------------------------------------------
bool _includeLibrary(Dart2JsLibraryMirror mirror) {
return const bool.fromEnvironment("list_all_libraries") ||
!mirror._element.isInternalLibrary;
}
bool _isPrivate(String name) {
return name.startsWith('_');
}
List<ParameterMirror> _parametersFromFunctionSignature(
Dart2JsDeclarationMirror owner,
FunctionSignature signature) {
var parameters = <ParameterMirror>[];
Link<Element> link = signature.requiredParameters;
while (!link.isEmpty) {
parameters.add(new Dart2JsParameterMirror(
owner, link.head, isOptional: false, isNamed: false));
link = link.tail;
}
link = signature.optionalParameters;
bool isNamed = signature.optionalParametersAreNamed;
while (!link.isEmpty) {
parameters.add(new Dart2JsParameterMirror(
owner, link.head, isOptional: true, isNamed: isNamed));
link = link.tail;
}
return parameters;
}
MethodMirror _convertElementMethodToMethodMirror(
Dart2JsDeclarationMirror library, Element element) {
if (element is FunctionElement) {
return new Dart2JsMethodMirror(library, element);
} else {
return null;
}
}
//------------------------------------------------------------------------------
// Dart2Js specific extensions of mirror interfaces
//------------------------------------------------------------------------------
abstract class Dart2JsMirror implements Mirror {
Dart2JsMirrorSystem get mirrorSystem;
}
abstract class Dart2JsDeclarationMirror extends Dart2JsMirror
implements DeclarationSourceMirror {
bool get isTopLevel => owner != null && owner is LibraryMirror;
bool get isPrivate => _isPrivate(_simpleNameString);
String get _simpleNameString;
String get _qualifiedNameString {
var parent = owner;
if (parent is Dart2JsDeclarationMirror) {
return '${parent._qualifiedNameString}.${_simpleNameString}';
}
assert(parent == null);
return _simpleNameString;
}
Symbol get simpleName => symbolOf(_simpleNameString, getLibrary(this));
Symbol get qualifiedName => symbolOf(_qualifiedNameString, getLibrary(this));
DeclarationMirror lookupInScope(String name) => null;
bool get isNameSynthetic => false;
/// Returns the type mirror for [type] in the context of this declaration.
TypeMirror _getTypeMirror(DartType type) {
return mirrorSystem._convertTypeToTypeMirror(type);
}
/// Returns a list of the declaration mirrorSystem for [element].
Iterable<Dart2JsMemberMirror> _getDeclarationMirrors(Element element) {
if (element.isSynthesized) {
return const <Dart2JsMemberMirror>[];
} else if (element is VariableElement) {
return <Dart2JsMemberMirror>[new Dart2JsFieldMirror(this, element)];
} else if (element is FunctionElement) {
return <Dart2JsMemberMirror>[new Dart2JsMethodMirror(this, element)];
} else if (element is AbstractFieldElement) {
var members = <Dart2JsMemberMirror>[];
AbstractFieldElement field = element;
if (field.getter != null) {
members.add(new Dart2JsMethodMirror(this, field.getter));
}
if (field.setter != null) {
members.add(new Dart2JsMethodMirror(this, field.setter));
}
return members;
}
mirrorSystem.compiler.internalError(element,
"Unexpected member type $element ${element.kind}.");
return null;
}
}
abstract class Dart2JsElementMirror extends Dart2JsDeclarationMirror {
final Dart2JsMirrorSystem mirrorSystem;
final Element _element;
List<InstanceMirror> _metadata;
Dart2JsElementMirror(this.mirrorSystem, this._element) {
assert (mirrorSystem != null);
assert (_element != null);
}
String get _simpleNameString => _element.name;
/**
* Computes the first token for this declaration using the begin token of the
* element node or element position as indicator.
*/
Token getBeginToken() {
Element element = _element;
if (element is AstElement) {
Node node = element.node;
if (node != null) {
return node.getBeginToken();
}
}
return element.position;
}
/**
* Computes the last token for this declaration using the end token of the
* element node or element position as indicator.
*/
Token getEndToken() {
Element element = _element;
if (element is AstElement) {
Node node = element.node;
if (node != null) {
return node.getEndToken();
}
}
return element.position;
}
/**
* Returns the first token for the source of this declaration, including
* metadata annotations.
*/
Token getFirstToken() {
if (!_element.metadata.isEmpty) {
for (MetadataAnnotation metadata in _element.metadata) {
if (metadata.beginToken != null) {
return metadata.beginToken;
}
}
}
return getBeginToken();
}
Script getScript() => _element.compilationUnit.script;
SourceLocation get location {
Token beginToken = getFirstToken();
Script script = getScript();
SourceSpan span;
if (beginToken == null) {
span = new SourceSpan(script.readableUri, 0, 0);
} else {
Token endToken = getEndToken();
span = mirrorSystem.compiler.spanFromTokens(
beginToken, endToken, script.readableUri);
}
return new Dart2JsSourceLocation(script, span);
}
String toString() => _element.toString();
void _appendCommentTokens(Token commentToken) {
while (commentToken != null && commentToken.kind == COMMENT_TOKEN) {
_metadata.add(new Dart2JsCommentInstanceMirror(
mirrorSystem, commentToken.value));
commentToken = commentToken.next;
}
}
List<InstanceMirror> get metadata {
if (_metadata == null) {
_metadata = <InstanceMirror>[];
for (MetadataAnnotation metadata in _element.metadata) {
_appendCommentTokens(
mirrorSystem.compiler.commentMap[metadata.beginToken]);
metadata.ensureResolved(mirrorSystem.compiler);
_metadata.add(_convertConstantToInstanceMirror(
mirrorSystem, metadata.constant, metadata.constant.value));
}
_appendCommentTokens(mirrorSystem.compiler.commentMap[getBeginToken()]);
}
// TODO(johnniwinther): Return an unmodifiable list instead.
return new List<InstanceMirror>.from(_metadata);
}
DeclarationMirror lookupInScope(String name) {
// TODO(11653): Support lookup of constructors.
Scope scope = _element.buildScope();
Element result;
int index = name.indexOf('.');
if (index != -1) {
// Lookup [: prefix.id :].
String prefix = name.substring(0, index);
String id = name.substring(index+1);
result = scope.lookup(prefix);
if (result != null && result.isPrefix) {
PrefixElement prefix = result;
result = prefix.lookupLocalMember(id);
} else {
result = null;
}
} else {
// Lookup [: id :].
result = scope.lookup(name);
}
if (result == null || result.isPrefix) return null;
return _convertElementToDeclarationMirror(mirrorSystem, result);
}
bool operator ==(var other) {
if (identical(this, other)) return true;
if (other == null) return false;
if (other is! Dart2JsElementMirror) return false;
return _element == other._element &&
owner == other.owner;
}
int get hashCode {
return 13 * _element.hashCode + 17 * owner.hashCode;
}
}
//------------------------------------------------------------------------------
// Mirror system implementation.
//------------------------------------------------------------------------------
class Dart2JsMirrorSystem extends MirrorSystem {
final Compiler compiler;
Map<LibraryElement, Dart2JsLibraryMirror> _libraryMap;
UnmodifiableMapView<Uri, LibraryMirror> _filteredLibraries;
Dart2JsMirrorSystem(this.compiler);
IsolateMirror get isolate => null;
void _ensureLibraries() {
if (_filteredLibraries == null) {
var filteredLibs = new Map<Uri, LibraryMirror>();
_libraryMap = new Map<LibraryElement, Dart2JsLibraryMirror>();
compiler.libraryLoader.libraries.forEach((LibraryElement v) {
var mirror = new Dart2JsLibraryMirror(mirrorSystem, v);
if (_includeLibrary(mirror)) {
filteredLibs[mirror.uri] = mirror;
}
_libraryMap[v] = mirror;
});
_filteredLibraries =
new UnmodifiableMapView<Uri, LibraryMirror>(filteredLibs);
}
}
Map<Uri, LibraryMirror> get libraries {
_ensureLibraries();
return _filteredLibraries;
}
Dart2JsLibraryMirror _getLibrary(LibraryElement element) =>
_libraryMap[element];
Dart2JsMirrorSystem get mirrorSystem => this;
TypeMirror get dynamicType =>
_convertTypeToTypeMirror(const DynamicType());
TypeMirror get voidType =>
_convertTypeToTypeMirror(const VoidType());
TypeMirror _convertTypeToTypeMirror(DartType type) {
assert(type != null);
if (type.treatAsDynamic) {
return new Dart2JsDynamicMirror(this, type);
} else if (type is InterfaceType) {
if (type.typeArguments.isEmpty) {
return _getTypeDeclarationMirror(type.element);
} else {
return new Dart2JsInterfaceTypeMirror(this, type);
}
} else if (type is TypeVariableType) {
return new Dart2JsTypeVariableMirror(this, type);
} else if (type is FunctionType) {
return new Dart2JsFunctionTypeMirror(this, type);
} else if (type is VoidType) {
return new Dart2JsVoidMirror(this, type);
} else if (type is TypedefType) {
if (type.typeArguments.isEmpty) {
return _getTypeDeclarationMirror(type.element);
} else {
return new Dart2JsTypedefMirror(this, type);
}
}
compiler.internalError(type.element,
"Unexpected type $type of kind ${type.kind}.");
return null;
}
DeclarationMirror _getTypeDeclarationMirror(TypeDeclarationElement element) {
if (element.isClass) {
return new Dart2JsClassDeclarationMirror(this, element.thisType);
} else if (element.isTypedef) {
return new Dart2JsTypedefDeclarationMirror(this, element.thisType);
}
compiler.internalError(element, "Unexpected element $element.");
return null;
}
}
abstract class ContainerMixin {
UnmodifiableMapView<Symbol, DeclarationMirror> _declarations;
void _ensureDeclarations() {
if (_declarations == null) {
var declarations = <Symbol, DeclarationMirror>{};
_forEachElement((Element element) {
for (DeclarationMirror mirror in _getDeclarationMirrors(element)) {
assert(invariant(_element,
!declarations.containsKey(mirror.simpleName),
message: "Declaration name '${nameOf(mirror)}' "
"is not unique in $_element."));
declarations[mirror.simpleName] = mirror;
}
});
_declarations =
new UnmodifiableMapView<Symbol, DeclarationMirror>(declarations);
}
}
Element get _element;
void _forEachElement(f(Element element));
Iterable<Dart2JsMemberMirror> _getDeclarationMirrors(Element element);
Map<Symbol, DeclarationMirror> get declarations {
_ensureDeclarations();
return _declarations;
}
}
/**
* Converts [element] into its corresponding [DeclarationMirror], if any.
*
* If [element] is an [AbstractFieldElement] the mirror for its getter is
* returned or, if not present, the mirror for its setter.
*/
DeclarationMirror _convertElementToDeclarationMirror(Dart2JsMirrorSystem system,
Element element) {
if (element.isTypeVariable) {
TypeVariableElement typeVariable = element;
return new Dart2JsTypeVariableMirror(system, typeVariable.type);
}
Dart2JsLibraryMirror library = system._libraryMap[element.library];
if (element.isLibrary) return library;
if (element.isTypedef) {
TypedefElement typedefElement = element;
return new Dart2JsTypedefMirror.fromLibrary(
library, typedefElement.thisType);
}
Dart2JsDeclarationMirror container = library;
if (element.enclosingClass != null) {
container = system._getTypeDeclarationMirror(element.enclosingClass);
}
if (element.isClass) return container;
if (element.isParameter) {
Dart2JsMethodMirror method = _convertElementMethodToMethodMirror(
container, element.outermostEnclosingMemberOrTopLevel);
// TODO(johnniwinther): Find the right info for [isOptional] and [isNamed].
return new Dart2JsParameterMirror(
method, element, isOptional: false, isNamed: false);
}
Iterable<DeclarationMirror> members =
container._getDeclarationMirrors(element);
if (members.isEmpty) return null;
return members.first;
}
/**
* Experimental API for accessing compilation units defined in a
* library.
*/
// TODO(ahe): Superclasses? Is this really a mirror?
class Dart2JsCompilationUnitMirror extends Dart2JsMirror with ContainerMixin {
final Dart2JsLibraryMirror _library;
final CompilationUnitElement _element;
Dart2JsCompilationUnitMirror(this._element, this._library);
Dart2JsMirrorSystem get mirrorSystem => _library.mirrorSystem;
// TODO(johnniwinther): make sure that these are returned in declaration
// order.
void _forEachElement(f(Element element)) => _element.forEachLocalMember(f);
Iterable<DeclarationMirror> _getDeclarationMirrors(Element element) =>
_library._getDeclarationMirrors(element);
Uri get uri => _element.script.resourceUri;
}
/**
* Transitional class that allows access to features that have not yet
* made it to the mirror API.
*
* All API in this class is experimental.
*/
class BackDoor {
/// Return the compilation units comprising [library].
static List<Mirror> compilationUnitsOf(Dart2JsLibraryMirror library) {
return library._element.compilationUnits.mapToList(
(cu) => new Dart2JsCompilationUnitMirror(cu, library));
}
static Iterable<ConstantExpression> metadataSyntaxOf(
Dart2JsElementMirror declaration) {
return declaration._element.metadata.map((metadata) => metadata.constant);
}
static ConstantExpression initializerSyntaxOf(Dart2JsFieldMirror variable) {
Compiler compiler = variable.mirrorSystem.compiler;
return compiler.constants.getConstantForVariable(variable._variable);
}
static ConstantExpression defaultValueSyntaxOf(
Dart2JsParameterMirror parameter) {
if (!parameter.hasDefaultValue) return null;
ParameterElement parameterElement = parameter._element;
Compiler compiler = parameter.mirrorSystem.compiler;
return compiler.constants.getConstantForVariable(parameterElement);
}
static Mirror getMirrorFromElement(Dart2JsMirror mirror, Element element) {
return _convertElementToDeclarationMirror(mirror.mirrorSystem, element);
}
}