| // 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:front_end/src/fasta/kernel/kernel_api.dart'; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/type_algebra.dart'; |
| import '../builder/member_builder.dart'; |
| import '../source/source_library_builder.dart'; |
| |
| /// Creates the synthesized name to use for the lowering of a generative |
| /// constructor by the given [constructorName] in [library]. |
| Name constructorTearOffName(String constructorName, Library library) { |
| return new Name( |
| '_#${constructorName.isEmpty ? 'new' : constructorName}#tearOff', |
| library); |
| } |
| |
| /// 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 new Procedure(constructorTearOffName(name, compilationUnit.library), |
| ProcedureKind.Method, new FunctionNode(null), |
| fileUri: fileUri, isStatic: true) |
| ..startFileOffset = fileOffset |
| ..fileOffset = fileOffset |
| ..fileEndOffset = fileOffset |
| ..isNonNullableByDefault = compilationUnit.isNonNullableByDefault; |
| } |
| return null; |
| } |
| |
| /// Creates the parameters and body for [tearOff] based on [constructor]. |
| void buildConstructorTearOffProcedure(Procedure tearOff, Member constructor, |
| Class enclosingClass, SourceLibraryBuilder libraryBuilder) { |
| assert(constructor is Constructor || |
| (constructor is Procedure && constructor.kind == ProcedureKind.Factory)); |
| |
| 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; |
| } |
| |
| List<TypeParameter> typeParameters; |
| List<DartType> typeArguments; |
| Substitution substitution = Substitution.empty; |
| if (classTypeParameters.isNotEmpty) { |
| FreshTypeParameters freshTypeParameters = |
| getFreshTypeParameters(classTypeParameters); |
| typeParameters = freshTypeParameters.freshTypeParameters; |
| typeArguments = freshTypeParameters.freshTypeArguments; |
| substitution = freshTypeParameters.substitution; |
| tearOff.function.typeParameters.addAll(typeParameters); |
| setParents(typeParameters, tearOff.function); |
| } else { |
| typeParameters = []; |
| typeArguments = []; |
| substitution = Substitution.empty; |
| } |
| |
| List<Expression> positionalArguments = []; |
| 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); |
| positionalArguments |
| .add(new VariableGet(tearOffParameter)..fileOffset = fileOffset); |
| tearOffParameter.parent = tearOff.function; |
| } |
| List<NamedExpression> namedArguments = []; |
| 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; |
| namedArguments.add(new NamedExpression(tearOffParameter.name!, |
| new VariableGet(tearOffParameter)..fileOffset = fileOffset) |
| ..fileOffset = fileOffset); |
| } |
| tearOff.function.returnType = |
| substitution.substituteType(function.returnType); |
| tearOff.function.requiredParameterCount = function.requiredParameterCount; |
| |
| Arguments arguments = new Arguments(positionalArguments, |
| named: namedArguments, types: typeArguments) |
| ..fileOffset = tearOff.fileOffset; |
| Expression constructorInvocation; |
| if (constructor is Constructor) { |
| constructorInvocation = new ConstructorInvocation(constructor, arguments) |
| ..fileOffset = tearOff.fileOffset; |
| } else { |
| constructorInvocation = |
| new StaticInvocation(constructor as Procedure, arguments) |
| ..fileOffset = tearOff.fileOffset; |
| } |
| tearOff.function.body = new ReturnStatement(constructorInvocation) |
| ..fileOffset = tearOff.fileOffset |
| ..parent = tearOff.function; |
| tearOff.function.fileOffset = tearOff.fileOffset; |
| tearOff.function.fileEndOffset = tearOff.fileOffset; |
| updatePrivateMemberName(tearOff, libraryBuilder); |
| } |
| |
| /// Copies the parameter types from [constructor] to [tearOff]. |
| /// |
| /// These might have been inferred and therefore not available when the |
| /// parameters were created. |
| // TODO(johnniwinther): Avoid doing this when parameter types are not inferred. |
| void buildConstructorTearOffOutline( |
| Procedure tearOff, Constructor constructor, Class enclosingClass) { |
| List<TypeParameter> classTypeParameters = enclosingClass.typeParameters; |
| Substitution substitution = Substitution.empty; |
| if (classTypeParameters.isNotEmpty) { |
| List<DartType> typeArguments = []; |
| for (TypeParameter typeParameter in tearOff.function.typeParameters) { |
| typeArguments.add(new TypeParameterType(typeParameter, |
| TypeParameterType.computeNullabilityFromBound(typeParameter))); |
| } |
| substitution = Substitution.fromPairs(classTypeParameters, typeArguments); |
| } |
| for (int i = 0; i < constructor.function.positionalParameters.length; i++) { |
| VariableDeclaration tearOffParameter = |
| tearOff.function.positionalParameters[i]; |
| VariableDeclaration constructorParameter = |
| constructor.function.positionalParameters[i]; |
| tearOffParameter.type = |
| substitution.substituteType(constructorParameter.type); |
| } |
| for (int i = 0; i < constructor.function.namedParameters.length; i++) { |
| VariableDeclaration tearOffParameter = tearOff.function.namedParameters[i]; |
| VariableDeclaration constructorParameter = |
| constructor.function.namedParameters[i]; |
| tearOffParameter.type = |
| substitution.substituteType(constructorParameter.type); |
| } |
| } |
| |
| void buildConstructorTearOffDefaultValues( |
| Procedure tearOff, Constructor constructor, Class enclosingClass) { |
| CloneVisitorNotMembers cloner = new CloneVisitorNotMembers(); |
| for (int i = 0; i < constructor.function.positionalParameters.length; i++) { |
| VariableDeclaration tearOffParameter = |
| tearOff.function.positionalParameters[i]; |
| VariableDeclaration constructorParameter = |
| constructor.function.positionalParameters[i]; |
| tearOffParameter.initializer = |
| cloner.cloneOptional(constructorParameter.initializer); |
| tearOffParameter.initializer?.parent = tearOffParameter; |
| } |
| for (int i = 0; i < constructor.function.namedParameters.length; i++) { |
| VariableDeclaration tearOffParameter = tearOff.function.namedParameters[i]; |
| VariableDeclaration constructorParameter = |
| constructor.function.namedParameters[i]; |
| tearOffParameter.initializer = |
| cloner.cloneOptional(constructorParameter.initializer); |
| tearOffParameter.initializer?.parent = tearOffParameter; |
| } |
| } |