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