Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(128)

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/nullable_type_inference.dart

Issue 2994203002: Optimize DDC private library files. (Closed)
Patch Set: Address comments Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 import 'dart:collection'; 5 import 'dart:collection';
6 import 'package:analyzer/dart/ast/ast.dart'; 6 import 'package:analyzer/dart/ast/ast.dart';
7 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; 7 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
8 import 'package:analyzer/dart/ast/token.dart' show TokenType; 8 import 'package:analyzer/dart/ast/token.dart' show TokenType;
9 import 'package:analyzer/dart/ast/visitor.dart' show RecursiveAstVisitor; 9 import 'package:analyzer/dart/ast/visitor.dart' show RecursiveAstVisitor;
10 import 'package:analyzer/dart/element/element.dart'; 10 import 'package:analyzer/dart/element/element.dart';
11 import 'package:analyzer/dart/element/type.dart'; 11 import 'package:analyzer/dart/element/type.dart';
12 import 'element_helpers.dart' show getStaticType, isInlineJS; 12 import 'element_helpers.dart' show getStaticType, isInlineJS, findAnnotation;
13 import 'js_interop.dart' show isNotNullAnnotation, isNullCheckAnnotation;
13 import 'property_model.dart'; 14 import 'property_model.dart';
14 15
15 /// An inference engine for nullable types. 16 /// An inference engine for nullable types.
16 /// 17 ///
17 /// This can answer questions about whether expressions are nullable 18 /// This can answer questions about whether expressions are nullable
18 /// (see [isNullable]). Given a set of compilation units in a library, it will 19 /// (see [isNullable]). Given a set of compilation units in a library, it will
19 /// determine if locals can be null using flow-insensitive analysis. 20 /// determine if locals can be null using flow-insensitive analysis.
20 /// 21 ///
21 /// The analysis for null expressions is conservative and incomplete, but it can 22 /// The analysis for null expressions is conservative and incomplete, but it can
22 /// optimize some patterns. 23 /// optimize some patterns.
23 // TODO(vsm): Revisit whether we really need this when we get 24 // TODO(vsm): Revisit whether we really need this when we get
24 // better non-nullability in the type system. 25 // better non-nullability in the type system.
25 abstract class NullableTypeInference { 26 abstract class NullableTypeInference {
26 LibraryElement get dartCoreLibrary; 27 LibraryElement get dartCoreLibrary;
27 VirtualFieldModel get virtualFields; 28 VirtualFieldModel get virtualFields;
28 29
30 InterfaceType getImplementationType(DartType type);
29 bool isPrimitiveType(DartType type); 31 bool isPrimitiveType(DartType type);
30 bool isObjectMember(String name); 32 bool isObjectMember(String name);
31 33
32 /// Known non-null local variables. 34 /// Known non-null local variables.
33 HashSet<LocalVariableElement> _notNullLocals; 35 HashSet<LocalVariableElement> _notNullLocals;
34 36
35 void inferNullableTypes(AstNode node) { 37 void inferNullableTypes(AstNode node) {
36 var visitor = new _NullableLocalInference(this); 38 var visitor = new _NullableLocalInference(this);
37 node.accept(visitor); 39 node.accept(visitor);
38 _notNullLocals = visitor.computeNotNullLocals(); 40 _notNullLocals = visitor.computeNotNullLocals();
39 } 41 }
40 42
41 /// Adds a new variable, typically a compiler generated temporary, and record 43 /// Adds a new variable, typically a compiler generated temporary, and record
42 /// whether its type is nullable. 44 /// whether its type is nullable.
43 void addTemporaryVariable(LocalVariableElement local, {bool nullable: true}) { 45 void addTemporaryVariable(LocalVariableElement local, {bool nullable: true}) {
44 if (!nullable) _notNullLocals.add(local); 46 if (!nullable) _notNullLocals.add(local);
45 } 47 }
46 48
47 /// Returns true if [expr] can be null. 49 /// Returns true if [expr] can be null.
48 bool isNullable(Expression expr) => _isNullable(expr); 50 bool isNullable(Expression expr) => _isNullable(expr);
49 51
52 bool _isNonNullMethodInvocation(MethodInvocation expr) {
53 // TODO(vsm): This logic overlaps with the resolver.
54 // Where is the best place to put this?
55 var e = resolutionMap.staticElementForIdentifier(expr.methodName);
56 if (e == null) return false;
57 if (isInlineJS(e)) {
58 // Fix types for JS builtin calls.
59 //
60 // This code was taken from analyzer. It's not super sophisticated:
61 // only looks for the type name in dart:core, so we just copy it here.
62 //
63 // TODO(jmesserly): we'll likely need something that can handle a wider
64 // variety of types, especially when we get to JS interop.
65 var args = expr.argumentList.arguments;
66 var first = args.isNotEmpty ? args.first : null;
67 if (first is SimpleStringLiteral) {
68 var types = first.stringValue;
69 if (types != '' &&
70 types != 'var' &&
71 !types.split('|').contains('Null')) {
72 return true;
73 }
74 }
75 }
76
77 if (e.name == 'identical' && identical(e.library, dartCoreLibrary)) {
78 return true;
79 }
80 // If this is a method call, check to see whether it is to a final
81 // type for which we have a known implementation type (i.e. int, bool,
82 // double, and String), and if so use the element for the implementation
83 // type instead.
84 if (e is MethodElement) {
Jennifer Messerly 2017/08/23 19:49:37 conceptually, are we asking if the method is final
85 Element container = e.enclosingElement;
86 if (container is ClassElement) {
87 DartType targetType = container.type;
88 InterfaceType implType = getImplementationType(targetType);
89 if (implType != null) {
90 MethodElement method = implType.lookUpMethod(e.name, dartCoreLibrary);
91 if (method != null) e = method;
92 }
93 }
94 }
95 // If the method or function is annotated as returning a non-null value
96 // then the result of the call is non-null.
97 return (e is MethodElement || e is FunctionElement) && _assertedNotNull(e);
Jennifer Messerly 2017/08/23 19:49:37 this seems potentially unsafe -- does it work if I
98 }
99
100 bool _isNonNullProperty(Element element, String name) {
101 if (element is! PropertyInducingElement &&
102 element is! PropertyAccessorElement) {
103 return false;
104 }
105 // If this is a reference to an element of a type for which
106 // we have a known implementation type (i.e. int, double,
107 // bool, String), then use the element for the implementation
108 // type.
109 Element container = element.enclosingElement;
110 if (container is ClassElement) {
111 DartType targetType = container.type;
112 InterfaceType implType = getImplementationType(targetType);
Jennifer Messerly 2017/08/23 19:49:38 var?
113 if (implType != null) {
114 PropertyAccessorElement getter =
Jennifer Messerly 2017/08/23 19:49:37 var?
115 implType.lookUpGetter(name, dartCoreLibrary);
116 if (getter != null) element = getter;
117 }
118 }
119 // If the getter is a synthetic element, then any annotations will
120 // be on the variable, so use those instead.
121 if (element is PropertyAccessorElement &&
122 element.isSynthetic &&
123 element.variable != null) {
Jennifer Messerly 2017/08/23 19:49:37 I'm pretty sure this is guaranteed to be non-null,
124 element = (element as PropertyAccessorElement).variable;
Jennifer Messerly 2017/08/23 19:49:37 this could just be `return _assertedNotNull(elemen
125 }
126 // Return true if the element is annotated as returning a non-null value.
127 return _assertedNotNull(element);
Jennifer Messerly 2017/08/23 19:51:11 this one also seems potentially unsafe -- I'm gues
128 }
129
50 /// Returns true if [expr] can be null, optionally using [localIsNullable] 130 /// Returns true if [expr] can be null, optionally using [localIsNullable]
51 /// for locals. 131 /// for locals.
52 /// 132 ///
53 /// If [localIsNullable] is not supplied, this will use the known list of 133 /// If [localIsNullable] is not supplied, this will use the known list of
54 /// [_notNullLocals]. 134 /// [_notNullLocals].
55 bool _isNullable(Expression expr, 135 bool _isNullable(Expression expr,
56 [bool localIsNullable(LocalVariableElement e)]) { 136 [bool localIsNullable(LocalVariableElement e)]) {
57 // TODO(jmesserly): we do recursive calls in a few places. This could 137 // TODO(jmesserly): we do recursive calls in a few places. This could
58 // leads to O(depth) cost for calling this function. We could store the 138 // leads to O(depth) cost for calling this function. We could store the
59 // resulting value if that becomes an issue, so we maintain the invariant 139 // resulting value if that becomes an issue, so we maintain the invariant
60 // that each node is visited once. 140 // that each node is visited once.
61 Element element = null; 141 Element element = null;
142 String name = null;
62 if (expr is PropertyAccess && 143 if (expr is PropertyAccess &&
63 expr.operator?.type != TokenType.QUESTION_PERIOD) { 144 expr.operator?.type != TokenType.QUESTION_PERIOD) {
64 element = expr.propertyName.staticElement; 145 element = expr.propertyName.staticElement;
146 name = expr.propertyName.name;
147 } else if (expr is PrefixedIdentifier) {
148 element = expr.staticElement;
149 name = expr.identifier.name;
65 } else if (expr is Identifier) { 150 } else if (expr is Identifier) {
66 element = expr.staticElement; 151 element = expr.staticElement;
152 name = expr.name;
67 } 153 }
68 if (element != null) { 154 if (element != null) {
155 if (_isNonNullProperty(element, name)) return false;
156
69 // Type literals are not null. 157 // Type literals are not null.
70 if (element is ClassElement || element is FunctionTypeAliasElement) { 158 if (element is ClassElement || element is FunctionTypeAliasElement) {
71 return false; 159 return false;
72 } 160 }
73 161
74 if (element is LocalVariableElement) { 162 if (element is LocalVariableElement) {
75 if (localIsNullable != null) { 163 if (localIsNullable != null) {
76 return localIsNullable(element); 164 return localIsNullable(element);
77 } 165 }
78 return !_notNullLocals.contains(element); 166 return !_notNullLocals.contains(element);
79 } 167 }
80 168
169 if (element is ParameterElement && _assertedNotNull(element)) {
170 return false;
171 }
172
81 if (element is FunctionElement || element is MethodElement) { 173 if (element is FunctionElement || element is MethodElement) {
82 // A function or method. This can't be null. 174 // A function or method. This can't be null.
83 return false; 175 return false;
84 } 176 }
85 177
86 if (element is PropertyAccessorElement && element.isGetter) { 178 if (element is PropertyAccessorElement && element.isGetter) {
87 PropertyInducingElement variable = element.variable; 179 PropertyInducingElement variable = element.variable;
88 var isVirtual = 180 var isVirtual =
89 variable is FieldElement && virtualFields.isVirtual(variable); 181 variable is FieldElement && virtualFields.isVirtual(variable);
90 return isVirtual || (variable.computeConstantValue()?.isNull ?? true); 182 return isVirtual || (variable.computeConstantValue()?.isNull ?? true);
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 type = getStaticType(expr.leftOperand); 254 type = getStaticType(expr.leftOperand);
163 } else if (expr is PrefixExpression) { 255 } else if (expr is PrefixExpression) {
164 if (expr.operator.type == TokenType.BANG) return false; 256 if (expr.operator.type == TokenType.BANG) return false;
165 type = getStaticType(expr.operand); 257 type = getStaticType(expr.operand);
166 } else if (expr is PostfixExpression) { 258 } else if (expr is PostfixExpression) {
167 type = getStaticType(expr.operand); 259 type = getStaticType(expr.operand);
168 } 260 }
169 if (type != null && isPrimitiveType(type)) { 261 if (type != null && isPrimitiveType(type)) {
170 return false; 262 return false;
171 } 263 }
172 if (expr is MethodInvocation) { 264 if (expr is MethodInvocation && _isNonNullMethodInvocation(expr)) {
173 // TODO(vsm): This logic overlaps with the resolver. 265 return false;
174 // Where is the best place to put this?
175 var e = resolutionMap.staticElementForIdentifier(expr.methodName);
176 if (isInlineJS(e)) {
177 // Fix types for JS builtin calls.
178 //
179 // This code was taken from analyzer. It's not super sophisticated:
180 // only looks for the type name in dart:core, so we just copy it here.
181 //
182 // TODO(jmesserly): we'll likely need something that can handle a wider
183 // variety of types, especially when we get to JS interop.
184 var args = expr.argumentList.arguments;
185 var first = args.isNotEmpty ? args.first : null;
186 if (first is SimpleStringLiteral) {
187 var types = first.stringValue;
188 if (types != '' &&
189 types != 'var' &&
190 !types.split('|').contains('Null')) {
191 return false;
192 }
193 }
194 }
195
196 if (e?.name == 'identical' && identical(e.library, dartCoreLibrary)) {
197 return false;
198 }
199 } 266 }
200 267
201 // TODO(ochafik,jmesserly): handle other cases such as: refs to top-level 268 // TODO(ochafik,jmesserly): handle other cases such as: refs to top-level
202 // finals that have been assigned non-nullable values. 269 // finals that have been assigned non-nullable values.
203 270
204 // Failed to recognize a non-nullable case: assume it can be null. 271 // Failed to recognize a non-nullable case: assume it can be null.
205 return true; 272 return true;
206 } 273 }
207 } 274 }
208 275
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 } 319 }
253 320
254 @override 321 @override
255 visitVariableDeclaration(VariableDeclaration node) { 322 visitVariableDeclaration(VariableDeclaration node) {
256 var element = node.element; 323 var element = node.element;
257 var initializer = node.initializer; 324 var initializer = node.initializer;
258 if (element is LocalVariableElement) { 325 if (element is LocalVariableElement) {
259 _locals.add(element); 326 _locals.add(element);
260 if (initializer != null) { 327 if (initializer != null) {
261 _visitAssignment(node.name, initializer); 328 _visitAssignment(node.name, initializer);
262 } else { 329 } else if (!_assertedNotNull(element)) {
263 _nullableLocals.add(element); 330 _nullableLocals.add(element);
264 } 331 }
265 } 332 }
266 super.visitVariableDeclaration(node); 333 super.visitVariableDeclaration(node);
267 } 334 }
268 335
269 @override 336 @override
337 visitForEachStatement(ForEachStatement node) {
338 if (node.identifier == null) {
339 var declaration = node.loopVariable;
340 var element = declaration.element;
341 _locals.add(element);
342 if (!_assertedNotNull(element)) {
343 _nullableLocals.add(element);
344 }
345 } else {
346 var element = node.identifier.staticElement;
347 if (element is LocalVariableElement && !_assertedNotNull(element)) {
348 _nullableLocals.add(element);
349 }
350 }
351 super.visitForEachStatement(node);
352 }
353
354 @override
270 visitCatchClause(CatchClause node) { 355 visitCatchClause(CatchClause node) {
271 var e = node.exceptionParameter?.staticElement; 356 var e = node.exceptionParameter?.staticElement;
272 if (e != null) { 357 if (e != null) {
273 _locals.add(e); 358 _locals.add(e);
274 // TODO(jmesserly): we allow throwing of `null`, for better or worse. 359 // TODO(jmesserly): we allow throwing of `null`, for better or worse.
275 _nullableLocals.add(e); 360 _nullableLocals.add(e);
276 } 361 }
277 362
278 e = node.stackTraceParameter?.staticElement; 363 e = node.stackTraceParameter?.staticElement;
279 if (e != null) _locals.add(e); 364 if (e != null) _locals.add(e);
(...skipping 30 matching lines...) Expand all
310 var op = node.operator.type; 395 var op = node.operator.type;
311 if (op.isIncrementOperator) { 396 if (op.isIncrementOperator) {
312 _visitAssignment(node.operand, node); 397 _visitAssignment(node.operand, node);
313 } 398 }
314 super.visitPrefixExpression(node); 399 super.visitPrefixExpression(node);
315 } 400 }
316 401
317 void _visitAssignment(Expression left, Expression right) { 402 void _visitAssignment(Expression left, Expression right) {
318 if (left is SimpleIdentifier) { 403 if (left is SimpleIdentifier) {
319 var element = left.staticElement; 404 var element = left.staticElement;
320 if (element is LocalVariableElement) { 405 if (element is LocalVariableElement && !_assertedNotNull(element)) {
321 bool visitLocal(LocalVariableElement otherLocal) { 406 bool visitLocal(LocalVariableElement otherLocal) {
322 // Record the assignment. 407 // Record the assignment.
323 _assignments 408 _assignments
324 .putIfAbsent(otherLocal, () => new HashSet.identity()) 409 .putIfAbsent(otherLocal, () => new HashSet.identity())
325 .add(element); 410 .add(element);
326 // Optimistically assume this local is not null. 411 // Optimistically assume this local is not null.
327 // We will validate this assumption later. 412 // We will validate this assumption later.
328 return false; 413 return false;
329 } 414 }
330 415
331 if (_nullInference._isNullable(right, visitLocal)) { 416 if (_nullInference._isNullable(right, visitLocal)) {
332 _nullableLocals.add(element); 417 _nullableLocals.add(element);
333 } 418 }
334 } 419 }
335 } 420 }
336 } 421 }
337 } 422 }
423
424 bool _assertedNotNull(Element e) =>
425 findAnnotation(e, isNotNullAnnotation) != null ||
426 findAnnotation(e, isNullCheckAnnotation) != null;
OLDNEW
« no previous file with comments | « pkg/dev_compiler/lib/src/compiler/js_interop.dart ('k') | pkg/dev_compiler/test/browser/language_tests.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698