blob: 15d7550b8d31b498a6691909e3db939e070739aa [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.
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/class_hierarchy.dart' as ir;
import 'package:kernel/core_types.dart' as ir;
import 'package:kernel/type_environment.dart' as ir;
import '../common.dart';
import '../elements/entities.dart';
/// Returns a textual representation of [node] that include the runtime type and
/// hash code of the node and a one line prefix of the node toString text.
String nodeToDebugString(ir.Node node, [int textLength = 40]) {
String blockText = node.toString().replaceAll('\n', ' ');
if (blockText.length > textLength) {
blockText = blockText.substring(0, textLength - 3) + '...';
}
return '(${node.runtimeType}:${node.hashCode})${blockText}';
}
/// Comparator for the canonical order or named parameters.
// TODO(johnniwinther): Remove this when named parameters are sorted in dill.
int namedOrdering(ir.VariableDeclaration a, ir.VariableDeclaration b) {
return a.name.compareTo(b.name);
}
/// Comparator for the declaration order of parameters.
int nativeOrdering(ir.VariableDeclaration a, ir.VariableDeclaration b) {
return a.fileOffset.compareTo(b.fileOffset);
}
SourceSpan computeSourceSpanFromTreeNode(ir.TreeNode node) {
// TODO(johnniwinther): Use [ir.Location] directly as a [SourceSpan].
Uri uri;
int offset;
while (node != null) {
if (node.fileOffset != ir.TreeNode.noOffset) {
offset = node.fileOffset;
// @patch annotations have no location.
uri = node.location?.file;
break;
}
node = node.parent;
}
if (uri != null) {
return new SourceSpan(uri, offset, offset + 1);
}
return null;
}
/// Returns the `AsyncMarker` corresponding to `node.asyncMarker`.
AsyncMarker getAsyncMarker(ir.FunctionNode node) {
switch (node.asyncMarker) {
case ir.AsyncMarker.Async:
return AsyncMarker.ASYNC;
case ir.AsyncMarker.AsyncStar:
return AsyncMarker.ASYNC_STAR;
case ir.AsyncMarker.Sync:
return AsyncMarker.SYNC;
case ir.AsyncMarker.SyncStar:
return AsyncMarker.SYNC_STAR;
case ir.AsyncMarker.SyncYielding:
default:
throw new UnsupportedError(
"Async marker ${node.asyncMarker} is not supported.");
}
}
/// Returns the `Variance` corresponding to `node.variance`.
Variance convertVariance(ir.TypeParameter node) {
if (node.isLegacyCovariant) return Variance.legacyCovariant;
switch (node.variance) {
case ir.Variance.covariant:
return Variance.covariant;
case ir.Variance.contravariant:
return Variance.contravariant;
case ir.Variance.invariant:
return Variance.invariant;
default:
throw new UnsupportedError("Variance ${node.variance} is not supported.");
}
}
/// Returns `true` if [node] is a null literal or a null constant.
bool isNullLiteral(ir.Expression node) {
return node is ir.NullLiteral ||
(node is ir.ConstantExpression && node.constant is ir.NullConstant);
}
/// Kernel encodes a null-aware expression `a?.b` as
///
/// let final #1 = a in #1 == null ? null : #1.b
///
/// [getNullAwareExpression] recognizes such expressions storing the result in
/// a [NullAwareExpression] object.
///
/// [syntheticVariable] holds the synthesized `#1` variable. [expression] holds
/// the `#1.b` expression. [receiver] returns `a` expression. [parent] returns
/// the parent of the let node, i.e. the parent node of the original null-aware
/// expression. [let] returns the let node created for the encoding.
class NullAwareExpression {
final ir.VariableDeclaration syntheticVariable;
final ir.Expression expression;
NullAwareExpression(this.syntheticVariable, this.expression);
ir.Expression get receiver => syntheticVariable.initializer;
ir.TreeNode get parent => syntheticVariable.parent.parent;
ir.Let get let => syntheticVariable.parent;
@override
String toString() => let.toString();
}
NullAwareExpression getNullAwareExpression(ir.TreeNode node) {
if (node is ir.Let) {
ir.Expression body = node.body;
if (node.variable.name == null &&
node.variable.isFinal &&
body is ir.ConditionalExpression &&
body.condition is ir.MethodInvocation &&
isNullLiteral(body.then)) {
ir.MethodInvocation invocation = body.condition;
ir.Expression receiver = invocation.receiver;
if (invocation.name.text == '==' &&
receiver is ir.VariableGet &&
receiver.variable == node.variable &&
isNullLiteral(invocation.arguments.positional.single)) {
// We have
// let #t1 = e0 in #t1 == null ? null : e1
return new NullAwareExpression(node.variable, body.otherwise);
}
}
}
return null;
}
/// Check whether [node] is immediately guarded by a
/// [ir.CheckLibraryIsLoaded], and hence the node is a deferred access.
ir.LibraryDependency getDeferredImport(ir.TreeNode node) {
// Note: this code relies on the CFE generating the code as we expect it here.
// If one day we optimize away redundant CheckLibraryIsLoaded instructions,
// we'd need to derive this information directly from the CFE (See #35005),
ir.TreeNode parent = node.parent;
// TODO(sigmund): remove when CFE generates the correct tree (#35320). For
// instance, it currently generates
//
// let _ = check(prefix) in (prefix::field.property)
//
// instead of:
//
// (let _ = check(prefix) in prefix::field).property
if (node is ir.StaticGet || node is ir.ConstantExpression) {
while (parent is ir.PropertyGet || parent is ir.MethodInvocation) {
parent = parent.parent;
}
}
if (parent is ir.Let) {
var initializer = parent.variable.initializer;
if (initializer is ir.CheckLibraryIsLoaded) {
return initializer.import;
}
}
return null;
}
class _FreeVariableVisitor implements ir.DartTypeVisitor<bool> {
const _FreeVariableVisitor();
bool visit(ir.DartType type) {
if (type != null) return type.accept(this);
return false;
}
bool visitList(List<ir.DartType> types) {
for (ir.DartType type in types) {
if (visit(type)) return true;
}
return false;
}
@override
bool visitTypedefType(ir.TypedefType node) {
return visitList(node.typeArguments);
}
@override
bool visitTypeParameterType(ir.TypeParameterType node) {
return true;
}
@override
bool visitFunctionType(ir.FunctionType node) {
if (visit(node.returnType)) return true;
if (visitList(node.positionalParameters)) return true;
for (ir.NamedType namedType in node.namedParameters) {
if (visit(namedType.type)) return true;
}
return false;
}
@override
bool visitInterfaceType(ir.InterfaceType node) {
return visitList(node.typeArguments);
}
@override
bool visitFutureOrType(ir.FutureOrType node) {
return visit(node.typeArgument);
}
@override
bool visitBottomType(ir.BottomType node) => false;
@override
bool visitNeverType(ir.NeverType node) => false;
@override
bool visitVoidType(ir.VoidType node) => false;
@override
bool visitDynamicType(ir.DynamicType node) => false;
@override
bool visitInvalidType(ir.InvalidType node) => false;
@override
bool defaultDartType(ir.DartType node) {
throw new UnsupportedError("FreeVariableVisitor.defaultTypeNode");
}
}
/// Returns `true` if [type] contains a type variable.
///
/// All type variables (class type variables, generic method type variables,
/// and function type variables) are considered.
bool containsFreeVariables(ir.DartType type) =>
type.accept(const _FreeVariableVisitor());