blob: 414807b2089e6a14e57ad7c7488353de0e336750 [file] [log] [blame]
// 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,
Opcode.kNegateDouble,
Opcode.kAddDouble,
Opcode.kSubDouble,
Opcode.kMulDouble,
Opcode.kDivDouble,
Opcode.kCompareDoubleEq,
Opcode.kCompareDoubleGt,
Opcode.kCompareDoubleLt,
Opcode.kCompareDoubleGe,
Opcode.kCompareDoubleLe,
]);