blob: 879b308631b91cac754d98cb6bf0f23ffd92a027 [file] [log] [blame]
// Copyright (c) 2014, 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 analyzer2dart.cps_generator;
import 'package:analyzer/analyzer.dart';
import 'package:compiler/src/dart_types.dart' as dart2js;
import 'package:compiler/src/elements/elements.dart' as dart2js;
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/element.dart' as analyzer;
import 'package:compiler/src/dart2jslib.dart'
show DART_CONSTANT_SYSTEM;
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart' as ir;
import 'package:compiler/src/cps_ir/cps_ir_builder.dart';
import 'package:compiler/src/universe/universe.dart';
import 'semantic_visitor.dart';
import 'element_converter.dart';
import 'util.dart';
import 'identifier_semantics.dart';
/// Visitor that converts the AST node of an analyzer element into a CPS ir
/// node.
class CpsElementVisitor extends analyzer.SimpleElementVisitor<ir.Node> {
final ElementConverter converter;
final AstNode node;
CpsElementVisitor(this.converter, this.node);
@override
ir.FunctionDefinition visitFunctionElement(analyzer.FunctionElement element) {
CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);
FunctionDeclaration functionDeclaration = node;
return visitor.handleFunctionDeclaration(
element, functionDeclaration.functionExpression.body);
}
@override
ir.FunctionDefinition visitMethodElement(analyzer.MethodElement element) {
CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);
MethodDeclaration methodDeclaration = node;
return visitor.handleFunctionDeclaration(element, methodDeclaration.body);
}
@override
ir.FieldDefinition visitTopLevelVariableElement(
analyzer.TopLevelVariableElement element) {
CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);
VariableDeclaration variableDeclaration = node;
return visitor.handleFieldDeclaration(element, variableDeclaration);
}
@override
ir.ExecutableDefinition visitConstructorElement(
analyzer.ConstructorElement element) {
CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);
if (!element.isFactory) {
ConstructorDeclaration constructorDeclaration = node;
FunctionBody body;
if (constructorDeclaration != null) {
body = constructorDeclaration.body;
} else {
assert(element.isSynthetic);
}
return visitor.handleConstructorDeclaration(element, body);
}
// TODO(johnniwinther): Support factory constructors.
return null;
}
}
/// Visitor that converts analyzer AST nodes into CPS ir nodes.
class CpsGeneratingVisitor extends SemanticVisitor<ir.Node>
with IrBuilderMixin<AstNode> {
/// Promote the type of [irBuilder] to [DartIrBuilder].
/// The JS backend requires closure conversion which we do not support yet.
DartIrBuilder get irBuilder => super.irBuilder;
final analyzer.Element element;
final ElementConverter converter;
CpsGeneratingVisitor(this.converter, this.element);
Source get currentSource => element.source;
analyzer.LibraryElement get currentLibrary => element.library;
ir.Node visit(AstNode node) => node.accept(this);
ir.ConstructorDefinition handleConstructorDeclaration(
analyzer.ConstructorElement constructor, FunctionBody body) {
dart2js.ConstructorElement element = converter.convertElement(constructor);
return withBuilder(
new DartIrBuilder(DART_CONSTANT_SYSTEM,
element,
// TODO(johnniwinther): Supported closure variables.
new NullCapturedVariableInfo()),
() {
irBuilder.buildFunctionHeader(
constructor.parameters.map(converter.convertElement));
// Visit the body directly to avoid processing the signature as
// expressions.
// Call to allow for `body == null` in case of synthesized constructors.
build(body);
return irBuilder.makeConstructorDefinition(const [], const []);
});
}
ir.FieldDefinition handleFieldDeclaration(
analyzer.PropertyInducingElement field, VariableDeclaration node) {
dart2js.FieldElement element = converter.convertElement(field);
return withBuilder(
new DartIrBuilder(DART_CONSTANT_SYSTEM,
element,
// TODO(johnniwinther): Supported closure variables.
new NullCapturedVariableInfo()),
() {
irBuilder.buildFieldInitializerHeader();
ir.Primitive initializer = build(node.initializer);
return irBuilder.makeFieldDefinition(initializer);
});
}
ir.FunctionDefinition handleFunctionDeclaration(
analyzer.ExecutableElement function, FunctionBody body) {
dart2js.FunctionElement element = converter.convertElement(function);
return withBuilder(
new DartIrBuilder(DART_CONSTANT_SYSTEM,
element,
// TODO(johnniwinther): Supported closure variables.
new NullCapturedVariableInfo()),
() {
irBuilder.buildFunctionHeader(
function.parameters.map(converter.convertElement));
// Visit the body directly to avoid processing the signature as
// expressions.
visit(body);
return irBuilder.makeFunctionDefinition(const []);
});
}
@override
ir.Primitive visitFunctionExpression(FunctionExpression node) {
return irBuilder.buildFunctionExpression(
handleFunctionDeclaration(node.element, node.body));
}
@override
ir.FunctionDefinition visitFunctionDeclaration(FunctionDeclaration node) {
return handleFunctionDeclaration(
node.element, node.functionExpression.body);
}
@override
visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
FunctionDeclaration functionDeclaration = node.functionDeclaration;
analyzer.FunctionElement function = functionDeclaration.element;
dart2js.FunctionElement element = converter.convertElement(function);
ir.FunctionDefinition definition = handleFunctionDeclaration(
function, functionDeclaration.functionExpression.body);
irBuilder.declareLocalFunction(element, definition);
}
List<ir.Primitive> visitArguments(ArgumentList argumentList) {
List<ir.Primitive> arguments = <ir.Primitive>[];
for (Expression argument in argumentList.arguments) {
ir.Primitive value = build(argument);
if (value == null) {
giveUp(argument,
'Unsupported argument: $argument (${argument.runtimeType}).');
}
arguments.add(value);
}
return arguments;
}
@override
ir.Node visitMethodInvocation(MethodInvocation node) {
// Overridden to avoid eager visits of the receiver and arguments.
return handleMethodInvocation(node);
}
@override
ir.Primitive visitDynamicInvocation(MethodInvocation node,
AccessSemantics semantics) {
// TODO(johnniwinther): Handle implicit `this`.
ir.Primitive receiver = build(semantics.target);
List<ir.Primitive> arguments = visitArguments(node.argumentList);
return irBuilder.buildDynamicInvocation(
receiver,
createSelectorFromMethodInvocation(
node.argumentList, node.methodName.name),
arguments);
}
@override
ir.Primitive visitStaticMethodInvocation(MethodInvocation node,
AccessSemantics semantics) {
analyzer.Element staticElement = semantics.element;
dart2js.Element element = converter.convertElement(staticElement);
List<ir.Primitive> arguments = visitArguments(node.argumentList);
return irBuilder.buildStaticInvocation(
element,
createSelectorFromMethodInvocation(
node.argumentList, node.methodName.name),
arguments);
}
@override
ir.Node visitLocalFunctionAccess(AstNode node, AccessSemantics semantics) {
return handleLocalAccess(node, semantics);
}
ir.Primitive handleLocalInvocation(MethodInvocation node,
AccessSemantics semantics) {
analyzer.Element staticElement = semantics.element;
dart2js.Element element = converter.convertElement(staticElement);
ir.Primitive receiver = irBuilder.buildLocalGet(element);
List<ir.Definition> arguments = visitArguments(node.argumentList);
return irBuilder.buildCallInvocation(
receiver,
createSelectorFromMethodInvocation(
node.argumentList, node.methodName.name),
arguments);
}
@override
ir.Node visitLocalVariableInvocation(MethodInvocation node,
AccessSemantics semantics) {
return handleLocalInvocation(node, semantics);
}
@override
ir.Primitive visitLocalFunctionInvocation(MethodInvocation node,
AccessSemantics semantics) {
return handleLocalInvocation(node, semantics);
}
@override
ir.Primitive visitFunctionExpressionInvocation(
FunctionExpressionInvocation node) {
ir.Primitive target = build(node.function);
List<ir.Definition> arguments = visitArguments(node.argumentList);
return irBuilder.buildCallInvocation(
target,
createSelectorFromMethodInvocation(node.argumentList, 'call'),
arguments);
}
@override
ir.Primitive visitInstanceCreationExpression(
InstanceCreationExpression node) {
analyzer.Element staticElement = node.staticElement;
if (staticElement != null) {
dart2js.Element element = converter.convertElement(staticElement);
dart2js.DartType type = converter.convertType(node.staticType);
List<ir.Primitive> arguments = visitArguments(node.argumentList);
String name = '';
if (node.constructorName.name != null) {
name = node.constructorName.name.name;
}
return irBuilder.buildConstructorInvocation(
element,
createSelectorFromMethodInvocation(node.argumentList, name),
type,
arguments);
}
return giveUp(node, "Unresolved constructor invocation.");
}
@override
ir.Constant visitNullLiteral(NullLiteral node) {
return irBuilder.buildNullLiteral();
}
@override
ir.Constant visitBooleanLiteral(BooleanLiteral node) {
return irBuilder.buildBooleanLiteral(node.value);
}
@override
ir.Constant visitDoubleLiteral(DoubleLiteral node) {
return irBuilder.buildDoubleLiteral(node.value);
}
@override
ir.Constant visitIntegerLiteral(IntegerLiteral node) {
return irBuilder.buildIntegerLiteral(node.value);
}
@override
visitAdjacentStrings(AdjacentStrings node) {
String value = node.stringValue;
if (value != null) {
return irBuilder.buildStringLiteral(value);
}
giveUp(node, "Non constant adjacent strings.");
}
@override
ir.Constant visitSimpleStringLiteral(SimpleStringLiteral node) {
return irBuilder.buildStringLiteral(node.value);
}
@override
visitStringInterpolation(StringInterpolation node) {
giveUp(node, "String interpolation.");
}
@override
visitReturnStatement(ReturnStatement node) {
irBuilder.buildReturn(build(node.expression));
}
@override
ir.Node visitPropertyAccess(PropertyAccess node) {
// Overridden to avoid eager visits of the receiver.
return handlePropertyAccess(node);
}
@override
ir.Node visitLocalVariableAccess(AstNode node, AccessSemantics semantics) {
return handleLocalAccess(node, semantics);
}
@override
ir.Node visitParameterAccess(AstNode node, AccessSemantics semantics) {
return handleLocalAccess(node, semantics);
}
@override
visitVariableDeclaration(VariableDeclaration node) {
// TODO(johnniwinther): Handle constant local variables.
ir.Node initialValue = build(node.initializer);
irBuilder.declareLocalVariable(
converter.convertElement(node.element),
initialValue: initialValue);
}
dart2js.Element getLocal(AstNode node, AccessSemantics semantics) {
analyzer.Element element = semantics.element;
dart2js.Element target = converter.convertElement(element);
assert(invariant(node, target.isLocal, '$target expected to be local.'));
return target;
}
ir.Primitive handleLocalAccess(AstNode node, AccessSemantics semantics) {
return irBuilder.buildLocalGet(getLocal(node, semantics));
}
ir.Primitive handleLocalAssignment(AssignmentExpression node,
AccessSemantics semantics) {
if (node.operator.lexeme != '=') {
return giveUp(node, 'Assignment operator: ${node.operator.lexeme}');
}
return irBuilder.buildLocalSet(
getLocal(node, semantics),
build(node.rightHandSide));
}
@override
ir.Node visitAssignmentExpression(AssignmentExpression node) {
// Avoid eager visiting of left and right hand side.
return handleAssignmentExpression(node);
}
@override
ir.Node visitLocalVariableAssignment(AssignmentExpression node,
AccessSemantics semantics) {
return handleLocalAssignment(node, semantics);
}
@override
ir.Node visitParameterAssignment(AssignmentExpression node,
AccessSemantics semantics) {
return handleLocalAssignment(node, semantics);
}
@override
ir.Node visitStaticFieldAssignment(AssignmentExpression node,
AccessSemantics semantics) {
if (node.operator.lexeme != '=') {
return giveUp(node, 'Assignment operator: ${node.operator.lexeme}');
}
analyzer.Element element = semantics.element;
dart2js.Element target = converter.convertElement(element);
// TODO(johnniwinther): Selector information should be computed in the
// [TreeShaker] and shared with the [CpsGeneratingVisitor].
assert(invariant(node, target.isTopLevel || target.isStatic,
'$target expected to be top-level or static.'));
return irBuilder.buildStaticSet(
target,
new Selector.setter(target.name, target.library),
build(node.rightHandSide));
}
@override
ir.Node visitDynamicAccess(AstNode node, AccessSemantics semantics) {
// TODO(johnniwinther): Handle implicit `this`.
ir.Primitive receiver = build(semantics.target);
return irBuilder.buildDynamicGet(receiver,
new Selector.getter(semantics.identifier.name,
converter.convertElement(element.library)));
}
@override
ir.Node visitStaticFieldAccess(AstNode node, AccessSemantics semantics) {
analyzer.Element element = semantics.element;
dart2js.Element target = converter.convertElement(element);
// TODO(johnniwinther): Selector information should be computed in the
// [TreeShaker] and shared with the [CpsGeneratingVisitor].
assert(invariant(node, target.isTopLevel || target.isStatic,
'$target expected to be top-level or static.'));
return irBuilder.buildStaticGet(target,
new Selector.getter(target.name, target.library));
}
ir.Primitive handleBinaryExpression(BinaryExpression node,
String op) {
ir.Primitive left = build(node.leftOperand);
ir.Primitive right = build(node.rightOperand);
Selector selector = new Selector.binaryOperator(op);
return irBuilder.buildDynamicInvocation(
left, selector, <ir.Primitive>[right]);
}
ir.Node handleLazyOperator(BinaryExpression node, {bool isLazyOr: false}) {
return irBuilder.buildLogicalOperator(
build(node.leftOperand),
subbuild(node.rightOperand),
isLazyOr: isLazyOr);
}
@override
ir.Node visitBinaryExpression(BinaryExpression node) {
// TODO(johnniwinther,paulberry,brianwilkerson): The operator should be
// available through an enum.
String op = node.operator.lexeme;
switch (op) {
case '||':
case '&&':
return handleLazyOperator(node, isLazyOr: op == '||');
case '!=':
return irBuilder.buildNegation(handleBinaryExpression(node, '=='));
default:
return handleBinaryExpression(node, op);
}
}
@override
ir.Node visitConditionalExpression(ConditionalExpression node) {
return irBuilder.buildConditional(
build(node.condition),
subbuild(node.thenExpression),
subbuild(node.elseExpression));
}
@override
visitIfStatement(IfStatement node) {
irBuilder.buildIf(
build(node.condition),
subbuild(node.thenStatement),
subbuild(node.elseStatement));
}
@override
visitBlock(Block node) {
irBuilder.buildBlock(node.statements, build);
}
@override
ir.Node visitListLiteral(ListLiteral node) {
dart2js.InterfaceType type = converter.convertType(node.staticType);
// TODO(johnniwinther): Use `build` instead of `(e) => build(e)` when issue
// 18630 has been resolved.
Iterable<ir.Primitive> values = node.elements.map((e) => build(e));
return irBuilder.buildListLiteral(type, values);
}
@override
ir.Node visitMapLiteral(MapLiteral node) {
dart2js.InterfaceType type = converter.convertType(node.staticType);
return irBuilder.buildMapLiteral(
type,
node.entries.map((e) => e.key),
node.entries.map((e) => e.value),
build);
}
@override
visitForStatement(ForStatement node) {
// TODO(johnniwinther): Support `for` as a jump target.
List<dart2js.LocalElement> loopVariables = <dart2js.LocalElement>[];
SubbuildFunction buildInitializer;
if (node.variables != null) {
buildInitializer = subbuild(node.variables);
for (VariableDeclaration variable in node.variables.variables) {
loopVariables.add(converter.convertElement(variable.element));
}
} else {
buildInitializer = subbuild(node.initialization);
}
irBuilder.buildFor(buildInitializer: buildInitializer,
buildCondition: subbuild(node.condition),
buildBody: subbuild(node.body),
buildUpdate: subbuildSequence(node.updaters),
loopVariables: loopVariables);
}
@override
visitWhileStatement(WhileStatement node) {
// TODO(johnniwinther): Support `while` as a jump target.
irBuilder.buildWhile(buildCondition: subbuild(node.condition),
buildBody: subbuild(node.body));
}
@override
visitDeclaredIdentifier(DeclaredIdentifier node) {
giveUp(node, "Unexpected node: DeclaredIdentifier");
}
@override
visitForEachStatement(ForEachStatement node) {
SubbuildFunction buildVariableDeclaration;
dart2js.Element variableElement;
Selector variableSelector;
if (node.identifier != null) {
AccessSemantics accessSemantics =
node.identifier.accept(ACCESS_SEMANTICS_VISITOR);
if (accessSemantics.kind == AccessKind.DYNAMIC) {
variableSelector = new Selector.setter(
node.identifier.name, converter.convertElement(currentLibrary));
} else if (accessSemantics.element != null) {
variableElement = converter.convertElement(accessSemantics.element);
variableSelector = new Selector.setter(
variableElement.name,
converter.convertElement(accessSemantics.element.library));
} else {
giveUp(node, 'For-in of unresolved variable: $accessSemantics');
}
} else {
assert(invariant(
node, node.loopVariable != null, "Loop variable expected"));
variableElement = converter.convertElement(node.loopVariable.element);
buildVariableDeclaration = (IrBuilder builder) {
builder.declareLocalVariable(variableElement);
};
}
// TODO(johnniwinther): Support `for-in` as a jump target.
irBuilder.buildForIn(
buildExpression: subbuild(node.iterable),
buildVariableDeclaration: buildVariableDeclaration,
variableElement: variableElement,
variableSelector: variableSelector,
buildBody: subbuild(node.body));
}
@override
ir.Primitive visitIsExpression(IsExpression node) {
return irBuilder.buildTypeOperator(
visit(node.expression),
converter.convertType(node.type.type),
isTypeTest: true,
isNotCheck: node.notOperator != null);
}
@override
ir.Primitive visitAsExpression(AsExpression node) {
return irBuilder.buildTypeOperator(
visit(node.expression),
converter.convertType(node.type.type),
isTypeTest: false);
}
}
class NullCapturedVariableInfo extends DartCapturedVariableInfo {
Iterable get capturedVariables => const [];
}