blob: 1a26c4383a966da649f8597322925b8ff023ce54 [file] [log] [blame]
// Copyright (c) 2018, 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.fangorn;
import 'dart:core' hide MapEntry;
import 'package:kernel/ast.dart';
import '../names.dart';
import '../parser.dart' show offsetForToken, optional;
import '../problems.dart' show unsupported;
import '../scanner.dart' show Token;
import 'collections.dart'
show
ForElement,
ForInElement,
ForInMapEntry,
ForMapEntry,
IfElement,
IfMapEntry,
SpreadElement;
import 'kernel_shadow_ast.dart';
/// A shadow tree factory.
class Forest {
const Forest();
Arguments createArguments(int fileOffset, List<Expression> positional,
{List<DartType> types, List<NamedExpression> named}) {
return new ArgumentsImpl(positional, types: types, named: named)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
Arguments createArgumentsForExtensionMethod(
int fileOffset,
int extensionTypeParameterCount,
int typeParameterCount,
Expression receiver,
{List<DartType> extensionTypeArguments = const <DartType>[],
List<DartType> typeArguments = const <DartType>[],
List<Expression> positionalArguments = const <Expression>[],
List<NamedExpression> namedArguments = const <NamedExpression>[]}) {
return new ArgumentsImpl.forExtensionMethod(
extensionTypeParameterCount, typeParameterCount, receiver,
extensionTypeArguments: extensionTypeArguments,
typeArguments: typeArguments,
positionalArguments: positionalArguments,
namedArguments: namedArguments)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
Arguments createArgumentsEmpty(int fileOffset) {
return createArguments(fileOffset, <Expression>[]);
}
List<NamedExpression> argumentsNamed(Arguments arguments) {
return arguments.named;
}
List<Expression> argumentsPositional(Arguments arguments) {
return arguments.positional;
}
List<DartType> argumentsTypeArguments(Arguments arguments) {
return arguments.types;
}
void argumentsSetTypeArguments(Arguments arguments, List<DartType> types) {
ArgumentsImpl.setNonInferrableArgumentTypes(arguments, types);
}
StringLiteral asLiteralString(Expression value) => value;
/// Return a representation of a boolean literal at the given [location]. The
/// literal has the given [value].
BoolLiteral createBoolLiteral(bool value, Token token) {
return new BoolLiteral(value)..fileOffset = offsetForToken(token);
}
/// Return a representation of a double literal at the given [location]. The
/// literal has the given [value].
DoubleLiteral createDoubleLiteral(double value, Token token) {
return new DoubleLiteral(value)..fileOffset = offsetForToken(token);
}
/// Return a representation of an integer literal at the given [location]. The
/// literal has the given [value].
IntLiteral createIntLiteral(int value, Token token) {
return new IntJudgment(value, token?.lexeme)
..fileOffset = offsetForToken(token);
}
IntLiteral createIntLiteralLarge(String literal, Token token) {
return new ShadowLargeIntLiteral(literal, offsetForToken(token));
}
/// Return a representation of a list literal. The [constKeyword] is the
/// location of the `const` keyword, or `null` if there is no keyword. The
/// [isConst] is `true` if the literal is either explicitly or implicitly a
/// constant. The [typeArgument] is the representation of the single valid
/// type argument preceding the list literal, or `null` if there is no type
/// argument, there is more than one type argument, or if the type argument
/// cannot be resolved. The [typeArguments] is the representation of all of
/// the type arguments preceding the list literal, or `null` if there are no
/// type arguments. The [leftBracket] is the location of the `[`. The list of
/// [expressions] is a list of the representations of the list elements. The
/// [rightBracket] is the location of the `]`.
ListLiteral createListLiteral(
Token constKeyword,
bool isConst,
Object typeArgument,
Object typeArguments,
Token leftBracket,
List<Expression> expressions,
Token rightBracket) {
// TODO(brianwilkerson): The file offset computed below will not be correct
// if there are type arguments but no `const` keyword.
return new ListLiteral(expressions,
typeArgument: typeArgument, isConst: isConst)
..fileOffset = offsetForToken(constKeyword ?? leftBracket);
}
/// Return a representation of a set literal. The [constKeyword] is the
/// location of the `const` keyword, or `null` if there is no keyword. The
/// [isConst] is `true` if the literal is either explicitly or implicitly a
/// constant. The [typeArgument] is the representation of the single valid
/// type argument preceding the set literal, or `null` if there is no type
/// argument, there is more than one type argument, or if the type argument
/// cannot be resolved. The [typeArguments] is the representation of all of
/// the type arguments preceding the set literal, or `null` if there are no
/// type arguments. The [leftBrace] is the location of the `{`. The list of
/// [expressions] is a list of the representations of the set elements. The
/// [rightBrace] is the location of the `}`.
SetLiteral createSetLiteral(
Token constKeyword,
bool isConst,
Object typeArgument,
Object typeArguments,
Token leftBrace,
List<Expression> expressions,
Token rightBrace) {
// TODO(brianwilkerson): The file offset computed below will not be correct
// if there are type arguments but no `const` keyword.
return new SetLiteral(expressions,
typeArgument: typeArgument, isConst: isConst)
..fileOffset = offsetForToken(constKeyword ?? leftBrace);
}
/// Return a representation of a map literal. The [constKeyword] is the
/// location of the `const` keyword, or `null` if there is no keyword. The
/// [isConst] is `true` if the literal is either explicitly or implicitly a
/// constant. The [keyType] is the representation of the first type argument
/// preceding the map literal, or `null` if there are not exactly two type
/// arguments or if the first type argument cannot be resolved. The
/// [valueType] is the representation of the second type argument preceding
/// the map literal, or `null` if there are not exactly two type arguments or
/// if the second type argument cannot be resolved. The [typeArguments] is the
/// representation of all of the type arguments preceding the map literal, or
/// `null` if there are no type arguments. The [leftBrace] is the location
/// of the `{`. The list of [entries] is a list of the representations of the
/// map entries. The [rightBrace] is the location of the `}`.
MapLiteral createMapLiteral(
Token constKeyword,
bool isConst,
DartType keyType,
DartType valueType,
Object typeArguments,
Token leftBrace,
List<MapEntry> entries,
Token rightBrace) {
// TODO(brianwilkerson): The file offset computed below will not be correct
// if there are type arguments but no `const` keyword.
return new MapLiteral(entries,
keyType: keyType, valueType: valueType, isConst: isConst)
..fileOffset = offsetForToken(constKeyword ?? leftBrace);
}
/// Return a representation of a null literal at the given [fileOffset].
NullLiteral createNullLiteral(int fileOffset) {
return new NullLiteral()..fileOffset = fileOffset ?? TreeNode.noOffset;
}
/// Return a representation of a simple string literal at the given
/// [location]. The literal has the given [value]. This does not include
/// either adjacent strings or interpolated strings.
StringLiteral createStringLiteral(String value, Token token) {
return new StringLiteral(value)..fileOffset = offsetForToken(token);
}
/// Return a representation of a symbol literal defined by [value].
SymbolLiteral createSymbolLiteral(String value, Token token) {
return new SymbolLiteral(value)..fileOffset = offsetForToken(token);
}
TypeLiteral createTypeLiteral(DartType type, Token token) {
return new TypeLiteral(type)..fileOffset = offsetForToken(token);
}
/// Return a representation of a key/value pair in a literal map. The [key] is
/// the representation of the expression used to compute the key. The [colon]
/// is the location of the colon separating the key and the value. The [value]
/// is the representation of the expression used to compute the value.
MapEntry createMapEntry(Expression key, Token colon, Expression value) {
return new MapEntry(key, value)..fileOffset = offsetForToken(colon);
}
int readOffset(TreeNode node) => node.fileOffset;
Expression createLoadLibrary(
LibraryDependency dependency, Arguments arguments) {
return new LoadLibraryImpl(dependency, arguments);
}
Expression checkLibraryIsLoaded(LibraryDependency dependency) {
return new CheckLibraryIsLoaded(dependency);
}
Expression createAsExpression(
Expression expression, DartType type, Token token) {
return new AsExpression(expression, type)
..fileOffset = offsetForToken(token);
}
Expression createSpreadElement(Expression expression, Token token) {
return new SpreadElement(expression, token.lexeme == '...?')
..fileOffset = offsetForToken(token);
}
Expression createIfElement(Expression condition, Expression then,
Expression otherwise, Token token) {
return new IfElement(condition, then, otherwise)
..fileOffset = offsetForToken(token);
}
MapEntry createIfMapEntry(
Expression condition, MapEntry then, MapEntry otherwise, Token token) {
return new IfMapEntry(condition, then, otherwise)
..fileOffset = offsetForToken(token);
}
Expression createForElement(
List<VariableDeclaration> variables,
Expression condition,
List<Expression> updates,
Expression body,
Token token) {
return new ForElement(variables, condition, updates, body)
..fileOffset = offsetForToken(token);
}
MapEntry createForMapEntry(
List<VariableDeclaration> variables,
Expression condition,
List<Expression> updates,
MapEntry body,
Token token) {
return new ForMapEntry(variables, condition, updates, body)
..fileOffset = offsetForToken(token);
}
Expression createForInElement(
VariableDeclaration variable,
Expression iterable,
Statement prologue,
Expression body,
Expression problem,
Token token,
{bool isAsync: false}) {
return new ForInElement(variable, iterable, prologue, body, problem,
isAsync: isAsync)
..fileOffset = offsetForToken(token);
}
MapEntry createForInMapEntry(
VariableDeclaration variable,
Expression iterable,
Statement prologue,
MapEntry body,
Expression problem,
Token token,
{bool isAsync: false}) {
return new ForInMapEntry(variable, iterable, prologue, body, problem,
isAsync: isAsync)
..fileOffset = offsetForToken(token);
}
/// Return a representation of an assert that appears in a constructor's
/// initializer list.
AssertInitializer createAssertInitializer(
Token assertKeyword,
Token leftParenthesis,
Expression condition,
Token comma,
Expression message) {
return new AssertInitializer(createAssertStatement(
assertKeyword, leftParenthesis, condition, comma, message, null));
}
/// Return a representation of an assert that appears as a statement.
Statement createAssertStatement(Token assertKeyword, Token leftParenthesis,
Expression condition, Token comma, Expression message, Token semicolon) {
// Compute start and end offsets for the condition expression.
// This code is a temporary workaround because expressions don't carry
// their start and end offsets currently.
//
// The token that follows leftParenthesis is considered to be the
// first token of the condition.
// TODO(ahe): this really should be condition.fileOffset.
int startOffset = leftParenthesis.next.offset;
int endOffset;
{
// Search forward from leftParenthesis to find the last token of
// the condition - which is a token immediately followed by a commaToken,
// right parenthesis or a trailing comma.
Token conditionBoundary = comma ?? leftParenthesis.endGroup;
Token conditionLastToken = leftParenthesis;
while (!conditionLastToken.isEof) {
Token nextToken = conditionLastToken.next;
if (nextToken == conditionBoundary) {
break;
} else if (optional(',', nextToken) &&
nextToken.next == conditionBoundary) {
// The next token is trailing comma, which means current token is
// the last token of the condition.
break;
}
conditionLastToken = nextToken;
}
if (conditionLastToken.isEof) {
endOffset = startOffset = -1;
} else {
endOffset = conditionLastToken.offset + conditionLastToken.length;
}
}
return new AssertStatement(condition,
conditionStartOffset: startOffset,
conditionEndOffset: endOffset,
message: message);
}
Expression createAwaitExpression(Expression operand, Token token) {
return new AwaitExpression(operand)..fileOffset = offsetForToken(token);
}
/// Return a representation of a block of [statements] enclosed between the
/// [openBracket] and [closeBracket].
Statement createBlock(
Token openBrace, List<Statement> statements, Token closeBrace) {
List<Statement> copy;
for (int i = 0; i < statements.length; i++) {
Statement statement = statements[i];
if (statement is _VariablesDeclaration) {
copy ??= new List<Statement>.from(statements.getRange(0, i));
copy.addAll(statement.declarations);
} else if (copy != null) {
copy.add(statement);
}
}
return new Block(copy ?? statements)
..fileOffset = offsetForToken(openBrace);
}
/// Return a representation of a break statement.
Statement createBreakStatement(
Token breakKeyword, Object label, Token semicolon) {
return new BreakStatement(null)..fileOffset = breakKeyword.charOffset;
}
/// Return a representation of a catch clause.
Catch createCatch(
Token onKeyword,
DartType exceptionType,
Token catchKeyword,
VariableDeclaration exceptionParameter,
VariableDeclaration stackTraceParameter,
DartType stackTraceType,
Statement body) {
return new Catch(exceptionParameter, body,
guard: exceptionType, stackTrace: stackTraceParameter)
..fileOffset = offsetForToken(onKeyword ?? catchKeyword);
}
/// Return a representation of a conditional expression. The [condition] is
/// the expression preceding the question mark. The [question] is the `?`. The
/// [thenExpression] is the expression following the question mark. The
/// [colon] is the `:`. The [elseExpression] is the expression following the
/// colon.
Expression createConditionalExpression(Expression condition, Token question,
Expression thenExpression, Token colon, Expression elseExpression) {
return new ConditionalExpression(
condition, thenExpression, elseExpression, null)
..fileOffset = offsetForToken(question);
}
/// Return a representation of a continue statement.
Statement createContinueStatement(
Token continueKeyword, Object label, Token semicolon) {
return new BreakStatement(null)..fileOffset = continueKeyword.charOffset;
}
/// Return a representation of a do statement.
Statement createDoStatement(Token doKeyword, Statement body,
Token whileKeyword, Expression condition, Token semicolon) {
return new DoStatement(body, condition)..fileOffset = doKeyword.charOffset;
}
/// Return a representation of an expression statement composed from the
/// [expression] and [semicolon].
Statement createExpressionStatement(Expression expression, Token semicolon) {
return new ExpressionStatement(expression);
}
/// Return a representation of an empty statement consisting of the given
/// [semicolon].
Statement createEmptyStatement(Token semicolon) {
return new EmptyStatement();
}
/// Return a representation of a for statement.
Statement createForStatement(
Token forKeyword,
Token leftParenthesis,
List<VariableDeclaration> variables,
Token leftSeparator,
Expression condition,
Statement conditionStatement,
List<Expression> updaters,
Token rightParenthesis,
Statement body) {
return new ForStatement(variables ?? [], condition, updaters, body)
..fileOffset = forKeyword.charOffset;
}
/// Return a representation of an `if` statement.
Statement createIfStatement(Token ifKeyword, Expression condition,
Statement thenStatement, Token elseKeyword, Statement elseStatement) {
return new IfStatement(condition, thenStatement, elseStatement)
..fileOffset = ifKeyword.charOffset;
}
/// Return a representation of an `is` expression. The [operand] is the
/// representation of the left operand. The [isOperator] is the `is` operator.
/// The [notOperator] is either the `!` or `null` if the test is not negated.
/// The [type] is a representation of the type that is the right operand.
Expression createIsExpression(
Expression operand, Token isOperator, Token notOperator, DartType type) {
Expression result = new IsExpression(operand, type)
..fileOffset = offsetForToken(isOperator);
if (notOperator != null) {
result = createNot(result, notOperator, false);
}
return result;
}
/// Return a representation of a logical expression having the [leftOperand],
/// [rightOperand] and the [operator] (either `&&` or `||`).
Expression createLogicalExpression(
Expression leftOperand, Token operator, Expression rightOperand) {
return new LogicalExpression(
leftOperand, operator.stringValue, rightOperand)
..fileOffset = offsetForToken(operator);
}
Expression createNot(Expression operand, Token token, bool isSynthetic) {
return new Not(operand)..fileOffset = offsetForToken(token);
}
/// Return a representation of a parenthesized condition consisting of the
/// given [expression] between the [leftParenthesis] and [rightParenthesis].
Expression createParenthesizedCondition(
Token leftParenthesis, Expression expression, Token rightParenthesis) {
return expression;
}
/// Return a representation of a rethrow statement consisting of the
/// [rethrowKeyword] followed by the [semicolon].
Statement createRethrowStatement(Token rethrowKeyword, Token semicolon) {
return new ExpressionStatement(
new Rethrow()..fileOffset = offsetForToken(rethrowKeyword));
}
/// Return a representation of a return statement.
Statement createReturnStatement(int fileOffset, Expression expression,
{bool isArrow: true}) {
return new ReturnStatementImpl(isArrow, expression)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
Expression createStringConcatenation(
List<Expression> expressions, Token token) {
return new StringConcatenation(expressions)
..fileOffset = offsetForToken(token);
}
/// The given [statement] is being used as the target of either a break or
/// continue statement. Return the statement that should be used as the actual
/// target.
Statement createLabeledStatement(Statement statement) {
return new LabeledStatement(statement);
}
Expression createThisExpression(int offset) {
assert(offset != null);
return new ThisExpression()..fileOffset = offset;
}
/// Return a representation of a throw expression consisting of the
/// [throwKeyword].
Expression createThrow(Token throwKeyword, Expression expression) {
return new Throw(expression)..fileOffset = offsetForToken(throwKeyword);
}
bool isThrow(Object o) => o is Throw;
/// Return a representation of a try statement. The statement is introduced by
/// the [tryKeyword] and the given [body]. If catch clauses were included,
/// then the [catchClauses] will represent them, otherwise it will be `null`.
/// Similarly, if a finally block was included, then the [finallyKeyword] and
/// [finallyBlock] will be non-`null`, otherwise both will be `null`. If there
/// was an error in some part of the try statement, then an [errorReplacement]
/// might be provided, in which case it could be returned instead of the
/// representation of the try statement.
Statement createTryStatement(Token tryKeyword, Statement body,
List<Catch> catchClauses, Token finallyKeyword, Statement finallyBlock) {
Statement result = body;
if (catchClauses != null) {
result = new TryCatch(result, catchClauses);
}
if (finallyBlock != null) {
result = new TryFinally(result, finallyBlock);
}
return result;
}
_VariablesDeclaration variablesDeclaration(
List<VariableDeclaration> declarations, Uri uri) {
return new _VariablesDeclaration(declarations, uri);
}
List<VariableDeclaration> variablesDeclarationExtractDeclarations(
_VariablesDeclaration variablesDeclaration) {
return variablesDeclaration.declarations;
}
Statement wrapVariables(Statement statement) {
if (statement is _VariablesDeclaration) {
return new Block(
new List<Statement>.from(statement.declarations, growable: true))
..fileOffset = statement.fileOffset;
} else if (statement is VariableDeclaration) {
return new Block(<Statement>[statement])
..fileOffset = statement.fileOffset;
} else {
return statement;
}
}
/// Return a representation of a while statement introduced by the
/// [whileKeyword] and consisting of the given [condition] and [body].
Statement createWhileStatement(
Token whileKeyword, Expression condition, Statement body) {
return new WhileStatement(condition, body)
..fileOffset = whileKeyword.charOffset;
}
/// Return a representation of a yield statement consisting of the
/// [yieldKeyword], [star], [expression], and [semicolon]. The [star] is null
/// when no star was included in the source code.
Statement createYieldStatement(
Token yieldKeyword, Token star, Expression expression, Token semicolon) {
return new YieldStatement(expression, isYieldStar: star != null)
..fileOffset = yieldKeyword.charOffset;
}
/// Return the expression from the given expression [statement].
Expression getExpressionFromExpressionStatement(Statement statement) {
return (statement as ExpressionStatement).expression;
}
bool isBlock(Object node) => node is Block;
/// Return `true` if the given [statement] is the representation of an empty
/// statement.
bool isEmptyStatement(Statement statement) => statement is EmptyStatement;
bool isErroneousNode(Object node) {
if (node is ExpressionStatement) {
ExpressionStatement statement = node;
node = statement.expression;
}
if (node is VariableDeclaration) {
VariableDeclaration variable = node;
node = variable.initializer;
}
if (node is Let) {
Let let = node;
node = let.variable.initializer;
}
return node is InvalidExpression;
}
/// Return `true` if the given [statement] is the representation of an
/// expression statement.
bool isExpressionStatement(Statement statement) =>
statement is ExpressionStatement;
bool isThisExpression(Object node) => node is ThisExpression;
bool isVariablesDeclaration(Object node) => node is _VariablesDeclaration;
/// Creates [VariableDeclaration] for a variable named [name] at the given
/// [functionNestingLevel].
VariableDeclaration createVariableDeclaration(
String name, int functionNestingLevel,
{Expression initializer,
DartType type,
bool isFinal: false,
bool isConst: false,
bool isFieldFormal: false,
bool isCovariant: false,
bool isLocalFunction: false}) {
return new VariableDeclarationImpl(name, functionNestingLevel,
type: type,
initializer: initializer,
isFinal: isFinal,
isConst: isConst,
isFieldFormal: isFieldFormal,
isCovariant: isCovariant,
isLocalFunction: isLocalFunction);
}
VariableDeclaration createVariableDeclarationForValue(
int fileOffset, Expression initializer,
{DartType type = const DynamicType()}) {
return new VariableDeclarationImpl.forValue(initializer)
..type = type
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
Let createLet(VariableDeclaration variable, Expression body) {
return new Let(variable, body);
}
FunctionNode createFunctionNode(Statement body,
{List<TypeParameter> typeParameters,
List<VariableDeclaration> positionalParameters,
List<VariableDeclaration> namedParameters,
int requiredParameterCount,
DartType returnType: const DynamicType(),
AsyncMarker asyncMarker: AsyncMarker.Sync,
AsyncMarker dartAsyncMarker}) {
return new FunctionNode(body,
typeParameters: typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount: requiredParameterCount,
returnType: returnType,
asyncMarker: asyncMarker,
dartAsyncMarker: dartAsyncMarker);
}
TypeParameter createTypeParameter(String name) {
return new TypeParameter(name);
}
TypeParameterType createTypeParameterType(TypeParameter typeParameter) {
return new TypeParameterType(typeParameter);
}
FunctionExpression createFunctionExpression(
int fileOffset, FunctionNode function) {
return new FunctionExpression(function)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
MethodInvocation createFunctionInvocation(
int fileOffset, Expression expression, Arguments arguments) {
return new MethodInvocationImpl(expression, callName, arguments)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
MethodInvocation createMethodInvocation(
int fileOffset, Expression expression, Name name, Arguments arguments,
{bool isImplicitCall: false, Member interfaceTarget}) {
return new MethodInvocationImpl(expression, name, arguments,
isImplicitCall: isImplicitCall)
..fileOffset = fileOffset ?? TreeNode.noOffset
..interfaceTarget = interfaceTarget;
}
NamedExpression createNamedExpression(String name, Expression expression) {
return new NamedExpression(name, expression);
}
StaticInvocation createStaticInvocation(
int fileOffset, Procedure procedure, Arguments arguments) {
return new StaticInvocation(procedure, arguments)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
SuperMethodInvocation createSuperMethodInvocation(
int fileOffset, Name name, Procedure procedure, Arguments arguments) {
return new SuperMethodInvocation(name, arguments, procedure)
..fileOffset = fileOffset ?? TreeNode.noOffset;
}
NullCheck createNullCheck(int fileOffset, Expression expression) {
return new NullCheck(expression)..fileOffset = fileOffset;
}
PropertySet createPropertySet(
int fileOffset, Expression receiver, Name name, Expression value,
{Member interfaceTarget, bool forEffect, bool readOnlyReceiver: false}) {
return new PropertySetImpl(receiver, name, value,
interfaceTarget: interfaceTarget,
forEffect: forEffect,
readOnlyReceiver: readOnlyReceiver)
..fileOffset = fileOffset;
}
}
class _VariablesDeclaration extends Statement {
final List<VariableDeclaration> declarations;
final Uri uri;
_VariablesDeclaration(this.declarations, this.uri) {
setParents(declarations, this);
}
R accept<R>(v) {
throw unsupported("accept", fileOffset, uri);
}
R accept1<R, A>(v, arg) {
throw unsupported("accept1", fileOffset, uri);
}
visitChildren(v) {
throw unsupported("visitChildren", fileOffset, uri);
}
transformChildren(v) {
throw unsupported("transformChildren", fileOffset, uri);
}
}