blob: a2f9279ab3d46d48e7c489d3b5ad8d090dca1f52 [file] [log] [blame]
// Copyright (c) 2016, 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 '../common.dart';
import '../common/names.dart';
import '../compiler.dart';
import '../constants/expressions.dart';
import '../dart_types.dart';
import '../elements/elements.dart';
import '../js_backend/backend.dart' show JavaScriptBackend;
import '../kernel/kernel.dart';
import '../kernel/kernel_debug.dart';
import '../kernel/kernel_visitor.dart';
import '../resolution/registry.dart' show ResolutionWorldImpactBuilder;
import '../universe/feature.dart';
import '../universe/selector.dart';
import '../universe/use.dart';
import 'kernel_ast_adapter.dart';
import '../common/resolution.dart';
/// Computes the [ResolutionImpact] for [resolvedAst] through kernel.
ResolutionImpact build(Compiler compiler, ResolvedAst resolvedAst) {
AstElement element = resolvedAst.element.implementation;
JavaScriptBackend backend = compiler.backend;
Kernel kernel = backend.kernelTask.kernel;
KernelImpactBuilder builder =
new KernelImpactBuilder(resolvedAst, compiler, kernel);
if (element.isFunction) {
ir.Procedure function = kernel.functions[element];
if (function == null) {
print("FOUND NULL FUNCTION: $element");
} else {
return builder.buildProcedure(function);
}
} else {
ir.Field field = kernel.fields[element];
if (field == null) {
print("FOUND NULL FUNCTION: $element");
} else {
return builder.buildField(field);
}
}
return null;
}
class KernelImpactBuilder extends ir.Visitor {
final ResolvedAst resolvedAst;
final Compiler compiler;
JavaScriptBackend get backend => compiler.backend;
ResolutionWorldImpactBuilder impactBuilder;
KernelAstAdapter astAdapter;
KernelImpactBuilder(this.resolvedAst, this.compiler, Kernel kernel) {
this.impactBuilder =
new ResolutionWorldImpactBuilder('${resolvedAst.element}');
this.astAdapter = new KernelAstAdapter(kernel, compiler.backend,
resolvedAst, kernel.nodeToAst, kernel.nodeToElement);
}
/// Add a checked-mode type use of [type] if it is not `dynamic`.
DartType checkType(ir.DartType irType) {
DartType type = astAdapter.getDartType(irType);
if (!type.isDynamic) {
impactBuilder.registerTypeUse(new TypeUse.checkedModeCheck(type));
}
return type;
}
/// Add a checked-mode type use of return type and parameters of [node].
void checkFunctionTypes(ir.FunctionNode node) {
checkType(node.returnType);
node.positionalParameters.forEach((v) => checkType(v.type));
node.namedParameters.forEach((v) => checkType(v.type));
}
ResolutionImpact buildField(ir.Field field) {
checkType(field.type);
if (field.initializer != null) {
visitNode(field.initializer);
} else {
impactBuilder.registerFeature(Feature.FIELD_WITHOUT_INITIALIZER);
}
return impactBuilder;
}
ResolutionImpact buildProcedure(ir.Procedure procedure) {
if (procedure.kind == ir.ProcedureKind.Method ||
procedure.kind == ir.ProcedureKind.Operator) {
checkFunctionTypes(procedure.function);
visitNode(procedure.function.body);
} else {
compiler.reporter.internalError(
resolvedAst.element,
"Unable to compute resolution impact for this kind of Kernel "
"procedure: ${procedure.kind}");
}
return impactBuilder;
}
void visitNode(ir.Node node) => node?.accept(this);
void visitNodes(Iterable<ir.Node> nodes) {
nodes.forEach(visitNode);
}
@override
void visitBlock(ir.Block block) => visitNodes(block.statements);
@override
void visitExpressionStatement(ir.ExpressionStatement exprStatement) {
visitNode(exprStatement.expression);
}
@override
void visitReturnStatement(ir.ReturnStatement returnStatement) {
visitNode(returnStatement.expression);
}
@override
void visitIfStatement(ir.IfStatement ifStatement) {
visitNode(ifStatement.condition);
visitNode(ifStatement.then);
visitNode(ifStatement.otherwise);
}
@override
void visitIntLiteral(ir.IntLiteral literal) {
impactBuilder
.registerConstantLiteral(new IntConstantExpression(literal.value));
}
@override
void visitDoubleLiteral(ir.DoubleLiteral literal) {
impactBuilder
.registerConstantLiteral(new DoubleConstantExpression(literal.value));
}
@override
void visitBoolLiteral(ir.BoolLiteral literal) {
impactBuilder
.registerConstantLiteral(new BoolConstantExpression(literal.value));
}
@override
void visitStringLiteral(ir.StringLiteral literal) {
impactBuilder
.registerConstantLiteral(new StringConstantExpression(literal.value));
}
@override
void visitSymbolLiteral(ir.SymbolLiteral literal) {
impactBuilder.registerConstSymbolName(literal.value);
}
@override
void visitNullLiteral(ir.NullLiteral literal) {
impactBuilder.registerConstantLiteral(new NullConstantExpression());
}
@override
void visitListLiteral(ir.ListLiteral literal) {
visitNodes(literal.expressions);
DartType elementType = checkType(literal.typeArgument);
impactBuilder.registerListLiteral(new ListLiteralUse(
compiler.coreTypes.listType(elementType),
isConstant: literal.isConst,
isEmpty: literal.expressions.isEmpty));
}
@override
void visitMapLiteral(ir.MapLiteral literal) {
visitNodes(literal.entries);
DartType keyType = checkType(literal.keyType);
DartType valueType = checkType(literal.valueType);
impactBuilder.registerMapLiteral(new MapLiteralUse(
compiler.coreTypes.mapType(keyType, valueType),
isConstant: literal.isConst,
isEmpty: literal.entries.isEmpty));
}
void visitMapEntry(ir.MapEntry entry) {
visitNode(entry.key);
visitNode(entry.value);
}
void _visitArguments(ir.Arguments arguments) {
arguments.positional.forEach(visitNode);
arguments.named.forEach(visitNode);
}
@override
void visitStaticInvocation(ir.StaticInvocation invocation) {
_visitArguments(invocation.arguments);
Element target = astAdapter.getElement(invocation.target).declaration;
if (target.isFactoryConstructor) {
impactBuilder.registerStaticUse(new StaticUse.constructorInvoke(
target, astAdapter.getCallStructure(invocation.arguments)));
// TODO(johnniwinther): We should not mark the type as instantiated but
// rather follow the type arguments directly.
//
// Consider this:
//
// abstract class A<T> {
// factory A.regular() => new B<T>();
// factory A.redirect() = B<T>;
// }
//
// class B<T> implements A<T> {}
//
// main() {
// print(new A<int>.regular() is B<int>);
// print(new A<String>.redirect() is B<String>);
// }
//
// To track that B is actually instantiated as B<int> and B<String> we
// need to follow the type arguments passed to A.regular and A.redirect
// to B. Currently, we only do this soundly if we register A<int> and
// A<String> as instantiated. We should instead register that A.T is
// instantiated as int and String.
ClassElement cls =
astAdapter.getElement(invocation.target.enclosingClass);
List<DartType> typeArguments =
astAdapter.getDartTypes(invocation.arguments.types);
impactBuilder.registerTypeUse(
new TypeUse.instantiation(new InterfaceType(cls, typeArguments)));
if (typeArguments.any((DartType type) => !type.isDynamic)) {
impactBuilder.registerFeature(Feature.TYPE_VARIABLE_BOUNDS_CHECK);
}
} else {
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
target, astAdapter.getCallStructure(invocation.arguments)));
}
}
@override
void visitStaticGet(ir.StaticGet node) {
ir.Member target = node.target;
Element element = astAdapter.getElement(target).declaration;
if (target is ir.Procedure && target.kind == ir.ProcedureKind.Method) {
impactBuilder.registerStaticUse(new StaticUse.staticTearOff(element));
} else {
impactBuilder.registerStaticUse(new StaticUse.staticGet(element));
}
}
@override
void visitMethodInvocation(ir.MethodInvocation invocation) {
var receiver = invocation.receiver;
if (receiver is ir.VariableGet &&
receiver.variable.isFinal &&
receiver.variable.parent is ir.FunctionDeclaration) {
// Invocation of a local function. No need for dynamic use.
} else {
visitNode(invocation.receiver);
impactBuilder.registerDynamicUse(
new DynamicUse(astAdapter.getSelector(invocation), null));
}
_visitArguments(invocation.arguments);
}
@override
void visitPropertyGet(ir.PropertyGet node) {
visitNode(node.receiver);
impactBuilder.registerDynamicUse(new DynamicUse(
new Selector.getter(astAdapter.getName(node.name)), null));
}
@override
void visitPropertySet(ir.PropertySet node) {
visitNode(node.receiver);
visitNode(node.value);
impactBuilder.registerDynamicUse(new DynamicUse(
new Selector.setter(astAdapter.getName(node.name)), null));
}
@override
void visitAssertStatement(ir.AssertStatement node) {
impactBuilder.registerFeature(
node.message != null ? Feature.ASSERT_WITH_MESSAGE : Feature.ASSERT);
visitNode(node.condition);
visitNode(node.message);
}
@override
void visitStringConcatenation(ir.StringConcatenation node) {
impactBuilder.registerFeature(Feature.STRING_INTERPOLATION);
impactBuilder.registerFeature(Feature.STRING_JUXTAPOSITION);
visitNodes(node.expressions);
}
@override
void visitFunctionDeclaration(ir.FunctionDeclaration node) {
impactBuilder
.registerStaticUse(new StaticUse.closure(astAdapter.getElement(node)));
checkFunctionTypes(node.function);
visitNode(node.function.body);
}
@override
void visitFunctionExpression(ir.FunctionExpression node) {
impactBuilder
.registerStaticUse(new StaticUse.closure(astAdapter.getElement(node)));
checkFunctionTypes(node.function);
visitNode(node.function.body);
}
@override
void visitVariableDeclaration(ir.VariableDeclaration node) {
checkType(node.type);
if (node.initializer != null) {
visitNode(node.initializer);
} else {
impactBuilder.registerFeature(Feature.LOCAL_WITHOUT_INITIALIZER);
}
}
// TODO(johnniwinther): Make this throw and visit child nodes explicitly
// instead to ensure that we don't visit unwanted parts of the ir.
@override
void defaultNode(ir.Node node) => node.visitChildren(this);
}