blob: d8d7573d4c9ccf053c000463ae0b3e78e5b5ae48 [file] [log] [blame]
library dart_codegen;
import 'dart_tree.dart' as tree;
import 'dart_printer.dart';
import 'dart_tree_printer.dart' show TreePrinter;
import '../tree/tree.dart' as frontend;
import '../dart2jslib.dart' as dart2js;
import '../elements/elements.dart';
import '../dart_types.dart';
import '../elements/modelx.dart' as modelx;
/// Translates the dart_tree IR to Dart frontend AST.
frontend.FunctionExpression emit(FunctionElement element,
dart2js.TreeElementMapping treeElements,
tree.FunctionDefinition definition) {
FunctionExpression fn = new ASTEmitter().emit(element, definition);
return new TreePrinter(treeElements).makeExpression(fn);
}
/// Translates the dart_tree IR to Dart backend AST.
class ASTEmitter extends tree.Visitor<dynamic, Expression> {
/// Variables to be hoisted at the top of the current function.
List<VariableDeclaration> variables;
/// Statements emitted by the most recent call to [visitStatement].
List<Statement> statementBuffer;
/// The function currently being emitted.
FunctionElement functionElement;
FunctionExpression emit(FunctionElement element,
tree.FunctionDefinition definition) {
functionElement = element;
variables = <VariableDeclaration>[];
statementBuffer = <Statement>[];
tree.Variable.counter = 0;
Parameters parameters = emitParameters(definition.parameters);
visitStatement(definition.body);
removeTrailingReturn();
Statement body = new Block(statementBuffer);
if (variables.length > 0) {
Statement head = new VariableDeclarations(variables);
body = new Block([head, body]);
}
FunctionType functionType = element.type;
variables = null;
statementBuffer = null;
functionElement = null;
return new FunctionExpression(
parameters,
body,
name: element.name,
returnType: emitOptionalType(functionType.returnType))
..element = element;
}
/// Removes a trailing "return null" from [statementBuffer].
void removeTrailingReturn() {
if (statementBuffer.isEmpty) return;
if (statementBuffer.last is! Return) return;
Return ret = statementBuffer.last;
Expression expr = ret.expression;
if (expr is Literal && expr.value is dart2js.NullConstant) {
statementBuffer.removeLast();
}
}
Parameter emitParameter(tree.Variable param) {
ParameterElement element = param.element;
TypeAnnotation type = emitOptionalType(element.type);
return new Parameter(element.name, type:type)
..element = element;
}
Parameters emitParameters(List<tree.Variable> params) {
return new Parameters(params.map(emitParameter).toList(growable:false));
}
void visitExpressionStatement(tree.ExpressionStatement stmt) {
Expression e = visitExpression(stmt.expression);
statementBuffer.add(new ExpressionStatement(e));
visitStatement(stmt.next);
}
void visitLetVal(tree.LetVal stmt) {
// Synthesize an element for the variable, if necessary.
if (stmt.variable.element == null) {
stmt.variable.element = new modelx.VariableElementX.synthetic(
stmt.variable.name,
ElementKind.VARIABLE,
functionElement);
}
variables.add(new VariableDeclaration(stmt.variable.name));
Expression def = visitExpression(stmt.definition);
statementBuffer.add(new ExpressionStatement(new Assignment(
visitVariable(stmt.variable),
'=',
def)));
visitStatement(stmt.body);
}
void visitReturn(tree.Return stmt) {
Expression inner = visitExpression(stmt.value);
statementBuffer.add(new Return(inner));
}
Expression visitConstant(tree.Constant exp) {
return emitConstant(exp.value);
}
Expression visitInvokeStatic(tree.InvokeStatic exp) {
List args = exp.arguments.map(visitExpression).toList(growable:false);
return new CallStatic(null, exp.target.name, args)
..element = exp.target;
}
Expression visitVariable(tree.Variable exp) {
return new Identifier(exp.name)
..element = exp.element;
}
TypeAnnotation emitType(DartType type) {
if (type is GenericType) { // TODO(asgerf): faster Link.map
return new TypeAnnotation(
type.element.name,
type.typeArguments.toList(growable:false)
.map(emitType).toList(growable:false))
..dartType = type;
} else if (type is VoidType) {
return new TypeAnnotation('void')
..dartType = type;
} else if (type is TypeVariableType) {
return new TypeAnnotation(type.name)
..dartType = type;
} else {
throw "Unsupported type annotation: ${type.runtimeType}";
}
}
/// Like [emitType] except the dynamic type is converted to null.
TypeAnnotation emitOptionalType(DartType type) {
if (type.isDynamic) {
return null;
} else {
return emitType(type);
}
}
Expression emitConstant(dart2js.Constant constant) {
if (constant is dart2js.PrimitiveConstant) {
return new Literal(constant);
} else if (constant is dart2js.ListConstant) {
return new LiteralList(constant.entries.map(emitConstant), isConst: true);
} else if (constant is dart2js.MapConstant) {
List<LiteralMapEntry> entries = <LiteralMapEntry>[];
for (var i = 0; i < constant.keys.length; i++) {
entries.add(new LiteralMapEntry(
emitConstant(constant.keys.entries[i]),
emitConstant(constant.values[i])));
}
return new LiteralMap(entries, isConst: true);
} else {
throw "Unsupported constant: ${constant.runtimeType}";
}
}
}