| 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}");
|
| + }
|
| +}
|
|
|