Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 /// Encapsulates how to invoke the analyzer resolver and overrides how it | 5 /// Encapsulates how to invoke the analyzer resolver and overrides how it |
| 6 /// computes types on expressions to use our restricted set of types. | 6 /// computes types on expressions to use our restricted set of types. |
| 7 library dev_compiler.src.checker.resolver; | 7 library dev_compiler.src.checker.resolver; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
| 10 import 'package:analyzer/src/generated/ast.dart'; | 10 import 'package:analyzer/src/generated/ast.dart'; |
| 11 import 'package:analyzer/src/generated/element.dart'; | 11 import 'package:analyzer/src/generated/element.dart'; |
| 12 import 'package:analyzer/src/generated/engine.dart'; | 12 import 'package:analyzer/src/generated/engine.dart'; |
| 13 import 'package:analyzer/src/generated/error.dart' as analyzer; | 13 import 'package:analyzer/src/generated/error.dart' as analyzer; |
| 14 import 'package:analyzer/src/generated/java_io.dart' show JavaFile; | 14 import 'package:analyzer/src/generated/java_io.dart' show JavaFile; |
| 15 import 'package:analyzer/src/generated/resolver.dart'; | 15 import 'package:analyzer/src/generated/resolver.dart'; |
| 16 import 'package:analyzer/src/generated/static_type_analyzer.dart'; | |
| 17 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; | 16 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; |
| 18 import 'package:analyzer/src/generated/source.dart' show DartUriResolver; | 17 import 'package:analyzer/src/generated/source.dart' show DartUriResolver; |
| 19 import 'package:analyzer/src/generated/source.dart' show Source; | 18 import 'package:analyzer/src/generated/source.dart' show Source; |
| 20 import 'package:analyzer/src/generated/source_io.dart'; | 19 import 'package:analyzer/src/generated/source_io.dart'; |
| 20 import 'package:analyzer/src/generated/static_type_analyzer.dart'; | |
| 21 import 'package:logging/logging.dart' as logger; | 21 import 'package:logging/logging.dart' as logger; |
| 22 | 22 |
| 23 import 'package:dev_compiler/src/options.dart'; | 23 import 'package:dev_compiler/src/options.dart'; |
| 24 import 'package:dev_compiler/src/report.dart'; | 24 import 'package:dev_compiler/src/report.dart'; |
| 25 import 'package:dev_compiler/src/utils.dart'; | 25 import 'package:dev_compiler/src/utils.dart'; |
| 26 import 'dart_sdk.dart'; | 26 import 'dart_sdk.dart'; |
| 27 import 'multi_package_resolver.dart'; | 27 import 'multi_package_resolver.dart'; |
| 28 | 28 |
| 29 final _log = new logger.Logger('dev_compiler.src.resolver'); | 29 final _log = new logger.Logger('dev_compiler.src.resolver'); |
| 30 | 30 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 | 99 |
| 100 const AnalyzerError(String message, logger.Level level, int begin, int end) | 100 const AnalyzerError(String message, logger.Level level, int begin, int end) |
| 101 : super('[from analyzer]: $message', level, begin, end); | 101 : super('[from analyzer]: $message', level, begin, end); |
| 102 } | 102 } |
| 103 | 103 |
| 104 /// Creates an analysis context that contains our restricted typing rules. | 104 /// Creates an analysis context that contains our restricted typing rules. |
| 105 InternalAnalysisContext _initContext(ResolverOptions options) { | 105 InternalAnalysisContext _initContext(ResolverOptions options) { |
| 106 var analysisOptions = new AnalysisOptionsImpl()..cacheSize = 512; | 106 var analysisOptions = new AnalysisOptionsImpl()..cacheSize = 512; |
| 107 AnalysisContextImpl res = AnalysisEngine.instance.createAnalysisContext(); | 107 AnalysisContextImpl res = AnalysisEngine.instance.createAnalysisContext(); |
| 108 res.analysisOptions = analysisOptions; | 108 res.analysisOptions = analysisOptions; |
| 109 res.resolverVisitorFactory = RestrictedResolverVisitor.constructor(options); | 109 res.libraryResolverFactory = |
| 110 if (options.inferFromOverrides) { | 110 (context) => new LibraryResolverWithInference(context, options); |
| 111 res.typeResolverVisitorFactory = RestrictedTypeResolverVisitor.constructor; | |
| 112 } | |
| 113 return res; | 111 return res; |
| 114 } | 112 } |
| 115 | 113 |
| 116 /// Overrides the default [ResolverVisitor] to comply with DDC's restricted | 114 /// A [LibraryResolver] that performs inference on top-levels and fields based |
| 117 /// type rules. This changes how types are promoted in conditional expressions | 115 /// on the value of the initializer, and on fields and methods based on |
| 118 /// and statements, and how types are computed on expressions. | 116 /// overriden members in super classes. |
|
vsm
2015/03/17 15:55:27
nit: overriden -> overridden
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Done.
| |
| 117 class LibraryResolverWithInference extends LibraryResolver { | |
| 118 final ResolverOptions _options; | |
| 119 | |
| 120 LibraryResolverWithInference(context, this._options) : super(context); | |
| 121 | |
| 122 @override | |
| 123 void resolveReferencesAndTypes() { | |
| 124 _resolveVariableReferences(); | |
| 125 | |
| 126 // Skip inference in the core libraries. | |
| 127 if (resolvedLibraries.any((l) => l.librarySource.isInSystemLibrary)) { | |
|
vsm
2015/03/17 15:55:27
Is resolvedLibraries the cycle here? I.e., if one
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Yeah, exactly, I added a comment to be clear.
| |
| 128 _resolveReferencesAndTypes(false); | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 // Run resolution in two stages, skipping method bodies first, so we can run | |
| 133 // type-inference before we fully analyze methods. | |
| 134 _resolveReferencesAndTypes(true); | |
| 135 _runInference(); | |
| 136 _resolveReferencesAndTypes(false); | |
| 137 } | |
| 138 | |
| 139 // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it | |
| 140 // only once. | |
| 141 void _resolveVariableReferences() { | |
| 142 for (Library library in resolvedLibraries) { | |
| 143 for (Source source in library.compilationUnitSources) { | |
| 144 library.getAST(source).accept( | |
| 145 new VariableResolverVisitor.con1(library, source, typeProvider)); | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do | |
| 151 // resolution in pieces. | |
| 152 void _resolveReferencesAndTypes(bool skipMethods) { | |
| 153 for (Library library in resolvedLibraries) { | |
| 154 for (Source source in library.compilationUnitSources) { | |
| 155 library.getAST(source).accept(new RestrictedResolverVisitor( | |
| 156 library, source, typeProvider, _options, skipMethods)); | |
| 157 } | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 _runInference() { | |
| 162 var consts = []; | |
| 163 var statics = []; | |
| 164 var classes = []; | |
| 165 | |
| 166 // Extract top-level members that are const, statics, or classes. | |
| 167 for (Library library in resolvedLibraries) { | |
| 168 for (Source source in library.compilationUnitSources) { | |
| 169 CompilationUnit ast = library.getAST(source); | |
| 170 for (var declaration in ast.declarations) { | |
| 171 if (declaration is TopLevelVariableDeclaration) { | |
| 172 if (declaration.variables.isConst) { | |
| 173 consts.add(declaration.variables); | |
| 174 } else { | |
| 175 statics.add(declaration.variables); | |
| 176 } | |
| 177 } else if (declaration is ClassDeclaration) { | |
| 178 classes.add(declaration); | |
| 179 for (var member in declaration.members) { | |
| 180 if (member is! FieldDeclaration) continue; | |
| 181 if (member.fields.isConst) { | |
| 182 consts.add(member.fields); | |
| 183 } else if (member.isStatic) { | |
| 184 statics.add(member.fields); | |
| 185 } | |
| 186 } | |
| 187 } | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // TODO(sigmund): consider propagating const types after this layer of | |
| 193 // inferece, so their types can be used to initialize other members below. | |
|
vsm
2015/03/17 15:55:27
nit: inferece -> inference
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Done.
| |
| 194 _inferVariableFromInitializer(consts); | |
| 195 _inferVariableFromInitializer(statics); | |
| 196 | |
| 197 // Track types in this strongest connected component, ensure we visit | |
|
vsm
2015/03/17 15:55:27
strongly?
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Done.
| |
| 198 // supertypes before subtypes. | |
| 199 var typeToDeclaration = <InterfaceType, ClassDeclaration>{}; | |
| 200 classes.forEach((c) => typeToDeclaration[c.element.type] = c); | |
| 201 var seen = new Set<InterfaceType>(); | |
| 202 visit(ClassDeclaration cls) { | |
| 203 var element = cls.element; | |
| 204 var type = element.type; | |
| 205 if (seen.contains(type)) return; | |
| 206 for (var supertype in element.allSupertypes) { | |
| 207 var supertypeClass = typeToDeclaration[supertype]; | |
| 208 if (supertypeClass != null) visit(supertypeClass); | |
| 209 } | |
| 210 seen.add(type); | |
| 211 | |
| 212 _isInstanceField(f) => | |
| 213 f is FieldDeclaration && !f.isStatic && !f.fields.isConst; | |
| 214 | |
| 215 if (_options.inferFromOverrides) { | |
| 216 // Infer field types from overrides first, otherwise from initializers. | |
| 217 var pending = new Set<VariableDeclarationList>(); | |
| 218 cls.members | |
| 219 .where(_isInstanceField) | |
| 220 .forEach((f) => _inferFieldTypeFromOverride(f, pending)); | |
| 221 if (pending.isNotEmpty) _inferVariableFromInitializer(pending); | |
| 222 | |
| 223 // Infer return-types from overrides | |
| 224 cls.members | |
| 225 .where((m) => m is MethodDeclaration && !m.isStatic) | |
| 226 .forEach(_inferMethodReturnTypeFromOverride); | |
| 227 } else { | |
| 228 _inferVariableFromInitializer( | |
| 229 cls.members.where(_isInstanceField).map((f) => f.fields)); | |
| 230 } | |
| 231 } | |
| 232 classes.forEach(visit); | |
| 233 } | |
| 234 | |
| 235 /// Attempts to infer the type on [field] from overriden fields or getters if | |
| 236 /// a type was not specified. If no type could be inferred, but it contains an | |
| 237 /// initializer, we add it to [pending] so we can try to infer it using the | |
| 238 /// initializer type instead. | |
| 239 void _inferFieldTypeFromOverride( | |
| 240 FieldDeclaration field, Set<VariableDeclarationList> pending) { | |
| 241 var variables = field.fields; | |
| 242 for (var variable in variables.variables) { | |
| 243 var varElement = variable.element; | |
| 244 if (!varElement.type.isDynamic || variables.type != null) continue; | |
| 245 var getter = varElement.getter; | |
| 246 var type = searchTypeFor(varElement.enclosingElement.type, getter); | |
| 247 if (type != null && !type.returnType.isDynamic) { | |
| 248 var newType = type.returnType; | |
| 249 varElement.type = newType; | |
| 250 varElement.getter.returnType = newType; | |
| 251 if (!varElement.isFinal) varElement.setter.parameters[0].type = newType; | |
| 252 } else if (variable.initializer != null) { | |
| 253 pending.add(variables); | |
| 254 } | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 void _inferMethodReturnTypeFromOverride(MethodDeclaration method) { | |
| 259 var methodElement = method.element; | |
| 260 if ((methodElement is MethodElement || | |
| 261 methodElement is PropertyAccessorElement) && | |
| 262 methodElement.returnType.isDynamic && | |
| 263 method.returnType == null) { | |
| 264 var type = | |
| 265 searchTypeFor(methodElement.enclosingElement.type, methodElement); | |
| 266 if (type != null && !type.returnType.isDynamic) { | |
| 267 methodElement.returnType = type.returnType; | |
| 268 } | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 void _inferVariableFromInitializer( | |
| 273 Iterable<VariableDeclarationList> variables) { | |
| 274 for (var variableList in variables) { | |
| 275 if (variableList.type != null) continue; | |
| 276 if (_options.onlyInferConstsAndFinalFields && | |
| 277 !variableList.isFinal && | |
| 278 !variableList.isConst) { | |
| 279 return; | |
| 280 } | |
| 281 for (var variable in variableList.variables) { | |
| 282 var initializer = variable.initializer; | |
| 283 if (initializer == null) continue; | |
| 284 var type = initializer.staticType; | |
| 285 if (type == null || type.isDynamic) continue; | |
|
vsm
2015/03/17 15:55:27
If the initializer type is Null or Bottom, we migh
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Ah, interesting. Good point. I added a check for i
| |
| 286 if (!_canInferFrom(initializer)) continue; | |
| 287 var element = variable.element; | |
|
vsm
2015/03/17 15:55:27
Shouldn't we only update the type if it was declar
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Correct, the check in line 275 above:
if (variab
vsm
2015/03/17 17:18:48
Ah, thanks - missed the check above.
| |
| 288 // Note: it's ok to update the type here, since initializer.staticType | |
| 289 // is already computed for all declarations in the library cycle. The | |
| 290 // new types will only be propagated on a second run of the | |
| 291 // ResolverVisitor. | |
| 292 element.type = type; | |
| 293 element.getter.returnType = type; | |
| 294 if (!element.isFinal && !element.isConst) { | |
| 295 element.setter.parameters[0].type = type; | |
| 296 } | |
| 297 } | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 bool _canInferFrom(Expression expression) { | |
| 302 if (expression is Literal) return true; | |
| 303 if (expression is InstanceCreationExpression) return true; | |
| 304 if (expression is FunctionExpression) return true; | |
| 305 if (expression is AsExpression) return true; | |
| 306 if (expression is CascadeExpression) { | |
| 307 return _canInferFrom(expression.target); | |
| 308 } | |
| 309 if (expression is SimpleIdentifier || expression is PropertyAccess) { | |
| 310 return _options.inferStaticsFromIdentifiers; | |
| 311 } | |
| 312 if (expression is PrefixedIdentifier) { | |
| 313 if (expression.staticElement is PropertyAccessorElement) { | |
| 314 return _options.inferStaticsFromIdentifiers; | |
| 315 } | |
| 316 return _canInferFrom(expression.identifier); | |
| 317 } | |
| 318 if (expression is MethodInvocation) { | |
| 319 return _canInferFrom(expression.target); | |
| 320 } | |
| 321 if (expression is BinaryExpression) { | |
| 322 return _canInferFrom(expression.leftOperand); | |
| 323 } | |
| 324 if (expression is ConditionalExpression) { | |
| 325 return _canInferFrom(expression.thenExpression) && | |
| 326 _canInferFrom(expression.elseExpression); | |
| 327 } | |
| 328 if (expression is PrefixExpression) { | |
| 329 return _canInferFrom(expression.operand); | |
| 330 } | |
| 331 if (expression is PostfixExpression) { | |
| 332 return _canInferFrom(expression.operand); | |
| 333 } | |
| 334 return false; | |
| 335 } | |
| 336 } | |
| 337 | |
| 338 /// Overrides the default [ResolverVisitor] to support type inference in | |
| 339 /// [LibraryResolverWithInference] above. | |
| 340 /// | |
| 341 /// Before inference, this visitor is used to resolve top-levels, classes, and | |
| 342 /// fields, but nothing withihn method bodies. After inference, this visitor is | |
| 343 /// used again to step into method bodies and complete resolution as a second | |
| 344 /// phase. | |
| 119 class RestrictedResolverVisitor extends ResolverVisitor { | 345 class RestrictedResolverVisitor extends ResolverVisitor { |
| 120 final TypeProvider _typeProvider; | 346 final TypeProvider _typeProvider; |
| 121 | 347 |
| 348 /// Whether to skip resolution within method bodies. | |
| 349 final bool skipMethodBodies; | |
| 350 | |
| 122 RestrictedResolverVisitor(Library library, Source source, | 351 RestrictedResolverVisitor(Library library, Source source, |
| 123 TypeProvider typeProvider, ResolverOptions options) | 352 TypeProvider typeProvider, ResolverOptions options, this.skipMethodBodies) |
| 124 : _typeProvider = typeProvider, | 353 : _typeProvider = typeProvider, |
| 125 super.con1(library, source, typeProvider, | 354 super.con1(library, source, typeProvider, |
| 126 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer | 355 typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor); |
| 127 .constructor(options)); | |
| 128 | |
| 129 static constructor(options) => | |
| 130 (Library library, Source source, TypeProvider typeProvider) => | |
| 131 new RestrictedResolverVisitor(library, source, typeProvider, options); | |
| 132 | 356 |
| 133 @override | 357 @override |
| 134 visitCatchClause(CatchClause node) { | 358 visitCatchClause(CatchClause node) { |
| 135 var stack = node.stackTraceParameter; | 359 var stack = node.stackTraceParameter; |
| 136 if (stack != null) { | 360 if (stack != null) { |
| 137 // TODO(jmesserly): analyzer does not correctly associate StackTrace type. | 361 // TODO(jmesserly): analyzer does not correctly associate StackTrace type. |
| 138 // It happens too late in TypeResolverVisitor visitCatchClause. | 362 // It happens too late in TypeResolverVisitor visitCatchClause. |
| 139 var element = stack.staticElement; | 363 var element = stack.staticElement; |
| 140 if (element is VariableElementImpl && element.type == null) { | 364 if (element is VariableElementImpl && element.type == null) { |
| 141 // From the language spec: | 365 // From the language spec: |
| 142 // The static type of p1 is T and the static type of p2 is StackTrace. | 366 // The static type of p1 is T and the static type of p2 is StackTrace. |
| 143 element.type = _typeProvider.stackTraceType; | 367 element.type = _typeProvider.stackTraceType; |
| 144 } | 368 } |
| 145 } | 369 } |
| 146 return super.visitCatchClause(node); | 370 return super.visitCatchClause(node); |
| 147 } | 371 } |
| 148 | 372 |
| 149 @override | 373 @override |
| 150 Object visitCompilationUnit(CompilationUnit node) { | 374 Object visitNode(AstNode node) { |
| 151 // Similar to the definition in ResolverVisitor.visitCompilationUnit, but | 375 if (skipMethodBodies && |
| 152 // changed to visit all top-level fields first, then static fields on all | 376 (node is FunctionBody || |
| 153 // classes, then all top-level functions, then the rest of the classes. | 377 node is FunctionExpression || |
| 154 RestrictedStaticTypeAnalyzer restrictedAnalyzer = typeAnalyzer_J2DAccessor; | 378 node is FunctionExpressionInvocation || |
| 155 overrideManager.enterScope(); | 379 node is SuperConstructorInvocation || |
| 156 try { | 380 node is RedirectingConstructorInvocation || |
| 157 var thisLib = node.element.enclosingElement; | 381 node is Annotation || |
| 158 restrictedAnalyzer._isLibraryContainedInSingleUnit.putIfAbsent(thisLib, | 382 node is Comment)) { |
| 159 () { | 383 return null; |
| 160 if (thisLib.units.length > 1) return false; | |
| 161 for (var lib in thisLib.visibleLibraries) { | |
| 162 if (lib != thisLib && lib.visibleLibraries.contains(thisLib)) { | |
| 163 return false; | |
| 164 } | |
| 165 } | |
| 166 return true; | |
| 167 }); | |
| 168 | |
| 169 void accept(n) { | |
| 170 n.accept(this); | |
| 171 } | |
| 172 node.directives.forEach(accept); | |
| 173 var declarations = node.declarations; | |
| 174 | |
| 175 declarations | |
| 176 .where((d) => d is TopLevelVariableDeclaration) | |
| 177 .forEach(accept); | |
| 178 | |
| 179 // Visit classes before top-level methods so that we can visit static | |
| 180 // fields first. | |
| 181 // TODO(sigmund): consider visiting static fields only at this point | |
| 182 // (the challenge is that to visit them we first need to create the scope | |
| 183 // for the class here, and reuse it later when visiting the class | |
| 184 // declaration to ensure that we correctly construct the scopes and that | |
| 185 // we visit each static field only once). | |
| 186 declarations.where((d) => d is ClassDeclaration).forEach(accept); | |
| 187 | |
| 188 declarations | |
| 189 .where((d) => | |
| 190 d is! TopLevelVariableDeclaration && d is! ClassDeclaration) | |
| 191 .forEach(accept); | |
| 192 } finally { | |
| 193 overrideManager.exitScope(); | |
| 194 } | 384 } |
| 195 node.accept(elementResolver_J2DAccessor); | 385 assert(node is! Statement || !skipMethodBodies); |
| 196 node.accept(restrictedAnalyzer); | 386 return super.visitNode(node); |
| 197 return null; | |
| 198 } | 387 } |
| 199 | 388 |
| 200 @override | 389 @override |
| 201 void visitClassMembersInScope(ClassDeclaration node) { | 390 Object visitMethodDeclaration(MethodDeclaration node) { |
| 202 safelyVisit(node.documentationComment); | 391 if (skipMethodBodies) { |
| 203 node.metadata.accept(this); | 392 node.accept(elementResolver_J2DAccessor); |
| 393 node.accept(typeAnalyzer_J2DAccessor); | |
| 394 return null; | |
| 395 } else { | |
| 396 return super.visitMethodDeclaration(node); | |
| 397 } | |
| 398 } | |
| 204 | 399 |
| 205 // This overrides the default way members are visited so that fields are | 400 @override |
| 206 // visited before method declarations. | 401 Object visitFunctionDeclaration(FunctionDeclaration node) { |
| 207 for (var n in node.members) { | 402 if (skipMethodBodies) { |
| 208 if (n is FieldDeclaration) n.accept(this); | 403 node.accept(elementResolver_J2DAccessor); |
| 404 node.accept(typeAnalyzer_J2DAccessor); | |
| 405 return null; | |
| 406 } else { | |
| 407 return super.visitFunctionDeclaration(node); | |
| 209 } | 408 } |
| 210 for (var n in node.members) { | 409 } |
| 211 if (n is! FieldDeclaration) n.accept(this); | 410 |
| 411 @override | |
| 412 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 413 if (skipMethodBodies) { | |
| 414 node.accept(elementResolver_J2DAccessor); | |
| 415 node.accept(typeAnalyzer_J2DAccessor); | |
| 416 return null; | |
| 417 } else { | |
| 418 return super.visitConstructorDeclaration(node); | |
| 212 } | 419 } |
| 213 } | 420 } |
| 214 } | 421 } |
| 215 | 422 |
| 216 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter | 423 /// Overrides the default [StaticTypeAnalyzer] to adjust rules that are stricter |
| 217 /// in the restricted type system and to infer types for untyped local | 424 /// in the restricted type system and to infer types for untyped local |
| 218 /// variables. | 425 /// variables. |
| 219 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { | 426 class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
| 220 final TypeProvider _typeProvider; | 427 final TypeProvider _typeProvider; |
| 221 final ResolverOptions _options; | |
| 222 | 428 |
| 223 // TODO(sigmund): this needs to go away. This is currently a restriction | 429 RestrictedStaticTypeAnalyzer(ResolverVisitor r) |
| 224 // because we are not overriding things early enough in the analyzer. This | |
| 225 // restriction makes it safe to run the inference later, but only on libraries | |
| 226 // that are contained in a single file and are not part of a cycle. | |
| 227 Map<LibraryElement, bool> _isLibraryContainedInSingleUnit = {}; | |
| 228 | |
| 229 RestrictedStaticTypeAnalyzer(ResolverVisitor r, this._options) | |
| 230 : _typeProvider = r.typeProvider, | 430 : _typeProvider = r.typeProvider, |
| 231 super(r); | 431 super(r); |
| 232 | 432 |
| 233 static constructor(options) => | 433 static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r); |
| 234 (r) => new RestrictedStaticTypeAnalyzer(r, options); | |
| 235 | 434 |
| 236 @override // to infer type from initializers | 435 @override // to infer type from initializers |
| 237 visitVariableDeclaration(VariableDeclaration node) { | 436 visitVariableDeclaration(VariableDeclaration node) { |
| 238 _inferType(node); | 437 _inferType(node); |
| 239 return super.visitVariableDeclaration(node); | 438 return super.visitVariableDeclaration(node); |
| 240 } | 439 } |
| 241 | 440 |
| 242 /// Infer the type of a variable based on the initializer's type. | 441 /// Infer the type of a variable based on the initializer's type. |
| 243 void _inferType(VariableDeclaration node) { | 442 void _inferType(VariableDeclaration node) { |
| 244 var initializer = node.initializer; | 443 var initializer = node.initializer; |
| 245 if (initializer == null) return; | 444 if (initializer == null) return; |
| 246 | 445 |
| 247 var declaredType = (node.parent as VariableDeclarationList).type; | 446 var declaredType = (node.parent as VariableDeclarationList).type; |
| 248 if (declaredType != null) return; | 447 if (declaredType != null) return; |
| 249 var element = node.element; | 448 var element = node.element; |
| 449 if (element is! LocalVariableElement) return; | |
| 250 if (element.type != _typeProvider.dynamicType) return; | 450 if (element.type != _typeProvider.dynamicType) return; |
| 251 | 451 |
| 252 // Local variables can be inferred automatically, for top-levels and fields | |
| 253 // we rule out cases that could depend on the order in which we process | |
| 254 // them. | |
| 255 if (element is! LocalVariableElement) { | |
| 256 if (_options.onlyInferConstsAndFinalFields && | |
| 257 !element.isConst && | |
| 258 !element.isFinal) { | |
| 259 return; | |
| 260 } | |
| 261 // Only infer types if the library is not in a cycle. Otherwise we can't | |
| 262 // guarantee that we are order independent (we can't guarantee that we'll | |
| 263 // visit all top-level declarations in all libraries, before we visit | |
| 264 // methods in all libraries). | |
| 265 var thisLib = enclosingLibrary(element); | |
| 266 if (!_canBeInferredIndependently(initializer, thisLib)) return; | |
| 267 } | |
| 268 | |
| 269 var type = initializer.staticType; | 452 var type = initializer.staticType; |
| 270 if (type == null || type == _typeProvider.bottomType) return; | 453 if (type == null || type == _typeProvider.bottomType) return; |
| 271 element.type = type; | 454 element.type = type; |
| 272 if (element is PropertyInducingElement) { | 455 if (element is PropertyInducingElement) { |
| 273 element.getter.returnType = type; | 456 element.getter.returnType = type; |
| 274 if (!element.isFinal && !element.isConst) { | 457 if (!element.isFinal && !element.isConst) { |
| 275 element.setter.parameters[0].type = type; | 458 element.setter.parameters[0].type = type; |
| 276 } | 459 } |
| 277 } | 460 } |
| 278 } | 461 } |
| 279 | 462 |
| 280 /// Whether we could determine the type of an [expression] in a way | |
| 281 /// that doesn't depend on the order in which we infer types within a | |
| 282 /// strongest connected component of libraries. | |
| 283 /// | |
| 284 /// This will return true if the expression consists just of literals or | |
| 285 /// allocations, if it only uses symbols that come from libraries that are | |
| 286 /// clearly processed before the library where this expression occurs | |
| 287 /// ([thisLib]), or if it's composed of these subexpressions (excluding fields | |
| 288 /// and top-levels that could've been inferred as well). | |
| 289 /// | |
| 290 /// The [inFieldContext] is used internally when visiting nested expressions | |
| 291 /// recursively. It indicates that the subexpression will be used in the | |
| 292 /// context of a field dereference. | |
| 293 bool _canBeInferredIndependently( | |
| 294 Expression expression, LibraryElement thisLib, | |
| 295 {bool inFieldContext: false}) { | |
| 296 if (_options.inferInNonStableOrder) return true; | |
| 297 if (!_options.inferStaticsFromIdentifiers && inFieldContext) return false; | |
| 298 if (!_isLibraryContainedInSingleUnit[thisLib]) return false; | |
| 299 if (expression is Literal) return true; | |
| 300 | |
| 301 if (expression is InstanceCreationExpression) { | |
| 302 if (!inFieldContext) return true; | |
| 303 var element = expression.staticElement; | |
| 304 if (element == null) { | |
| 305 print('Unexpected `null` element for $expression'); | |
| 306 return false; | |
| 307 } | |
| 308 return !_sameConnectedComponent(thisLib, element); | |
| 309 } | |
| 310 if (expression is FunctionExpression) return true; | |
| 311 if (expression is CascadeExpression) { | |
| 312 return _canBeInferredIndependently(expression.target, thisLib, | |
| 313 inFieldContext: inFieldContext); | |
| 314 } | |
| 315 | |
| 316 if (expression is MethodInvocation) { | |
| 317 return _canBeInferredIndependently(expression.target, thisLib, | |
| 318 inFieldContext: true); | |
| 319 } | |
| 320 | |
| 321 // Binary expressions, prefix/postfix expressions are are derived from the | |
| 322 // type of the operand, which is known at this time even for classes in the | |
| 323 // same library. | |
| 324 if (expression is BinaryExpression) { | |
| 325 return _canBeInferredIndependently(expression.leftOperand, thisLib, | |
| 326 inFieldContext: false); | |
| 327 } | |
| 328 if (expression is PrefixExpression) { | |
| 329 return _canBeInferredIndependently(expression.operand, thisLib, | |
| 330 inFieldContext: false); | |
| 331 } | |
| 332 if (expression is PostfixExpression) { | |
| 333 return _canBeInferredIndependently(expression.operand, thisLib, | |
| 334 inFieldContext: false); | |
| 335 } | |
| 336 | |
| 337 // Property accesses and prefix identifiers can be resolved as fields, in | |
| 338 // which case, we need to choose whether or not to infer based on the | |
| 339 // target. | |
| 340 if (expression is PropertyAccess) { | |
| 341 return _canBeInferredIndependently(expression.target, thisLib, | |
| 342 inFieldContext: true); | |
| 343 } | |
| 344 if (expression is PrefixedIdentifier) { | |
| 345 return _canBeInferredIndependently(expression.identifier, thisLib, | |
| 346 inFieldContext: true); | |
| 347 } | |
| 348 | |
| 349 if (expression is SimpleIdentifier) { | |
| 350 if (!_options.inferStaticsFromIdentifiers) return false; | |
| 351 var element = expression.bestElement; | |
| 352 if (element == null) { | |
| 353 print('Unexpected `null` element for $expression'); | |
| 354 return false; | |
| 355 } | |
| 356 return !_sameConnectedComponent(thisLib, element); | |
| 357 } | |
| 358 return false; | |
| 359 } | |
| 360 | |
| 361 /// Whether [dependency] is in the same strongest connected component of | |
| 362 /// libraries as [declaration]. | |
| 363 bool _sameConnectedComponent(LibraryElement thisLib, Element dependency) { | |
| 364 assert(dependency != null); | |
| 365 var otherLib = enclosingLibrary(dependency); | |
| 366 // Note: we would check here also whether | |
| 367 // otherLib.visibleLibraries.contains(thisLib), however because we are not | |
| 368 // inferring type on any library that belongs to a cycle or that contains | |
| 369 // parts, we know that this cannot be true. | |
| 370 return thisLib == otherLib; | |
| 371 } | |
| 372 | |
| 373 @override // to propagate types to identifiers | 463 @override // to propagate types to identifiers |
| 374 visitMethodInvocation(MethodInvocation node) { | 464 visitMethodInvocation(MethodInvocation node) { |
| 375 // TODO(sigmund): follow up with analyzer team - why is this needed? | 465 // TODO(sigmund): follow up with analyzer team - why is this needed? |
| 376 visitSimpleIdentifier(node.methodName); | 466 visitSimpleIdentifier(node.methodName); |
| 377 super.visitMethodInvocation(node); | 467 super.visitMethodInvocation(node); |
| 378 | 468 |
| 379 var e = node.methodName.staticElement; | 469 var e = node.methodName.staticElement; |
| 380 if (e is FunctionElement && | 470 if (e is FunctionElement && |
| 381 e.library.name == '_foreign_helper' && | 471 e.library.name == '_foreign_helper' && |
| 382 e.name == 'JS') { | 472 e.name == 'JS') { |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 415 } | 505 } |
| 416 } | 506 } |
| 417 | 507 |
| 418 // Review note: no longer need to override visitFunctionExpression, this is | 508 // Review note: no longer need to override visitFunctionExpression, this is |
| 419 // handled by the analyzer internally. | 509 // handled by the analyzer internally. |
| 420 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? | 510 // TODO(vsm): in visitbinaryExpression: check computeStaticReturnType result? |
| 421 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression | 511 // TODO(vsm): in visitFunctionDeclaration: Should we ever use the expression |
| 422 // type in a (...) => expr or just the written type? | 512 // type in a (...) => expr or just the written type? |
| 423 | 513 |
| 424 } | 514 } |
| 425 | |
| 426 class RestrictedTypeResolverVisitor extends TypeResolverVisitor { | |
| 427 RestrictedTypeResolverVisitor( | |
| 428 Library library, Source source, TypeProvider typeProvider) | |
| 429 : super.con1(library, source, typeProvider); | |
| 430 | |
| 431 static TypeResolverVisitor constructor( | |
| 432 Library library, Source source, TypeProvider typeProvider) => | |
| 433 new RestrictedTypeResolverVisitor(library, source, typeProvider); | |
| 434 | |
| 435 @override | |
| 436 Object visitVariableDeclaration(VariableDeclaration node) { | |
| 437 var res = super.visitVariableDeclaration(node); | |
| 438 | |
| 439 var element = node.element; | |
| 440 VariableDeclarationList parent = node.parent; | |
| 441 // only infer types if it was left blank | |
| 442 if (!element.type.isDynamic || parent.type != null) return res; | |
| 443 | |
| 444 // const fields and top-levels will be inferred from the initializer value | |
| 445 // somewhere else. | |
| 446 if (parent.isConst) return res; | |
| 447 | |
| 448 // If the type was omitted on a field, we can infer it from a supertype. | |
| 449 if (node.element is FieldElement) { | |
| 450 var getter = element.getter; | |
| 451 var type = searchTypeFor(element.enclosingElement.type, getter); | |
| 452 if (type != null && !type.returnType.isDynamic) { | |
| 453 var newType = type.returnType; | |
| 454 element.type = newType; | |
| 455 getter.returnType = newType; | |
| 456 if (!element.isFinal) element.setter.parameters[0].type = newType; | |
| 457 } | |
| 458 } | |
| 459 return res; | |
| 460 } | |
| 461 | |
| 462 @override | |
| 463 Object visitMethodDeclaration(MethodDeclaration node) { | |
| 464 var res = super.visitMethodDeclaration(node); | |
| 465 var element = node.element; | |
| 466 if ((element is MethodElement || element is PropertyAccessorElement) && | |
| 467 element.returnType.isDynamic && | |
| 468 node.returnType == null) { | |
| 469 var type = searchTypeFor(element.enclosingElement.type, element); | |
| 470 if (type != null && !type.returnType.isDynamic) { | |
| 471 element.returnType = type.returnType; | |
| 472 } | |
| 473 } | |
| 474 return res; | |
| 475 } | |
| 476 } | |
| OLD | NEW |