blob: 0b4a1660c6a3a7791a02d12d908e13898d40e16a [file] [log] [blame]
// Copyright (c) 2019, 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"
show
Arguments,
Class,
DartType,
Expression,
FunctionNode,
Member,
Name,
NamedExpression,
Procedure,
ProcedureKind,
ReturnStatement,
SuperMethodInvocation,
SuperPropertyGet,
SuperPropertySet,
TypeParameterType,
VariableGet;
import 'package:kernel/transformations/flags.dart' show TransformerFlag;
import "../source/source_class_builder.dart";
import "../problems.dart" show unhandled;
import 'class_hierarchy_builder.dart';
import 'combined_member_signature.dart';
class ForwardingNode {
final CombinedClassMemberSignature _combinedMemberSignature;
final ProcedureKind kind;
ForwardingNode(this._combinedMemberSignature, this.kind);
/// Finishes handling of this node by propagating covariance and creating
/// forwarding stubs if necessary.
Member finalize() => _computeCovarianceFixes();
/// Tag the parameters of [interfaceMember] that need type checks
///
/// Parameters can need type checks for calls coming from statically typed
/// call sites, due to covariant generics and overrides with explicit
/// `covariant` parameters.
///
/// Tag parameters of [interfaceMember] that need such checks when the member
/// occurs in [enclosingClass]'s interface. If parameters need checks but
/// they would not be checked in an inherited implementation, a forwarding
/// stub is introduced as a place to put the checks.
Member _computeCovarianceFixes() {
SourceClassBuilder classBuilder = _combinedMemberSignature.classBuilder;
ClassMember canonicalMember = _combinedMemberSignature.canonicalMember;
Member interfaceMember =
canonicalMember.getMember(_combinedMemberSignature.hierarchy);
if (_combinedMemberSignature.members.length == 1) {
// Covariance can only come from [interfaceMember] so we never need a
// forwarding stub.
if (_combinedMemberSignature.neededLegacyErasure) {
return _combinedMemberSignature.createMemberFromSignature(
// TODO(johnniwinther): Change member signatures to use location
// of origin.
copyLocation: false);
} else {
// Nothing to do.
return interfaceMember;
}
}
bool cannotReuseExistingMember =
!(_combinedMemberSignature.isCanonicalMemberFirst ||
_combinedMemberSignature.isCanonicalMemberDeclared);
bool needsTypeOrCovarianceUpdate =
_combinedMemberSignature.neededNnbdTopMerge ||
_combinedMemberSignature.neededLegacyErasure ||
_combinedMemberSignature.needsCovarianceMerging;
bool stubNeeded = cannotReuseExistingMember ||
(canonicalMember.classBuilder != classBuilder &&
needsTypeOrCovarianceUpdate);
if (stubNeeded) {
Procedure stub = _combinedMemberSignature.createMemberFromSignature(
copyLocation: false);
if (_combinedMemberSignature.needsCovarianceMerging ||
_combinedMemberSignature.needsSuperImpl) {
// This is a forward stub.
Member finalTarget;
if (interfaceMember is Procedure && interfaceMember.isForwardingStub) {
finalTarget = interfaceMember.forwardingStubInterfaceTarget;
} else {
finalTarget =
interfaceMember.memberSignatureOrigin ?? interfaceMember;
}
stub.isMemberSignature = false;
stub.memberSignatureOrigin = null;
stub.isForwardingStub = true;
stub.forwardingStubInterfaceTarget = finalTarget;
if (_combinedMemberSignature.needsSuperImpl) {
_createForwardingImplIfNeeded(
stub.function, stub.name, classBuilder.cls);
}
}
return stub;
} else {
if (_combinedMemberSignature.needsCovarianceMerging) {
_combinedMemberSignature.combinedMemberSignatureCovariance
.applyCovariance(interfaceMember);
}
if (_combinedMemberSignature.needsSuperImpl) {
_createForwardingImplIfNeeded(
interfaceMember.function, interfaceMember.name, classBuilder.cls);
}
return interfaceMember;
}
}
void _createForwardingImplIfNeeded(
FunctionNode function, Name name, Class enclosingClass) {
if (function.body != null) {
// There is already an implementation; nothing further needs to be done.
return;
}
// Find the concrete implementation in the superclass; this is what we need
// to forward to. If we can't find one, then the method is fully abstract
// and we don't need to do anything.
Class superclass = enclosingClass.superclass;
if (superclass == null) return;
Procedure procedure = function.parent;
Member superTarget = _combinedMemberSignature.hierarchy
.getDispatchTargetKernel(
superclass, procedure.name, kind == ProcedureKind.Setter);
if (superTarget == null) return;
if (superTarget is Procedure && superTarget.isForwardingStub) {
Procedure superProcedure = superTarget;
superTarget = superProcedure.forwardingStubSuperTarget;
} else {
superTarget = superTarget.memberSignatureOrigin ?? superTarget;
}
procedure.isAbstract = false;
if (!procedure.isForwardingStub) {
// This procedure exists abstractly in the source code; we need to make it
// concrete and give it a body that is a forwarding stub. This situation
// is called a "forwarding semi-stub".
procedure.isForwardingStub = true;
procedure.isForwardingSemiStub = true;
}
List<Expression> positionalArguments = function.positionalParameters
.map<Expression>((parameter) => new VariableGet(parameter))
.toList();
List<NamedExpression> namedArguments = function.namedParameters
.map((parameter) =>
new NamedExpression(parameter.name, new VariableGet(parameter)))
.toList();
List<DartType> typeArguments = function.typeParameters
.map<DartType>((typeParameter) =>
new TypeParameterType.withDefaultNullabilityForLibrary(
typeParameter, enclosingClass.enclosingLibrary))
.toList();
Arguments arguments = new Arguments(positionalArguments,
types: typeArguments, named: namedArguments);
Expression superCall;
assert(superTarget != null,
"No super target found for '${name}' in ${enclosingClass}.");
assert(
!superTarget.isAbstract,
"Abstract super target $superTarget found for '${name}' in "
"${enclosingClass}.");
switch (kind) {
case ProcedureKind.Method:
case ProcedureKind.Operator:
superCall = new SuperMethodInvocation(name, arguments, superTarget);
break;
case ProcedureKind.Getter:
superCall = new SuperPropertyGet(name, superTarget);
break;
case ProcedureKind.Setter:
superCall =
new SuperPropertySet(name, positionalArguments[0], superTarget);
break;
default:
unhandled('$kind', '_createForwardingImplIfNeeded', -1, null);
break;
}
function.body = new ReturnStatement(superCall)..parent = function;
procedure.transformerFlags |= TransformerFlag.superCalls;
procedure.forwardingStubSuperTarget = superTarget;
}
}