Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: pkg/front_end/lib/src/fasta/kernel/body_builder.dart

Issue 2916333002: Setup correct scope for initializers and complain about fields initialized more than once. (Closed)
Patch Set: A parenthesized expression doesn't have a name. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library fasta.body_builder; 5 library fasta.body_builder;
6 6
7 import '../fasta_codes.dart' 7 import '../fasta_codes.dart'
8 show FastaMessage, codeExpectedButGot, codeExpectedFunctionBody; 8 show FastaMessage, codeExpectedButGot, codeExpectedFunctionBody;
9 9
10 import '../parser/parser.dart' show FormalParameterType, MemberKind, optional; 10 import '../parser/parser.dart' show FormalParameterType, MemberKind, optional;
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 final MemberBuilder member; 71 final MemberBuilder member;
72 72
73 final KernelClassBuilder classBuilder; 73 final KernelClassBuilder classBuilder;
74 74
75 final ClassHierarchy hierarchy; 75 final ClassHierarchy hierarchy;
76 76
77 final CoreTypes coreTypes; 77 final CoreTypes coreTypes;
78 78
79 final bool isInstanceMember; 79 final bool isInstanceMember;
80 80
81 final Map<String, FieldInitializer> fieldInitializers =
82 <String, FieldInitializer>{};
83
84 final Scope enclosingScope; 81 final Scope enclosingScope;
85 82
86 final bool enableNative; 83 final bool enableNative;
87 84
88 final bool isPlatformLibrary; 85 final bool isPlatformLibrary;
89 86
90 @override 87 @override
91 final Uri uri; 88 final Uri uri;
92 89
93 final TypeInferrer _typeInferrer; 90 final TypeInferrer _typeInferrer;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 CloneVisitor cloner; 123 CloneVisitor cloner;
127 124
128 bool constantExpressionRequired = false; 125 bool constantExpressionRequired = false;
129 126
130 DartType currentLocalVariableType; 127 DartType currentLocalVariableType;
131 128
132 // Using non-null value to initialize this field based on performance advice 129 // Using non-null value to initialize this field based on performance advice
133 // from VM engineers. TODO(ahe): Does this still apply? 130 // from VM engineers. TODO(ahe): Does this still apply?
134 int currentLocalVariableModifiers = -1; 131 int currentLocalVariableModifiers = -1;
135 132
133 /// If non-null, records instance fields which have already been initialized
134 /// and where that was.
135 Map<String, int> initializedFields;
136
136 BodyBuilder( 137 BodyBuilder(
137 KernelLibraryBuilder library, 138 KernelLibraryBuilder library,
138 this.member, 139 this.member,
139 Scope scope, 140 Scope scope,
140 this.formalParameterScope, 141 this.formalParameterScope,
141 this.hierarchy, 142 this.hierarchy,
142 this.coreTypes, 143 this.coreTypes,
143 this.classBuilder, 144 this.classBuilder,
144 this.isInstanceMember, 145 this.isInstanceMember,
145 this.uri, 146 this.uri,
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after
385 if (beginToken == null) { 386 if (beginToken == null) {
386 assert(count == 0); 387 assert(count == 0);
387 push(NullValue.Block); 388 push(NullValue.Block);
388 } else { 389 } else {
389 Block block = popBlock(count, beginToken); 390 Block block = popBlock(count, beginToken);
390 exitLocalScope(); 391 exitLocalScope();
391 push(block); 392 push(block);
392 } 393 }
393 } 394 }
394 395
395 @override
396 void prepareInitializers() { 396 void prepareInitializers() {
397 scope = formalParameterScope; 397 ProcedureBuilder member = this.member;
398 assert(fieldInitializers.isEmpty); 398 scope = member.computeFormalParameterInitializerScope(scope);
399 final member = this.member;
400 if (member is KernelConstructorBuilder) { 399 if (member is KernelConstructorBuilder) {
401 Constructor constructor = member.constructor;
402 classBuilder.forEach((String name, Builder builder) {
403 if (builder is KernelFieldBuilder && builder.isInstanceMember) {
404 // TODO(ahe): Compute initializers (as in `field = initializer`).
405 fieldInitializers[name] = new FieldInitializer(builder.field, null)
406 ..parent = constructor;
407 }
408 });
409 if (member.formals != null) { 400 if (member.formals != null) {
410 for (KernelFormalParameterBuilder formal in member.formals) { 401 for (KernelFormalParameterBuilder formal in member.formals) {
411 if (formal.hasThis) { 402 if (formal.hasThis) {
412 FieldInitializer initializer = fieldInitializers[formal.name]; 403 Initializer initializer;
413 if (initializer != null) { 404 if (member.isExternal) {
414 fieldInitializers.remove(formal.name); 405 initializer = buildInvalidIntializer(
415 initializer.value = new VariableGet(formal.declaration) 406 buildCompileTimeError(
416 ..parent = initializer; 407 "An external constructor can't initialize fields.",
417 member.addInitializer(initializer); 408 formal.charOffset),
409 formal.charOffset);
410 } else {
411 initializer = buildFieldInitializer(formal.name,
412 formal.charOffset, new VariableGet(formal.declaration));
418 } 413 }
414 member.addInitializer(initializer);
419 } 415 }
420 } 416 }
421 } 417 }
422 } 418 }
423 } 419 }
424 420
425 @override 421 @override
422 void handleNoInitializers() {
423 debugEvent("NoInitializers");
424 if (functionNestingLevel == 0) {
425 prepareInitializers();
426 scope = formalParameterScope;
427 }
428 }
429
430 @override
431 void beginInitializers(Token token) {
432 debugEvent("beginInitializers");
433 if (functionNestingLevel == 0) {
434 prepareInitializers();
435 }
436 }
437
438 @override
439 void endInitializers(int count, Token beginToken, Token endToken) {
440 debugEvent("Initializers");
441 if (functionNestingLevel == 0) {
442 scope = formalParameterScope;
443 }
444 }
445
446 @override
426 void beginInitializer(Token token) { 447 void beginInitializer(Token token) {
427 debugEvent("beginInitializer"); 448 debugEvent("beginInitializer");
428 inInitializer = true; 449 inInitializer = true;
429 } 450 }
430 451
431 @override 452 @override
432 void endInitializer(Token token) { 453 void endInitializer(Token token) {
433 debugEvent("endInitializer"); 454 debugEvent("endInitializer");
434 assert(!inInitializer); 455 assert(!inInitializer);
435 final member = this.member; 456 final member = this.member;
436 var node = pop(); 457 var node = pop();
437 Initializer initializer; 458 Initializer initializer;
438 if (node is Initializer) { 459 if (node is Initializer) {
439 initializer = node; 460 initializer = node;
440 } else if (node is FastaAccessor) { 461 } else if (node is FastaAccessor) {
441 initializer = node.buildFieldInitializer(fieldInitializers); 462 initializer = node.buildFieldInitializer(initializedFields);
442 } else if (node is ConstructorInvocation) { 463 } else if (node is ConstructorInvocation) {
443 initializer = 464 initializer =
444 buildSuperInitializer(node.target, node.arguments, token.charOffset); 465 buildSuperInitializer(node.target, node.arguments, token.charOffset);
445 } else { 466 } else {
446 Expression value = toValue(node); 467 Expression value = toValue(node);
447 if (node is! Throw) { 468 if (node is! Throw) {
448 value = wrapInCompileTimeError(value, "Expected an initializer."); 469 value = wrapInCompileTimeError(value, "Expected an initializer.");
449 } 470 }
450 initializer = buildInvalidIntializer(node, token.charOffset); 471 initializer = buildInvalidIntializer(node, token.charOffset);
451 } 472 }
452 _typeInferrer.inferInitializer(initializer); 473 _typeInferrer.inferInitializer(initializer);
453 if (member is KernelConstructorBuilder) { 474 if (member is KernelConstructorBuilder) {
454 member.addInitializer(initializer); 475 member.addInitializer(initializer);
455 } else { 476 } else {
456 addCompileTimeError( 477 addCompileTimeError(
457 token.charOffset, "Can't have initializers: ${member.name}"); 478 token.charOffset, "Can't have initializers: ${member.name}");
458 } 479 }
459 } 480 }
460 481
461 @override
462 void handleNoInitializers() {
463 debugEvent("NoInitializers");
464 }
465
466 @override
467 void endInitializers(int count, Token beginToken, Token endToken) {
468 debugEvent("Initializers");
469 }
470
471 DartType _computeReturnTypeContext(MemberBuilder member) { 482 DartType _computeReturnTypeContext(MemberBuilder member) {
472 if (member is KernelProcedureBuilder) { 483 if (member is KernelProcedureBuilder) {
473 return member.target.function.returnType; 484 return member.target.function.returnType;
474 } else { 485 } else {
475 assert(member is KernelConstructorBuilder); 486 assert(member is KernelConstructorBuilder);
476 return null; 487 return null;
477 } 488 }
478 } 489 }
479 490
480 @override 491 @override
(...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after
917 offsetForToken(token), "Not a constant expression."); 928 offsetForToken(token), "Not a constant expression.");
918 } 929 }
919 return new TypeDeclarationAccessor(this, builder, name, token); 930 return new TypeDeclarationAccessor(this, builder, name, token);
920 } else if (builder.isLocal) { 931 } else if (builder.isLocal) {
921 if (constantExpressionRequired && 932 if (constantExpressionRequired &&
922 !builder.isConst && 933 !builder.isConst &&
923 !member.isConstructor) { 934 !member.isConstructor) {
924 addCompileTimeError( 935 addCompileTimeError(
925 offsetForToken(token), "Not a constant expression."); 936 offsetForToken(token), "Not a constant expression.");
926 } 937 }
927 return new VariableAccessor(this, token, builder.target); 938 // An initializing formal parameter might be final without its
939 // VariableDeclaration being final. See
940 // [ProcedureBuilder.computeFormalParameterInitializerScope]. If that
941 // wasn't the case, we could always use VariableAccessor.
942 if (builder.isFinal) {
943 var fact =
944 typePromoter.getFactForAccess(builder.target, functionNestingLevel);
945 var scope = typePromoter.currentScope;
946 return new ReadOnlyAccessor(
947 this,
948 new KernelVariableGet(builder.target, fact, scope)
949 ..fileOffset = offsetForToken(token),
950 name,
951 token);
952 } else {
953 return new VariableAccessor(this, token, builder.target);
954 }
928 } else if (builder.isInstanceMember) { 955 } else if (builder.isInstanceMember) {
929 if (constantExpressionRequired && 956 if (constantExpressionRequired &&
930 !inInitializer && 957 !inInitializer &&
931 // TODO(ahe): This is a hack because Fasta sets up the scope 958 // TODO(ahe): This is a hack because Fasta sets up the scope
932 // "this.field" parameters according to old semantics. Under the new 959 // "this.field" parameters according to old semantics. Under the new
933 // semantics, such parameters introduces a new parameter with that 960 // semantics, such parameters introduces a new parameter with that
934 // name that should be resolved here. 961 // name that should be resolved here.
935 !member.isConstructor) { 962 !member.isConstructor) {
936 addCompileTimeError( 963 addCompileTimeError(
937 offsetForToken(token), "Not a constant expression."); 964 offsetForToken(token), "Not a constant expression.");
(...skipping 648 matching lines...) Expand 10 before | Expand all | Expand 10 after
1586 if (inCatchClause) { 1613 if (inCatchClause) {
1587 modifiers |= finalMask; 1614 modifiers |= finalMask;
1588 } 1615 }
1589 bool isConst = (modifiers & constMask) != 0; 1616 bool isConst = (modifiers & constMask) != 0;
1590 bool isFinal = (modifiers & finalMask) != 0; 1617 bool isFinal = (modifiers & finalMask) != 0;
1591 ignore(Unhandled.Metadata); 1618 ignore(Unhandled.Metadata);
1592 VariableDeclaration variable; 1619 VariableDeclaration variable;
1593 if (!inCatchClause && 1620 if (!inCatchClause &&
1594 functionNestingLevel == 0 && 1621 functionNestingLevel == 0 &&
1595 memberKind != MemberKind.GeneralizedFunctionType) { 1622 memberKind != MemberKind.GeneralizedFunctionType) {
1596 dynamic builder = formalParameterScope.lookup( 1623 ProcedureBuilder member = this.member;
1597 name.name, offsetForToken(name.token), uri); 1624 KernelFormalParameterBuilder formal = member.getFormal(name.name);
1598 if (builder == null) { 1625 if (formal == null) {
1599 if (thisKeyword == null) { 1626 internalError("Internal error: formal missing for '${name.name}'");
1600 internalError("Internal error: formal missing for '${name.name}'"); 1627 } else {
1601 } else { 1628 variable = formal.build(library);
1602 addCompileTimeError(thisKeyword.charOffset,
1603 "'${name.name}' isn't a field in this class.");
1604 thisKeyword = null;
1605 }
1606 } else if (thisKeyword == null) {
1607 variable = builder.build(library);
1608 variable.initializer = name.initializer; 1629 variable.initializer = name.initializer;
1609 } else if (builder.isField && builder.parent == classBuilder) { 1630 }
1610 FieldBuilder field = builder; 1631 } else {
1611 if (type != null) { 1632 variable = new KernelVariableDeclaration(name?.name, functionNestingLevel,
1612 nit("Ignoring type on 'this' parameter '${name.name}'.", 1633 type: type,
1613 thisKeyword.charOffset); 1634 initializer: name?.initializer,
1614 } 1635 isFinal: isFinal,
1615 type = field.target.type; 1636 isConst: isConst);
1616 variable = new KernelVariableDeclaration( 1637 if (name != null) {
1617 name.name, functionNestingLevel, 1638 // TODO(ahe): Need an offset when name is null.
1618 type: type, 1639 variable.fileOffset = offsetForToken(name.token);
1619 initializer: name.initializer,
1620 isFinal: isFinal,
1621 isConst: isConst)
1622 ..fileOffset = offsetForToken(name.token);
1623 } else {
1624 addCompileTimeError(offsetForToken(name.token),
1625 "'${name.name}' isn't a field in this class.");
1626 } 1640 }
1627 } 1641 }
1628 variable ??= new KernelVariableDeclaration(name?.name, functionNestingLevel,
1629 type: type,
1630 initializer: name?.initializer,
1631 isFinal: isFinal,
1632 isConst: isConst)
1633 ..fileOffset = offsetForToken(name?.token);
1634 push(variable); 1642 push(variable);
1635 } 1643 }
1636 1644
1637 @override 1645 @override
1638 void endOptionalFormalParameters( 1646 void endOptionalFormalParameters(
1639 int count, Token beginToken, Token endToken) { 1647 int count, Token beginToken, Token endToken) {
1640 debugEvent("OptionalFormalParameters"); 1648 debugEvent("OptionalFormalParameters");
1641 FormalParameterType kind = optional("{", beginToken) 1649 FormalParameterType kind = optional("{", beginToken)
1642 ? FormalParameterType.NAMED 1650 ? FormalParameterType.NAMED
1643 : FormalParameterType.POSITIONAL; 1651 : FormalParameterType.POSITIONAL;
(...skipping 1031 matching lines...) Expand 10 before | Expand all | Expand 10 after
2675 } 2683 }
2676 2684
2677 @override 2685 @override
2678 Initializer buildInvalidIntializer(Expression expression, 2686 Initializer buildInvalidIntializer(Expression expression,
2679 [int charOffset = -1]) { 2687 [int charOffset = -1]) {
2680 needsImplicitSuperInitializer = false; 2688 needsImplicitSuperInitializer = false;
2681 return new LocalInitializer(new VariableDeclaration.forValue(expression)) 2689 return new LocalInitializer(new VariableDeclaration.forValue(expression))
2682 ..fileOffset = charOffset; 2690 ..fileOffset = charOffset;
2683 } 2691 }
2684 2692
2693 Initializer buildDuplicatedInitializer(
2694 String name, int offset, int previousInitializerOffset) {
2695 Initializer initializer = buildInvalidIntializer(
Johnni Winther 2017/06/06 10:35:08 buildInvalidIntializer -> buildInvalidInitializer
ahe 2017/06/06 15:08:24 Done.
2696 buildCompileTimeError("'$name' has already been initialized.", offset),
2697 offset);
2698 addCompileTimeError(
2699 initializedFields[name], "'$name' was initialized here.");
2700 return initializer;
2701 }
2702
2703 @override
2704 Initializer buildFieldInitializer(
2705 String name, int offset, Expression expression) {
2706 Builder builder = classBuilder.scope.local[name];
2707 if (builder is KernelFieldBuilder && builder.isInstanceMember) {
2708 initializedFields ??= <String, int>{};
2709 if (initializedFields.containsKey(name)) {
2710 return buildDuplicatedInitializer(
2711 name, offset, initializedFields[name]);
2712 }
2713 initializedFields[name] = offset;
2714 if (builder.isFinal && builder.hasInitializer) {
2715 // TODO(ahe): If CL 2843733002 is landed, this becomes a compile-time
2716 // error. Also, this is a compile-time error in strong mode.
2717 warningNotError(
2718 "'$name' is final instance variable that has already been "
2719 "initialized.",
2720 offset);
2721 warningNotError("'$name' was initialized here.", builder.charOffset);
2722 Builder constructor =
2723 library.loader.getDuplicatedFieldInitializerError();
2724 return buildInvalidIntializer(
2725 new Throw(buildStaticInvocation(constructor.target,
2726 new Arguments(<Expression>[new StringLiteral(name)]),
2727 charOffset: offset)),
2728 offset);
2729 } else {
2730 return new FieldInitializer(builder.field, expression)
2731 ..fileOffset = offset;
2732 }
2733 } else {
2734 return buildInvalidIntializer(
2735 buildCompileTimeError(
2736 "'$name' isn't an instance field of this class.", offset),
2737 offset);
2738 }
2739 }
2740
2685 @override 2741 @override
2686 Initializer buildSuperInitializer( 2742 Initializer buildSuperInitializer(
2687 Constructor constructor, Arguments arguments, 2743 Constructor constructor, Arguments arguments,
2688 [int charOffset = -1]) { 2744 [int charOffset = -1]) {
2689 needsImplicitSuperInitializer = false; 2745 needsImplicitSuperInitializer = false;
2690 return new SuperInitializer(constructor, arguments) 2746 return new SuperInitializer(constructor, arguments)
2691 ..fileOffset = charOffset; 2747 ..fileOffset = charOffset;
2692 } 2748 }
2693 2749
2694 @override 2750 @override
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
2981 return accessor.buildCompoundAssignment(barName, value, 3037 return accessor.buildCompoundAssignment(barName, value,
2982 offset: offsetForToken(token), voidContext: voidContext); 3038 offset: offsetForToken(token), voidContext: voidContext);
2983 } else if (identical("~/=", assignmentOperator)) { 3039 } else if (identical("~/=", assignmentOperator)) {
2984 return accessor.buildCompoundAssignment(mustacheName, value, 3040 return accessor.buildCompoundAssignment(mustacheName, value,
2985 offset: offsetForToken(token), voidContext: voidContext); 3041 offset: offsetForToken(token), voidContext: voidContext);
2986 } else { 3042 } else {
2987 return internalError("Unhandled: $assignmentOperator"); 3043 return internalError("Unhandled: $assignmentOperator");
2988 } 3044 }
2989 } 3045 }
2990 3046
2991 Initializer buildFieldInitializer( 3047 @override
2992 Map<String, FieldInitializer> initializers) { 3048 Initializer buildFieldInitializer(Map<String, int> initializedFields) {
2993 if (!identical("=", assignmentOperator) || 3049 if (!identical("=", assignmentOperator) ||
2994 !accessor.isThisPropertyAccessor) { 3050 !accessor.isThisPropertyAccessor) {
2995 return accessor.buildFieldInitializer(initializers); 3051 return accessor.buildFieldInitializer(initializedFields);
2996 } 3052 }
2997 String name = accessor.plainNameForRead; 3053 return helper.buildFieldInitializer(
2998 FieldInitializer initializer = initializers[name]; 3054 accessor.plainNameForRead, offsetForToken(token), value);
2999 if (initializer != null && initializer.value == null) {
3000 initializers.remove(name);
3001 initializer.value = value..parent = initializer;
3002 return initializer;
3003 }
3004 return accessor.buildFieldInitializer(initializers);
3005 } 3055 }
3006 } 3056 }
3007 3057
3008 class DelayedPostfixIncrement extends ContextAccessor { 3058 class DelayedPostfixIncrement extends ContextAccessor {
3009 final Name binaryOperator; 3059 final Name binaryOperator;
3010 3060
3011 final Procedure interfaceTarget; 3061 final Procedure interfaceTarget;
3012 3062
3013 DelayedPostfixIncrement(BuilderHelper helper, Token token, 3063 DelayedPostfixIncrement(BuilderHelper helper, Token token,
3014 FastaAccessor accessor, this.binaryOperator, this.interfaceTarget) 3064 FastaAccessor accessor, this.binaryOperator, this.interfaceTarget)
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after
3275 if (starToken == null) { 3325 if (starToken == null) {
3276 return AsyncMarker.Async; 3326 return AsyncMarker.Async;
3277 } else { 3327 } else {
3278 assert(identical(starToken.stringValue, "*")); 3328 assert(identical(starToken.stringValue, "*"));
3279 return AsyncMarker.AsyncStar; 3329 return AsyncMarker.AsyncStar;
3280 } 3330 }
3281 } else { 3331 } else {
3282 return internalError("Unknown async modifier: $asyncToken"); 3332 return internalError("Unknown async modifier: $asyncToken");
3283 } 3333 }
3284 } 3334 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698