blob: ae3a7d37747d8c7993a9c433c2b5761a737b3a4d [file] [log] [blame]
// Copyright (c) 2021, 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';
import 'package:kernel/type_algebra.dart';
import '../builder/member_builder.dart';
import '../source/source_library_builder.dart';
import 'kernel_api.dart';
import 'kernel_helper.dart';
const String _tearOffNamePrefix = '_#';
const String _tearOffNameSuffix = '#tearOff';
/// Creates the synthesized name to use for the lowering of the tear off of a
/// constructor or factory by the given [name] in [library].
Name constructorTearOffName(String name, Library library) {
return new Name(
'$_tearOffNamePrefix'
'${name.isEmpty ? 'new' : name}'
'$_tearOffNameSuffix',
library);
}
/// Creates the synthesized name to use for the lowering of the tear off of a
/// constructor or factory by the given [constructorName] in [library].
Name typedefTearOffName(
String typedefName, String constructorName, Library library) {
return new Name(
'$_tearOffNamePrefix'
'$typedefName#'
'${constructorName.isEmpty ? 'new' : constructorName}'
'$_tearOffNameSuffix',
library);
}
/// Returns the name of the corresponding constructor or factory if [name] is
/// the synthesized name of a lowering of the tear off of a constructor or
/// factory. Returns `null` otherwise.
String? extractConstructorNameFromTearOff(Name name) {
if (name.text.startsWith(_tearOffNamePrefix) &&
name.text.endsWith(_tearOffNameSuffix) &&
name.text.length >
_tearOffNamePrefix.length + _tearOffNameSuffix.length) {
String text =
name.text.substring(0, name.text.length - _tearOffNameSuffix.length);
text = text.substring(_tearOffNamePrefix.length);
if (text.contains('#')) {
return null;
}
return text == 'new' ? '' : text;
}
return null;
}
/// If [name] is the synthesized name of a lowering of a typedef tear off, a
/// list containing the [String] name of the typedef and the [Name] name of the
/// corresponding constructor or factory is returned. Returns `null` otherwise.
List<Object>? extractTypedefNameFromTearOff(Name name) {
if (name.text.startsWith(_tearOffNamePrefix) &&
name.text.endsWith(_tearOffNameSuffix) &&
name.text.length >
_tearOffNamePrefix.length + _tearOffNameSuffix.length) {
String text =
name.text.substring(0, name.text.length - _tearOffNameSuffix.length);
text = text.substring(_tearOffNamePrefix.length);
int hashIndex = text.indexOf('#');
if (hashIndex == -1) {
return null;
}
String typedefName = text.substring(0, hashIndex);
String constructorName = text.substring(hashIndex + 1);
constructorName = constructorName == 'new' ? '' : constructorName;
return [typedefName, new Name(constructorName, name.library)];
}
return null;
}
/// Creates the [Procedure] for the lowering of a generative constructor of
/// the given [name] in [compilationUnit].
///
/// If constructor tear off lowering is not enabled, `null` is returned.
Procedure? createConstructorTearOffProcedure(String name,
SourceLibraryBuilder compilationUnit, Uri fileUri, int fileOffset,
{required bool forAbstractClassOrEnum}) {
if (!forAbstractClassOrEnum &&
compilationUnit
.loader.target.backendTarget.isConstructorTearOffLoweringEnabled) {
return _createTearOffProcedure(
compilationUnit,
constructorTearOffName(name, compilationUnit.library),
fileUri,
fileOffset);
}
return null;
}
/// Creates the [Procedure] for the lowering of a non-redirecting factory of
/// the given [name] in [compilationUnit].
///
/// If constructor tear off lowering is not enabled, `null` is returned.
Procedure? createFactoryTearOffProcedure(String name,
SourceLibraryBuilder compilationUnit, Uri fileUri, int fileOffset) {
if (compilationUnit
.loader.target.backendTarget.isFactoryTearOffLoweringEnabled) {
return _createTearOffProcedure(
compilationUnit,
constructorTearOffName(name, compilationUnit.library),
fileUri,
fileOffset);
}
return null;
}
/// Creates the [Procedure] for the lowering of a typedef tearoff of a
/// constructor of the given [name] in with the typedef defined in
/// [libraryBuilder].
Procedure createTypedefTearOffProcedure(String typedefName, String name,
SourceLibraryBuilder libraryBuilder, Uri fileUri, int fileOffset) {
return _createTearOffProcedure(
libraryBuilder,
typedefTearOffName(typedefName, name, libraryBuilder.library),
fileUri,
fileOffset);
}
/// Creates the parameters and body for [tearOff] based on [constructor] in
/// [enclosingClass].
void buildConstructorTearOffProcedure(Procedure tearOff, Member constructor,
Class enclosingClass, SourceLibraryBuilder libraryBuilder) {
assert(
constructor is Constructor ||
(constructor is Procedure && constructor.isFactory) ||
(constructor is Procedure && constructor.isStatic),
"Unexpected constructor tear off target $constructor "
"(${constructor.runtimeType}).");
int fileOffset = tearOff.fileOffset;
FunctionNode function = constructor.function!;
List<TypeParameter> classTypeParameters;
if (constructor is Constructor) {
// Generative constructors implicitly have the type parameters of the
// enclosing class.
classTypeParameters = enclosingClass.typeParameters;
} else {
// Factory constructors explicitly copy over the type parameters of the
// enclosing class.
classTypeParameters = function.typeParameters;
}
FreshTypeParameters freshTypeParameters =
_createFreshTypeParameters(classTypeParameters, tearOff.function);
List<DartType> typeArguments = freshTypeParameters.freshTypeArguments;
Substitution substitution = freshTypeParameters.substitution;
_createParameters(tearOff, constructor, substitution, libraryBuilder);
Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
_createTearOffBody(tearOff, constructor, arguments);
tearOff.function.fileOffset = tearOff.fileOffset;
tearOff.function.fileEndOffset = tearOff.fileOffset;
updatePrivateMemberName(tearOff, libraryBuilder);
}
/// Creates the parameters and body for [tearOff] for a typedef tearoff of
/// [constructor] in [enclosingClass] with [typeParameters] as the typedef
/// parameters and [typeArguments] as the arguments passed to the
/// [enclosingClass].
void buildTypedefTearOffProcedure(
Procedure tearOff,
Member constructor,
Class enclosingClass,
List<TypeParameter> typeParameters,
List<DartType> typeArguments,
SourceLibraryBuilder libraryBuilder) {
assert(
constructor is Constructor ||
(constructor is Procedure && constructor.isFactory) ||
(constructor is Procedure && constructor.isStatic),
"Unexpected constructor tear off target $constructor "
"(${constructor.runtimeType}).");
int fileOffset = tearOff.fileOffset;
FunctionNode function = constructor.function!;
List<TypeParameter> classTypeParameters;
if (constructor is Constructor) {
// Generative constructors implicitly have the type parameters of the
// enclosing class.
classTypeParameters = enclosingClass.typeParameters;
} else {
// Factory constructors explicitly copy over the type parameters of the
// enclosing class.
classTypeParameters = function.typeParameters;
}
FreshTypeParameters freshTypeParameters =
_createFreshTypeParameters(typeParameters, tearOff.function);
Substitution substitution = freshTypeParameters.substitution;
if (!substitution.isEmpty) {
if (typeArguments.isNotEmpty) {
// Translate [typeArgument] into the context of the synthesized procedure.
typeArguments = new List<DartType>.generate(typeArguments.length,
(int index) => substitution.substituteType(typeArguments[index]));
}
}
_createParameters(
tearOff,
constructor,
Substitution.fromPairs(classTypeParameters, typeArguments),
libraryBuilder);
Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
_createTearOffBody(tearOff, constructor, arguments);
tearOff.function.fileOffset = tearOff.fileOffset;
tearOff.function.fileEndOffset = tearOff.fileOffset;
updatePrivateMemberName(tearOff, libraryBuilder);
}
/// Creates the parameters for the redirecting factory [tearOff] based on the
/// [redirectingConstructor] declaration.
FreshTypeParameters buildRedirectingFactoryTearOffProcedureParameters(
Procedure tearOff,
Procedure redirectingConstructor,
SourceLibraryBuilder libraryBuilder) {
assert(redirectingConstructor.isRedirectingFactory);
FunctionNode function = redirectingConstructor.function;
FreshTypeParameters freshTypeParameters =
_createFreshTypeParameters(function.typeParameters, tearOff.function);
Substitution substitution = freshTypeParameters.substitution;
_createParameters(
tearOff, redirectingConstructor, substitution, libraryBuilder);
tearOff.function.fileOffset = tearOff.fileOffset;
tearOff.function.fileEndOffset = tearOff.fileOffset;
updatePrivateMemberName(tearOff, libraryBuilder);
return freshTypeParameters;
}
/// Creates the body for the redirecting factory [tearOff] with the target
/// [constructor] and [typeArguments].
///
/// Returns the [SynthesizedFunctionNode] object need to perform default value
/// computation.
SynthesizedFunctionNode buildRedirectingFactoryTearOffBody(
Procedure tearOff,
Member target,
List<DartType> typeArguments,
FreshTypeParameters freshTypeParameters) {
int fileOffset = tearOff.fileOffset;
if (!freshTypeParameters.substitution.isEmpty) {
if (typeArguments.isNotEmpty) {
// Translate [typeArgument] into the context of the synthesized procedure.
typeArguments = new List<DartType>.generate(
typeArguments.length,
(int index) => freshTypeParameters.substitution
.substituteType(typeArguments[index]));
}
}
Arguments arguments = _createArguments(tearOff, typeArguments, fileOffset);
_createTearOffBody(tearOff, target, arguments);
return new SynthesizedFunctionNode(
new Map<TypeParameter, DartType>.fromIterables(
target.enclosingClass!.typeParameters, typeArguments),
target.function!,
tearOff.function,
identicalSignatures: false);
}
/// Creates the synthesized [Procedure] node for a tear off lowering by the
/// given [name].
Procedure _createTearOffProcedure(SourceLibraryBuilder libraryBuilder,
Name name, Uri fileUri, int fileOffset) {
return new Procedure(name, ProcedureKind.Method, new FunctionNode(null),
fileUri: fileUri, isStatic: true)
..startFileOffset = fileOffset
..fileOffset = fileOffset
..fileEndOffset = fileOffset
..isNonNullableByDefault = libraryBuilder.isNonNullableByDefault;
}
/// Creates the synthesized type parameters for a tear off lowering. The type
/// parameters are based [originalTypeParameters] and are inserted into
/// [newFunctionNode]. The created [FreshTypeParameters] is returned.
FreshTypeParameters _createFreshTypeParameters(
List<TypeParameter> originalTypeParameters, FunctionNode newFunctionNode) {
FreshTypeParameters freshTypeParameters;
if (originalTypeParameters.isNotEmpty) {
freshTypeParameters = getFreshTypeParameters(originalTypeParameters);
List<TypeParameter> typeParameters =
freshTypeParameters.freshTypeParameters;
newFunctionNode.typeParameters.addAll(typeParameters);
setParents(typeParameters, newFunctionNode);
} else {
freshTypeParameters = new FreshTypeParameters([], [], Substitution.empty);
}
return freshTypeParameters;
}
/// Creates the parameters for the [tearOff] lowering based of the parameters
/// in [constructor] and using the [substitution] to compute the parameter and
/// return types.
void _createParameters(Procedure tearOff, Member constructor,
Substitution substitution, SourceLibraryBuilder libraryBuilder) {
FunctionNode function = constructor.function!;
for (VariableDeclaration constructorParameter
in function.positionalParameters) {
VariableDeclaration tearOffParameter = new VariableDeclaration(
constructorParameter.name,
type: substitution.substituteType(constructorParameter.type))
..fileOffset = constructorParameter.fileOffset;
tearOff.function.positionalParameters.add(tearOffParameter);
tearOffParameter.parent = tearOff.function;
}
for (VariableDeclaration constructorParameter in function.namedParameters) {
VariableDeclaration tearOffParameter = new VariableDeclaration(
constructorParameter.name,
type: substitution.substituteType(constructorParameter.type),
isRequired: constructorParameter.isRequired)
..fileOffset = constructorParameter.fileOffset;
tearOff.function.namedParameters.add(tearOffParameter);
tearOffParameter.parent = tearOff.function;
}
tearOff.function.returnType =
substitution.substituteType(function.returnType);
tearOff.function.requiredParameterCount = function.requiredParameterCount;
libraryBuilder.loader.registerTypeDependency(
tearOff, new TypeDependency(tearOff, constructor, substitution));
}
/// Creates the [Arguments] for passing the parameters from [tearOff] to its
/// target, using [typeArguments] as the passed type arguments.
Arguments _createArguments(
Procedure tearOff, List<DartType> typeArguments, int fileOffset) {
List<Expression> positionalArguments = [];
for (VariableDeclaration tearOffParameter
in tearOff.function.positionalParameters) {
positionalArguments
.add(new VariableGet(tearOffParameter)..fileOffset = fileOffset);
}
List<NamedExpression> namedArguments = [];
for (VariableDeclaration tearOffParameter
in tearOff.function.namedParameters) {
namedArguments.add(new NamedExpression(tearOffParameter.name!,
new VariableGet(tearOffParameter)..fileOffset = fileOffset)
..fileOffset = fileOffset);
}
Arguments arguments = new Arguments(positionalArguments,
named: namedArguments, types: typeArguments)
..fileOffset = tearOff.fileOffset;
return arguments;
}
/// Creates the tear of body for [tearOff] which calls [target] with
/// [arguments].
void _createTearOffBody(Procedure tearOff, Member target, Arguments arguments) {
assert(target is Constructor ||
(target is Procedure && target.isFactory) ||
(target is Procedure && target.isStatic));
Expression constructorInvocation;
if (target is Constructor) {
constructorInvocation = new ConstructorInvocation(target, arguments)
..fileOffset = tearOff.fileOffset;
} else {
constructorInvocation = new StaticInvocation(target as Procedure, arguments)
..fileOffset = tearOff.fileOffset;
}
tearOff.function.body = new ReturnStatement(constructorInvocation)
..fileOffset = tearOff.fileOffset
..parent = tearOff.function;
}