Index: pkg/fasta/lib/src/kernel/kernel_procedure_builder.dart |
diff --git a/pkg/fasta/lib/src/kernel/kernel_procedure_builder.dart b/pkg/fasta/lib/src/kernel/kernel_procedure_builder.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d0a690df07b1beccb8a2c48620de3e087d814cc9 |
--- /dev/null |
+++ b/pkg/fasta/lib/src/kernel/kernel_procedure_builder.dart |
@@ -0,0 +1,353 @@ |
+// 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. |
+ |
+library fasta.kernel_procedure_builder; |
+ |
+import 'package:kernel/ast.dart' show |
+ Arguments, |
+ AsyncMarker, |
+ Constructor, |
+ DartType, |
+ DynamicType, |
+ EmptyStatement, |
+ Expression, |
+ FunctionNode, |
+ Initializer, |
+ Library, |
+ LocalInitializer, |
+ Member, |
+ Name, |
+ NamedExpression, |
+ Procedure, |
+ ProcedureKind, |
+ RedirectingInitializer, |
+ Statement, |
+ SuperInitializer, |
+ TypeParameter, |
+ VariableDeclaration, |
+ VariableGet, |
+ VoidType, |
+ setParents; |
+ |
+import 'package:kernel/type_algebra.dart' show |
+ containsTypeVariable, |
+ substitute; |
+ |
+import '../errors.dart' show |
+ internalError; |
+ |
+import '../modifier.dart' show |
+ abstractMask; |
+ |
+import 'kernel_builder.dart' show |
+ ClassBuilder, |
+ ConstructorReferenceBuilder, |
+ FormalParameterBuilder, |
+ KernelFormalParameterBuilder, |
+ KernelTypeBuilder, |
+ KernelTypeVariableBuilder, |
+ MetadataBuilder, |
+ ProcedureBuilder, |
+ TypeBuilder, |
+ TypeVariableBuilder, |
+ memberError; |
+ |
+abstract class KernelFunctionBuilder |
+ extends ProcedureBuilder<KernelTypeBuilder> { |
+ FunctionNode function; |
+ |
+ Statement actualBody; |
+ |
+ KernelFunctionBuilder( |
+ List<MetadataBuilder> metadata, |
+ int modifiers, KernelTypeBuilder returnType, String name, |
+ List<TypeVariableBuilder> typeVariables, |
+ List<FormalParameterBuilder> formals) |
+ : super(metadata, modifiers, returnType, name, typeVariables, formals); |
+ |
+ void set body(Statement newBody) { |
+ if (isAbstract && newBody != null) { |
+ return internalError("Attempting to set body on abstract method."); |
+ } |
+ actualBody = newBody; |
+ if (function != null) { |
+ function.body = newBody; |
+ newBody?.parent = function; |
+ } |
+ } |
+ |
+ Statement get body => actualBody ??= new EmptyStatement(); |
+ |
+ FunctionNode buildFunction() { |
+ assert(function == null); |
+ FunctionNode result = new FunctionNode(body, asyncMarker: asyncModifier); |
+ if (typeVariables != null) { |
+ for (KernelTypeVariableBuilder t in typeVariables) { |
+ result.typeParameters.add(t.parameter); |
+ } |
+ setParents(result.typeParameters, result); |
+ } |
+ if (formals != null) { |
+ for (KernelFormalParameterBuilder formal in formals) { |
+ VariableDeclaration parameter = formal.build(); |
+ if (formal.isNamed) { |
+ result.namedParameters.add(parameter); |
+ } else { |
+ result.positionalParameters.add(parameter); |
+ } |
+ parameter.parent = result; |
+ if (formal.isRequired) { |
+ result.requiredParameterCount++; |
+ } |
+ } |
+ } |
+ if (returnType != null) { |
+ result.returnType = returnType.build(); |
+ } |
+ if (!isConstructor && !isInstanceMember && parent is ClassBuilder) { |
+ List<TypeParameter> typeParameters = parent.target.typeParameters; |
+ if (typeParameters.isNotEmpty) { |
+ Map<TypeParameter, DartType> substitution; |
+ DartType removeTypeVariables(DartType type) { |
+ if (substitution == null) { |
+ substitution = <TypeParameter, DartType>{}; |
+ for (TypeParameter parameter in typeParameters) { |
+ substitution[parameter] = const DynamicType(); |
+ } |
+ } |
+ print("Can only use type variables in instance methods."); |
+ return substitute(type, substitution); |
+ } |
+ Set<TypeParameter> set = typeParameters.toSet(); |
+ for (VariableDeclaration parameter in result.positionalParameters) { |
+ if (containsTypeVariable(parameter.type, set)) { |
+ parameter.type = removeTypeVariables(parameter.type); |
+ } |
+ } |
+ for (VariableDeclaration parameter in result.namedParameters) { |
+ if (containsTypeVariable(parameter.type, set)) { |
+ parameter.type = removeTypeVariables(parameter.type); |
+ } |
+ } |
+ if (containsTypeVariable(result.returnType, set)) { |
+ result.returnType = removeTypeVariables(result.returnType); |
+ } |
+ } |
+ } |
+ return function = result; |
+ } |
+ |
+ Member build(Library library); |
+} |
+ |
+class KernelProcedureBuilder extends KernelFunctionBuilder { |
+ final Procedure procedure; |
+ |
+ AsyncMarker actualAsyncModifier; |
+ |
+ final ConstructorReferenceBuilder redirectionTarget; |
+ |
+ KernelProcedureBuilder( |
+ List<MetadataBuilder> metadata, |
+ int modifiers, KernelTypeBuilder returnType, String name, |
+ List<TypeVariableBuilder> typeVariables, |
+ List<FormalParameterBuilder> formals, this.actualAsyncModifier, |
+ ProcedureKind kind, [this.redirectionTarget]) |
+ : procedure = new Procedure(null, kind, null), |
+ super(metadata, modifiers, returnType, name, typeVariables, formals); |
+ |
+ ProcedureKind get kind => procedure.kind; |
+ |
+ AsyncMarker get asyncModifier => actualAsyncModifier; |
+ |
+ Statement get body { |
+ if (actualBody == null && redirectionTarget == null && !isAbstract && |
+ !isExternal) { |
+ actualBody = new EmptyStatement(); |
+ } |
+ return actualBody; |
+ } |
+ |
+ void set asyncModifier(AsyncMarker newModifier) { |
+ actualAsyncModifier = newModifier; |
+ if (function != null) { |
+ // No parent, it's an enum. |
+ function.asyncMarker = actualAsyncModifier; |
+ } |
+ } |
+ |
+ Procedure build(Library library) { |
+ // TODO(ahe): I think we may call this twice on parts. Investigate. |
+ if (procedure.name == null) { |
+ procedure.function = buildFunction(); |
+ procedure.function.parent = procedure; |
+ procedure.isAbstract = isAbstract; |
+ procedure.isStatic = isStatic; |
+ procedure.isExternal = isExternal; |
+ procedure.isConst = isConst; |
+ procedure.name = new Name(name, library); |
+ } |
+ return procedure; |
+ } |
+ |
+ KernelFunctionBuilder toConstructor(String name, |
+ List<TypeVariableBuilder> classTypeVariables) { |
+ assert(procedure.name == null); |
+ assert(actualBody == null); |
+ if (isFactory) { |
+ if (this.typeVariables != null) { |
+ return memberError(target, "Factories can't be generic."); |
+ } |
+ List<KernelTypeVariableBuilder> typeVariables; |
+ KernelTypeBuilder returnType = this.returnType; |
+ List<FormalParameterBuilder> formals; |
+ if (classTypeVariables != null) { |
+ typeVariables = <KernelTypeVariableBuilder>[]; |
+ for (KernelTypeVariableBuilder variable in classTypeVariables) { |
+ typeVariables.add(new KernelTypeVariableBuilder(variable.name)); |
+ } |
+ Map<TypeVariableBuilder, TypeBuilder> substitution = |
+ <TypeVariableBuilder, TypeBuilder>{}; |
+ int i = 0; |
+ for (KernelTypeVariableBuilder variable in classTypeVariables) { |
+ substitution[variable] = typeVariables[i++].asTypeBuilder(); |
+ } |
+ i = 0; |
+ for (KernelTypeVariableBuilder variable in classTypeVariables) { |
+ typeVariables[i++].bound = variable?.bound?.subst(substitution); |
+ } |
+ returnType = returnType?.subst(substitution); |
+ i = 0; |
+ if (this.formals != null) { |
+ for (KernelFormalParameterBuilder formal in this.formals) { |
+ KernelTypeBuilder type = formal.type?.subst(substitution); |
+ if (type != formal.type) { |
+ formals ??= this.formals.toList(); |
+ formals[i] = new KernelFormalParameterBuilder(formal.metadata, |
+ formal.modifiers, type, formal.name, formal.hasThis); |
+ } |
+ i++; |
+ } |
+ } |
+ } |
+ formals ??= this.formals; |
+ return new KernelProcedureBuilder( |
+ metadata, modifiers, returnType, name, typeVariables, formals, |
+ actualAsyncModifier, kind, redirectionTarget) |
+ ..parent = parent; |
+ } else { |
+ return new KernelConstructorBuilder(metadata, modifiers & ~abstractMask, |
+ returnType, name, typeVariables, formals) |
+ ..parent = parent; |
+ } |
+ } |
+ |
+ Procedure get target => procedure; |
+} |
+ |
+// TODO(ahe): Move this to own file? |
+class KernelConstructorBuilder extends KernelFunctionBuilder { |
+ final Constructor constructor = new Constructor(null); |
+ |
+ bool hasMovedSuperInitializer = false; |
+ |
+ SuperInitializer superInitializer; |
+ |
+ RedirectingInitializer redirectingInitializer; |
+ |
+ KernelConstructorBuilder( |
+ List<MetadataBuilder> metadata, |
+ int modifiers, KernelTypeBuilder returnType, String name, |
+ List<TypeVariableBuilder> typeVariables, |
+ List<FormalParameterBuilder> formals) |
+ : super(metadata, modifiers, returnType, name, typeVariables, formals); |
+ |
+ bool get isInstanceMember => false; |
+ |
+ bool get isConstructor => true; |
+ |
+ AsyncMarker get asyncModifier => AsyncMarker.Sync; |
+ |
+ ProcedureKind get kind => null; |
+ |
+ Constructor build(Library library) { |
+ if (constructor.name == null) { |
+ constructor.function = buildFunction(); |
+ constructor.function.parent = constructor; |
+ constructor.isConst = isConst; |
+ constructor.isExternal = isExternal; |
+ constructor.name = new Name(name, library); |
+ } |
+ return constructor; |
+ } |
+ |
+ FunctionNode buildFunction() { |
+ // TODO(ahe): Should complain if another type is explicitly set. |
+ return super.buildFunction() |
+ ..returnType = const VoidType(); |
+ } |
+ |
+ Constructor get target => constructor; |
+ |
+ void checkSuperOrThisInitializer(Initializer initializer) { |
+ if (superInitializer != null || redirectingInitializer != null) { |
+ memberError(target, |
+ "Can't have more than one 'super' or 'this' initializer.", |
+ initializer.fileOffset); |
+ } |
+ } |
+ |
+ void addInitializer(Initializer initializer) { |
+ List<Initializer> initializers = constructor.initializers; |
+ if (initializer is SuperInitializer) { |
+ checkSuperOrThisInitializer(initializer); |
+ superInitializer = initializer; |
+ } else if (initializer is RedirectingInitializer) { |
+ checkSuperOrThisInitializer(initializer); |
+ redirectingInitializer = initializer; |
+ if (constructor.initializers.isNotEmpty) { |
+ memberError(target, "'this' initializer must be the only initializer.", |
+ initializer.fileOffset); |
+ } |
+ } else if (redirectingInitializer != null) { |
+ memberError(target, "'this' initializer must be the only initializer.", |
+ initializer.fileOffset); |
+ } else if (superInitializer != null) { |
+ // If there is a super initializer ([initializer] isn't it), we need to |
+ // insert [initializer] before the super initializer (thus ensuring that |
+ // the super initializer is always last). |
+ assert(superInitializer != initializer); |
+ assert(initializers.last == superInitializer); |
+ initializers.removeLast(); |
+ if (!hasMovedSuperInitializer) { |
+ // To preserve correct evaluation order, the arguments to super call |
+ // must be evaluated before [initializer]. Once the super initializer |
+ // has been moved once, the arguments are evaluated in the correct |
+ // order. |
+ hasMovedSuperInitializer = true; |
+ Arguments arguments = superInitializer.arguments; |
+ List<Expression> positional = arguments.positional; |
+ for (int i = 0; i < positional.length; i++) { |
+ VariableDeclaration variable = |
+ new VariableDeclaration.forValue(positional[i], isFinal: true); |
+ initializers.add( |
+ new LocalInitializer(variable)..parent = constructor); |
+ positional[i] = new VariableGet(variable)..parent = arguments; |
+ } |
+ for (NamedExpression named in arguments.named) { |
+ VariableDeclaration variable = |
+ new VariableDeclaration.forValue(named.value, isFinal: true); |
+ named.value = new VariableGet(variable)..parent = named; |
+ initializers.add( |
+ new LocalInitializer(variable)..parent = constructor); |
+ } |
+ } |
+ initializers.add(initializer..parent = constructor); |
+ initializers.add(superInitializer); |
+ return; |
+ } |
+ initializers.add(initializer); |
+ initializer.parent = constructor; |
+ } |
+} |