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