| Index: third_party/pkg/angular/lib/tools/symbol_inspector/symbol_inspector.dart
|
| diff --git a/third_party/pkg/angular/lib/tools/symbol_inspector/symbol_inspector.dart b/third_party/pkg/angular/lib/tools/symbol_inspector/symbol_inspector.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..313864d3c57016bd63cf28cedda118c927f6ff61
|
| --- /dev/null
|
| +++ b/third_party/pkg/angular/lib/tools/symbol_inspector/symbol_inspector.dart
|
| @@ -0,0 +1,215 @@
|
| +library angular.tools.symbol_inspector;
|
| +
|
| +import 'dart:mirrors';
|
| +
|
| +class QualifiedSymbol {
|
| + Symbol symbol;
|
| + Symbol qualified;
|
| + Symbol libraryName;
|
| +
|
| + QualifiedSymbol(this.symbol, this.qualified, this.libraryName);
|
| +
|
| + toString() => "QS($qualified)";
|
| +}
|
| +
|
| +class LibraryInfo {
|
| + List<QualifiedSymbol> names;
|
| + Map<Symbol, List<Symbol>> symbolsUsedForName;
|
| +
|
| + LibraryInfo(this.names, this.symbolsUsedForName);
|
| +}
|
| +
|
| +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) {
|
| + var tddecl = decl as TypedefMirror;
|
| + used.addAll(_getUsedSymbols(tddecl.referent, seenDecls, path, onlyType));
|
| + }
|
| + if (decl is FunctionTypeMirror) {
|
| + var ftdecl = decl as FunctionTypeMirror;
|
| +
|
| + 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 as TypeMirror;
|
| + used.add(tdecl.qualifiedName);
|
| + }
|
| +
|
| +
|
| + if (!onlyType) {
|
| + if (decl is ClassMirror) {
|
| + var cdecl = decl as ClassMirror;
|
| + 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) {
|
| + var mdecl = decl as MethodMirror;
|
| + 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) {
|
| + var vdecl = decl as VariableMirror;
|
| + used.addAll(_getUsedSymbols(vdecl.type, seenDecls, path, true));
|
| + }
|
| + }
|
| +
|
| + // Strip out type variables.
|
| + if (decl is TypeMirror) {
|
| + var tdecl = decl as TypeMirror;
|
| + var typeVariables = tdecl.typeVariables.map((tv) => tv.qualifiedName);
|
| + used = used.where((x) => !typeVariables.contains(x));
|
| + }
|
| +
|
| + return used;
|
| +}
|
| +
|
| +getSymbolsFromLibrary(String libraryName) {
|
| +// Set this to true to see how symbols are exported from angular.
|
| + var SHOULD_PRINT_SYMBOL_TREE = false;
|
| +
|
| +// TODO(deboer): Add types once Dart VM 1.2 is deprecated.
|
| + LibraryInfo extractSymbols(/* LibraryMirror */ lib, [String printPrefix = ""]) {
|
| + List<QualifiedSymbol> names = [];
|
| + Map<Symbol, List<Symbol>> used = {};
|
| +
|
| + if (SHOULD_PRINT_SYMBOL_TREE) print(printPrefix + unwrapSymbol(lib.qualifiedName));
|
| + printPrefix += " ";
|
| + lib.declarations.forEach((symbol, decl) {
|
| + if (decl.isPrivate) return;
|
| +
|
| + // Work-around for dartbug.com/18271
|
| + if (decl is TypedefMirror && unwrapSymbol(symbol).startsWith('_')) return;
|
| +
|
| + if (SHOULD_PRINT_SYMBOL_TREE) print(printPrefix + unwrapSymbol(symbol));
|
| + names.add(new QualifiedSymbol(symbol, decl.qualifiedName, lib.qualifiedName));
|
| + 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);
|
| + });
|
| + }
|
| +
|
| + names.addAll(childNames);
|
| + used.addAll(childInfo.symbolsUsedForName);
|
| + });
|
| + return new LibraryInfo(names, used);
|
| + };
|
| +
|
| + var lib = currentMirrorSystem().findLibrary(new Symbol(libraryName));
|
| + return extractSymbols(lib);
|
| +}
|
| +
|
| +var _SYMBOL_NAME = new RegExp('"(.*)"');
|
| +unwrapSymbol(sym) => _SYMBOL_NAME.firstMatch(sym.toString()).group(1);
|
| +
|
| +assertSymbolNamesAreOk(List<String> allowedNames, LibraryInfo libraryInfo) {
|
| + var _nameMap = {};
|
| + var _qualifiedNameMap = {};
|
| +
|
| + allowedNames.forEach((x) => _nameMap[x] = true);
|
| +
|
| + libraryInfo.names.forEach((x) => _qualifiedNameMap[x.qualified] = true);
|
| +
|
| + var usedButNotExported = {};
|
| + var exported = [];
|
| +
|
| +
|
| + libraryInfo.names.forEach((nameInfo) {
|
| + String name = unwrapSymbol(nameInfo.qualified);
|
| + String libName = unwrapSymbol(nameInfo.libraryName);
|
| +
|
| + var key = "$name";
|
| + if (_nameMap.containsKey(key)) {
|
| + _nameMap[key] = false;
|
| +
|
| + // Check that all the exposed types are also exported
|
| + assert(libraryInfo.symbolsUsedForName.containsKey(nameInfo.qualified));
|
| + libraryInfo.symbolsUsedForName[nameInfo.qualified].forEach((usedSymbol) {
|
| + if ("$usedSymbol".contains('"dart.')) return;
|
| + if ("$usedSymbol" == 'Symbol("dynamic")') return;
|
| + if ("$usedSymbol" == 'Symbol("void")') return;
|
| +
|
| + if (!_qualifiedNameMap.containsKey(usedSymbol)) {
|
| + usedButNotExported.putIfAbsent(usedSymbol, () => []);
|
| + usedButNotExported[usedSymbol].add(nameInfo.qualified);
|
| + }
|
| + });
|
| + return;
|
| + }
|
| +
|
| + exported.add(key);
|
| + });
|
| + if (exported.isNotEmpty) {
|
| + throw "These symbols are exported thru the angular library, but it shouldn't be:\n"
|
| + "${exported.join('\n')}";
|
| + }
|
| +
|
| + bool needHeader = true;
|
| + usedButNotExported.forEach((used, locs) {
|
| + print(" ${unwrapSymbol(used)} : unexported, used from:");
|
| + locs.forEach((l) {
|
| + print(" ${unwrapSymbol(l)}");
|
| + });
|
| + print("");
|
| + });
|
| +
|
| + // If there are keys that no longer need to be in the ALLOWED_NAMES list, complain.
|
| + var keys = [];
|
| + _nameMap.forEach((k,v) {
|
| + if (v) keys.add(k);
|
| + });
|
| + if (keys.isNotEmpty) {
|
| + throw "These whitelisted symbols are not used:\n${keys.join('\n')}";
|
| + }
|
| +}
|
|
|