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);
 |