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

Unified Diff: pkg/fasta/lib/src/kernel/body_builder.dart

Issue 2628043005: Fasta kernel AST builder. (Closed)
Patch Set: Rebased. Created 3 years, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/fasta/lib/src/kernel/builder_accessors.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/fasta/lib/src/kernel/body_builder.dart
diff --git a/pkg/fasta/lib/src/kernel/body_builder.dart b/pkg/fasta/lib/src/kernel/body_builder.dart
new file mode 100644
index 0000000000000000000000000000000000000000..293aa100b93097e91caa6f6a03cfc19c65be3fe6
--- /dev/null
+++ b/pkg/fasta/lib/src/kernel/body_builder.dart
@@ -0,0 +1,2514 @@
+// 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.body_builder;
+
+import 'package:dart_parser/src/parser.dart' show
+ FormalParameterType,
+ optional;
+
+import 'package:dart_parser/src/error_kind.dart' show
+ ErrorKind;
+
+import 'package:kernel/ast.dart';
+
+import 'package:kernel/clone.dart' show
+ CloneVisitor;
+
+import 'package:kernel/transformations/flags.dart' show
+ TransformerFlag;
+
+import 'package:kernel/class_hierarchy.dart' show
+ ClassHierarchy;
+
+import 'package:kernel/core_types.dart' show
+ CoreTypes;
+
+import 'package:dart_scanner/src/token.dart' show
+ BeginGroupToken,
+ ErrorToken,
+ Token,
+ isBinaryOperator,
+ isMinusOperator;
+
+import '../errors.dart' show
+ InputError,
+ internalError;
+
+import '../errors.dart' as errors show
+ inputError;
+
+import '../source/scope_listener.dart' show
+ JumpTargetKind,
+ NullValue,
+ ScopeListener;
+
+import '../builder/scope.dart' show
+ AccessErrorBuilder,
+ AmbiguousBuilder,
+ Scope;
+
+import '../source/outline_builder.dart' show
+ asyncMarkerFromTokens;
+
+import 'builder_accessors.dart';
+
+import 'frontend_accessors.dart' show
+ buildIsNull,
+ makeBinary,
+ makeLet;
+
+import 'builder_accessors.dart' as builder_accessors show
+ throwNoSuchMethodError;
+
+import '../quote.dart' show
+ Quote,
+ analyzeQuote,
+ unescape,
+ unescapeFirstStringPart,
+ unescapeLastStringPart,
+ unescapeString;
+
+import '../modifier.dart' show
+ Modifier,
+ constMask,
+ finalMask;
+
+import 'redirecting_factory_body.dart' show
+ getRedirectionTarget;
+
+import 'kernel_builder.dart';
+
+const bool showNits = false;
+
+final Name callName = new Name("call");
+
+final Name plusName = new Name("+");
+
+final Name minusName = new Name("-");
+
+final Name multiplyName = new Name("*");
+
+final Name divisionName = new Name("/");
+
+final Name percentName = new Name("%");
+
+final Name ampersandName = new Name("&");
+
+final Name leftShiftName = new Name("<<");
+
+final Name rightShiftName = new Name(">>");
+
+final Name caretName = new Name("^");
+
+final Name barName = new Name("|");
+
+final Name mustacheName = new Name("~/");
+
+class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
+ final KernelLibraryBuilder library;
+
+ final MemberBuilder member;
+
+ final KernelClassBuilder classBuilder;
+
+ final ClassHierarchy hierarchy;
+
+ final CoreTypes coreTypes;
+
+ final bool isInstanceMember;
+
+ final Map<String, FieldInitializer> fieldInitializers =
+ <String, FieldInitializer>{};
+
+ final Scope enclosingScope;
+
+ Scope formalParameterScope;
+
+ bool isFirstIdentifier = false;
+
+ bool hasParserError = false;
+
+ bool inInitializer = false;
+
+ bool inCatchClause = false;
+
+ int functionNestingLevel = 0;
+
+ Statement compileTimeErrorInTry;
+
+ Statement compileTimeErrorInLoopOrSwitch;
+
+ Scope switchScope;
+
+ CloneVisitor cloner;
+
+ BodyBuilder(this.library, this.member, Scope scope, this.formalParameterScope,
+ this.hierarchy, this.coreTypes, this.classBuilder, this.isInstanceMember)
+ : enclosingScope = scope,
+ super(scope);
+
+ bool get inConstructor {
+ return functionNestingLevel == 0 && member is KernelConstructorBuilder;
+ }
+
+ bool get isInstanceContext {
+ return isInstanceMember || member is KernelConstructorBuilder;
+ }
+
+ void push(Object node) {
+ isFirstIdentifier = false;
+ inInitializer = false;
+ super.push(node);
+ }
+
+ Expression popForValue() => toValue(pop());
+
+ Expression popForEffect() => toEffect(pop());
+
+ Expression toValue(Object node) {
+ if (node is UnresolvedIdentifier) {
+ return throwNoSuchMethodError(node.name.name, new Arguments.empty(),
+ node.fileOffset, isGetter: true);
+ } else if (node is BuilderAccessor) {
+ return node.buildSimpleRead();
+ } else if (node is TypeVariableBuilder) {
+ TypeParameterType type = node.buildTypesWithBuiltArguments(null);
+ if (!isInstanceContext && type.parameter.parent is Class) {
+ return buildCompileTimeError(
+ "Type variables can only be used in instance methods.");
+ } else {
+ return new TypeLiteral(type);
+ }
+ } else if (node is TypeDeclarationBuilder) {
+ return new TypeLiteral(node.buildTypesWithBuiltArguments(null));
+ } else if (node is KernelTypeBuilder) {
+ return new TypeLiteral(node.build());
+ } else if (node is Expression) {
+ return node;
+ } else if (node is PrefixBuilder) {
+ return buildCompileTimeError("A library can't be used as an expression.");
+ } else {
+ return internalError("Unhandled: ${node.runtimeType}");
+ }
+ }
+
+ Expression toEffect(Object node) {
+ if (node is BuilderAccessor) return node.buildForEffect();
+ return toValue(node);
+ }
+
+ List<Expression> popListForValue(int n) {
+ List<Expression> list =
+ new List<Expression>.filled(n, null, growable: true);
+ for (int i = n - 1; i >= 0; i--) {
+ list[i] = popForValue();
+ }
+ return list;
+ }
+
+ List<Expression> popListForEffect(int n) {
+ List<Expression> list =
+ new List<Expression>.filled(n, null, growable: true);
+ for (int i = n - 1; i >= 0; i--) {
+ list[i] = popForEffect();
+ }
+ return list;
+ }
+
+ Block popBlock(int count) {
+ List<Statement> statements = popList(count) ?? <Statement>[];
+ List<Statement> copy;
+ for (int i = 0; i < statements.length; i++) {
+ var statement = statements[i];
+ if (statement is List) {
+ copy ??= new List<Statement>.from(statements.getRange(0, i));
+ copy.addAll(statement);
+ } else if (copy != null) {
+ copy.add(statement);
+ }
+ }
+ return new Block(copy ?? statements);
+ }
+
+ Statement popStatementIfNotNull(Object value) {
+ return value == null ? null : popStatement();
+ }
+
+ Statement popStatement() {
+ var statement = pop();
+ if (statement is List) {
+ return new Block(new List<Statement>.from(statement));
+ } else {
+ return statement;
+ }
+ }
+
+ void ignore(Unhandled value) {
+ pop();
+ }
+
+ void enterSwitchScope() {
+ push(switchScope ?? NullValue.SwitchScope);
+ switchScope = scope;
+ }
+
+ void exitSwitchScope() {
+ switchScope = pop();
+ }
+
+ Uri get uri => library.fileUri ?? library.uri;
+
+ JumpTarget createJumpTarget(JumpTargetKind kind) => new JumpTarget(kind);
+
+ void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) {
+ debugEvent("Metadata");
+ pop(); // Arguments.
+ popIfNotNull(periodBeforeName); // Postfix.
+ pop(); // Type arguments.
+ pop(); // Expression or type name (depends on arguments).
+ // TODO(ahe): Implement metadata on local declarations.
+ }
+
+ void endMetadataStar(int count, bool forParameter) {
+ debugEvent("MetadataStar");
+ push(NullValue.Metadata);
+ }
+
+ void endTopLevelFields(int count, Token beginToken, Token endToken) {
+ debugEvent("TopLevelFields");
+ doFields(count);
+ // There's no metadata here because of a slight assymetry between
+ // [parseTopLevelMember] and [parseMember]. This assymetry leads to
+ // DietListener discarding top-level member metadata.
+ }
+
+ void endFields(int count, Token beginToken, Token endToken) {
+ debugEvent("Fields");
+ doFields(count);
+ pop(); // Metadata.
+ }
+
+ void doFields(int count) {
+ List nodes = popList(count);
+ pop(); // Type.
+ pop(); // Modifiers.
+ for (var node in nodes) {
+ if (node is Identifier) {
+ // Ignore, there's no initializer.
+ } else if (node is VariableDeclaration) {
+ FieldBuilder field;
+ if (classBuilder != null) {
+ field = classBuilder.members[node.name];
+ } else {
+ field = library.members[node.name];
+ }
+ if (field.next != null) {
+ // TODO(ahe): This can happen, for example, if a final field is
+ // combined with a setter.
+ internalError(
+ "Unhandled: '${field.name}' has more than one declaration.");
+ }
+ field.initializer = node.initializer;
+ } else {
+ internalError("Unhandled: ${node.runtimeType}");
+ }
+ }
+ }
+
+ void endMember() {
+ debugEvent("Member");
+ checkEmpty();
+ }
+
+ void endFunctionBody(int count, Token beginToken, Token endToken) {
+ debugEvent("FunctionBody");
+ if (beginToken == null) {
+ assert(count == 0);
+ push(NullValue.Block);
+ } else {
+ Block block = popBlock(count);
+ exitLocalScope();
+ push(block);
+ }
+ }
+
+ void prepareInitializers() {
+ scope = formalParameterScope;
+ assert(fieldInitializers.isEmpty);
+ final member = this.member;
+ if (member is KernelConstructorBuilder) {
+ Constructor constructor = member.constructor;
+ classBuilder.members.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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ void beginConstructorInitializer(Token token) {
+ debugEvent("beginConstructorInitializer");
+ inInitializer = true;
+ }
+
+ void endConstructorInitializer(Token token) {
+ debugEvent("endConstructorInitializer");
+ assert(!inInitializer);
+ final member = this.member;
+ var node = pop();
+ Initializer initializer;
+ if (node is Initializer) {
+ initializer = node;
+ } else if (node is BuilderAccessor) {
+ initializer = node.buildFieldInitializer(fieldInitializers);
+ } else if (node is ConstructorInvocation) {
+ initializer = new SuperInitializer(node.target, node.arguments);
+ } else {
+ if (node is !Throw) {
+ node = wrapInvalid(node);
+ }
+ initializer =
+ new LocalInitializer(new VariableDeclaration.forValue(node));
+ }
+ if (member is KernelConstructorBuilder) {
+ member.addInitializer(initializer);
+ } else {
+ inputError("Can't have initializers: ${member.name}", token.charOffset);
+ }
+ }
+
+ void handleNoInitializers() {
+ debugEvent("NoInitializers");
+ }
+
+ void endInitializers(int count, Token beginToken, Token endToken) {
+ debugEvent("Initializers");
+ }
+
+ void finishFunction(FormalParameters formals,
+ AsyncMarker asyncModifier, Statement body) {
+ debugEvent("finishFunction");
+ KernelFunctionBuilder builder = member;
+ if (builder is KernelConstructorBuilder) {
+ if (asyncModifier != AsyncMarker.Sync) {
+ // TODO(ahe): Change this to a null check.
+ inputError("Can't be marked as ${asyncModifier}: ${builder.name}",
+ body?.fileOffset);
+ }
+ } else if (builder is KernelProcedureBuilder) {
+ builder.asyncModifier = asyncModifier;
+ } else {
+ internalError("Unhandled: ${builder.runtimeType}");
+ }
+ builder.body = body;
+ if (formals?.optional != null) {
+ Iterator<FormalParameterBuilder> formalBuilders =
+ builder.formals.skip(formals.required.length).iterator;
+ for (VariableDeclaration parameter in formals.optional.formals) {
+ bool hasMore = formalBuilders.moveNext();
+ assert(hasMore);
+ VariableDeclaration realParameter = formalBuilders.current.target;
+ Expression initializer = parameter.initializer ?? new NullLiteral();
+ realParameter.initializer = initializer
+ ..parent = realParameter;
+ }
+ }
+ }
+
+ void endExpressionStatement(Token token) {
+ debugEvent("ExpressionStatement");
+ push(new ExpressionStatement(popForEffect()));
+ }
+
+ void endArguments(int count, Token beginToken, Token endToken) {
+ debugEvent("Arguments");
+ List arguments = popList(count) ?? <Expression>[];
+ int firstNamedArgument = arguments.length;
+ for (int i = 0; i < arguments.length; i++) {
+ var node = arguments[i];
+ if (node is NamedExpression) {
+ firstNamedArgument = i < firstNamedArgument ? i : firstNamedArgument;
+ } else {
+ arguments[i] = node = toValue(node);
+ if (i > firstNamedArgument) {
+ internalError("Expected named argument: $node");
+ }
+ }
+ }
+ if (firstNamedArgument < arguments.length) {
+ List<Expression> positional = new List<Expression>.from(
+ arguments.getRange(0, firstNamedArgument));
+ List<NamedExpression> named = new List<NamedExpression>.from(
+ arguments.getRange(firstNamedArgument,arguments.length));
+ push(new Arguments(positional, named: named));
+ } else {
+ push(new Arguments(arguments));
+ }
+ }
+
+ void handleParenthesizedExpression(BeginGroupToken token) {
+ debugEvent("ParenthesizedExpression");
+ push(popForValue());
+ }
+
+ void endSend(Token token) {
+ debugEvent("Send");
+ Arguments arguments = pop();
+ List typeArguments = pop();
+ Object receiver = pop();
+ if (arguments != null && typeArguments != null) {
+ arguments.types.addAll(typeArguments);
+ } else {
+ assert(typeArguments == null);
+ }
+ if (receiver is Identifier) {
+ Name name = new Name(receiver.name, library.library);
+ if (arguments == null) {
+ push(new IncompletePropertyAccessor(this, token.charOffset, name));
+ } else {
+ push(new SendAccessor(this, token.charOffset, name, arguments));
+ }
+ } else if (arguments == null) {
+ push(receiver);
+ } else {
+ push(finishSend(receiver, arguments, token.charOffset));
+ }
+ }
+
+ finishSend(Object receiver, Arguments arguments, int charOffset) {
+ if (receiver is BuilderAccessor) {
+ return receiver.doInvocation(charOffset, arguments);
+ } else if (receiver is UnresolvedIdentifier) {
+ return throwNoSuchMethodError(receiver.name.name, arguments,
+ receiver.fileOffset);
+ } else {
+ return buildMethodInvocation(toValue(receiver), callName,
+ arguments, charOffset);
+ }
+ }
+
+ void beginCascade(Token token) {
+ debugEvent("beginCascade");
+ Expression expression = popForValue();
+ if (expression is CascadeReceiver) {
+ push(expression);
+ push(new VariableAccessor(
+ this, expression.fileOffset, expression.variable));
+ expression.extend();
+ } else {
+ VariableDeclaration variable =
+ new VariableDeclaration.forValue(expression);
+ push(new CascadeReceiver(variable));
+ push(new VariableAccessor(this, expression.fileOffset, variable));
+ }
+ }
+
+ void endCascade() {
+ debugEvent("endCascade");
+ Expression expression = popForEffect();
+ CascadeReceiver cascadeReceiver = pop();
+ cascadeReceiver.finalize(expression);
+ push(cascadeReceiver);
+ }
+
+ void handleBinaryExpression(Token token) {
+ debugEvent("BinaryExpression");
+ if (optional(".", token) || optional("..", token)) {
+ return doDotOrCascadeExpression(token);
+ }
+ if (optional("&&", token) || optional("||", token)) {
+ return doLogicalExpression(token);
+ }
+ if (optional("??", token)) return doIfNull(token);
+ if (optional("?.", token)) return doIfNotNull(token);
+ Expression argument = popForValue();
+ var receiver = pop();
+ bool isSuper = false;
+ if (receiver is ThisAccessor && receiver.isSuper) {
+ isSuper = true;
+ receiver = new ThisExpression();
+ }
+ push(buildBinaryOperator(toValue(receiver), token, argument, isSuper));
+ }
+
+ Expression buildBinaryOperator(Expression a, Token token, Expression b,
+ bool isSuper) {
+ bool negate = false;
+ String operator = token.stringValue;
+ if (identical("!=", operator)) {
+ operator = "==";
+ negate = true;
+ }
+ if (!isBinaryOperator(operator) && !isMinusOperator(operator)) {
+ return buildCompileTimeError("Not an operator: '$operator'.",
+ token.charOffset);
+ } else {
+ Expression result = makeBinary(a, new Name(operator), null, b);
+ if (isSuper) {
+ result = toSuperMethodInvocation(result);
+ }
+ return negate ? new Not(result) : result;
+ }
+ }
+
+ void doLogicalExpression(Token token) {
+ Expression argument = popForValue();
+ Expression receiver = popForValue();
+ push(new LogicalExpression(receiver, token.stringValue, argument));
+ }
+
+ /// Handle `a ?? b`.
+ void doIfNull(Token token) {
+ Expression b = popForValue();
+ Expression a = popForValue();
+ VariableDeclaration variable = new VariableDeclaration.forValue(a);
+ push(makeLet(variable,
+ new ConditionalExpression(buildIsNull(new VariableGet(variable)),
+ b, new VariableGet(variable), const DynamicType())));
+ }
+
+ /// Handle `a?.b(...)`.
+ void doIfNotNull(Token token) {
+ IncompleteSend send = pop();
+ push(send.withReceiver(pop(), isNullAware: true));
+ }
+
+ void doDotOrCascadeExpression(Token token) {
+ // TODO(ahe): Handle null-aware.
+ IncompleteSend send = pop();
+ Object receiver = optional(".", token) ? pop() : popForValue();
+ push(send.withReceiver(receiver));
+ }
+
+ Expression toSuperMethodInvocation(MethodInvocation node) {
+ Member target = lookupSuperMember(node.name);
+ bool isNoSuchMethod = target == null;
+ if (target is Procedure) {
+ if (!target.isAccessor) {
+ if (areArgumentsCompatible(target.function, node.arguments)) {
+ // TODO(ahe): Use [DirectMethodInvocation] when possible.
+ Expression result = new DirectMethodInvocation(new ThisExpression(),
+ target, node.arguments);
+ result = new SuperMethodInvocation(node.name, node.arguments, null);
+ return result;
+ } else {
+ isNoSuchMethod = true;
+ }
+ }
+ }
+ if (isNoSuchMethod) {
+ return throwNoSuchMethodError(
+ node.name.name, node.arguments, node.fileOffset, isSuper: true);
+ }
+ // TODO(ahe): Use [DirectPropertyGet] when possible.
+ Expression receiver = new DirectPropertyGet(new ThisExpression(), target);
+ receiver = new SuperPropertyGet(node.name, target);
+ return buildMethodInvocation(receiver, callName, node.arguments,
+ node.fileOffset);
+ }
+
+ bool areArgumentsCompatible(FunctionNode function, Arguments arguments) {
+ // TODO(ahe): Implement this.
+ return true;
+ }
+
+ Expression throwNoSuchMethodError(String name, Arguments arguments,
+ int charOffset, {bool isSuper: false, isGetter: false, isSetter: false}) {
+ return builder_accessors.throwNoSuchMethodError(name, arguments, uri,
+ charOffset, coreTypes, isSuper: isSuper, isGetter: isGetter,
+ isSetter: isSetter);
+ }
+
+ Member lookupSuperMember(Name name, {bool isSetter: false}) {
+ Class superclass = classBuilder.cls.superclass;
+ return superclass == null
+ ? null
+ : hierarchy.getDispatchTarget(superclass, name, setter: isSetter);
+ }
+
+ Constructor lookupConstructor(Name name, {bool isSuper}) {
+ Class cls = classBuilder.cls;
+ if (isSuper) {
+ cls = cls.superclass;
+ while (cls.isMixinApplication) {
+ cls = cls.superclass;
+ }
+ }
+ if (cls != null) {
+ for (Constructor constructor in cls.constructors) {
+ if (constructor.name == name) return constructor;
+ }
+ }
+ return null;
+ }
+
+ void beginExpression(Token token) {
+ debugEvent("beginExpression");
+ isFirstIdentifier = true;
+ }
+
+ Builder computeSetter(Builder builder, Scope scope, String name) {
+ if (builder.isSetter) return builder;
+ if (builder.isGetter) return scope.lookupSetter(name);
+ return builder.isField ? (builder.isFinal ? null : builder) : null;
+ }
+
+ void handleIdentifier(Token token) {
+ debugEvent("handleIdentifier");
+ String name = token.value;
+ if (isFirstIdentifier) {
+ assert(!inInitializer || this.scope == enclosingScope ||
+ this.scope.parent == enclosingScope);
+ // This deals with this kind of initializer: `C(a) : a = a;`
+ Scope scope = inInitializer ? enclosingScope : this.scope;
+ Builder builder = scope.lookup(name);
+ push(builderToFirstExpression(builder, name, token.charOffset));
+ } else {
+ push(new Identifier(name)..fileOffset = token.charOffset);
+ }
+ }
+
+ builderToFirstExpression(Builder builder, String name, int charOffset,
+ {bool isPrefix: false}) {
+ if (builder == null || (!isInstanceContext && builder.isInstanceMember)) {
+ if (!isPrefix && identical(name, "dynamic") && builder == null) {
+ return new KernelInterfaceTypeBuilder(name, null);
+ }
+ Name n = new Name(name, library.library);
+ if (!isPrefix && isInstanceContext) {
+ assert(builder == null);
+ return new ThisPropertyAccessor(this, charOffset, n, null, null);
+ } else {
+ return new UnresolvedIdentifier(n)
+ ..fileOffset = charOffset;
+ }
+ } else if (builder.isTypeDeclaration) {
+ return builder;
+ } else if (builder.isLocal) {
+ return new VariableAccessor(this, charOffset, builder.target);
+ } else if (builder.isInstanceMember) {
+ return new ThisPropertyAccessor(this, charOffset,
+ new Name(name, library.library), null, null);
+ } else if (builder.isRegularMethod) {
+ assert(builder.isStatic || builder.isTopLevel);
+ return new StaticAccessor(this, charOffset, builder.target, null);
+ } else if (builder is PrefixBuilder) {
+ return builder;
+ } else if (builder is MixedAccessor) {
+ return new StaticAccessor(this, charOffset, builder.getter.target,
+ builder.setter.target);
+ } else {
+ if (builder is AccessErrorBuilder) {
+ AccessErrorBuilder error = builder;
+ builder = error.builder;
+ }
+ if (builder.target == null) {
+ return internalError("Unhandled: ${builder}");
+ }
+ Member getter = builder.target.hasGetter ? builder.target : null;
+ Member setter = builder.target.hasSetter ? builder.target : null;
+ setter ??= computeSetter(builder, scope, name)?.target;
+ return
+ new StaticAccessor(this, charOffset, getter, setter);
+ }
+ }
+
+ void handleQualified(Token period) {
+ debugEvent("Qualified");
+ Identifier name = pop();
+ var receiver = pop();
+ push([receiver, name]);
+ }
+
+ void beginLiteralString(Token token) {
+ debugEvent("beginLiteralString");
+ push(token);
+ }
+
+ void handleStringPart(Token token) {
+ debugEvent("StringPart");
+ push(token);
+ }
+
+ void endLiteralString(int interpolationCount) {
+ debugEvent("endLiteralString");
+ if (interpolationCount == 0) {
+ Token token = pop();
+ push(new StringLiteral(unescapeString(token.value)));
+ } else {
+ List parts = popList(1 + interpolationCount * 2);
+ Token first = parts.first;
+ Token last = parts.last;
+ Quote quote = analyzeQuote(first.value);
+ List<Expression> expressions = <Expression>[];
+ expressions.add(new StringLiteral(unescapeFirstStringPart(
+ first.value, quote)));
+ for (int i = 1; i < parts.length - 1; i++) {
+ var part = parts[i];
+ if (part is Token) {
+ expressions.add(new StringLiteral(unescape(part.value, quote)));
+ } else {
+ expressions.add(toValue(part));
+ }
+ }
+ expressions.add(
+ new StringLiteral(unescapeLastStringPart(last.value, quote)));
+ push(new StringConcatenation(expressions));
+ }
+ }
+
+ void handleStringJuxtaposition(int literalCount) {
+ debugEvent("StringJuxtaposition");
+ List<Expression> parts = popListForValue(literalCount);
+ List<Expression> expressions;
+ // Flatten string juxtapositions of string interpolation.
+ for (int i = 0; i < parts.length; i++) {
+ Expression part = parts[i];
+ if (part is StringConcatenation) {
+ if (expressions == null) {
+ expressions = parts.sublist(0, i);
+ }
+ expressions.addAll(part.expressions);
+ } else {
+ if (expressions != null) {
+ expressions.add(part);
+ }
+ }
+ }
+ push(new StringConcatenation(expressions ?? parts));
+ }
+
+ void handleLiteralInt(Token token) {
+ debugEvent("LiteralInt");
+ push(new IntLiteral(int.parse(token.value)));
+ }
+
+ void endReturnStatement(
+ bool hasExpression, Token beginToken, Token endToken) {
+ debugEvent("ReturnStatement");
+ Expression expression = hasExpression ? popForValue() : null;
+ if (expression != null && inConstructor) {
+ push(buildCompileTimeErrorStatement("Can't return from a constructor.",
+ beginToken.charOffset));
+ } else {
+ push(new ReturnStatement(expression));
+ }
+ }
+
+ void endIfStatement(Token ifToken, Token elseToken) {
+ Statement elsePart = popStatementIfNotNull(elseToken);
+ Statement thenPart = popStatement();
+ Expression condition = popForValue();
+ push(new IfStatement(condition, thenPart, elsePart));
+ }
+
+ void endInitializer(Token assignmentOperator) {
+ debugEvent("Initializer");
+ assert(assignmentOperator.stringValue == "=");
+ Expression initializer = popForValue();
+ Identifier identifier = pop();
+ push(new VariableDeclaration(identifier.name, initializer: initializer));
+ }
+
+ void endInitializedIdentifier() {
+ // TODO(ahe): Use [InitializedIdentifier] here?
+ debugEvent("InitializedIdentifier");
+ TreeNode node = pop();
+ VariableDeclaration variable;
+ if (node is VariableDeclaration) {
+ variable = node;
+ } else if (node is Identifier) {
+ variable = new VariableDeclaration(node.name);
+ } else {
+ internalError("unhandled identifier: ${node.runtimeType}");
+ }
+ push(variable);
+ scope[variable.name] = new KernelVariableBuilder(variable);
+ }
+
+ void endVariablesDeclaration(int count, Token endToken) {
+ debugEvent("VariablesDeclaration");
+ List<VariableDeclaration> variables = popList(count);
+ DartType type = pop();
+ int modifiers = Modifier.validate(pop());
+ bool isConst = (modifiers & constMask) != 0;
+ bool isFinal = (modifiers & finalMask) != 0;
+ if (type != null || isConst || isFinal) {
+ type ??= const DynamicType();
+ for (VariableDeclaration variable in variables) {
+ variable
+ ..type = type
+ ..isConst = isConst
+ ..isFinal = isFinal;
+ }
+ }
+ if (variables.length != 1) {
+ push(variables);
+ } else {
+ push(variables.single);
+ }
+ }
+
+ void endBlock(int count, Token beginToken, Token endToken) {
+ debugEvent("Block");
+ Block block = popBlock(count);
+ exitLocalScope();
+ push(block);
+ }
+
+ void handleAssignmentExpression(Token token) {
+ debugEvent("AssignmentExpression");
+ Expression value = popForValue();
+ var accessor = pop();
+ if (accessor is TypeDeclarationBuilder) {
+ push(wrapInvalid(
+ new TypeLiteral(accessor.buildTypesWithBuiltArguments(null))));
+ } else if (accessor is! BuilderAccessor) {
+ push(buildCompileTimeError("Can't assign to this.", token.charOffset));
+ } else {
+ push(new DelayedAssignment(this, token.charOffset, accessor, value,
+ token.stringValue));
+ }
+ }
+
+ void enterLoop() {
+ if (peek() is LabelTarget) {
+ LabelTarget target = peek();
+ enterBreakTarget(target.breakTarget);
+ enterContinueTarget(target.continueTarget);
+ } else{
+ enterBreakTarget();
+ enterContinueTarget();
+ }
+ }
+
+ void exitLoopOrSwitch(Statement statement) {
+ if (compileTimeErrorInLoopOrSwitch != null) {
+ push(compileTimeErrorInLoopOrSwitch);
+ compileTimeErrorInLoopOrSwitch = null;
+ } else {
+ push(statement);
+ }
+ }
+
+ void endForStatement(
+ int updateExpressionCount, Token beginToken, Token endToken) {
+ debugEvent("ForStatement");
+ Statement body = popStatement();
+ List<Expression> updates = popListForEffect(updateExpressionCount);
+ Statement conditionStatement = popStatement();
+ Expression condition = null;
+ if (conditionStatement is ExpressionStatement) {
+ condition = conditionStatement.expression;
+ } else {
+ assert(conditionStatement is EmptyStatement);
+ }
+ List<VariableDeclaration> variables = <VariableDeclaration>[];
+ var variableOrExpression = pop();
+ Statement begin;
+ if (variableOrExpression is BuilderAccessor) {
+ variableOrExpression = variableOrExpression.buildForEffect();
+ }
+ if (variableOrExpression is VariableDeclaration) {
+ variables.add(variableOrExpression);
+ } else if (variableOrExpression is List) {
+ variables.addAll(variableOrExpression);
+ } else if (variableOrExpression == null) {
+ // Do nothing.
+ } else if (variableOrExpression is Expression) {
+ begin = new ExpressionStatement(variableOrExpression);
+ } else {
+ return internalError("Unhandled: ${variableOrExpression.runtimeType}");
+ }
+ exitLocalScope();
+ JumpTarget continueTarget = exitContinueTarget();
+ JumpTarget breakTarget = exitBreakTarget();
+ if (continueTarget.hasUsers) {
+ body = new LabeledStatement(body);
+ continueTarget.resolveContinues(body);
+ }
+ Statement result = new ForStatement(variables, condition, updates, body);
+ if (begin != null) {
+ result = new Block(<Statement>[begin, result]);
+ }
+ if (breakTarget.hasUsers) {
+ result = new LabeledStatement(result);
+ breakTarget.resolveBreaks(result);
+ }
+ exitLoopOrSwitch(result);
+ }
+
+ void endAwaitExpression(Token beginToken, Token endToken) {
+ debugEvent("AwaitExpression");
+ push(new AwaitExpression(popForValue()));
+ }
+
+ void handleAsyncModifier(Token asyncToken, Token starToken) {
+ debugEvent("AsyncModifier");
+ push(asyncMarkerFromTokens(asyncToken, starToken));
+ }
+
+ void handleLiteralList(
+ int count, Token beginToken, Token constKeyword, Token endToken) {
+ debugEvent("LiteralList");
+ List<Expression> expressions = popListForValue(count);
+ List<DartType> typeArguments = pop();
+ DartType typeArgument = const DynamicType();
+ if (typeArguments != null) {
+ typeArgument = typeArguments.first;
+ if (typeArguments.length > 1) {
+ typeArgument = const DynamicType();
+ warning("Too many type arguments on List literal.",
+ beginToken.charOffset);
+ }
+ }
+ push(new ListLiteral(expressions, typeArgument: typeArgument,
+ isConst: constKeyword != null));
+ }
+
+ void handleLiteralBool(Token token) {
+ debugEvent("LiteralBool");
+ bool value = optional("true", token);
+ assert(value || optional("false", token));
+ push(new BoolLiteral(value));
+ }
+
+ void handleLiteralDouble(Token token) {
+ debugEvent("LiteralDouble");
+ push(new DoubleLiteral(double.parse(token.value)));
+ }
+
+ void handleLiteralNull(Token token) {
+ debugEvent("LiteralNull");
+ push(new NullLiteral());
+ }
+
+ void handleLiteralMap(
+ int count, Token beginToken, Token constKeyword, Token endToken) {
+ debugEvent("LiteralMap");
+ List<MapEntry> entries = popList(count) ?? <MapEntry>[];
+ List<DartType> typeArguments = pop();
+ DartType keyType = const DynamicType();
+ DartType valueType = const DynamicType();
+ if (typeArguments != null) {
+ if (typeArguments.length != 2) {
+ keyType = const DynamicType();
+ valueType = const DynamicType();
+ warning("Map literal requires two type arguments.",
+ beginToken.charOffset);
+ } else {
+ keyType = typeArguments[0];
+ valueType = typeArguments[1];
+ }
+ }
+ push(new MapLiteral(entries, keyType: keyType, valueType: valueType,
+ isConst: constKeyword != null));
+ }
+
+ void endLiteralMapEntry(Token colon, Token endToken) {
+ debugEvent("LiteralMapEntry");
+ Expression value = popForValue();
+ Expression key = popForValue();
+ push(new MapEntry(key, value));
+ }
+
+ void beginLiteralSymbol(Token token) {
+ isFirstIdentifier = false;
+ }
+
+ String symbolPartToString(name) {
+ if (name is Identifier) {
+ return name.name;
+ } else if (name is Operator) {
+ return name.name;
+ } else {
+ return internalError("Unhandled: ${name.runtimeType}");
+ }
+ }
+
+ void endLiteralSymbol(Token hashToken, int identifierCount) {
+ debugEvent("LiteralSymbol");
+ String value;
+ if (identifierCount == 1) {
+ value = symbolPartToString(popForValue());
+ } else {
+ List parts = popList(identifierCount);
+ value = symbolPartToString(parts.first);
+ for (int i = 1; i < parts.length; i++) {
+ value += ".${symbolPartToString(parts[i])}";
+ }
+ }
+ push(new SymbolLiteral(value));
+ }
+
+ DartType toKernelType(String name, List<DartType> arguments) {
+ if (identical(name, "void")) return const VoidType();
+ if (identical(name, "dynamic")) return const DynamicType();
+ Builder builder = scope.lookup(name);
+ if (builder is TypeDeclarationBuilder) {
+ return builder.buildTypesWithBuiltArguments(arguments);
+ }
+ if (builder == null) {
+ print("$uri: Type not found: $name");
+ } else {
+ print("$uri: Not a type: $name");
+ }
+ // TODO(ahe): Create an error somehow.
+ return const DynamicType();
+ }
+
+ void endType(Token beginToken, Token endToken) {
+ // TODO(ahe): The scope is wrong for return types of generic functions.
+ debugEvent("Type");
+ List<DartType> arguments = pop();
+ var name = pop();
+ if (name is List) {
+ if (name.length != 2) {
+ return internalError("Unexpected: $name.length");
+ }
+ var prefix = name[0];
+ if (prefix is Identifier) {
+ prefix = prefix.name;
+ }
+ var suffix = name[1];
+ if (suffix is Identifier) {
+ suffix = suffix.name;
+ }
+ Builder builder;
+ if (prefix is Builder) {
+ builder = prefix;
+ } else {
+ builder = scope.lookup(prefix);
+ }
+ if (builder is PrefixBuilder) {
+ name = builder.exports[suffix];
+ } else {
+ return inputError(
+ "Can't be used as a type: '${debugName(prefix, suffix)}'.",
+ beginToken.charOffset);
+ }
+ }
+ if (name is Identifier) {
+ name = name.name;
+ }
+ if (name is BuilderAccessor) {
+ warning("'${beginToken.value}' isn't a type.", beginToken.charOffset);
+ push(const DynamicType());
+ } else if (name is UnresolvedIdentifier) {
+ warning("'${name.name}' isn't a type.", beginToken.charOffset);
+ push(const DynamicType());
+ } else if (name is TypeVariableBuilder) {
+ push(name.buildTypesWithBuiltArguments(arguments));
+ } else if (name is TypeDeclarationBuilder) {
+ push(name.buildTypesWithBuiltArguments(arguments));
+ } else if (name is TypeBuilder) {
+ push(name.build());
+ } else {
+ push(toKernelType(name, arguments));
+ }
+ if (peek() is TypeParameterType) {
+ TypeParameterType type = peek();
+ if (!isInstanceContext && type.parameter.parent is Class) {
+ pop();
+ warning("Type variables can only be used in instance methods.",
+ beginToken.charOffset);
+ push(const DynamicType());
+ }
+ }
+ }
+
+ void handleVoidKeyword(Token token) {
+ debugEvent("VoidKeyword");
+ push(const VoidType());
+ }
+
+ void handleAsOperator(Token operator, Token endToken) {
+ debugEvent("AsOperator");
+ DartType type = pop();
+ Expression expression = popForValue();
+ push(new AsExpression(expression, type));
+ }
+
+ void handleIsOperator(Token operator, Token not, Token endToken) {
+ debugEvent("IsOperator");
+ DartType type = pop();
+ Expression expression = popForValue();
+ expression = new IsExpression(expression, type);
+ if (not != null) {
+ expression = new Not(expression);
+ }
+ push(expression);
+ }
+
+ void handleConditionalExpression(Token question, Token colon) {
+ debugEvent("ConditionalExpression");
+ Expression elseExpression = popForValue();
+ Expression thenExpression = popForValue();
+ Expression condition = popForValue();
+ push(new ConditionalExpression(
+ condition, thenExpression, elseExpression, const DynamicType()));
+ }
+
+ void endThrowExpression(Token throwToken, Token endToken) {
+ debugEvent("ThrowExpression");
+ Expression expression = popForValue();
+ push(new Throw(expression));
+ }
+
+ void endFormalParameter(Token thisKeyword) {
+ debugEvent("FormalParameter");
+ if (thisKeyword != null) {
+ if (!inConstructor) {
+ return inputError("'this' parameters can only be used on constructors.",
+ thisKeyword.charOffset);
+ }
+ }
+ Identifier name = pop();
+ DartType type = pop();
+ pop(); // Modifiers.
+ ignore(Unhandled.Metadata);
+ VariableDeclaration variable;
+ if (!inCatchClause && functionNestingLevel == 0) {
+ var builder = formalParameterScope.lookup(name.name);
+ if (builder == null) {
+ return inputError("'${name.name}' isn't a field in this class.",
+ name.fileOffset);
+ }
+ if (thisKeyword == null) {
+ variable = builder.build();
+ 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}'.",
+ name.fileOffset);
+ }
+ type = field.target.type ?? const DynamicType();
+ variable = new VariableDeclaration(name.name, type: type,
+ initializer: name.initializer);
+ } else {
+ return inputError("'${name.name}' isn't a field in this class.",
+ name.fileOffset);
+ }
+ } else {
+ variable = new VariableDeclaration(name.name,
+ type: type ?? const DynamicType(), initializer: name.initializer);
+ }
+ push(variable);
+ }
+
+ void endOptionalFormalParameters(
+ int count, Token beginToken, Token endToken) {
+ debugEvent("OptionalFormalParameters");
+ FormalParameterType kind = optional("{", beginToken)
+ ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL;
+ push(new OptionalFormals(kind, popList(count)));
+ }
+
+ void beginFunctionTypedFormalParameter(Token token) {
+ debugEvent("beginFunctionTypedFormalParameter");
+ functionNestingLevel++;
+ }
+
+ void handleFunctionTypedFormalParameter(Token token) {
+ debugEvent("FunctionTypedFormalParameter");
+ if (inCatchClause || functionNestingLevel != 0) {
+ exitLocalScope();
+ }
+ FormalParameters formals = pop();
+ ignore(Unhandled.TypeVariables);
+ Identifier name = pop();
+ DartType returnType = pop();
+ push(formals.toFunctionType(returnType));
+ push(name);
+ functionNestingLevel--;
+ }
+
+ void handleValuedFormalParameter(Token equals, Token token) {
+ debugEvent("ValuedFormalParameter");
+ Expression initializer = popForValue();
+ Identifier name = pop();
+ push(new InitializedIdentifier(name.name, initializer));
+ }
+
+ void endFormalParameters(int count, Token beginToken, Token endToken) {
+ debugEvent("FormalParameters");
+ OptionalFormals optional;
+ if (count > 0 && peek() is OptionalFormals) {
+ optional = pop();
+ count--;
+ }
+ FormalParameters formals = new FormalParameters(
+ popList(count) ?? <VariableDeclaration>[], optional);
+ push(formals);
+ if (inCatchClause || functionNestingLevel != 0) {
+ enterLocalScope(formals.computeFormalParameterScope(scope));
+ }
+ }
+
+ void beginCatchClause(Token token) {
+ debugEvent("beginCatchClause");
+ inCatchClause = true;
+ }
+
+ void endCatchClause(Token token) {
+ debugEvent("CatchClause");
+ inCatchClause = false;
+ }
+
+ void handleCatchBlock(Token onKeyword, Token catchKeyword) {
+ debugEvent("CatchBlock");
+ Block body = pop();
+ if (catchKeyword != null) {
+ exitLocalScope();
+ }
+ FormalParameters catchParameters = popIfNotNull(catchKeyword);
+ DartType type = popIfNotNull(onKeyword) ?? const DynamicType();
+ VariableDeclaration exception;
+ VariableDeclaration stackTrace;
+ if (catchParameters != null) {
+ if (catchParameters.required.length > 0) {
+ exception = catchParameters.required[0];
+ }
+ if (catchParameters.required.length > 1) {
+ stackTrace = catchParameters.required[1];
+ }
+ if (catchParameters.required.length > 2 ||
+ catchParameters.optional != null) {
+ body = new Block(<Statement>[new InvalidStatement()]);
+ compileTimeErrorInTry ??= buildCompileTimeErrorStatement(
+ "Invalid catch arguments.", catchKeyword.next.charOffset);
+ }
+ }
+ push(new Catch(exception, body, guard: type, stackTrace: stackTrace));
+ }
+
+ void endTryStatement(
+ int catchCount, Token tryKeyword, Token finallyKeyword) {
+ Statement finallyBlock = popStatementIfNotNull(finallyKeyword);
+ List<Catch> catches = popList(catchCount);
+ Statement tryBlock = popStatement();
+ if (compileTimeErrorInTry == null) {
+ if (catches != null) {
+ tryBlock = new TryCatch(tryBlock, catches);
+ }
+ if (finallyBlock != null) {
+ tryBlock = new TryFinally(tryBlock, finallyBlock);
+ }
+ push(tryBlock);
+ } else {
+ push(compileTimeErrorInTry);
+ compileTimeErrorInTry = null;
+ }
+ }
+
+ void handleNoExpression(Token token) {
+ debugEvent("NoExpression");
+ push(NullValue.Expression);
+ }
+
+ void handleIndexedExpression(
+ Token openCurlyBracket, Token closeCurlyBracket) {
+ debugEvent("IndexedExpression");
+ Expression index = popForValue();
+ Expression receiver = popForValue();
+ push(IndexAccessor.make(this, openCurlyBracket.charOffset, receiver, index,
+ null, null));
+ }
+
+ void handleUnaryPrefixExpression(Token token) {
+ debugEvent("UnaryPrefixExpression");
+ Expression expression = popForValue();
+ if (optional("!", token)) {
+ push(new Not(expression));
+ } else {
+ String operator = token.stringValue;
+ if (optional("-", token)) {
+ operator = "unary-";
+ }
+ push(buildMethodInvocation(expression, new Name(operator),
+ new Arguments.empty(), token.charOffset));
+ }
+ }
+
+ Name incrementOperator(Token token) {
+ if (optional("++", token)) return plusName;
+ if (optional("--", token)) return minusName;
+ return internalError("Unknown increment operator: ${token.value}");
+ }
+
+ void handleUnaryPrefixAssignmentExpression(Token token) {
+ debugEvent("UnaryPrefixAssignmentExpression");
+ var accessor = pop();
+ if (accessor is BuilderAccessor) {
+ push(accessor.buildPrefixIncrement(incrementOperator(token)));
+ } else {
+ push(wrapInvalid(toValue(accessor)));
+ }
+ }
+
+ void handleUnaryPostfixAssignmentExpression(Token token) {
+ debugEvent("UnaryPostfixAssignmentExpression");
+ var accessor = pop();
+ if (accessor is BuilderAccessor) {
+ push(new DelayedPostfixIncrement(this, token.charOffset, accessor,
+ incrementOperator(token), null));
+ } else {
+ push(wrapInvalid(toValue(accessor)));
+ }
+ }
+
+ void endConstructorReference(
+ Token start, Token periodBeforeName, Token endToken) {
+ debugEvent("ConstructorReference");
+ // A constructor reference can contain up to three identifiers:
+ //
+ // a) type <type-arguments>?
+ // b) type <type-arguments>? . name
+ // c) prefix . type <type-arguments>?
+ // d) prefix . type <type-arguments>? . name
+ //
+ // This isn't a legal constructor reference:
+ //
+ // type . name <type-arguments>
+ //
+ // But the parser can't tell this from type c) above.
+ //
+ // This method pops 2 (or 3 if periodBeforeName != null) values from the
+ // stack and pushes 3 values: a type, a list of type arguments, and a name.
+ //
+ // If the constructor reference can be resolved, type is either a
+ // ClassBuilder, or a ThisPropertyAccessor. Otherwise, it's an error that
+ // should be handled later.
+ Identifier suffix = popIfNotNull(periodBeforeName);
+ Identifier identifier;
+ List<DartType> typeArguments = pop();
+ var type = pop();
+ if (type is List) {
+ var prefix = type[0];
+ identifier = type[1];
+ if (prefix is PrefixBuilder) {
+ // TODO(ahe): Handle privacy in prefix.exports.
+ type = builderToFirstExpression(
+ prefix.exports[identifier.name], identifier.name, start.charOffset);
+ identifier = null;
+ } else if (prefix is ClassBuilder) {
+ type = prefix;
+ } else {
+ type = new Identifier(start.value)..fileOffset = start.charOffset;
+ }
+ }
+ String name;
+ if (identifier != null && suffix != null) {
+ name = "${identifier.name}.${suffix.name}";
+ } else if (identifier != null) {
+ name = identifier.name;
+ } else if (suffix != null) {
+ name = suffix.name;
+ } else {
+ name = "";
+ }
+ push(type);
+ push(typeArguments ?? NullValue.TypeArguments);
+ push(name);
+ }
+
+ Expression buildStaticInvocation(Member target, Arguments arguments,
+ {bool isConst: false}) {
+ List<TypeParameter> typeParameters = target.function.typeParameters;
+ if (target is Constructor) {
+ typeParameters = target.enclosingClass.typeParameters;
+ }
+ if (!addDefaultArguments(target.function, arguments, typeParameters)) {
+ return throwNoSuchMethodError(target.name.name, arguments, -1);
+ }
+ if (target is Constructor) {
+ return new ConstructorInvocation(target, arguments)
+ ..isConst = isConst;
+ } else {
+ return new StaticInvocation(target, arguments)
+ ..isConst = isConst;
+ }
+ }
+
+ bool addDefaultArguments(FunctionNode function, Arguments arguments,
+ List<TypeParameter> typeParameters) {
+ bool missingInitializers = false;
+
+ Expression defaultArgumentFrom(Expression expression) {
+ if (expression == null) {
+ missingInitializers = true;
+ return null;
+ }
+ cloner ??= new CloneVisitor();
+ return cloner.clone(expression);
+ }
+
+ if (arguments.positional.length < function.requiredParameterCount ||
+ arguments.positional.length > function.positionalParameters.length) {
+ return false;
+ }
+ for (int i = arguments.positional.length;
+ i < function.positionalParameters.length;
+ i++) {
+ var expression =
+ defaultArgumentFrom(function.positionalParameters[i].initializer);
+ expression?.parent = arguments;
+ arguments.positional.add(expression);
+ }
+ Map<String, VariableDeclaration> names;
+ if (function.namedParameters.isNotEmpty) {
+ names = <String, VariableDeclaration>{};
+ for (VariableDeclaration parameter in function.namedParameters) {
+ names[parameter.name] = parameter;
+ }
+ }
+ if (arguments.named.isNotEmpty) {
+ if (names == null) return false;
+ for (NamedExpression argument in arguments.named) {
+ VariableDeclaration parameter = names.remove(argument.name);
+ if (parameter == null) {
+ return false;
+ }
+ }
+ }
+ if (names != null) {
+ for (String name in names.keys) {
+ VariableDeclaration parameter = names[name];
+ arguments.named.add(
+ new NamedExpression(
+ name, defaultArgumentFrom(parameter.initializer))
+ ..parent = arguments);
+ }
+ }
+ if (typeParameters.length != arguments.types.length) {
+ arguments.types.clear();
+ for (int i = 0; i < typeParameters.length; i++) {
+ arguments.types.add(const DynamicType());
+ }
+ }
+
+ if (missingInitializers) {
+ library.addArgumentsWithMissingDefaultValues(arguments, function);
+ }
+ return true;
+ }
+
+ void handleNewExpression(Token token) {
+ debugEvent("NewExpression");
+ Arguments arguments = pop();
+ String name = pop();
+ List typeArguments = pop();
+ var type = pop();
+
+ if (typeArguments != null) {
+ assert(arguments.types.isEmpty);
+ arguments.types.addAll(typeArguments);
+ }
+
+ String errorName;
+ if (type is ClassBuilder) {
+ Builder b = type.findConstructorOrFactory(name);
+ Member target;
+ if (b == null) {
+ // Not found. Reported below.
+ } else if (b.isConstructor) {
+ if (type.cls.isAbstract) {
+ // TODO(ahe): Generate abstract instantiation error.
+ } else {
+ target = b.target;
+ }
+ } else if (b.isFactory) {
+ target = getRedirectionTarget(b.target);
+ if (target == null) {
+ push(buildCompileTimeError(
+ "Cyclic definition of factory '${name}'.",
+ token.charOffset));
+ return;
+ }
+ }
+ if (target is Constructor ||
+ (target is Procedure && target.kind == ProcedureKind.Factory)) {
+ push(buildStaticInvocation(
+ target, arguments, isConst: optional("const", token)));
+ return;
+ } else {
+ errorName = debugName(type.cls.name, name);
+ }
+ } else {
+ errorName = debugName(getNodeName(type), name);
+ }
+ errorName ??= name;
+ push(throwNoSuchMethodError(errorName, arguments, token.charOffset));
+ }
+
+ void handleConstExpression(Token token) {
+ debugEvent("ConstExpression");
+ handleNewExpression(token);
+ }
+
+ void endTypeArguments(int count, Token beginToken, Token endToken) {
+ debugEvent("TypeArguments");
+ push(popList(count));
+ }
+
+ void handleThisExpression(Token token) {
+ debugEvent("ThisExpression");
+ if (isFirstIdentifier && isInstanceContext) {
+ push(new ThisAccessor(this, token.charOffset, inInitializer));
+ } else {
+ push(new IncompleteError(
+ this, token.charOffset, "Expected identifier, but got 'this'."));
+ }
+ }
+
+ void handleSuperExpression(Token token) {
+ debugEvent("SuperExpression");
+ if (isFirstIdentifier && isInstanceContext) {
+ Member member = this.member.target;
+ member.transformerFlags |= TransformerFlag.superCalls;
+ push(new ThisAccessor(this, token.charOffset, inInitializer,
+ isSuper: true));
+ } else {
+ push(new IncompleteError(
+ this, token.charOffset, "Expected identifier, but got 'super'."));
+ }
+ }
+
+ void handleNamedArgument(Token colon) {
+ debugEvent("NamedArgument");
+ Expression value = popForValue();
+ Identifier identifier = pop();
+ push(new NamedExpression(identifier.name, value));
+ }
+
+ void endFunctionName(Token token) {
+ debugEvent("FunctionName");
+ Identifier name = pop();
+ VariableDeclaration variable = new VariableDeclaration(
+ name.name, isFinal: true);
+ push(new FunctionDeclaration(variable,
+ new FunctionNode(new InvalidStatement())));
+ scope[variable.name] = new KernelVariableBuilder(variable);
+ enterLocalScope();
+ }
+
+ void beginFunction(Token token) {
+ debugEvent("beginFunction");
+ functionNestingLevel++;
+ }
+
+ void beginUnnamedFunction(Token token) {
+ debugEvent("beginUnnamedFunction");
+ functionNestingLevel++;
+ }
+
+ void endFunction(Token getOrSet, Token endToken) {
+ debugEvent("Function");
+ Statement body = popStatement();
+ AsyncMarker asyncModifier = pop();
+ if (functionNestingLevel != 0) {
+ exitLocalScope();
+ }
+ FormalParameters formals = pop();
+ List typeParameters = pop();
+ push(formals.addToFunction(new FunctionNode(body,
+ typeParameters: typeParameters, asyncMarker: asyncModifier)));
+ functionNestingLevel--;
+ }
+
+ void endFunctionDeclaration(Token token) {
+ debugEvent("FunctionDeclaration");
+ FunctionNode function = pop();
+ exitLocalScope();
+ FunctionDeclaration declaration = pop();
+ function.returnType = pop() ?? const DynamicType();
+ pop(); // Modifiers.
+ declaration.function = function;
+ function.parent = declaration;
+ push(declaration);
+ }
+
+ void endUnnamedFunction(Token token) {
+ debugEvent("UnnamedFunction");
+ Statement body = popStatement();
+ AsyncMarker asyncModifier = pop();
+ exitLocalScope();
+ FormalParameters formals = pop();
+ List typeParameters = pop();
+ FunctionNode function = formals.addToFunction(new FunctionNode(body,
+ typeParameters: typeParameters, asyncMarker: asyncModifier));
+ push(new FunctionExpression(function));
+ functionNestingLevel--;
+ }
+
+ void endDoWhileStatement(
+ Token doKeyword, Token whileKeyword, Token endToken) {
+ debugEvent("DoWhileStatement");
+ Expression condition = popForValue();
+ Statement body = popStatement();
+ JumpTarget continueTarget = exitContinueTarget();
+ JumpTarget breakTarget = exitBreakTarget();
+ if (continueTarget.hasUsers) {
+ body = new LabeledStatement(body);
+ continueTarget.resolveContinues(body);
+ }
+ Statement result = new DoStatement(body, condition);
+ if (breakTarget.hasUsers) {
+ result = new LabeledStatement(result);
+ breakTarget.resolveBreaks(result);
+ }
+ exitLoopOrSwitch(result);
+ }
+
+ void endForIn(
+ Token awaitToken, Token forToken, Token inKeyword, Token endToken) {
+ debugEvent("ForIn");
+ Statement body = popStatement();
+ Expression expression = popForValue();
+ var lvalue = pop();
+ exitLocalScope();
+ JumpTarget continueTarget = exitContinueTarget();
+ JumpTarget breakTarget = exitBreakTarget();
+ if (continueTarget.hasUsers) {
+ body = new LabeledStatement(body);
+ continueTarget.resolveContinues(body);
+ }
+ VariableDeclaration variable;
+ if (lvalue is VariableDeclaration) {
+ variable = lvalue;
+ } else if (lvalue is BuilderAccessor) {
+ /// We are in this case, where `lvalue` isn't a [VariableDeclaration]:
+ ///
+ /// for (lvalue in expression) body
+ ///
+ /// This is normalized to:
+ ///
+ /// for (final #t in expression) {
+ /// lvalue = #t;
+ /// body;
+ /// }
+ variable = new VariableDeclaration.forValue(null);
+ body = combineStatements(
+ new ExpressionStatement(
+ lvalue.buildAssignment(
+ new VariableGet(variable), voidContext: true)),
+ body);
+ } else {
+ throw inputError("Expected lvalue, but got ${lvalue}",
+ forToken.next.next.charOffset);
+ }
+ Statement result = new ForInStatement(variable, expression, body,
+ isAsync: awaitToken != null);
+ if (breakTarget.hasUsers) {
+ result = new LabeledStatement(result);
+ breakTarget.resolveBreaks(result);
+ }
+ exitLoopOrSwitch(result);
+ }
+
+ void handleLabel(Token token) {
+ debugEvent("Label");
+ Identifier identifier = pop();
+ push(new Label(identifier.name));
+ }
+
+ void beginLabeledStatement(Token token, int labelCount) {
+ debugEvent("beginLabeledStatement");
+ List<Label> labels = popList(labelCount);
+ enterLocalScope();
+ LabelTarget target = new LabelTarget();
+ for (Label label in labels) {
+ scope[label.name] = target;
+ }
+ push(target);
+ }
+
+ void endLabeledStatement(int labelCount) {
+ debugEvent("LabeledStatement");
+ Statement statement = popStatement();
+ LabelTarget target = pop();
+ exitLocalScope();
+ if (target.breakTarget.hasUsers) {
+ if (statement is! LabeledStatement) {
+ statement = new LabeledStatement(statement);
+ }
+ target.breakTarget.resolveBreaks(statement);
+ }
+ if (target.continueTarget.hasUsers) {
+ if (statement is! LabeledStatement) {
+ statement = new LabeledStatement(statement);
+ }
+ target.continueTarget.resolveContinues(statement);
+ }
+ push(statement);
+ }
+
+ void endRethrowStatement(Token throwToken, Token endToken) {
+ debugEvent("RethrowStatement");
+ push(new ExpressionStatement(new Rethrow()));
+ }
+
+ void handleFinallyBlock(Token finallyKeyword) {
+ debugEvent("FinallyBlock");
+ // Do nothing, handled by [endTryStatement].
+ }
+
+ void endWhileStatement(Token whileKeyword, Token endToken) {
+ debugEvent("WhileStatement");
+ Statement body = popStatement();
+ Expression condition = popForValue();
+ JumpTarget continueTarget = exitContinueTarget();
+ JumpTarget breakTarget = exitBreakTarget();
+ if (continueTarget.hasUsers) {
+ body = new LabeledStatement(body);
+ continueTarget.resolveContinues(body);
+ }
+ Statement result = new WhileStatement(condition, body);
+ if (breakTarget.hasUsers) {
+ result = new LabeledStatement(result);
+ breakTarget.resolveBreaks(result);
+ }
+ exitLoopOrSwitch(result);
+ }
+
+ void handleEmptyStatement(Token token) {
+ debugEvent("EmptyStatement");
+ push(new EmptyStatement());
+ }
+
+ void handleAssertStatement(
+ Token assertKeyword, Token commaToken, Token semicolonToken) {
+ debugEvent("AssertStatement");
+ Expression message = popIfNotNull(commaToken);
+ Expression condition = popForValue();
+ push(new AssertStatement(condition, message));
+ }
+
+ void endYieldStatement(Token yieldToken, Token starToken, Token endToken) {
+ debugEvent("YieldStatement");
+ push(new YieldStatement(popForValue(), isYieldStar: starToken != null));
+ }
+
+ void beginSwitchBlock(Token token) {
+ debugEvent("beginSwitchBlock");
+ enterLocalScope();
+ enterSwitchScope();
+ enterBreakTarget();
+ }
+
+ void beginSwitchCase(int labelCount, int expressionCount, Token firstToken) {
+ debugEvent("beginSwitchCase");
+ List labelsAndExpressions = popList(labelCount + expressionCount);
+ List<Label> labels = <Label>[];
+ List<Expression> expressions = <Expression>[];
+ if (labelsAndExpressions != null) {
+ for (var labelOrExpression in labelsAndExpressions) {
+ if (labelOrExpression is Label) {
+ labels.add(labelOrExpression);
+ } else {
+ expressions.add(toValue(labelOrExpression));
+ }
+ }
+ }
+ assert(scope == switchScope);
+ for (Label label in labels) {
+ Builder existing = scope.local[label.name];
+ if (existing == null) {
+ scope[label.name] = createGotoTarget();
+ } else {
+ // TODO(ahe): Should validate this is a goto target and not duplicated.
+ }
+ }
+ push(expressions);
+ push(labels);
+ enterLocalScope();
+ }
+
+ void handleSwitchCase(
+ int labelCount,
+ int expressionCount,
+ Token defaultKeyword,
+ int statementCount,
+ Token firstToken,
+ Token endToken) {
+ debugEvent("SwitchCase");
+ Block block = popBlock(statementCount);
+ exitLocalScope();
+ List<Label> labels = pop();
+ List<Expression> expressions = pop();
+ push(new SwitchCase(expressions, block, isDefault: defaultKeyword != null));
+ push(labels);
+ }
+
+ void endSwitchStatement(Token switchKeyword, Token endToken) {
+ debugEvent("SwitchStatement");
+ // Do nothing. Handled by [endSwitchBlock].
+ }
+
+ void endSwitchBlock(int caseCount, Token beginToken, Token endToken) {
+ debugEvent("SwitchBlock");
+ List<SwitchCase> cases =
+ new List<SwitchCase>.filled(caseCount, null, growable: true);
+ for (int i = caseCount - 1; i >= 0; i--) {
+ List<Label> labels = pop();
+ SwitchCase current = cases[i] = pop();
+ for (Label label in labels) {
+ JumpTarget target = switchScope.lookup(label.name);
+ if (target != null) {
+ target.resolveGotos(current);
+ }
+ }
+ // TODO(ahe): Validate that there's only one default and it's last.
+ }
+ JumpTarget target = exitBreakTarget();
+ exitSwitchScope();
+ exitLocalScope();
+ Expression expression = popForValue();
+ Statement result = new SwitchStatement(expression, cases);
+ if (target.hasUsers) {
+ result = new LabeledStatement(result);
+ target.resolveBreaks(result);
+ }
+ exitLoopOrSwitch(result);
+ }
+
+ void handleCaseMatch(Token caseKeyword, Token colon) {
+ debugEvent("CaseMatch");
+ // Do nothing. Handled by [handleSwitchCase].
+ }
+
+ void handleBreakStatement(
+ bool hasTarget, Token breakKeyword, Token endToken) {
+ debugEvent("BreakStatement");
+ var target = breakTarget;
+ String name;
+ if (hasTarget) {
+ Identifier identifier = pop();
+ name = identifier.name;
+ target = scope.lookup(identifier.name);
+ }
+ if (target == null && name == null) {
+ push(compileTimeErrorInLoopOrSwitch =
+ buildCompileTimeErrorStatement(
+ "No target of break.", breakKeyword.charOffset));
+ } else if (target == null || target is! JumpTarget
+ || !target.isBreakTarget) {
+ push(compileTimeErrorInLoopOrSwitch =
+ buildCompileTimeErrorStatement("Can't break to '$name'.",
+ breakKeyword.next.charOffset));
+ } else {
+ BreakStatement statement = new BreakStatement(null);
+ target.addBreak(statement);
+ push(statement);
+ }
+ }
+
+ void handleContinueStatement(
+ bool hasTarget, Token continueKeyword, Token endToken) {
+ debugEvent("ContinueStatement");
+ var target = continueTarget;
+ String name;
+ if (hasTarget) {
+ Identifier identifier = pop();
+ name = identifier.name;
+ target = scope.lookup(identifier.name);
+ if (target != null && target is! JumpTarget) {
+ push(compileTimeErrorInLoopOrSwitch =
+ buildCompileTimeErrorStatement(
+ "Target of continue must be a label.",
+ continueKeyword.charOffset));
+ return;
+ }
+ if (target == null) {
+ if (switchScope == null) {
+ push(buildCompileTimeErrorStatement("Can't find label '$name'.",
+ continueKeyword.next.charOffset));
+ return;
+ }
+ switchScope[identifier.name] = target = createGotoTarget();
+ }
+ if (target.isGotoTarget) {
+ ContinueSwitchStatement statement = new ContinueSwitchStatement(null);
+ target.addGoto(statement);
+ push(statement);
+ return;
+ }
+ }
+ if (target == null) {
+ push(compileTimeErrorInLoopOrSwitch =
+ buildCompileTimeErrorStatement("No target of continue.",
+ continueKeyword.charOffset));
+ } else if (!target.isContinueTarget) {
+ push(compileTimeErrorInLoopOrSwitch =
+ buildCompileTimeErrorStatement("Can't continue at '$name'.",
+ continueKeyword.next.charOffset));
+ } else {
+ BreakStatement statement = new BreakStatement(null);
+ target.addContinue(statement);
+ push(statement);
+ }
+ }
+
+ void endTypeVariable(Token token, Token extendsOrSuper) {
+ logEvent("TypeVariable");
+ // TODO(ahe): Implement this when enabling generic method syntax.
+ }
+
+ void endTypeVariables(int count, Token beginToken, Token endToken) {
+ logEvent("TypeVariables");
+ // TODO(ahe): Implement this when enabling generic method syntax.
+ }
+
+ void handleModifier(Token token) {
+ debugEvent("Modifier");
+ // TODO(ahe): Copied from outline_builder.dart.
+ push(new Modifier.fromString(token.stringValue));
+ }
+
+ void handleModifiers(int count) {
+ debugEvent("Modifiers");
+ // TODO(ahe): Copied from outline_builder.dart.
+ push(popList(count) ?? NullValue.Modifiers);
+ }
+
+ void reportErrorHelper(Token token, ErrorKind kind, Map arguments) {
+ super.reportErrorHelper(token, kind, arguments);
+ if (!hasParserError) {
+ print("$uri:${recoverableErrors.last}");
+ }
+ hasParserError = true;
+ }
+
+ Token expectedExpression(Token token) {
+ if (token is ErrorToken) {
+ reportErrorToken(token);
+ push(new Throw(new StringLiteral("${recoverableErrors.last}")));
+ do {
+ token = token.next;
+ } while (token is ErrorToken);
+ return token;
+ } else {
+ push(new InvalidExpression());
+ return super.expectedExpression(token);
+ }
+ }
+
+ Token expected(String string, Token token) {
+ if (token is ErrorToken) {
+ reportErrorToken(token);
+ do {
+ token = token.next;
+ } while (token is ErrorToken);
+ return token;
+ }
+ const List<String> trailing = const <String>[")", "}", ";", ","];
+ if (trailing.contains(token.stringValue) && trailing.contains(string)) {
+ // We're just trying to get out an error.
+ if (recoverableErrors.isNotEmpty) {
+ reportError(token, ErrorKind.UNSPECIFIED,
+ {"text": "Expected: '$string', but got '${token.value}'"});
+ }
+ return token;
+ }
+ return super.expected(string, token);
+ }
+
+ void warning(error, [int charOffset = -1]) {
+ String message = new InputError(uri, charOffset, error).format();
+ print(message);
+ }
+
+ void nit(error, [int charOffset = -1]) {
+ if (!showNits) return;
+ String message = new InputError(uri, charOffset, error).format();
+ print(message);
+ }
+
+ Expression buildCompileTimeError(error, [int charOffset = -1]) {
+ String message = new InputError(uri, charOffset, error).format();
+ print(message);
+ return new Throw(new StringLiteral(message));
+ }
+
+ Statement buildCompileTimeErrorStatement(error, [int charOffset = -1]) {
+ return new ExpressionStatement(buildCompileTimeError(error, charOffset));
+ }
+
+ Initializer buildCompileTimeErrorIntializer(error, [int charOffset = -1]) {
+ return new LocalInitializer(
+ new VariableDeclaration.forValue(
+ buildCompileTimeError(error, charOffset)));
+ }
+
+
+ Expression buildProblemExpression(Builder builder, String name) {
+ if (builder is AmbiguousBuilder) {
+ return buildCompileTimeError("Duplicated named: '$name'.");
+ } else if (builder is AccessErrorBuilder) {
+ return buildCompileTimeError("Access error: '$name'.");
+ } else {
+ return internalError("Unhandled: ${builder.runtimeType}");
+ }
+ }
+
+ void handleOperator(Token token) {
+ debugEvent("Operator");
+ push(new Operator(token.stringValue)..fileOffset = token.charOffset);
+ }
+
+ dynamic inputError(String message, [int charOffset = -1]) {
+ return errors.inputError(uri, charOffset, message);
+ }
+
+ void debugEvent(String name) {
+ // printEvent(name);
+ }
+}
+
+// TODO(ahe): Shouldn't need to be an expression.
+class UnresolvedIdentifier extends InvalidExpression {
+ final Name name;
+
+ UnresolvedIdentifier(this.name);
+
+ String toString() => "unresolved-identifier($name)";
+}
+
+// TODO(ahe): Shouldn't need to be an expression.
+class Identifier extends InvalidExpression {
+ final String name;
+
+ Identifier(this.name);
+
+ Expression get initializer => null;
+
+ String toString() => "identifier($name)";
+}
+
+// TODO(ahe): Shouldn't need to be an expression.
+class Operator extends InvalidExpression {
+ final String name;
+
+ Operator(this.name);
+
+ String toString() => "operator($name)";
+}
+
+class InitializedIdentifier extends Identifier {
+ final Expression initializer;
+
+ InitializedIdentifier(String name, this.initializer)
+ : super(name);
+
+ String toString() => "initialized-identifier($name, $initializer)";
+}
+
+// TODO(ahe): Shouldn't need to be an expression.
+class Label extends InvalidExpression {
+ String name;
+
+ Label(this.name);
+
+ String toString() => "label($name)";
+}
+
+class CascadeReceiver extends Let {
+ Let nextCascade;
+
+ CascadeReceiver(VariableDeclaration variable)
+ : super(variable,
+ makeLet(new VariableDeclaration.forValue(new InvalidExpression()),
+ new VariableGet(variable))) {
+ nextCascade = body;
+ }
+
+ void extend() {
+ assert(nextCascade.variable.initializer is! InvalidExpression);
+ Let newCascade = makeLet(
+ new VariableDeclaration.forValue(new InvalidExpression()),
+ nextCascade.body);
+ nextCascade.body = newCascade;
+ newCascade.parent = nextCascade;
+ nextCascade = newCascade;
+ }
+
+ void finalize(Expression expression) {
+ assert(nextCascade.variable.initializer is InvalidExpression);
+ nextCascade.variable.initializer = expression;
+ expression.parent = nextCascade.variable;
+ }
+}
+
+abstract class ContextAccessor extends BuilderAccessor {
+ final BuilderHelper helper;
+
+ final int charOffset;
+
+ final BuilderAccessor accessor;
+
+ ContextAccessor(this.helper, this.charOffset, this.accessor);
+
+ String get plainNameForRead => internalError("Unsupported operation.");
+
+ Expression doInvocation(int charOffset, Arguments arguments) {
+ print("$uri:$charOffset: Internal error: Unhandled: ${runtimeType}");
+ return internalError("Unhandled: ${runtimeType}");
+ }
+
+ Expression buildSimpleRead();
+
+ Expression buildForEffect();
+
+ Expression buildAssignment(Expression value, {bool voidContext: false}) {
+ return internalError("not supported");
+ }
+
+ Expression buildNullAwareAssignment(Expression value, DartType type,
+ {bool voidContext: false}) {
+ return internalError("not supported");
+ }
+
+ Expression buildCompoundAssignment(Name binaryOperator, Expression value,
+ {bool voidContext: false, Procedure interfaceTarget}) {
+ return internalError("not supported");
+ }
+
+ Expression buildPrefixIncrement(Name binaryOperator,
+ {bool voidContext: false, Procedure interfaceTarget}) {
+ return internalError("not supported");
+ }
+
+ Expression buildPostfixIncrement(Name binaryOperator,
+ {bool voidContext: false, Procedure interfaceTarget}) {
+ return internalError("not supported");
+ }
+
+ makeInvalidRead() => internalError("not supported");
+
+ makeInvalidWrite(Expression value) => internalError("not supported");
+}
+
+class DelayedAssignment extends ContextAccessor {
+ final Expression value;
+
+ final String assignmentOperator;
+
+ DelayedAssignment(BuilderHelper helper, int charOffset,
+ BuilderAccessor accessor, this.value, this.assignmentOperator)
+ : super(helper, charOffset, accessor);
+
+ Expression buildSimpleRead() {
+ return handleAssignment(false);
+ }
+
+ Expression buildForEffect() {
+ return handleAssignment(true);
+ }
+
+ Expression handleAssignment(bool voidContext) {
+ if (identical("=", assignmentOperator)) {
+ return accessor.buildAssignment(value, voidContext: voidContext);
+ } else if (identical("+=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ plusName, value, voidContext: voidContext);
+ } else if (identical("-=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ minusName, value, voidContext: voidContext);
+ } else if (identical("*=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ multiplyName, value, voidContext: voidContext);
+ } else if (identical("%=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ percentName, value, voidContext: voidContext);
+ } else if (identical("&=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ ampersandName, value, voidContext: voidContext);
+ } else if (identical("/=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ divisionName, value, voidContext: voidContext);
+ } else if (identical("<<=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ leftShiftName, value, voidContext: voidContext);
+ } else if (identical(">>=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ rightShiftName, value, voidContext: voidContext);
+ } else if (identical("??=", assignmentOperator)) {
+ return accessor.buildNullAwareAssignment(
+ value, const DynamicType(), voidContext: voidContext);
+ } else if (identical("^=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ caretName, value, voidContext: voidContext);
+ } else if (identical("|=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ barName, value, voidContext: voidContext);
+ } else if (identical("~/=", assignmentOperator)) {
+ return accessor.buildCompoundAssignment(
+ mustacheName, value, voidContext: voidContext);
+ } else {
+ return internalError("Unhandled: $assignmentOperator");
+ }
+ }
+
+ Initializer buildFieldInitializer(
+ Map<String, FieldInitializer> initializers) {
+ 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(initializers);
+ }
+}
+
+class DelayedPostfixIncrement extends ContextAccessor {
+ final Name binaryOperator;
+
+ final Procedure interfaceTarget;
+
+ DelayedPostfixIncrement(BuilderHelper helper, int charOffset,
+ BuilderAccessor accessor, this.binaryOperator, this.interfaceTarget)
+ : super(helper, charOffset, accessor);
+
+ Expression buildSimpleRead() {
+ return accessor.buildPostfixIncrement(binaryOperator, voidContext: false,
+ interfaceTarget: interfaceTarget);
+ }
+
+ Expression buildForEffect() {
+ return accessor.buildPostfixIncrement(binaryOperator, voidContext: true,
+ interfaceTarget: interfaceTarget);
+ }
+}
+
+class JumpTarget extends Builder {
+ final List<Statement> users = <Statement>[];
+
+ final JumpTargetKind kind;
+
+ JumpTarget(this.kind);
+
+ bool get isBreakTarget => kind == JumpTargetKind.Break;
+
+ bool get isContinueTarget => kind == JumpTargetKind.Continue;
+
+ bool get isGotoTarget => kind == JumpTargetKind.Goto;
+
+ bool get hasUsers => users.isNotEmpty;
+
+ void addBreak(BreakStatement statement) {
+ assert(isBreakTarget);
+ users.add(statement);
+ }
+
+ void addContinue(BreakStatement statement) {
+ assert(isContinueTarget);
+ users.add(statement);
+ }
+
+ void addGoto(ContinueSwitchStatement statement) {
+ assert(isGotoTarget);
+ users.add(statement);
+ }
+
+ void resolveBreaks(LabeledStatement target) {
+ assert(isBreakTarget);
+ for (BreakStatement user in users) {
+ user.target = target;
+ }
+ users.clear();
+ }
+
+ void resolveContinues(LabeledStatement target) {
+ assert(isContinueTarget);
+ for (BreakStatement user in users) {
+ user.target = target;
+ }
+ users.clear();
+ }
+
+ void resolveGotos(SwitchCase target) {
+ assert(isGotoTarget);
+ for (ContinueSwitchStatement user in users) {
+ user.target = target;
+ }
+ users.clear();
+ }
+}
+
+class LabelTarget extends Builder implements JumpTarget {
+ final JumpTarget breakTarget = new JumpTarget(JumpTargetKind.Break);
+
+ final JumpTarget continueTarget = new JumpTarget(JumpTargetKind.Continue);
+
+ LabelTarget();
+
+ bool get hasUsers => breakTarget.hasUsers || continueTarget.hasUsers;
+
+ List<Statement> get users => internalError("Unsupported operation.");
+
+ JumpTargetKind get kind => internalError("Unsupported operation.");
+
+ bool get isBreakTarget => true;
+
+ bool get isContinueTarget => true;
+
+ bool get isGotoTarget => false;
+
+ void addBreak(BreakStatement statement) {
+ breakTarget.addBreak(statement);
+ }
+
+ void addContinue(BreakStatement statement) {
+ continueTarget.addContinue(statement);
+ }
+
+ void addGoto(ContinueSwitchStatement statement) {
+ internalError("Unsupported operation.");
+ }
+
+ void resolveBreaks(LabeledStatement target) {
+ breakTarget.resolveBreaks(target);
+ }
+
+ void resolveContinues(LabeledStatement target) {
+ continueTarget.resolveContinues(target);
+ }
+
+ void resolveGotos(SwitchCase target) {
+ internalError("Unsupported operation.");
+ }
+}
+
+class OptionalFormals {
+ final FormalParameterType kind;
+
+ final List<VariableDeclaration> formals;
+
+ OptionalFormals(this.kind, this.formals);
+}
+
+class FormalParameters {
+ final List<VariableDeclaration> required;
+ final OptionalFormals optional;
+
+ FormalParameters(this.required, this.optional);
+
+ FunctionNode addToFunction(FunctionNode function) {
+ function.requiredParameterCount = required.length;
+ function.positionalParameters.addAll(required);
+ if (optional != null) {
+ if (optional.kind.isPositional) {
+ function.positionalParameters.addAll(optional.formals);
+ } else {
+ function.namedParameters.addAll(optional.formals);
+ setParents(function.namedParameters, function);
+ }
+ }
+ setParents(function.positionalParameters, function);
+ return function;
+ }
+
+ FunctionType toFunctionType(DartType returnType) {
+ returnType ??= const DynamicType();
+ int requiredParameterCount = required.length;
+ List<DartType> positionalParameters = <DartType>[];
+ List<NamedType> namedParameters = const <NamedType>[];
+ for (VariableDeclaration parameter in required) {
+ positionalParameters.add(parameter.type);
+ }
+ if (optional != null) {
+ if (optional.kind.isPositional) {
+ for (VariableDeclaration parameter in optional.formals) {
+ positionalParameters.add(parameter.type);
+ }
+ } else {
+ namedParameters = <NamedType>[];
+ for (VariableDeclaration parameter in optional.formals) {
+ namedParameters.add(new NamedType(parameter.name, parameter.type));
+ }
+ namedParameters.sort();
+ }
+ }
+ return new FunctionType(positionalParameters, returnType,
+ namedParameters: namedParameters,
+ requiredParameterCount: requiredParameterCount);
+ }
+
+ Scope computeFormalParameterScope(Scope parent) {
+ if (required.length == 0 && optional == null) return parent;
+ Map<String, Builder> local = <String, Builder>{};
+ for (VariableDeclaration parameter in required) {
+ local[parameter.name] = new KernelVariableBuilder(parameter);
+ }
+ if (optional != null) {
+ for (VariableDeclaration parameter in optional.formals) {
+ local[parameter.name] = new KernelVariableBuilder(parameter);
+ }
+ }
+ return new Scope(local, parent, isModifiable: false);
+ }
+}
+
+/// Returns a block like this:
+///
+/// {
+/// statement;
+/// body;
+/// }
+///
+/// If [body] is a [Block], it's returned with [statement] prepended to it.
+Block combineStatements(Statement statement, Statement body) {
+ if (body is Block) {
+ body.statements.insert(0, statement);
+ statement.parent = body;
+ return body;
+ } else {
+ return new Block(<Statement>[statement, body]);
+ }
+}
+
+String debugName(String className, String name, [String prefix]) {
+ String result = name.isEmpty ? className : "$className.$name";
+ return prefix == null ? result : "$prefix.result";
+}
+
+String getNodeName(Object node) {
+ if (node is Identifier) {
+ return node.name;
+ } else if (node is UnresolvedIdentifier) {
+ return node.name.name;
+ } else if (node is TypeDeclarationBuilder) {
+ return node.name;
+ } else if (node is PrefixBuilder) {
+ return node.name;
+ } else if (node is ThisPropertyAccessor) {
+ return node.name.name;
+ } else {
+ return internalError("Unhandled: ${node.runtimeType}");
+ }
+}
« no previous file with comments | « no previous file | pkg/fasta/lib/src/kernel/builder_accessors.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698