blob: 2ce8d7d569a8cc28fc40a6665e6d99c04afa17a2 [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.");
}
}
/// 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 &&
body.then is ir.NullLiteral) {
ir.MethodInvocation invocation = body.condition;
ir.Expression receiver = invocation.receiver;
if (invocation.name.name == '==' &&
receiver is ir.VariableGet &&
receiver.variable == node.variable &&
invocation.arguments.positional.single is ir.NullLiteral) {
// 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) {
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 visitBottomType(ir.BottomType 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());