215 lines
6.1 KiB
Dart
215 lines
6.1 KiB
Dart
library angular.symbol_inspector.symbol_inspector;
|
|
|
|
import 'dart:mirrors';
|
|
import './simple_library.dart' as simple_library;
|
|
import 'package:angular2/angular2.dart' as angular2;
|
|
|
|
const IGNORE = const {
|
|
'runtimeType': true,
|
|
'toString': true,
|
|
'noSuchMethod': true,
|
|
'hashCode': true,
|
|
'originalException': true,
|
|
'originalStack': true
|
|
};
|
|
|
|
const LIB_MAP = const {
|
|
'simple_library': 'angular2.test.symbol_inspector.simple_library',
|
|
'ng': 'angular2'
|
|
};
|
|
|
|
// Have this list here to trick dart to force import.
|
|
var libs = [simple_library.A, angular2.Component];
|
|
|
|
List<String> getSymbolsFromLibrary(String name) {
|
|
var libraryName = LIB_MAP[name];
|
|
if (libs.isEmpty) throw "No libriries loaded.";
|
|
if (libraryName == null) throw "Don't know how to load '$name' library.";
|
|
var lib = currentMirrorSystem().findLibrary(new Symbol(libraryName));
|
|
var names = [];
|
|
extractSymbols(lib).addTo(names);
|
|
names.sort();
|
|
// remove duplicates;
|
|
var lastValue;
|
|
names = names.where((v) {
|
|
var duplicate = v == lastValue;
|
|
lastValue = v;
|
|
return !duplicate;
|
|
}).toList();
|
|
return names;
|
|
}
|
|
|
|
class ExportedSymbol {
|
|
Symbol symbol;
|
|
DeclarationMirror declaration;
|
|
LibraryMirror library;
|
|
|
|
ExportedSymbol(this.symbol, this.declaration, this.library);
|
|
|
|
addTo(List<String> names) {
|
|
var name = unwrapSymbol(symbol);
|
|
if (declaration is MethodMirror) {
|
|
names.add('$name()');
|
|
} else if (declaration is ClassMirror) {
|
|
var classMirror = declaration as ClassMirror;
|
|
if (classMirror.isAbstract) name = '{$name}';
|
|
names.add(name);
|
|
classMirror.staticMembers.forEach(members('$name#', names));
|
|
classMirror.instanceMembers.forEach(members('$name.', names));
|
|
} else if (declaration is TypedefMirror) {
|
|
names.add(name);
|
|
} else if (declaration is VariableMirror) {
|
|
names.add(name);
|
|
} else {
|
|
throw 'UNEXPECTED: $declaration';
|
|
}
|
|
}
|
|
|
|
toString() => unwrapSymbol(symbol);
|
|
}
|
|
|
|
members(String prefix, List<String> names) {
|
|
return (Symbol symbol, MethodMirror method) {
|
|
var name = unwrapSymbol(symbol);
|
|
if (method.isOperator || method.isPrivate || IGNORE[name] == true) return;
|
|
var suffix = (method.isSetter || method.isGetter) ? '' : '()';
|
|
names.add('$prefix$name$suffix');
|
|
};
|
|
}
|
|
|
|
class LibraryInfo {
|
|
List<ExportedSymbol> names;
|
|
Map<Symbol, List<Symbol>> symbolsUsedForName;
|
|
|
|
LibraryInfo(this.names, this.symbolsUsedForName);
|
|
|
|
addTo(List<String> names) {
|
|
this.names.forEach((ExportedSymbol es) => es.addTo(names));
|
|
//this.names.addAll(symbolsUsedForName.keys.map(unwrapSymbol));
|
|
}
|
|
}
|
|
|
|
Iterable<Symbol> _getUsedSymbols(DeclarationMirror decl, seenDecls, path, onlyType) {
|
|
|
|
if (seenDecls.containsKey(decl.qualifiedName)) return [];
|
|
seenDecls[decl.qualifiedName] = true;
|
|
|
|
if (decl.isPrivate) return [];
|
|
|
|
path = "$path -> $decl";
|
|
|
|
var used = [];
|
|
|
|
if (decl is TypedefMirror) {
|
|
TypedefMirror tddecl = decl;
|
|
used.addAll(_getUsedSymbols(tddecl.referent, seenDecls, path, onlyType));
|
|
}
|
|
if (decl is FunctionTypeMirror) {
|
|
FunctionTypeMirror ftdecl = decl;
|
|
|
|
ftdecl.parameters.forEach((ParameterMirror p) {
|
|
used.addAll(_getUsedSymbols(p.type, seenDecls, path, onlyType));
|
|
});
|
|
used.addAll(_getUsedSymbols(ftdecl.returnType, seenDecls, path, onlyType));
|
|
}
|
|
else if (decl is TypeMirror) {
|
|
var tdecl = decl;
|
|
used.add(tdecl.qualifiedName);
|
|
}
|
|
|
|
|
|
if (!onlyType) {
|
|
if (decl is ClassMirror) {
|
|
ClassMirror cdecl = decl;
|
|
cdecl.declarations.forEach((s, d) {
|
|
try {
|
|
used.addAll(_getUsedSymbols(d, seenDecls, path, false));
|
|
} catch (e, s) {
|
|
print("Got error [$e] when visiting $d\n$s");
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
if (decl is MethodMirror) {
|
|
MethodMirror mdecl = decl;
|
|
if (mdecl.parameters != null)
|
|
mdecl.parameters.forEach((p) {
|
|
used.addAll(_getUsedSymbols(p.type, seenDecls, path, true));
|
|
});
|
|
used.addAll(_getUsedSymbols(mdecl.returnType, seenDecls, path, true));
|
|
}
|
|
|
|
if (decl is VariableMirror) {
|
|
VariableMirror vdecl = decl;
|
|
used.addAll(_getUsedSymbols(vdecl.type, seenDecls, path, true));
|
|
}
|
|
}
|
|
|
|
// Strip out type variables.
|
|
if (decl is TypeMirror) {
|
|
TypeMirror tdecl = decl;
|
|
var typeVariables = tdecl.typeVariables.map((tv) => tv.qualifiedName);
|
|
used = used.where((x) => !typeVariables.contains(x));
|
|
}
|
|
|
|
return used;
|
|
}
|
|
|
|
LibraryInfo extractSymbols(LibraryMirror lib, [String printPrefix = ""]) {
|
|
List<ExportedSymbol> exportedSymbols = [];
|
|
Map<Symbol, List<Symbol>> used = {};
|
|
|
|
printPrefix += " ";
|
|
lib.declarations.forEach((Symbol symbol, DeclarationMirror decl) {
|
|
if (decl.isPrivate) return;
|
|
|
|
// Work-around for dartbug.com/18271
|
|
if (decl is TypedefMirror && unwrapSymbol(symbol).startsWith('_')) return;
|
|
|
|
exportedSymbols.add(new ExportedSymbol(symbol, decl, lib));
|
|
used[decl.qualifiedName] = _getUsedSymbols(decl, {}, "", false);
|
|
});
|
|
|
|
lib.libraryDependencies.forEach((LibraryDependencyMirror libDep) {
|
|
LibraryMirror target = libDep.targetLibrary;
|
|
if (!libDep.isExport) return;
|
|
|
|
var childInfo = extractSymbols(target, printPrefix);
|
|
var childNames = childInfo.names;
|
|
|
|
// If there was a "show" or "hide" on the exported library, filter the results.
|
|
// This API needs love :-(
|
|
var showSymbols = [], hideSymbols = [];
|
|
libDep.combinators.forEach((CombinatorMirror c) {
|
|
if (c.isShow) {
|
|
showSymbols.addAll(c.identifiers);
|
|
}
|
|
if (c.isHide) {
|
|
hideSymbols.addAll(c.identifiers);
|
|
}
|
|
});
|
|
|
|
// I don't think you can show and hide from the same library
|
|
assert(showSymbols.isEmpty || hideSymbols.isEmpty);
|
|
if (!showSymbols.isEmpty) {
|
|
childNames = childNames.where((symAndLib) {
|
|
return showSymbols.contains(symAndLib.symbol);
|
|
});
|
|
}
|
|
if (!hideSymbols.isEmpty) {
|
|
childNames = childNames.where((symAndLib) {
|
|
return !hideSymbols.contains(symAndLib.symbol);
|
|
});
|
|
}
|
|
|
|
exportedSymbols.addAll(childNames);
|
|
used.addAll(childInfo.symbolsUsedForName);
|
|
});
|
|
return new LibraryInfo(exportedSymbols, used);
|
|
}
|
|
|
|
|
|
var _SYMBOL_NAME = new RegExp('"(.*)"');
|
|
unwrapSymbol(sym) => _SYMBOL_NAME.firstMatch(sym.toString()).group(1);
|