| OLD | NEW | 
 | (Empty) | 
|    1 // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file |  | 
|    2 // for details. All rights reserved. Use of this source code is governed by a |  | 
|    3 // BSD-style license that can be found in the LICENSE file. |  | 
|    4  |  | 
|    5 /// Static implementation of smoke services using code-generated data. |  | 
|    6 library smoke.static; |  | 
|    7  |  | 
|    8 import 'dart:math' as math; |  | 
|    9  |  | 
|   10 import 'package:smoke/smoke.dart'; |  | 
|   11  |  | 
|   12 import 'src/common.dart'; |  | 
|   13  |  | 
|   14 typedef T Getter<T>(object); |  | 
|   15 typedef void Setter<T>(object, value); |  | 
|   16  |  | 
|   17 class StaticConfiguration { |  | 
|   18   /// Maps symbol to a function that reads that symbol of an object. For |  | 
|   19   /// instance, `#i: (o) => o.i`. |  | 
|   20   final Map<Symbol, Getter> getters; |  | 
|   21  |  | 
|   22   /// Maps symbol to a function that updates that symbol of an object. For |  | 
|   23   /// instance, `#i: (o, v) { o.i = v; }`. |  | 
|   24   final Map<Symbol, Setter> setters; |  | 
|   25  |  | 
|   26   /// Maps a type to its super class. For example, String: Object. |  | 
|   27   final Map<Type, Type> parents; |  | 
|   28  |  | 
|   29   /// For each type, a map of declarations per symbol (property or method). |  | 
|   30   final Map<Type, Map<Symbol, Declaration>> declarations; |  | 
|   31  |  | 
|   32   /// Static methods for each type. |  | 
|   33   // TODO(sigmund): should we add static getters & setters too? |  | 
|   34   final Map<Type, Map<Symbol, Function>> staticMethods; |  | 
|   35  |  | 
|   36   /// A map from symbol to strings. |  | 
|   37   final Map<Symbol, String> names; |  | 
|   38  |  | 
|   39   /// A map from strings to symbols (the reverse of [names]). |  | 
|   40   final Map<String, Symbol> _symbols = {}; |  | 
|   41  |  | 
|   42   /// Whether to check for missing declarations, otherwise, return default |  | 
|   43   /// values (for example a missing parent class can be treated as Object) |  | 
|   44   final bool checkedMode; |  | 
|   45  |  | 
|   46   StaticConfiguration({Map<Symbol, Getter> getters, Map<Symbol, Setter> setters, |  | 
|   47       Map<Type, Type> parents, Map<Type, Map<Symbol, Declaration>> declarations, |  | 
|   48       Map<Type, Map<Symbol, Function>> staticMethods, Map<Symbol, String> names, |  | 
|   49       this.checkedMode: true}) |  | 
|   50       : getters = getters != null ? getters : {}, |  | 
|   51         setters = setters != null ? setters : {}, |  | 
|   52         parents = parents != null ? parents : {}, |  | 
|   53         declarations = declarations != null ? declarations : {}, |  | 
|   54         staticMethods = staticMethods != null ? staticMethods : {}, |  | 
|   55         names = names != null ? names : {} { |  | 
|   56     this.names.forEach((k, v) { |  | 
|   57       _symbols[v] = k; |  | 
|   58     }); |  | 
|   59   } |  | 
|   60  |  | 
|   61   void addAll(StaticConfiguration other) { |  | 
|   62     getters.addAll(other.getters); |  | 
|   63     setters.addAll(other.setters); |  | 
|   64     parents.addAll(other.parents); |  | 
|   65     _nestedAddAll(declarations, other.declarations); |  | 
|   66     _nestedAddAll(staticMethods, other.staticMethods); |  | 
|   67     names.addAll(other.names); |  | 
|   68     other.names.forEach((k, v) { |  | 
|   69       _symbols[v] = k; |  | 
|   70     }); |  | 
|   71   } |  | 
|   72  |  | 
|   73   static _nestedAddAll(Map a, Map b) { |  | 
|   74     for (var key in b.keys) { |  | 
|   75       a.putIfAbsent(key, () => {}); |  | 
|   76       a[key].addAll(b[key]); |  | 
|   77     } |  | 
|   78   } |  | 
|   79 } |  | 
|   80  |  | 
|   81 /// Set up the smoke package to use a static implementation based on the given |  | 
|   82 /// [configuration]. |  | 
|   83 useGeneratedCode(StaticConfiguration configuration) { |  | 
|   84   configure(new GeneratedObjectAccessorService(configuration), |  | 
|   85       new GeneratedTypeInspectorService(configuration), |  | 
|   86       new GeneratedSymbolConverterService(configuration)); |  | 
|   87 } |  | 
|   88  |  | 
|   89 /// Implements [ObjectAccessorService] using a static configuration. |  | 
|   90 class GeneratedObjectAccessorService implements ObjectAccessorService { |  | 
|   91   final StaticConfiguration _configuration; |  | 
|   92   Map<Symbol, Getter> get _getters => _configuration.getters; |  | 
|   93   Map<Symbol, Setter> get _setters => _configuration.setters; |  | 
|   94   Map<Type, Map<Symbol, Function>> get _staticMethods => |  | 
|   95       _configuration.staticMethods; |  | 
|   96  |  | 
|   97   GeneratedObjectAccessorService(this._configuration); |  | 
|   98  |  | 
|   99   read(Object object, Symbol name) { |  | 
|  100     var getter = _getters[name]; |  | 
|  101     if (getter == null) { |  | 
|  102       throw new MissingCodeException('getter "$name" in $object'); |  | 
|  103     } |  | 
|  104     return getter(object); |  | 
|  105   } |  | 
|  106  |  | 
|  107   void write(Object object, Symbol name, value) { |  | 
|  108     var setter = _setters[name]; |  | 
|  109     if (setter == null) { |  | 
|  110       throw new MissingCodeException('setter "$name" in $object'); |  | 
|  111     } |  | 
|  112     setter(object, value); |  | 
|  113   } |  | 
|  114  |  | 
|  115   invoke(object, Symbol name, List args, {Map namedArgs, bool adjust: false}) { |  | 
|  116     var method; |  | 
|  117     if (object is Type && name != #toString) { |  | 
|  118       var classMethods = _staticMethods[object]; |  | 
|  119       method = classMethods == null ? null : classMethods[name]; |  | 
|  120     } else { |  | 
|  121       var getter = _getters[name]; |  | 
|  122       method = getter == null ? null : getter(object); |  | 
|  123     } |  | 
|  124     if (method == null) { |  | 
|  125       throw new MissingCodeException('method "$name" in $object'); |  | 
|  126     } |  | 
|  127     var tentativeError; |  | 
|  128     if (adjust) { |  | 
|  129       var min = minArgs(method); |  | 
|  130       if (min > SUPPORTED_ARGS) { |  | 
|  131         tentativeError = 'we tried to adjust the arguments for calling "$name"' |  | 
|  132             ', but we couldn\'t determine the exact number of arguments it ' |  | 
|  133             'expects (it is more than $SUPPORTED_ARGS).'; |  | 
|  134         // The argument list might be correct, so we still invoke the function |  | 
|  135         // and let the user see the error. |  | 
|  136         args = adjustList(args, min, math.max(min, args.length)); |  | 
|  137       } else { |  | 
|  138         var max = maxArgs(method); |  | 
|  139         args = adjustList(args, min, max >= 0 ? max : args.length); |  | 
|  140       } |  | 
|  141     } |  | 
|  142     if (namedArgs != null) { |  | 
|  143       throw new UnsupportedError( |  | 
|  144           'smoke.static doesn\'t support namedArguments in invoke'); |  | 
|  145     } |  | 
|  146     try { |  | 
|  147       return Function.apply(method, args); |  | 
|  148     } on NoSuchMethodError catch (e) { |  | 
|  149       // TODO(sigmund): consider whether this should just be in a logger or if |  | 
|  150       // we should wrap `e` as a new exception (what's the best way to let users |  | 
|  151       // know about this tentativeError?) |  | 
|  152       if (tentativeError != null) print(tentativeError); |  | 
|  153       rethrow; |  | 
|  154     } |  | 
|  155   } |  | 
|  156 } |  | 
|  157  |  | 
|  158 /// Implements [TypeInspectorService] using a static configuration. |  | 
|  159 class GeneratedTypeInspectorService implements TypeInspectorService { |  | 
|  160   final StaticConfiguration _configuration; |  | 
|  161  |  | 
|  162   Map<Type, Type> get _parents => _configuration.parents; |  | 
|  163   Map<Type, Map<Symbol, Declaration>> get _declarations => |  | 
|  164       _configuration.declarations; |  | 
|  165   bool get _checkedMode => _configuration.checkedMode; |  | 
|  166  |  | 
|  167   GeneratedTypeInspectorService(this._configuration); |  | 
|  168  |  | 
|  169   bool isSubclassOf(Type type, Type supertype) { |  | 
|  170     if (type == supertype || supertype == Object) return true; |  | 
|  171     while (type != Object) { |  | 
|  172       var parentType = _parents[type]; |  | 
|  173       if (parentType == supertype) return true; |  | 
|  174       if (parentType == null) { |  | 
|  175         if (!_checkedMode) return false; |  | 
|  176         throw new MissingCodeException('superclass of "$type" ($parentType)'); |  | 
|  177       } |  | 
|  178       type = parentType; |  | 
|  179     } |  | 
|  180     return false; |  | 
|  181   } |  | 
|  182  |  | 
|  183   bool hasGetter(Type type, Symbol name) { |  | 
|  184     var decl = _findDeclaration(type, name); |  | 
|  185     // No need to check decl.isProperty because methods are also automatically |  | 
|  186     // considered getters (auto-closures). |  | 
|  187     return decl != null && !decl.isStatic; |  | 
|  188   } |  | 
|  189  |  | 
|  190   bool hasSetter(Type type, Symbol name) { |  | 
|  191     var decl = _findDeclaration(type, name); |  | 
|  192     return decl != null && !decl.isMethod && !decl.isFinal && !decl.isStatic; |  | 
|  193   } |  | 
|  194  |  | 
|  195   bool hasInstanceMethod(Type type, Symbol name) { |  | 
|  196     var decl = _findDeclaration(type, name); |  | 
|  197     return decl != null && decl.isMethod && !decl.isStatic; |  | 
|  198   } |  | 
|  199  |  | 
|  200   bool hasStaticMethod(Type type, Symbol name) { |  | 
|  201     final map = _declarations[type]; |  | 
|  202     if (map == null) { |  | 
|  203       if (!_checkedMode) return false; |  | 
|  204       throw new MissingCodeException('declarations for $type'); |  | 
|  205     } |  | 
|  206     final decl = map[name]; |  | 
|  207     return decl != null && decl.isMethod && decl.isStatic; |  | 
|  208   } |  | 
|  209  |  | 
|  210   Declaration getDeclaration(Type type, Symbol name) { |  | 
|  211     var decl = _findDeclaration(type, name); |  | 
|  212     if (decl == null) { |  | 
|  213       if (!_checkedMode) return null; |  | 
|  214       throw new MissingCodeException('declaration for $type.$name'); |  | 
|  215     } |  | 
|  216     return decl; |  | 
|  217   } |  | 
|  218  |  | 
|  219   List<Declaration> query(Type type, QueryOptions options) { |  | 
|  220     var result = []; |  | 
|  221     if (options.includeInherited) { |  | 
|  222       var superclass = _parents[type]; |  | 
|  223       if (superclass == null) { |  | 
|  224         if (_checkedMode) { |  | 
|  225           throw new MissingCodeException('superclass of "$type"'); |  | 
|  226         } |  | 
|  227       } else if (superclass != options.includeUpTo) { |  | 
|  228         result = query(superclass, options); |  | 
|  229       } |  | 
|  230     } |  | 
|  231     var map = _declarations[type]; |  | 
|  232     if (map == null) { |  | 
|  233       if (!_checkedMode) return result; |  | 
|  234       throw new MissingCodeException('declarations for $type'); |  | 
|  235     } |  | 
|  236     for (var decl in map.values) { |  | 
|  237       if (!options.includeFields && decl.isField) continue; |  | 
|  238       if (!options.includeProperties && decl.isProperty) continue; |  | 
|  239       if (options.excludeFinal && decl.isFinal) continue; |  | 
|  240       if (!options.includeMethods && decl.isMethod) continue; |  | 
|  241       if (options.matches != null && !options.matches(decl.name)) continue; |  | 
|  242       if (options.withAnnotations != null && |  | 
|  243           !matchesAnnotation(decl.annotations, options.withAnnotations)) { |  | 
|  244         continue; |  | 
|  245       } |  | 
|  246       if (options.excludeOverriden) { |  | 
|  247         result.retainWhere((value) => decl.name != value.name); |  | 
|  248       } |  | 
|  249       result.add(decl); |  | 
|  250     } |  | 
|  251     return result; |  | 
|  252   } |  | 
|  253  |  | 
|  254   Declaration _findDeclaration(Type type, Symbol name) { |  | 
|  255     while (type != Object) { |  | 
|  256       final declarations = _declarations[type]; |  | 
|  257       if (declarations != null) { |  | 
|  258         final declaration = declarations[name]; |  | 
|  259         if (declaration != null) return declaration; |  | 
|  260       } |  | 
|  261       var parentType = _parents[type]; |  | 
|  262       if (parentType == null) { |  | 
|  263         if (!_checkedMode) return null; |  | 
|  264         throw new MissingCodeException('superclass of "$type"'); |  | 
|  265       } |  | 
|  266       type = parentType; |  | 
|  267     } |  | 
|  268     return null; |  | 
|  269   } |  | 
|  270 } |  | 
|  271  |  | 
|  272 /// Implements [SymbolConverterService] using a static configuration. |  | 
|  273 class GeneratedSymbolConverterService implements SymbolConverterService { |  | 
|  274   final StaticConfiguration _configuration; |  | 
|  275   Map<Symbol, String> get _names => _configuration.names; |  | 
|  276   Map<String, Symbol> get _symbols => _configuration._symbols; |  | 
|  277  |  | 
|  278   GeneratedSymbolConverterService(this._configuration); |  | 
|  279  |  | 
|  280   String symbolToName(Symbol symbol) => _names[symbol]; |  | 
|  281   Symbol nameToSymbol(String name) => _symbols[name]; |  | 
|  282 } |  | 
|  283  |  | 
|  284 /// Exception thrown when trynig to access something that should be there, but |  | 
|  285 /// the code generator didn't include it. |  | 
|  286 class MissingCodeException implements Exception { |  | 
|  287   final String description; |  | 
|  288   MissingCodeException(this.description); |  | 
|  289  |  | 
|  290   String toString() => 'Missing $description. ' |  | 
|  291       'Code generation for the smoke package seems incomplete.'; |  | 
|  292 } |  | 
| OLD | NEW |