| // Copyright (c) 2018, 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 vm.bytecode.nullability_detector; |
| |
| import 'package:kernel/ast.dart'; |
| import 'dbc.dart'; |
| import 'recognized_methods.dart' show RecognizedMethods; |
| |
| class NullabilityDetector { |
| final _IsNullableVisitor _isNullableVisitor; |
| |
| NullabilityDetector(RecognizedMethods recognizedMethods) |
| : _isNullableVisitor = new _IsNullableVisitor(recognizedMethods); |
| |
| bool isNullable(Expression expr) => expr.accept(_isNullableVisitor); |
| } |
| |
| class _IsNullableVisitor extends ExpressionVisitor<bool> { |
| final RecognizedMethods recognizedMethods; |
| |
| _IsNullableVisitor(this.recognizedMethods); |
| |
| @override |
| bool defaultExpression(Expression node) => true; |
| |
| @override |
| bool visitNullLiteral(NullLiteral node) => true; |
| |
| // All basic literals except NullLiteral are non-nullable. |
| @override |
| bool defaultBasicLiteral(BasicLiteral node) => false; |
| |
| @override |
| bool visitVariableGet(VariableGet node) { |
| final v = node.variable; |
| if ((v.isConst || v.isFinal) && v.initializer != null) { |
| return v.initializer.accept(this); |
| } |
| return true; |
| } |
| |
| @override |
| bool visitVariableSet(VariableSet node) => node.value.accept(this); |
| |
| @override |
| bool visitMethodInvocation(MethodInvocation node) { |
| final Opcode opcode = recognizedMethods.specializedBytecodeFor(node); |
| if (opcode != null) { |
| return !_nonNullableBytecodeInstructions.contains(opcode); |
| } |
| return true; |
| } |
| |
| @override |
| bool visitConstructorInvocation(ConstructorInvocation node) => false; |
| |
| @override |
| bool visitNot(Not node) => false; |
| |
| @override |
| bool visitLogicalExpression(LogicalExpression node) => false; |
| |
| @override |
| bool visitConditionalExpression(ConditionalExpression node) => |
| node.then.accept(this) || node.otherwise.accept(this); |
| |
| @override |
| bool visitStringConcatenation(StringConcatenation node) => false; |
| |
| @override |
| bool visitIsExpression(IsExpression node) => false; |
| |
| @override |
| bool visitAsExpression(AsExpression node) => node.operand.accept(this); |
| |
| @override |
| bool visitSymbolLiteral(SymbolLiteral node) => false; |
| |
| @override |
| bool visitTypeLiteral(TypeLiteral node) => false; |
| |
| @override |
| bool visitThisExpression(ThisExpression node) => false; |
| |
| @override |
| bool visitRethrow(Rethrow node) => false; |
| |
| @override |
| bool visitThrow(Throw node) => false; |
| |
| @override |
| bool visitListLiteral(ListLiteral node) => false; |
| |
| @override |
| bool visitMapLiteral(MapLiteral node) => false; |
| |
| @override |
| bool visitFunctionExpression(FunctionExpression node) => false; |
| |
| @override |
| bool visitConstantExpression(ConstantExpression node) => |
| node.constant is NullConstant; |
| |
| @override |
| bool visitLet(Let node) => node.body.accept(this); |
| |
| @override |
| bool visitInstantiation(Instantiation node) => false; |
| } |
| |
| final _nonNullableBytecodeInstructions = new Set<Opcode>.from([ |
| Opcode.kBooleanNegateTOS, |
| Opcode.kEqualsNull, |
| Opcode.kNegateInt, |
| Opcode.kAddInt, |
| Opcode.kSubInt, |
| Opcode.kMulInt, |
| Opcode.kTruncDivInt, |
| Opcode.kModInt, |
| Opcode.kBitAndInt, |
| Opcode.kBitOrInt, |
| Opcode.kBitXorInt, |
| Opcode.kShlInt, |
| Opcode.kShrInt, |
| Opcode.kCompareIntEq, |
| Opcode.kCompareIntGt, |
| Opcode.kCompareIntLt, |
| Opcode.kCompareIntGe, |
| Opcode.kCompareIntLe, |
| ]); |