| Index: pkg/front_end/lib/src/fasta/kernel/body_builder.dart
|
| diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
|
| index 91e6b45ebd945724cb0bdff08196fccaa7b964f0..bda931c11b2e301c9bc3b815b8fe2497f8de71e4 100644
|
| --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
|
| +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
|
| @@ -78,9 +78,6 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
|
|
| final bool isInstanceMember;
|
|
|
| - final Map<String, FieldInitializer> fieldInitializers =
|
| - <String, FieldInitializer>{};
|
| -
|
| final Scope enclosingScope;
|
|
|
| final bool enableNative;
|
| @@ -133,6 +130,10 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| // from VM engineers. TODO(ahe): Does this still apply?
|
| int currentLocalVariableModifiers = -1;
|
|
|
| + /// If non-null, records instance fields which have already been initialized
|
| + /// and where that was.
|
| + Map<String, int> initializedFields;
|
| +
|
| BodyBuilder(
|
| KernelLibraryBuilder library,
|
| this.member,
|
| @@ -392,30 +393,25 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| }
|
| }
|
|
|
| - @override
|
| void prepareInitializers() {
|
| - scope = formalParameterScope;
|
| - assert(fieldInitializers.isEmpty);
|
| - final member = this.member;
|
| + ProcedureBuilder member = this.member;
|
| + scope = member.computeFormalParameterInitializerScope(scope);
|
| if (member is KernelConstructorBuilder) {
|
| - Constructor constructor = member.constructor;
|
| - classBuilder.forEach((String name, Builder builder) {
|
| - if (builder is KernelFieldBuilder && builder.isInstanceMember) {
|
| - // TODO(ahe): Compute initializers (as in `field = initializer`).
|
| - fieldInitializers[name] = new FieldInitializer(builder.field, null)
|
| - ..parent = constructor;
|
| - }
|
| - });
|
| if (member.formals != null) {
|
| for (KernelFormalParameterBuilder formal in member.formals) {
|
| if (formal.hasThis) {
|
| - FieldInitializer initializer = fieldInitializers[formal.name];
|
| - if (initializer != null) {
|
| - fieldInitializers.remove(formal.name);
|
| - initializer.value = new VariableGet(formal.declaration)
|
| - ..parent = initializer;
|
| - member.addInitializer(initializer);
|
| + Initializer initializer;
|
| + if (member.isExternal) {
|
| + initializer = buildInvalidInitializer(
|
| + buildCompileTimeError(
|
| + "An external constructor can't initialize fields.",
|
| + formal.charOffset),
|
| + formal.charOffset);
|
| + } else {
|
| + initializer = buildFieldInitializer(formal.name,
|
| + formal.charOffset, new VariableGet(formal.declaration));
|
| }
|
| + member.addInitializer(initializer);
|
| }
|
| }
|
| }
|
| @@ -423,6 +419,31 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| }
|
|
|
| @override
|
| + void handleNoInitializers() {
|
| + debugEvent("NoInitializers");
|
| + if (functionNestingLevel == 0) {
|
| + prepareInitializers();
|
| + scope = formalParameterScope;
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void beginInitializers(Token token) {
|
| + debugEvent("beginInitializers");
|
| + if (functionNestingLevel == 0) {
|
| + prepareInitializers();
|
| + }
|
| + }
|
| +
|
| + @override
|
| + void endInitializers(int count, Token beginToken, Token endToken) {
|
| + debugEvent("Initializers");
|
| + if (functionNestingLevel == 0) {
|
| + scope = formalParameterScope;
|
| + }
|
| + }
|
| +
|
| + @override
|
| void beginInitializer(Token token) {
|
| debugEvent("beginInitializer");
|
| inInitializer = true;
|
| @@ -438,7 +459,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| if (node is Initializer) {
|
| initializer = node;
|
| } else if (node is FastaAccessor) {
|
| - initializer = node.buildFieldInitializer(fieldInitializers);
|
| + initializer = node.buildFieldInitializer(initializedFields);
|
| } else if (node is ConstructorInvocation) {
|
| initializer =
|
| buildSuperInitializer(node.target, node.arguments, token.charOffset);
|
| @@ -447,7 +468,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| if (node is! Throw) {
|
| value = wrapInCompileTimeError(value, "Expected an initializer.");
|
| }
|
| - initializer = buildInvalidIntializer(node, token.charOffset);
|
| + initializer = buildInvalidInitializer(node, token.charOffset);
|
| }
|
| _typeInferrer.inferInitializer(initializer);
|
| if (member is KernelConstructorBuilder) {
|
| @@ -458,16 +479,6 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| }
|
| }
|
|
|
| - @override
|
| - void handleNoInitializers() {
|
| - debugEvent("NoInitializers");
|
| - }
|
| -
|
| - @override
|
| - void endInitializers(int count, Token beginToken, Token endToken) {
|
| - debugEvent("Initializers");
|
| - }
|
| -
|
| DartType _computeReturnTypeContext(MemberBuilder member) {
|
| if (member is KernelProcedureBuilder) {
|
| return member.target.function.returnType;
|
| @@ -519,7 +530,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| if (asyncModifier != AsyncMarker.Sync) {
|
| // TODO(ahe): Change this to a null check.
|
| int offset = builder.body?.fileOffset ?? builder.charOffset;
|
| - constructor.initializers.add(buildInvalidIntializer(
|
| + constructor.initializers.add(buildInvalidInitializer(
|
| buildCompileTimeError(
|
| "A constructor can't be '${asyncModifier}'.", offset),
|
| offset));
|
| @@ -538,7 +549,7 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| String message = superTarget == null
|
| ? "'$superclass' doesn't have an unnamed constructor."
|
| : "The unnamed constructor in '$superclass' requires arguments.";
|
| - initializer = buildInvalidIntializer(
|
| + initializer = buildInvalidInitializer(
|
| buildCompileTimeError(message, builder.charOffset),
|
| builder.charOffset);
|
| } else {
|
| @@ -924,7 +935,23 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| addCompileTimeError(
|
| offsetForToken(token), "Not a constant expression.");
|
| }
|
| - return new VariableAccessor(this, token, builder.target);
|
| + // An initializing formal parameter might be final without its
|
| + // VariableDeclaration being final. See
|
| + // [ProcedureBuilder.computeFormalParameterInitializerScope]. If that
|
| + // wasn't the case, we could always use VariableAccessor.
|
| + if (builder.isFinal) {
|
| + var fact =
|
| + typePromoter.getFactForAccess(builder.target, functionNestingLevel);
|
| + var scope = typePromoter.currentScope;
|
| + return new ReadOnlyAccessor(
|
| + this,
|
| + new KernelVariableGet(builder.target, fact, scope)
|
| + ..fileOffset = offsetForToken(token),
|
| + name,
|
| + token);
|
| + } else {
|
| + return new VariableAccessor(this, token, builder.target);
|
| + }
|
| } else if (builder.isInstanceMember) {
|
| if (constantExpressionRequired &&
|
| !inInitializer &&
|
| @@ -1593,44 +1620,25 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| if (!inCatchClause &&
|
| functionNestingLevel == 0 &&
|
| memberKind != MemberKind.GeneralizedFunctionType) {
|
| - dynamic builder = formalParameterScope.lookup(
|
| - name.name, offsetForToken(name.token), uri);
|
| - if (builder == null) {
|
| - if (thisKeyword == null) {
|
| - internalError("Internal error: formal missing for '${name.name}'");
|
| - } else {
|
| - addCompileTimeError(thisKeyword.charOffset,
|
| - "'${name.name}' isn't a field in this class.");
|
| - thisKeyword = null;
|
| - }
|
| - } else if (thisKeyword == null) {
|
| - variable = builder.build(library);
|
| - variable.initializer = name.initializer;
|
| - } else if (builder.isField && builder.parent == classBuilder) {
|
| - FieldBuilder field = builder;
|
| - if (type != null) {
|
| - nit("Ignoring type on 'this' parameter '${name.name}'.",
|
| - thisKeyword.charOffset);
|
| - }
|
| - type = field.target.type;
|
| - variable = new KernelVariableDeclaration(
|
| - name.name, functionNestingLevel,
|
| - type: type,
|
| - initializer: name.initializer,
|
| - isFinal: isFinal,
|
| - isConst: isConst)
|
| - ..fileOffset = offsetForToken(name.token);
|
| + ProcedureBuilder member = this.member;
|
| + KernelFormalParameterBuilder formal = member.getFormal(name.name);
|
| + if (formal == null) {
|
| + internalError("Internal error: formal missing for '${name.name}'");
|
| } else {
|
| - addCompileTimeError(offsetForToken(name.token),
|
| - "'${name.name}' isn't a field in this class.");
|
| + variable = formal.build(library);
|
| + variable.initializer = name.initializer;
|
| + }
|
| + } else {
|
| + variable = new KernelVariableDeclaration(name?.name, functionNestingLevel,
|
| + type: type,
|
| + initializer: name?.initializer,
|
| + isFinal: isFinal,
|
| + isConst: isConst);
|
| + if (name != null) {
|
| + // TODO(ahe): Need an offset when name is null.
|
| + variable.fileOffset = offsetForToken(name.token);
|
| }
|
| }
|
| - variable ??= new KernelVariableDeclaration(name?.name, functionNestingLevel,
|
| - type: type,
|
| - initializer: name?.initializer,
|
| - isFinal: isFinal,
|
| - isConst: isConst)
|
| - ..fileOffset = offsetForToken(name?.token);
|
| push(variable);
|
| }
|
|
|
| @@ -2675,13 +2683,61 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| }
|
|
|
| @override
|
| - Initializer buildInvalidIntializer(Expression expression,
|
| + Initializer buildInvalidInitializer(Expression expression,
|
| [int charOffset = -1]) {
|
| needsImplicitSuperInitializer = false;
|
| return new LocalInitializer(new VariableDeclaration.forValue(expression))
|
| ..fileOffset = charOffset;
|
| }
|
|
|
| + Initializer buildDuplicatedInitializer(
|
| + String name, int offset, int previousInitializerOffset) {
|
| + Initializer initializer = buildInvalidInitializer(
|
| + buildCompileTimeError("'$name' has already been initialized.", offset),
|
| + offset);
|
| + addCompileTimeError(
|
| + initializedFields[name], "'$name' was initialized here.");
|
| + return initializer;
|
| + }
|
| +
|
| + @override
|
| + Initializer buildFieldInitializer(
|
| + String name, int offset, Expression expression) {
|
| + Builder builder = classBuilder.scope.local[name];
|
| + if (builder is KernelFieldBuilder && builder.isInstanceMember) {
|
| + initializedFields ??= <String, int>{};
|
| + if (initializedFields.containsKey(name)) {
|
| + return buildDuplicatedInitializer(
|
| + name, offset, initializedFields[name]);
|
| + }
|
| + initializedFields[name] = offset;
|
| + if (builder.isFinal && builder.hasInitializer) {
|
| + // TODO(ahe): If CL 2843733002 is landed, this becomes a compile-time
|
| + // error. Also, this is a compile-time error in strong mode.
|
| + warningNotError(
|
| + "'$name' is final instance variable that has already been "
|
| + "initialized.",
|
| + offset);
|
| + warningNotError("'$name' was initialized here.", builder.charOffset);
|
| + Builder constructor =
|
| + library.loader.getDuplicatedFieldInitializerError();
|
| + return buildInvalidInitializer(
|
| + new Throw(buildStaticInvocation(constructor.target,
|
| + new Arguments(<Expression>[new StringLiteral(name)]),
|
| + charOffset: offset)),
|
| + offset);
|
| + } else {
|
| + return new FieldInitializer(builder.field, expression)
|
| + ..fileOffset = offset;
|
| + }
|
| + } else {
|
| + return buildInvalidInitializer(
|
| + buildCompileTimeError(
|
| + "'$name' isn't an instance field of this class.", offset),
|
| + offset);
|
| + }
|
| + }
|
| +
|
| @override
|
| Initializer buildSuperInitializer(
|
| Constructor constructor, Arguments arguments,
|
| @@ -2988,20 +3044,14 @@ class DelayedAssignment extends ContextAccessor {
|
| }
|
| }
|
|
|
| - Initializer buildFieldInitializer(
|
| - Map<String, FieldInitializer> initializers) {
|
| + @override
|
| + Initializer buildFieldInitializer(Map<String, int> initializedFields) {
|
| if (!identical("=", assignmentOperator) ||
|
| !accessor.isThisPropertyAccessor) {
|
| - return accessor.buildFieldInitializer(initializers);
|
| - }
|
| - String name = accessor.plainNameForRead;
|
| - FieldInitializer initializer = initializers[name];
|
| - if (initializer != null && initializer.value == null) {
|
| - initializers.remove(name);
|
| - initializer.value = value..parent = initializer;
|
| - return initializer;
|
| + return accessor.buildFieldInitializer(initializedFields);
|
| }
|
| - return accessor.buildFieldInitializer(initializers);
|
| + return helper.buildFieldInitializer(
|
| + accessor.plainNameForRead, offsetForToken(token), value);
|
| }
|
| }
|
|
|
|
|