Index: lib/src/codegen/nullability_inferrer.dart |
diff --git a/lib/src/codegen/nullability_inferrer.dart b/lib/src/codegen/nullability_inferrer.dart |
deleted file mode 100644 |
index 7104f76d8ac6ae476bd9b04996ba963e18304511..0000000000000000000000000000000000000000 |
--- a/lib/src/codegen/nullability_inferrer.dart |
+++ /dev/null |
@@ -1,164 +0,0 @@ |
-// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-library dev_compiler.src.codegen.nullability_inferrer; |
- |
-import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
-import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
-import 'package:analyzer/src/generated/element.dart'; |
-import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
- |
-import 'assignments_index.dart'; |
-import 'js_codegen.dart' show TemporaryVariableElement; |
-import '../utils.dart' show isInlineJS, DirectedGraph; |
- |
-typedef bool NullableExpressionPredicate(Expression expr); |
- |
-typedef DartType _StaticTypeGetter(Expression e); |
-typedef bool _JSBuiltinTypePredicate(DartType t); |
- |
-/// Infers flow-insensitive nullability for local variables. |
-// TODO(ochafik): Use flow-sensitive inference. |
-class NullabilityInferrer { |
- /// Index of assignments of [LocalVariableElement]s defined in this inferer's |
- /// context. |
- final Map<LocalVariableElement, List<Expression>> _assignments; |
- final _StaticTypeGetter getStaticType; |
- final _JSBuiltinTypePredicate isJSBuiltinType; |
- |
- NullabilityInferrer(Iterable<AstNode> context, |
- {this.getStaticType, this.isJSBuiltinType}) |
- : _assignments = indexLocalAssignments(context); |
- |
- /// Tests whether [expr] is nullable, with assumptions on local variable |
- /// nullability provided by [isNullableLocal]. |
- /// |
- /// If [assignmentsTarget] and [assignments] are not null, this also builds an |
- /// assignments graph (records assignments from each variable to other |
- /// variables it can be assigned to). For example, given an assignment: |
- /// |
- /// y = x; |
- /// |
- /// This will lead us to conclude that `y` can be any value that `x` could |
- /// hold at that location. For example, if we are not considering control |
- /// flow, this means `y` could contain any value that can ever be assigned to |
- /// `x`. |
- bool _isNullableExpression(Expression expr, |
- [bool isNullableLocal(LocalVariableElement e), |
- LocalVariableElement assignmentsTarget, |
- DirectedGraph<LocalVariableElement> assignments]) { |
- // TODO(vsm): Revisit whether we really need this when we get |
- // better non-nullability in the type system. |
- // TODO(jmesserly): we do recursive calls in a few places. This could |
- // leads to O(depth) cost for calling this function. We could store the |
- // resulting value if that becomes an issue, so we maintain the invariant |
- // that each node is visited once. |
- |
- if (expr is SimpleIdentifier) { |
- // Type literals are not null. |
- var e = expr.staticElement; |
- if (e is ClassElement || e is FunctionTypeAliasElement) { |
- return false; |
- } |
- |
- if (e is LocalVariableElement && isNullableLocal != null) { |
- assignments?.addEdge(e, assignmentsTarget); |
- return isNullableLocal(e); |
- } |
- return true; |
- } |
- bool recurse(Expression x) => _isNullableExpression( |
- x, isNullableLocal, assignmentsTarget, assignments); |
- |
- if (expr is Literal) return expr is NullLiteral; |
- if (expr is IsExpression) return false; |
- if (expr is FunctionExpression) return false; |
- if (expr is ThisExpression) return false; |
- if (expr is SuperExpression) return false; |
- if (expr is CascadeExpression) return recurse(expr.target); |
- if (expr is ConditionalExpression) { |
- return recurse(expr.thenExpression) || recurse(expr.elseExpression); |
- } |
- if (expr is ParenthesizedExpression) { |
- return recurse(expr.expression); |
- } |
- |
- DartType type = null; |
- if (expr is BinaryExpression) { |
- switch (expr.operator.type) { |
- case TokenType.EQ_EQ: |
- case TokenType.BANG_EQ: |
- case TokenType.AMPERSAND_AMPERSAND: |
- case TokenType.BAR_BAR: |
- return false; |
- case TokenType.QUESTION_QUESTION: |
- return recurse(expr.leftOperand) && recurse(expr.rightOperand); |
- } |
- type = getStaticType(expr.leftOperand); |
- } else if (expr is PrefixExpression) { |
- if (expr.operator.type == TokenType.BANG) return false; |
- type = getStaticType(expr.operand); |
- } else if (expr is PostfixExpression) { |
- type = getStaticType(expr.operand); |
- } |
- if (type != null && isJSBuiltinType(type)) { |
- return false; |
- } |
- if (expr is MethodInvocation) { |
- // TODO(vsm): This logic overlaps with the resolver. |
- // Where is the best place to put this? |
- var e = expr.methodName.staticElement; |
- if (isInlineJS(e)) { |
- // Fix types for JS builtin calls. |
- // |
- // This code was taken from analyzer. It's not super sophisticated: |
- // only looks for the type name in dart:core, so we just copy it here. |
- // |
- // TODO(jmesserly): we'll likely need something that can handle a wider |
- // variety of types, especially when we get to JS interop. |
- var args = expr.argumentList.arguments; |
- var first = args.isNotEmpty ? args.first : null; |
- if (first is SimpleStringLiteral) { |
- var types = first.stringValue; |
- if (!types.split('|').contains('Null')) { |
- return false; |
- } |
- } |
- } |
- // TODO(ochafik): Handle `identical` invocations. |
- } |
- |
- // TODO(ochafik): Handle PrefixedIdentifier, refs to top-level finals |
- // that have been assigned non-nullable values, non-generative constructor |
- // calls, refs to local functions... |
- |
- // Failed to recognize a non-nullable case: assume it's trivially nullable. |
- return true; |
- } |
- |
- NullableExpressionPredicate buildNullabilityPredicate() { |
- // Collect the transitive closure of every variable that can be assigned |
- // trivially nullable values. |
- var assignmentsGraph = new DirectedGraph<LocalVariableElement>(); |
- |
- /// Detect vars that are "trivially nullable" (i.e. provably nullable |
- /// if we assume all known variables are non-nullable). |
- var trivialNullables = new Set<LocalVariableElement>(); |
- _assignments.forEach((local, expressions) { |
- for (var expr in expressions) { |
- // In the unlikely event of an unknown var, assume it's nullable. |
- var isTriviallyNullable = _isNullableExpression(expr, |
- (e) => e is TemporaryVariableElement, local, assignmentsGraph); |
- if (isTriviallyNullable) trivialNullables.add(local); |
- } |
- }); |
- var nullables = assignmentsGraph.getTransitiveClosure(trivialNullables); |
- |
- bool isNullableLocal(LocalVariableElement e) { |
- // TODO(jmesserly): we should be able to make this work for temps too. |
- return e is TemporaryVariableElement || nullables.contains(e); |
- } |
- return (Expression expr) => _isNullableExpression(expr, isNullableLocal); |
- } |
-} |