blob: 87d342a8beaf85088d37dac971fa0ba72505846b [file] [log] [blame]
// 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 '../parser/parser.dart' show FormalParameterType, optional;
import '../parser/error_kind.dart' show ErrorKind;
import '../parser/identifier_context.dart' show IdentifierContext;
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 '../parser/dart_vm_native.dart' show skipNativeClause;
import '../scanner/token.dart'
show BeginGroupToken, Token, isBinaryOperator, isMinusOperator;
import '../errors.dart' show formatUnexpected, internalError;
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';
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("~/");
final Name indexGetName = new Name("[]");
final Name indexSetName = new Name("[]=");
class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper {
final KernelLibraryBuilder library;
final MemberBuilder member;
final KernelClassBuilder classBuilder;
final ClassHierarchy hierarchy;
@override
final CoreTypes coreTypes;
final bool isInstanceMember;
final Map<String, FieldInitializer> fieldInitializers =
<String, FieldInitializer>{};
final Scope enclosingScope;
final bool isDartLibrary;
@override
final Uri uri;
Scope formalParameterScope;
bool isFirstIdentifier = false;
bool inInitializer = false;
bool inCatchClause = false;
int functionNestingLevel = 0;
Statement compileTimeErrorInTry;
Statement compileTimeErrorInLoopOrSwitch;
Scope switchScope;
CloneVisitor cloner;
BodyBuilder(
KernelLibraryBuilder library,
this.member,
Scope scope,
this.formalParameterScope,
this.hierarchy,
this.coreTypes,
this.classBuilder,
this.isInstanceMember,
this.uri)
: enclosingScope = scope,
library = library,
isDartLibrary = library.uri.scheme == "dart",
super(scope);
bool get hasParserError => recoverableErrors.isNotEmpty;
bool get inConstructor {
return functionNestingLevel == 0 && member is KernelConstructorBuilder;
}
bool get isInstanceContext {
return isInstanceMember || member is KernelConstructorBuilder;
}
@override
void push(Object node) {
isFirstIdentifier = false;
inInitializer = false;
super.push(node);
}
Expression popForValue() => toValue(pop());
Expression popForEffect() => toEffect(pop());
Expression popForValueIfNotNull(Object value) {
return value == null ? null : popForValue();
}
@override
Expression toValue(Object node) {
if (node is UnresolvedIdentifier) {
if (isDartLibrary &&
node.name.name == "main" &&
library.uri.path == "_builtin" &&
member?.name == "_getMainClosure") {
// TODO(ahe): https://github.com/dart-lang/sdk/issues/28989
return new NullLiteral()..fileOffset = node.fileOffset;
}
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(library, 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(library, null));
} else if (node is KernelTypeBuilder) {
return new TypeLiteral(node.build(library));
} 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<dynamic /*Statement | 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));
// TODO(sigmund): remove this assignment (issue #28651)
Iterable subStatements = statement;
copy.addAll(subStatements);
} 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() {
Scope outerSwitchScope = pop();
if (switchScope.unclaimedForwardDeclarations != null) {
switchScope.unclaimedForwardDeclarations
.forEach((String name, Builder builder) {
if (outerSwitchScope == null) {
addCompileTimeError(-1, "Label not found: '$name'.");
} else {
outerSwitchScope.forwardDeclareLabel(name, builder);
}
});
}
switchScope = outerSwitchScope;
}
@override
JumpTarget createJumpTarget(JumpTargetKind kind, int charOffset) {
return new JumpTarget(kind, functionNestingLevel, member, charOffset);
}
@override
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.
}
@override
void endMetadataStar(int count, bool forParameter) {
debugEvent("MetadataStar");
push(NullValue.Metadata);
}
@override
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.
}
@override
void endFields(
int count, Token covariantKeyword, Token beginToken, Token endToken) {
debugEvent("Fields");
doFields(count);
pop(); // Metadata.
}
void doFields(int count) {
for (int i = 0; i < count; i++) {
Expression initializer = pop();
Identifier identifier = pop();
if (initializer != null) {
String name = identifier.name;
FieldBuilder field;
if (classBuilder != null) {
field = classBuilder.members[name];
} else {
field = library.members[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 = initializer;
}
}
pop(); // Type.
pop(); // Modifiers.
}
@override
void endMember() {
debugEvent("Member");
checkEmpty(-1);
}
@override
void endBlockFunctionBody(int count, Token beginToken, Token endToken) {
debugEvent("BlockFunctionBody");
if (beginToken == null) {
assert(count == 0);
push(NullValue.Block);
} else {
Block block = popBlock(count);
exitLocalScope();
push(block);
}
}
@override
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);
}
}
}
}
}
}
@override
void beginInitializer(Token token) {
debugEvent("beginInitializer");
inInitializer = true;
}
@override
void endInitializer(Token token) {
debugEvent("endInitializer");
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 {
addCompileTimeError(
token.charOffset, "Can't have initializers: ${member.name}");
}
}
@override
void handleNoInitializers() {
debugEvent("NoInitializers");
}
@override
void endInitializers(int count, Token beginToken, Token endToken) {
debugEvent("Initializers");
}
@override
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.
addCompileTimeError(body?.fileOffset,
"Can't be marked as ${asyncModifier}: ${builder.name}");
}
} 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;
}
}
}
@override
void endExpressionStatement(Token token) {
debugEvent("ExpressionStatement");
push(new ExpressionStatement(popForEffect()));
}
@override
void endArguments(int count, Token beginToken, Token endToken) {
debugEvent("Arguments");
List arguments = popList(count) ?? <Expression>[];
int firstNamedArgumentIndex = arguments.length;
for (int i = 0; i < arguments.length; i++) {
var node = arguments[i];
if (node is NamedExpression) {
firstNamedArgumentIndex =
i < firstNamedArgumentIndex ? i : firstNamedArgumentIndex;
} else {
arguments[i] = toValue(node);
if (i > firstNamedArgumentIndex) {
arguments[i] = new NamedExpression(
"#$i",
buildCompileTimeError(
"Expected named argument.", arguments[i].fileOffset));
}
}
}
if (firstNamedArgumentIndex < arguments.length) {
List<Expression> positional = new List<Expression>.from(
arguments.getRange(0, firstNamedArgumentIndex));
List<NamedExpression> named = new List<NamedExpression>.from(
arguments.getRange(firstNamedArgumentIndex, arguments.length));
push(new Arguments(positional, named: named));
} else {
push(new Arguments(arguments));
}
}
@override
void handleParenthesizedExpression(BeginGroupToken token) {
debugEvent("ParenthesizedExpression");
push(popForValue());
}
@override
void endSend(Token beginToken, Token endToken) {
debugEvent("Send");
Arguments arguments = pop();
List<DartType> 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, beginToken.charOffset, name));
} else {
push(new SendAccessor(this, endToken.charOffset, name, arguments));
}
} else if (arguments == null) {
push(receiver);
} else {
push(finishSend(receiver, arguments, beginToken.charOffset));
}
}
@override
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);
}
}
@override
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));
}
}
@override
void endCascade() {
debugEvent("endCascade");
Expression expression = popForEffect();
CascadeReceiver cascadeReceiver = pop();
cascadeReceiver.finalize(expression);
push(cascadeReceiver);
}
@override
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, token.charOffset);
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));
}
@override
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);
}
@override
Member lookupSuperMember(Name name, {bool isSetter: false}) {
Class superclass = classBuilder.cls.superclass;
return superclass == null
? null
: hierarchy.getDispatchTarget(superclass, name, setter: isSetter);
}
@override
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;
}
@override
void beginExpression(Token token) {
debugEvent("beginExpression");
isFirstIdentifier = true;
}
Builder computeSetter(
Builder builder, Scope scope, String name, int charOffset) {
if (builder.isSetter) return builder;
if (builder.isGetter) return scope.lookupSetter(name, charOffset, uri);
return builder.isField ? (builder.isFinal ? null : builder) : null;
}
@override
void handleIdentifier(Token token, IdentifierContext context) {
debugEvent("handleIdentifier");
String name = token.lexeme;
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, token.charOffset, uri);
push(builderToFirstExpression(builder, name, token.charOffset));
} else {
push(new Identifier(name)..fileOffset = token.charOffset);
}
}
@override
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 KernelNamedTypeBuilder(name, null, charOffset, uri);
}
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, charOffset)?.target;
return new StaticAccessor(this, charOffset, getter, setter);
}
}
@override
void handleQualified(Token period) {
debugEvent("Qualified");
Identifier name = pop();
var receiver = pop();
push([receiver, name]);
}
@override
void beginLiteralString(Token token) {
debugEvent("beginLiteralString");
push(token);
}
@override
void handleStringPart(Token token) {
debugEvent("StringPart");
push(token);
}
@override
void endLiteralString(int interpolationCount, Token endToken) {
debugEvent("endLiteralString");
if (interpolationCount == 0) {
Token token = pop();
push(new StringLiteral(unescapeString(token.lexeme)));
} else {
List parts = popList(1 + interpolationCount * 2);
Token first = parts.first;
Token last = parts.last;
Quote quote = analyzeQuote(first.lexeme);
List<Expression> expressions = <Expression>[];
expressions
.add(new StringLiteral(unescapeFirstStringPart(first.lexeme, quote)));
for (int i = 1; i < parts.length - 1; i++) {
var part = parts[i];
if (part is Token) {
expressions.add(new StringLiteral(unescape(part.lexeme, quote)));
} else {
expressions.add(toValue(part));
}
}
expressions
.add(new StringLiteral(unescapeLastStringPart(last.lexeme, quote)));
push(new StringConcatenation(expressions)
..fileOffset = endToken.charOffset);
}
}
@override
void handleScript(Token token) {
debugEvent("Script");
}
@override
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));
}
@override
void handleLiteralInt(Token token) {
debugEvent("LiteralInt");
push(new IntLiteral(int.parse(token.lexeme)));
}
@override
void handleEmptyFunctionBody(Token semicolon) {
debugEvent("ExpressionFunctionBody");
endBlockFunctionBody(0, null, semicolon);
}
@override
void handleExpressionFunctionBody(Token arrowToken, Token endToken) {
debugEvent("ExpressionFunctionBody");
endReturnStatement(true, arrowToken, endToken);
}
@override
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)..fileOffset = beginToken.charOffset);
}
}
@override
void endIfStatement(Token ifToken, Token elseToken) {
Statement elsePart = popStatementIfNotNull(elseToken);
Statement thenPart = popStatement();
Expression condition = popForValue();
push(new IfStatement(condition, thenPart, elsePart));
}
@override
void endVariableInitializer(Token assignmentOperator) {
debugEvent("VariableInitializer");
assert(assignmentOperator.stringValue == "=");
Expression initializer = popForValue();
Identifier identifier = pop();
push(new VariableDeclaration(identifier.name, initializer: initializer)
..fileEqualsOffset = assignmentOperator.charOffset);
}
@override
void handleNoVariableInitializer(Token token) {
debugEvent("NoVariableInitializer");
}
@override
void endFieldInitializer(Token assignmentOperator) {
debugEvent("FieldInitializer");
assert(assignmentOperator.stringValue == "=");
push(popForValue());
}
@override
void handleNoFieldInitializer(Token token) {
debugEvent("NoFieldInitializer");
push(NullValue.FieldInitializer);
}
@override
void endInitializedIdentifier(Token nameToken) {
// 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}");
}
variable.fileOffset = nameToken.charOffset;
push(variable);
scope[variable.name] = new KernelVariableBuilder(
variable, member ?? classBuilder ?? library, uri);
}
@override
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);
}
}
@override
void endBlock(int count, Token beginToken, Token endToken) {
debugEvent("Block");
Block block = popBlock(count);
exitLocalScope();
push(block);
}
@override
void handleAssignmentExpression(Token token) {
debugEvent("AssignmentExpression");
Expression value = popForValue();
var accessor = pop();
if (accessor is TypeDeclarationBuilder) {
push(wrapInvalid(new TypeLiteral(
accessor.buildTypesWithBuiltArguments(library, 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));
}
}
@override
void enterLoop(int charOffset) {
if (peek() is LabelTarget) {
LabelTarget target = peek();
enterBreakTarget(charOffset, target.breakTarget);
enterContinueTarget(charOffset, target.continueTarget);
} else {
enterBreakTarget(charOffset);
enterContinueTarget(charOffset);
}
}
void exitLoopOrSwitch(Statement statement) {
if (compileTimeErrorInLoopOrSwitch != null) {
push(compileTimeErrorInLoopOrSwitch);
compileTimeErrorInLoopOrSwitch = null;
} else {
push(statement);
}
}
@override
void endForStatement(Token forKeyword, Token leftSeparator,
int updateExpressionCount, 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>[];
dynamic variableOrExpression = pop();
Statement begin;
if (variableOrExpression is BuilderAccessor) {
variableOrExpression = variableOrExpression.buildForEffect();
}
if (variableOrExpression is VariableDeclaration) {
variables.add(variableOrExpression);
} else if (variableOrExpression is List) {
// TODO(sigmund): remove this assignment (see issue #28651)
Iterable vars = variableOrExpression;
variables.addAll(vars);
} 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);
}
@override
void endAwaitExpression(Token beginToken, Token endToken) {
debugEvent("AwaitExpression");
push(new AwaitExpression(popForValue()));
}
@override
void handleAsyncModifier(Token asyncToken, Token starToken) {
debugEvent("AsyncModifier");
push(asyncMarkerFromTokens(asyncToken, starToken));
}
@override
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)
..fileOffset = constKeyword?.charOffset ?? beginToken.charOffset);
}
@override
void handleLiteralBool(Token token) {
debugEvent("LiteralBool");
bool value = optional("true", token);
assert(value || optional("false", token));
push(new BoolLiteral(value));
}
@override
void handleLiteralDouble(Token token) {
debugEvent("LiteralDouble");
push(new DoubleLiteral(double.parse(token.lexeme)));
}
@override
void handleLiteralNull(Token token) {
debugEvent("LiteralNull");
push(new NullLiteral());
}
@override
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)
..fileOffset = constKeyword?.charOffset ?? beginToken.charOffset);
}
@override
void endLiteralMapEntry(Token colon, Token endToken) {
debugEvent("LiteralMapEntry");
Expression value = popForValue();
Expression key = popForValue();
push(new MapEntry(key, value));
}
@override
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}");
}
}
@override
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, int charOffset) {
if (identical(name, "void")) return const VoidType();
if (identical(name, "dynamic")) return const DynamicType();
Builder builder = scope.lookup(name, charOffset, uri);
if (builder is TypeDeclarationBuilder) {
return builder.buildTypesWithBuiltArguments(library, arguments);
}
if (builder == null) {
warning("Type not found: '$name'.", charOffset);
} else {
warning("Not a type: '$name'.", charOffset);
}
// TODO(ahe): Create an error somehow.
return const DynamicType();
}
@override
void handleType(Token beginToken, Token endToken) {
// TODO(ahe): The scope is wrong for return types of generic functions.
debugEvent("Type");
List<DartType> arguments = pop();
dynamic name = pop();
if (name is List) {
if (name.length != 2) {
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, beginToken.charOffset, uri);
}
if (builder is PrefixBuilder) {
name = builder.exports[suffix];
} else {
push(const DynamicType());
addCompileTimeError(beginToken.charOffset,
"Can't be used as a type: '${debugName(prefix, suffix)}'.");
return;
}
}
if (name is Identifier) {
name = name.name;
}
if (name is BuilderAccessor) {
warning("'${beginToken.lexeme}' 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(library, arguments));
} else if (name is TypeDeclarationBuilder) {
push(name.buildTypesWithBuiltArguments(library, arguments));
} else if (name is TypeBuilder) {
push(name.build(library));
} else {
push(toKernelType(name, arguments, beginToken.charOffset));
}
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());
}
}
}
@override
void handleVoidKeyword(Token token) {
debugEvent("VoidKeyword");
push(const VoidType());
}
@override
void handleAsOperator(Token operator, Token endToken) {
debugEvent("AsOperator");
DartType type = pop();
Expression expression = popForValue();
push(new AsExpression(expression, type)..fileOffset = operator.charOffset);
}
@override
void handleIsOperator(Token operator, Token not, Token endToken) {
debugEvent("IsOperator");
DartType type = pop();
Expression expression = popForValue();
expression = new IsExpression(expression, type)
..fileOffset = operator.charOffset;
if (not != null) {
expression = new Not(expression);
}
push(expression);
}
@override
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()));
}
@override
void endThrowExpression(Token throwToken, Token endToken) {
debugEvent("ThrowExpression");
Expression expression = popForValue();
push(new Throw(expression)..fileOffset = throwToken.charOffset);
}
@override
void endFormalParameter(
Token covariantKeyword, Token thisKeyword, FormalParameterType kind) {
debugEvent("FormalParameter");
// TODO(ahe): Need beginToken here.
int charOffset = thisKeyword?.charOffset;
if (thisKeyword != null) {
if (!inConstructor) {
addCompileTimeError(thisKeyword.charOffset,
"'this' parameters can only be used on constructors.");
thisKeyword = null;
}
}
Identifier name = pop();
DartType type = pop();
pop(); // Modifiers.
ignore(Unhandled.Metadata);
VariableDeclaration variable;
if (!inCatchClause && functionNestingLevel == 0) {
dynamic builder = formalParameterScope.lookup(name.name, charOffset, uri);
if (builder == null) {
if (thisKeyword == null) {
internalError("Internal error: formal missing for '${name.name}'");
} else {
addCompileTimeError(thisKeyword.charOffset,
"'${name.name}' isn't a field in this class.");
thisKeyword = null;
}
} else if (thisKeyword == null) {
variable = builder.build(library);
variable.initializer = name.initializer;
} else if (builder.isField && builder.parent == classBuilder) {
FieldBuilder field = builder;
if (type != null) {
nit("Ignoring type on 'this' parameter '${name.name}'.",
thisKeyword.charOffset);
}
type = field.target.type ?? const DynamicType();
variable = new VariableDeclaration(name.name,
type: type, initializer: name.initializer);
} else {
addCompileTimeError(
name.fileOffset, "'${name.name}' isn't a field in this class.");
}
}
variable ??= new VariableDeclaration(name.name,
type: type ?? const DynamicType(),
initializer: name.initializer)..fileOffset = name.fileOffset;
push(variable);
}
@override
void endOptionalFormalParameters(
int count, Token beginToken, Token endToken) {
debugEvent("OptionalFormalParameters");
FormalParameterType kind = optional("{", beginToken)
? FormalParameterType.NAMED
: FormalParameterType.POSITIONAL;
push(new OptionalFormals(kind, popList(count) ?? []));
}
@override
void beginFunctionTypedFormalParameter(Token token) {
debugEvent("beginFunctionTypedFormalParameter");
functionNestingLevel++;
}
@override
void endFunctionTypedFormalParameter(
Token covariantKeyword, Token thisKeyword, FormalParameterType kind) {
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--;
}
@override
void handleValuedFormalParameter(Token equals, Token token) {
debugEvent("ValuedFormalParameter");
Expression initializer = popForValue();
Identifier name = pop();
push(new InitializedIdentifier(name.name, initializer));
}
@override
void handleFormalParameterWithoutValue(Token token) {
debugEvent("FormalParameterWithoutValue");
}
@override
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, member ?? classBuilder ?? library));
}
}
@override
void beginCatchClause(Token token) {
debugEvent("beginCatchClause");
inCatchClause = true;
}
@override
void endCatchClause(Token token) {
debugEvent("CatchClause");
inCatchClause = false;
}
@override
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));
}
@override
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;
}
}
@override
void handleNoExpression(Token token) {
debugEvent("NoExpression");
push(NullValue.Expression);
}
@override
void handleIndexedExpression(
Token openCurlyBracket, Token closeCurlyBracket) {
debugEvent("IndexedExpression");
Expression index = popForValue();
var receiver = pop();
if (receiver is ThisAccessor && receiver.isSuper) {
push(new SuperIndexAccessor(this, receiver.charOffset, index,
lookupSuperMember(indexGetName), lookupSuperMember(indexSetName)));
} else {
push(IndexAccessor.make(this, openCurlyBracket.charOffset,
toValue(receiver), index, null, null));
}
}
@override
void handleUnaryPrefixExpression(Token token) {
debugEvent("UnaryPrefixExpression");
var receiver = pop();
if (optional("!", token)) {
push(new Not(toValue(receiver)));
} else {
String operator = token.stringValue;
if (optional("-", token)) {
operator = "unary-";
}
if (receiver is ThisAccessor && receiver.isSuper) {
push(toSuperMethodInvocation(buildMethodInvocation(
new ThisExpression()..fileOffset = receiver.charOffset,
new Name(operator),
new Arguments.empty(),
token.charOffset)));
} else {
push(buildMethodInvocation(toValue(receiver), 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.lexeme}");
}
@override
void handleUnaryPrefixAssignmentExpression(Token token) {
debugEvent("UnaryPrefixAssignmentExpression");
var accessor = pop();
if (accessor is BuilderAccessor) {
push(accessor.buildPrefixIncrement(
incrementOperator(token), token.charOffset));
} else {
push(wrapInvalid(toValue(accessor)));
}
}
@override
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)));
}
}
@override
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();
dynamic 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.lexeme)..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);
}
@override
Expression buildStaticInvocation(Member target, Arguments arguments,
{bool isConst: false, int charOffset: -1}) {
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, charOffset);
}
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;
}
@override
void handleNewExpression(Token token) {
debugEvent("NewExpression");
Arguments arguments = pop();
String name = pop();
List<DartType> typeArguments = pop();
var type = pop();
if (arguments == null) {
push(buildCompileTimeError("No arguments.", token.charOffset));
return;
}
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.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), charOffset: token.charOffset));
return;
} else {
errorName = debugName(type.name, name);
}
} else {
errorName = debugName(getNodeName(type), name);
}
errorName ??= name;
push(throwNoSuchMethodError(errorName, arguments, token.charOffset));
}
@override
void handleConstExpression(Token token) {
debugEvent("ConstExpression");
handleNewExpression(token);
}
@override
void endTypeArguments(int count, Token beginToken, Token endToken) {
debugEvent("TypeArguments");
push(popList(count));
}
@override
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'."));
}
}
@override
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'."));
}
}
@override
void handleNamedArgument(Token colon) {
debugEvent("NamedArgument");
Expression value = popForValue();
Identifier identifier = pop();
push(new NamedExpression(identifier.name, value));
}
@override
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, member ?? classBuilder ?? library, uri);
enterLocalScope();
}
void enterFunction() {
debugEvent("enterFunction");
functionNestingLevel++;
push(switchScope ?? NullValue.SwitchScope);
switchScope = null;
}
void exitFunction() {
debugEvent("exitFunction");
functionNestingLevel--;
switchScope = pop();
}
@override
void beginFunction(Token token) {
debugEvent("beginFunction");
enterFunction();
}
@override
void beginUnnamedFunction(Token token) {
debugEvent("beginUnnamedFunction");
enterFunction();
}
@override
void endFunction(Token getOrSet, Token endToken) {
debugEvent("Function");
Statement body = popStatement();
AsyncMarker asyncModifier = pop();
if (functionNestingLevel != 0) {
exitLocalScope();
}
FormalParameters formals = pop();
List<TypeParameter> typeParameters = pop();
push(formals.addToFunction(new FunctionNode(body,
typeParameters: typeParameters, asyncMarker: asyncModifier)));
}
@override
void endFunctionDeclaration(Token token) {
debugEvent("FunctionDeclaration");
FunctionNode function = pop();
exitLocalScope();
FunctionDeclaration declaration = pop();
function.returnType = pop() ?? const DynamicType();
pop(); // Modifiers.
exitFunction();
declaration.function = function;
function.parent = declaration;
push(declaration);
}
@override
void endUnnamedFunction(Token token) {
debugEvent("UnnamedFunction");
Statement body = popStatement();
AsyncMarker asyncModifier = pop();
exitLocalScope();
FormalParameters formals = pop();
exitFunction();
List<TypeParameter> typeParameters = pop();
FunctionNode function = formals.addToFunction(new FunctionNode(body,
typeParameters: typeParameters, asyncMarker: asyncModifier));
push(new FunctionExpression(function));
}
@override
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);
}
@override
void beginForInExpression(Token token) {
enterLocalScope(scope.parent);
}
@override
void endForInExpression(Token token) {
debugEvent("ForInExpression");
Expression expression = popForValue();
exitLocalScope();
push(expression ?? NullValue.Expression);
}
@override
void endForIn(Token awaitToken, Token forToken, Token leftParenthesis,
Token inKeyword, Token rightParenthesis, 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 {
variable = new VariableDeclaration.forValue(buildCompileTimeError(
"Expected lvalue, but got ${lvalue}", forToken.next.next.charOffset));
}
Statement result = new ForInStatement(variable, expression, body,
isAsync: awaitToken != null)..fileOffset = body.fileOffset;
if (breakTarget.hasUsers) {
result = new LabeledStatement(result);
breakTarget.resolveBreaks(result);
}
exitLoopOrSwitch(result);
}
@override
void handleLabel(Token token) {
debugEvent("Label");
Identifier identifier = pop();
push(new Label(identifier.name));
}
@override
void beginLabeledStatement(Token token, int labelCount) {
debugEvent("beginLabeledStatement");
List<Label> labels = popList(labelCount);
enterLocalScope();
LabelTarget target =
new LabelTarget(member, functionNestingLevel, token.charOffset);
for (Label label in labels) {
scope.declareLabel(label.name, target);
}
push(target);
}
@override
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);
}
@override
void endRethrowStatement(Token throwToken, Token endToken) {
debugEvent("RethrowStatement");
push(new ExpressionStatement(new Rethrow()));
}
@override
void handleFinallyBlock(Token finallyKeyword) {
debugEvent("FinallyBlock");
// Do nothing, handled by [endTryStatement].
}
@override
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);
}
@override
void handleEmptyStatement(Token token) {
debugEvent("EmptyStatement");
push(new EmptyStatement());
}
@override
void handleAssertStatement(Token assertKeyword, Token leftParenthesis,
Token commaToken, Token rightParenthesis, Token semicolonToken) {
debugEvent("AssertStatement");
Expression message = popForValueIfNotNull(commaToken);
Expression condition = popForValue();
push(new AssertStatement(condition, message));
}
@override
void endYieldStatement(Token yieldToken, Token starToken, Token endToken) {
debugEvent("YieldStatement");
push(new YieldStatement(popForValue(), isYieldStar: starToken != null));
}
@override
void beginSwitchBlock(Token token) {
debugEvent("beginSwitchBlock");
enterLocalScope();
enterSwitchScope();
enterBreakTarget(token.charOffset);
}
@override
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) {
if (scope.hasLocalLabel(label.name)) {
// TODO(ahe): Should validate this is a goto target and not duplicated.
scope.claimLabel(label.name);
} else {
scope.declareLabel(label.name, createGotoTarget(firstToken.charOffset));
}
}
push(expressions);
push(labels);
enterLocalScope();
}
@override
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)
..fileOffset = firstToken.charOffset);
push(labels);
}
@override
void endSwitchStatement(Token switchKeyword, Token endToken) {
debugEvent("SwitchStatement");
// Do nothing. Handled by [endSwitchBlock].
}
@override
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.lookupLabel(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);
}
@override
void handleCaseMatch(Token caseKeyword, Token colon) {
debugEvent("CaseMatch");
// Do nothing. Handled by [handleSwitchCase].
}
@override
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.lookupLabel(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 if (target.functionNestingLevel != functionNestingLevel) {
push(compileTimeErrorInLoopOrSwitch = buildCompileTimeErrorStatement(
"Can't break to '$name' in a different function.",
breakKeyword.next.charOffset));
} else {
BreakStatement statement = new BreakStatement(null)
..fileOffset = breakKeyword.charOffset;
target.addBreak(statement);
push(statement);
}
}
@override
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.lookupLabel(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.forwardDeclareLabel(
identifier.name, target = createGotoTarget(identifier.fileOffset));
}
if (target.isGotoTarget &&
target.functionNestingLevel == functionNestingLevel) {
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 if (target.functionNestingLevel != functionNestingLevel) {
push(compileTimeErrorInLoopOrSwitch = buildCompileTimeErrorStatement(
"Can't continue at '$name' in a different function.",
continueKeyword.next.charOffset));
} else {
BreakStatement statement = new BreakStatement(null)
..fileOffset = continueKeyword.charOffset;
target.addContinue(statement);
push(statement);
}
}
@override
void endTypeVariable(Token token, Token extendsOrSuper) {
debugEvent("TypeVariable");
// TODO(ahe): Do not discard these when enabling generic method syntax.
pop(); // Bound.
pop(); // Name.
pop(); // Metadata.
}
@override
void endTypeVariables(int count, Token beginToken, Token endToken) {
debugEvent("TypeVariables");
// TODO(ahe): Implement this when enabling generic method syntax.
push(NullValue.TypeVariables);
}
@override
void handleModifier(Token token) {
debugEvent("Modifier");
// TODO(ahe): Copied from outline_builder.dart.
push(new Modifier.fromString(token.stringValue));
}
@override
void handleModifiers(int count) {
debugEvent("Modifiers");
// TODO(ahe): Copied from outline_builder.dart.
push(popList(count) ?? NullValue.Modifiers);
}
@override
void handleRecoverableError(Token token, ErrorKind kind, Map arguments) {
bool silent = hasParserError;
super.handleRecoverableError(token, kind, arguments);
addCompileTimeError(recoverableErrors.last.beginOffset,
'${recoverableErrors.last.kind} $arguments',
silent: silent);
}
@override
Token handleUnrecoverableError(Token token, ErrorKind kind, Map arguments) {
if (isDartLibrary && kind == ErrorKind.ExpectedFunctionBody) {
Token recover = skipNativeClause(token);
if (recover != null) return recover;
} else if (kind == ErrorKind.UnexpectedToken) {
String expected = arguments["expected"];
const List<String> trailing = const <String>[")", "}", ";", ","];
if (trailing.contains(token.stringValue) && trailing.contains(expected)) {
arguments.putIfAbsent("actual", () => token.lexeme);
handleRecoverableError(token, ErrorKind.ExpectedButGot, arguments);
return newSyntheticToken(token);
}
}
return super.handleUnrecoverableError(token, kind, arguments);
}
@override
Expression buildCompileTimeError(error, [int charOffset = -1]) {
addCompileTimeError(charOffset, error);
String message = formatUnexpected(uri, charOffset, error);
Builder constructor = library.loader.getCompileTimeError();
return new Throw(buildStaticInvocation(constructor.target,
new Arguments(<Expression>[new StringLiteral(message)]),
isConst: false)); // TODO(ahe): Make this const.
}
Statement buildCompileTimeErrorStatement(error, [int charOffset = -1]) {
return new ExpressionStatement(buildCompileTimeError(error, charOffset));
}
@override
Initializer buildCompileTimeErrorIntializer(error, [int charOffset = -1]) {
return new LocalInitializer(new VariableDeclaration.forValue(
buildCompileTimeError(error, charOffset)));
}
@override
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}");
}
}
@override
void handleOperator(Token token) {
debugEvent("Operator");
push(new Operator(token.stringValue)..fileOffset = token.charOffset);
}
@override
void handleSymbolVoid(Token token) {
logEvent("SymbolVoid");
}
dynamic addCompileTimeError(int charOffset, String message,
{bool silent: false}) {
return library.addCompileTimeError(charOffset, message, fileUri: uri);
}
@override
void handleInvalidFunctionBody(Token token) {
if (member.isNative) {
push(NullValue.FunctionBody);
} else {
push(new Block(<Statement>[new InvalidStatement()]));
}
}
@override
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 makeInvalidWrite(value);
}
Expression buildNullAwareAssignment(Expression value, DartType type,
{bool voidContext: false}) {
return makeInvalidWrite(value);
}
Expression buildCompoundAssignment(
Name binaryOperator, Expression value, int charOffset,
{bool voidContext: false, Procedure interfaceTarget}) {
return makeInvalidWrite(value);
}
Expression buildPrefixIncrement(Name binaryOperator, int charOffset,
{bool voidContext: false, Procedure interfaceTarget}) {
return makeInvalidWrite(null);
}
Expression buildPostfixIncrement(Name binaryOperator, int charOffset,
{bool voidContext: false, Procedure interfaceTarget}) {
return makeInvalidWrite(null);
}
makeInvalidRead() => internalError("not supported");
Expression makeInvalidWrite(Expression value) {
return helper.buildCompileTimeError(
"Can't be used as left-hand side of assignment.", charOffset);
}
}
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, charOffset,
voidContext: voidContext);
} else if (identical("-=", assignmentOperator)) {
return accessor.buildCompoundAssignment(minusName, value, charOffset,
voidContext: voidContext);
} else if (identical("*=", assignmentOperator)) {
return accessor.buildCompoundAssignment(multiplyName, value, charOffset,
voidContext: voidContext);
} else if (identical("%=", assignmentOperator)) {
return accessor.buildCompoundAssignment(percentName, value, charOffset,
voidContext: voidContext);
} else if (identical("&=", assignmentOperator)) {
return accessor.buildCompoundAssignment(ampersandName, value, charOffset,
voidContext: voidContext);
} else if (identical("/=", assignmentOperator)) {
return accessor.buildCompoundAssignment(divisionName, value, charOffset,
voidContext: voidContext);
} else if (identical("<<=", assignmentOperator)) {
return accessor.buildCompoundAssignment(leftShiftName, value, charOffset,
voidContext: voidContext);
} else if (identical(">>=", assignmentOperator)) {
return accessor.buildCompoundAssignment(rightShiftName, value, charOffset,
voidContext: voidContext);
} else if (identical("??=", assignmentOperator)) {
return accessor.buildNullAwareAssignment(value, const DynamicType(),
voidContext: voidContext);
} else if (identical("^=", assignmentOperator)) {
return accessor.buildCompoundAssignment(caretName, value, charOffset,
voidContext: voidContext);
} else if (identical("|=", assignmentOperator)) {
return accessor.buildCompoundAssignment(barName, value, charOffset,
voidContext: voidContext);
} else if (identical("~/=", assignmentOperator)) {
return accessor.buildCompoundAssignment(mustacheName, value, charOffset,
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, charOffset,
voidContext: false, interfaceTarget: interfaceTarget);
}
Expression buildForEffect() {
return accessor.buildPostfixIncrement(binaryOperator, charOffset,
voidContext: true, interfaceTarget: interfaceTarget);
}
}
class JumpTarget extends Builder {
final List<Statement> users = <Statement>[];
final JumpTargetKind kind;
final int functionNestingLevel;
JumpTarget(this.kind, this.functionNestingLevel, MemberBuilder member,
int charOffset)
: super(member, charOffset, member.fileUri);
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;
final JumpTarget continueTarget;
final int functionNestingLevel;
LabelTarget(MemberBuilder member, this.functionNestingLevel, int charOffset)
: breakTarget = new JumpTarget(
JumpTargetKind.Break, functionNestingLevel, member, charOffset),
continueTarget = new JumpTarget(
JumpTargetKind.Continue, functionNestingLevel, member, charOffset),
super(member, charOffset, member.fileUri);
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, Builder builder) {
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, builder, builder.fileUri);
}
if (optional != null) {
for (VariableDeclaration parameter in optional.formals) {
local[parameter.name] =
new KernelVariableBuilder(parameter, builder, builder.fileUri);
}
}
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 ThisAccessor) {
return node.isSuper ? "super" : "this";
} else if (node is BuilderAccessor) {
return node.plainNameForRead;
} else {
return internalError("Unhandled: ${node.runtimeType}");
}
}