| 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 904cd8084b9b063e179bce12afe3d3fd4cf17a4f..c1b55db209eb91affa7a7446f8be19fdcaf18c08 100644
|
| --- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
|
| +++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
|
| @@ -7,7 +7,8 @@ library fasta.body_builder;
|
| import '../fasta_codes.dart'
|
| show FastaMessage, codeExpectedButGot, codeExpectedFunctionBody;
|
|
|
| -import '../parser/parser.dart' show FormalParameterType, MemberKind, optional;
|
| +import '../parser/parser.dart'
|
| + show Assert, FormalParameterType, MemberKind, optional;
|
|
|
| import '../parser/identifier_context.dart' show IdentifierContext;
|
|
|
| @@ -106,6 +107,17 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
|
|
| Scope formalParameterScope;
|
|
|
| + /// This is set to true when we start parsing an initializer. We use this to
|
| + /// find the correct scope for initializers like in this example:
|
| + ///
|
| + /// class C {
|
| + /// final x;
|
| + /// C(x) : x = x;
|
| + /// }
|
| + ///
|
| + /// When parsing this initializer `x = x`, `x` must be resolved in two
|
| + /// different scopes. The first `x` must be resolved in the class' scope, the
|
| + /// second in the formal parameter scope.
|
| bool inInitializer = false;
|
|
|
| bool inCatchClause = false;
|
| @@ -2449,12 +2461,55 @@ class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
|
| }
|
|
|
| @override
|
| - void handleAssertStatement(Token assertKeyword, Token leftParenthesis,
|
| + void beginAssert(Token assertKeyword, Assert kind) {
|
| + debugEvent("beginAssert");
|
| + // If in an assert initializer, make sure [inInitializer] is false so we
|
| + // use the formal parameter scope. If this is any other kind of assert,
|
| + // inInitializer should be false anyway.
|
| + inInitializer = false;
|
| + }
|
| +
|
| + @override
|
| + void endAssert(Token assertKeyword, Assert kind, Token leftParenthesis,
|
| Token commaToken, Token rightParenthesis, Token semicolonToken) {
|
| - debugEvent("AssertStatement");
|
| + debugEvent("Assert");
|
| Expression message = popForValueIfNotNull(commaToken);
|
| Expression condition = popForValue();
|
| - push(new AssertStatement(condition, message));
|
| + AssertStatement statement = new AssertStatement(condition, message);
|
| + switch (kind) {
|
| + case Assert.Statement:
|
| + push(statement);
|
| + break;
|
| +
|
| + case Assert.Expression:
|
| + push(buildCompileTimeError("`assert` can't be used as an expression."));
|
| + break;
|
| +
|
| + case Assert.Initializer:
|
| + push(buildAssertInitializer(statement));
|
| + break;
|
| + }
|
| + }
|
| +
|
| + Initializer buildAssertInitializer(AssertStatement statement) {
|
| + // Since kernel only has asserts in statment form, we convert it to an
|
| + // expression by wrapping it in an anonymous function which we call
|
| + // immediately.
|
| + //
|
| + // Additionally, kernel has no initializer that evaluates an expression,
|
| + // but it does have `LocalInitializer` which requires a variable declartion.
|
| + //
|
| + // So we produce an initializer like this:
|
| + //
|
| + // var #t0 = (() { statement; }) ()
|
| + return new LocalInitializer(new VariableDeclaration.forValue(
|
| + buildMethodInvocation(
|
| + new FunctionExpression(new FunctionNode(statement)),
|
| + callName,
|
| + new Arguments.empty(),
|
| + statement.fileOffset,
|
| + isConstantExpression: true,
|
| + isImplicitCall: true)));
|
| }
|
|
|
| @override
|
|
|