Index: pkg/dev_compiler/lib/src/compiler/code_generator.dart |
diff --git a/pkg/dev_compiler/lib/src/compiler/code_generator.dart b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
index 04b929a04e2a61fc68b8857b7d183e5a7f2bd4a2..782523034fe0a551be3265d5eb1e89b1b523a8a1 100644 |
--- a/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
+++ b/pkg/dev_compiler/lib/src/compiler/code_generator.dart |
@@ -53,8 +53,23 @@ import 'reify_coercions.dart' show CoercionReifier; |
import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; |
import 'type_utilities.dart'; |
-class CodeGenerator extends GeneralizingAstVisitor |
- with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference { |
+/// The code generator for Dart Dev Compiler. |
+/// |
+/// Takes as input resolved Dart ASTs for every compilation unit in every |
+/// library in the module. Produces a single JavaScript AST for the module as |
+/// output, along with its source map. |
+/// |
+/// This class attempts to preserve identifier names and structure of the input |
+/// Dart code, whenever this is possible to do in the generated code. |
+// |
+// TODO(jmesserly): we should use separate visitors for statements and |
+// expressions. Declarations are handled directly, and many minor component |
+// AST nodes aren't visited, so the visitor pattern isn't helping except for |
+// expressions (which result in JS.Expression) and statements |
+// (which result in (JS.Statement). |
+class CodeGenerator extends Object |
+ with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference |
+ implements AstVisitor<JS.Node> { |
final AnalysisContext context; |
final SummaryDataStore summaryData; |
@@ -239,11 +254,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
assembler.recordDependencies(summaryData); |
var uriToUnit = new Map<String, UnlinkedUnit>.fromIterable(units, |
- key: (u) => u.element.source.uri.toString(), value: (unit) { |
- var unlinked = serializeAstUnlinked(unit); |
- assembler.addUnlinkedUnit(unit.element.source, unlinked); |
- return unlinked; |
- }); |
+ key: (u) => u.element.source.uri.toString(), |
+ value: (unit) { |
+ var unlinked = serializeAstUnlinked(unit); |
+ assembler.addUnlinkedUnit(unit.element.source, unlinked); |
+ return unlinked; |
+ }); |
summary_link |
.link( |
@@ -329,7 +345,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
// NOTE: declarations are not necessarily emitted in this order. |
// Order will be changed as needed so the resulting code can execute. |
// This is done by forward declaring items. |
- compilationUnits.forEach(_emitCompilationUnit); |
+ compilationUnits.forEach(visitCompilationUnit); |
assert(_deferredProperties.isEmpty); |
// Visit directives (for exports) |
@@ -565,7 +581,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
} |
- void _emitCompilationUnit(CompilationUnit unit) { |
+ @override |
+ visitCompilationUnit(CompilationUnit unit) { |
// NOTE: this method isn't the right place to initialize |
// per-compilation-unit state. Declarations can be visited out of order, |
// this is only to catch things that haven't been emitted yet. |
@@ -621,10 +638,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
@override |
- void visitLibraryDirective(LibraryDirective node) {} |
+ visitLibraryDirective(LibraryDirective node) => null; |
@override |
- void visitImportDirective(ImportDirective node) { |
+ visitImportDirective(ImportDirective node) { |
// We don't handle imports here. |
// |
// Instead, we collect imports whenever we need to generate a reference |
@@ -633,16 +650,17 @@ class CodeGenerator extends GeneralizingAstVisitor |
// |
// TODO(jmesserly): if this is a prefixed import, consider adding the prefix |
// as an alias? |
+ return null; |
} |
@override |
- void visitPartDirective(PartDirective node) {} |
+ visitPartDirective(PartDirective node) => null; |
@override |
- void visitPartOfDirective(PartOfDirective node) {} |
+ visitPartOfDirective(PartOfDirective node) => null; |
@override |
- void visitExportDirective(ExportDirective node) { |
+ visitExportDirective(ExportDirective node) { |
ExportElement element = node.element; |
var currentLibrary = element.library; |
@@ -659,7 +677,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (export is FunctionElement) { |
// Don't allow redefining names from this library. |
- if (currentNames.containsKey(export.name)) return; |
+ if (currentNames.containsKey(export.name)) return null; |
var name = _emitTopLevelName(export); |
_moduleItems.add(js.statement( |
@@ -755,6 +773,12 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
@override |
+ visitGenericTypeAlias(GenericTypeAlias node) { |
+ throw new UnimplementedError('Generic type aliases are not implemented. ' |
+ 'See https://github.com/dart-lang/sdk/issues/27971'); |
+ } |
+ |
+ @override |
JS.Expression visitTypeName(TypeName node) { |
if (node.type == null) { |
// TODO(jmesserly): if the type fails to resolve, should we generate code |
@@ -796,8 +820,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var className = isGeneric ? element.name : _emitTopLevelName(element); |
JS.Statement declareInterfaces(JS.Statement decl) { |
if (element.interfaces.isNotEmpty) { |
- var body = [decl] |
- ..add(js.statement('#[#.implements] = () => #;', [ |
+ var body = [decl]..add(js.statement('#[#.implements] = () => #;', [ |
className, |
_runtimeModule, |
new JS.ArrayInitializer( |
@@ -2073,18 +2096,18 @@ class CodeGenerator extends GeneralizingAstVisitor |
// TODO(jmesserly): we'll need something different once we have |
// rest/spread support, but this should work for now. |
var params = |
- visitFormalParameterList(node.parameters, destructure: false); |
+ _emitFormalParameterList(node.parameters, destructure: false); |
var fun = new JS.Fun( |
params, |
- js.statement('{ return $newKeyword #(#); }', |
- [_visit(redirect) as JS.Node, params]), |
+ js.statement( |
+ '{ return $newKeyword #(#); }', [_visit(redirect), params]), |
returnType: returnType); |
return annotate( |
new JS.Method(name, fun, isStatic: true), node, node.element); |
} |
- var params = visitFormalParameterList(node.parameters); |
+ var params = _emitFormalParameterList(node.parameters); |
// Factory constructors are essentially static methods. |
if (node.factoryKeyword != null) { |
@@ -2177,7 +2200,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
return js.statement('#.prototype.#.call(this, #);', [ |
new JS.Identifier(cls.name), |
_constructorName(ctor), |
- _visit(node.argumentList) |
+ _emitArgumentList(node.argumentList) |
]); |
} |
@@ -2209,7 +2232,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
var name = _constructorName(superCtor); |
- var args = node != null ? _visit(node.argumentList) : []; |
+ var args = node != null ? _emitArgumentList(node.argumentList) : []; |
return annotate(js.statement('super.#(#);', [name, args]), node); |
} |
@@ -2269,6 +2292,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (init is ConstructorFieldInitializer) { |
var element = init.fieldName.staticElement as FieldElement; |
fields[element] = _visit(init.expression); |
+ } else if (init is AssertInitializer) { |
+ throw new UnimplementedError( |
+ 'Assert initializers are not implemented. ' |
+ 'See https://github.com/dart-lang/sdk/issues/27809'); |
} |
} |
} |
@@ -2390,7 +2417,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
return new JS.Fun([], js.statement('{ return this.#; }', [name])); |
} else if (node.isSetter) { |
var params = |
- visitFormalParameterList(node.parameters, destructure: false); |
+ _emitFormalParameterList(node.parameters, destructure: false); |
return new JS.Fun( |
params, js.statement('{ this.# = #; }', [name, params.last])); |
} else { |
@@ -2450,8 +2477,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
// Rewrite the function to include the return. |
return new JS.Fun( |
fn.params, new JS.Block([body, new JS.Return(fn.params.last)]), |
- typeParams: fn.typeParams, |
- returnType: fn.returnType)..sourceInformation = fn.sourceInformation; |
+ typeParams: fn.typeParams, returnType: fn.returnType) |
+ ..sourceInformation = fn.sourceInformation; |
} |
@override |
@@ -2656,7 +2683,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
// normal function (sync), vs (sync*, async, async*) |
var stdFn = !(element.isAsynchronous || element.isGenerator); |
- var formals = visitFormalParameterList(parameters, destructure: stdFn); |
+ var formals = _emitFormalParameterList(parameters, destructure: stdFn); |
var code = (stdFn) |
? _visit(body) |
: new JS.Block( |
@@ -2709,7 +2736,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
// `await` is generated as `yield`. |
// runtime/_generators.js has an example of what the code is generated as. |
var savedController = _asyncStarController; |
- var jsParams = visitFormalParameterList(parameters); |
+ var jsParams = _emitFormalParameterList(parameters); |
if (kind == 'asyncStar') { |
_asyncStarController = new JS.TemporaryId('stream'); |
jsParams.insert(0, _asyncStarController); |
@@ -2733,7 +2760,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var T = _emitType(returnType); |
return _callHelper('#(#)', [ |
kind, |
- [gen, T]..addAll(visitFormalParameterList(parameters, destructure: false)) |
+ [gen, T]..addAll(_emitFormalParameterList(parameters, destructure: false)) |
]); |
} |
@@ -3381,13 +3408,14 @@ class CodeGenerator extends GeneralizingAstVisitor |
target = new JS.Identifier(element.name); |
} |
- return _visit(rhs).toAssignExpression(annotate(target, node)); |
+ return _visit<JS.Expression>(rhs) |
+ .toAssignExpression(annotate(target, node)); |
} |
/// Emits assignment to library scope element [element]. |
JS.Expression _emitSetTopLevel( |
Expression lhs, Element element, Expression rhs) { |
- return _visit(rhs) |
+ return _visit<JS.Expression>(rhs) |
.toAssignExpression(annotate(_emitTopLevelName(element), lhs)); |
} |
@@ -3402,7 +3430,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var dynType = _emitStaticAccess(type); |
var member = _emitMemberName(element.name, |
isStatic: true, type: type, element: element); |
- return _visit(rhs).toAssignExpression( |
+ return _visit<JS.Expression>(rhs).toAssignExpression( |
annotate(new JS.PropertyAccess(dynType, member), lhs)); |
} |
@@ -3421,7 +3449,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
SimpleIdentifier id, Expression rhs) { |
// TODO(sra): Determine whether and access helper is required for the |
// setter. For now fall back on the r-value path. |
- return _visit(rhs).toAssignExpression(_visit(lhs)); |
+ return _visit<JS.Expression>(rhs).toAssignExpression(_visit(lhs)); |
} |
JS.Expression _emitNullSafeSet(PropertyAccess node, Expression right) { |
@@ -3498,8 +3526,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
if (targetType.isDartCoreFunction || targetType.isDynamic) { |
// TODO(vsm): Can a call method take generic type parameters? |
- return _emitDynamicInvoke(node, _visit(target), |
- _visit(node.argumentList) as List<JS.Expression>); |
+ return _emitDynamicInvoke( |
+ node, _visit(target), _emitArgumentList(node.argumentList)); |
} |
} |
@@ -3507,7 +3535,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { |
- var args = _visit(node.argumentList) as List<JS.Expression>; |
+ var args = _emitArgumentList(node.argumentList); |
var typeArgs = _emitInvokeTypeArguments(node); |
if (target is SuperExpression && !_superAllowed) { |
@@ -3647,7 +3675,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
function = node.function; |
} |
var fn = _visit(function); |
- var args = _visit(node.argumentList) as List<JS.Expression>; |
+ var args = _emitArgumentList(node.argumentList); |
if (isDynamicInvoke(function)) { |
return _emitDynamicInvoke(node, fn, args); |
} else { |
@@ -3796,7 +3824,11 @@ class CodeGenerator extends GeneralizingAstVisitor |
_emitFunctionCall(node); |
@override |
- List<JS.Expression> visitArgumentList(ArgumentList node) { |
+ visitArgumentList(ArgumentList node) { |
+ assert(false); |
vsm
2017/04/14 13:14:39
Should this just be in your unreachable list below
Jennifer Messerly
2017/04/14 17:03:58
yes! oops :) done
|
+ } |
+ |
+ List<JS.Expression> _emitArgumentList(ArgumentList node) { |
var args = <JS.Expression>[]; |
var named = <JS.Property>[]; |
for (var arg in node.arguments) { |
@@ -3822,8 +3854,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
_propertyName(node.name.label.name), _visit(node.expression)); |
} |
- @override |
- List<JS.Parameter> visitFormalParameterList(FormalParameterList node, |
+ List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, |
{bool destructure: true}) { |
if (node == null) return []; |
@@ -3980,10 +4011,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
var v = variables[0]; |
if (v.initializer != null) { |
var name = new JS.Identifier(v.name.name); |
- return _visit(v.initializer).toVariableDeclaration(name); |
+ return _visit<JS.Expression>(v.initializer).toVariableDeclaration(name); |
} |
} |
- return _visit(node.variables).toStatement(); |
+ return _visit<JS.Expression>(node.variables).toStatement(); |
} |
@override |
@@ -4122,7 +4153,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
var classElem = element.enclosingElement; |
isNative = _isJSNative(classElem); |
} |
- var args = _visit(argumentList) as List<JS.Expression>; |
+ var args = _emitArgumentList(argumentList); |
// Native factory constructors are JS constructors - use new here. |
return isFactory && !isNative |
? new JS.Call(ctor, args) |
@@ -4145,7 +4176,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
findAnnotation(classElem, isPublicJSAnnotation) != null; |
JS.Expression _emitObjectLiteral(ArgumentList argumentList) { |
- var args = _visit(argumentList) as List<JS.Expression>; |
+ var args = _emitArgumentList(argumentList); |
if (args.isEmpty) { |
return js.call('{}'); |
} |
@@ -4742,7 +4773,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
// [PropertyAccess]. The code generation for those is handled in their |
// respective visit methods. |
@override |
- JS.Node visitCascadeExpression(CascadeExpression node) { |
+ visitCascadeExpression(CascadeExpression node) { |
var savedCascadeTemp = _cascadeTarget; |
var vars = <JS.MetaLetVariable, JS.Expression>{}; |
@@ -4760,13 +4791,29 @@ class CodeGenerator extends GeneralizingAstVisitor |
_visit(node.expression); |
@override |
- visitFormalParameter(FormalParameter node) { |
+ visitDefaultFormalParameter(DefaultFormalParameter node) { |
+ return _emitParameter(node.element, declaration: true); |
+ } |
+ |
+ JS.Parameter _emitNormalFormalParameter(NormalFormalParameter node) { |
var id = _emitParameter(node.element, declaration: true); |
var isRestArg = findAnnotation(node.element, isJsRestAnnotation) != null; |
return isRestArg ? new JS.RestParameter(id) : id; |
} |
@override |
+ visitSimpleFormalParameter(SimpleFormalParameter node) => |
+ _emitNormalFormalParameter(node); |
+ |
+ @override |
+ visitFieldFormalParameter(FieldFormalParameter node) => |
+ _emitNormalFormalParameter(node); |
+ |
+ @override |
+ visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) => |
+ _emitNormalFormalParameter(node); |
+ |
+ @override |
JS.This visitThisExpression(ThisExpression node) => new JS.This(); |
@override |
@@ -5414,15 +5461,27 @@ class CodeGenerator extends GeneralizingAstVisitor |
@override |
JS.Expression visitStringInterpolation(StringInterpolation node) { |
+ var strings = <String>[]; |
+ var interpolations = <JS.Expression>[]; |
+ |
+ var expectString = true; |
+ for (var e in node.elements) { |
+ if (e is InterpolationString) { |
+ assert(expectString); |
+ expectString = false; |
+ |
+ // Escape the string as necessary for use in the eventual `` quotes. |
+ // TODO(jmesserly): this call adds quotes, and then we strip them off. |
+ var str = js.escapedString(e.value, '`').value; |
+ strings.add(str.substring(1, str.length - 1)); |
+ } else { |
+ assert(!expectString); |
+ expectString = true; |
+ interpolations.add(_visit(e)); |
+ } |
+ } |
return new JS.TaggedTemplate( |
- _callHelper('str'), new JS.TemplateString(_visitList(node.elements))); |
- } |
- |
- @override |
- String visitInterpolationString(InterpolationString node) { |
- // TODO(jmesserly): this call adds quotes, and then we strip them off. |
- var str = js.escapedString(node.value, '`').value; |
- return str.substring(1, str.length - 1); |
+ _callHelper('str'), new JS.TemplateString(strings, interpolations)); |
} |
@override |
@@ -5432,24 +5491,10 @@ class CodeGenerator extends GeneralizingAstVisitor |
@override |
visitBooleanLiteral(BooleanLiteral node) => js.boolean(node.value); |
- @override |
- JS.Expression visitExpression(Expression node) => |
- _unimplementedCall('Unimplemented ${node.runtimeType}: $node'); |
- |
- JS.Expression _unimplementedCall(String comment) { |
- return _callHelper('throw(#)', [js.escapedString(comment)]); |
- } |
- |
- @override |
- visitNode(AstNode node) { |
- // TODO(jmesserly): verify this is unreachable. |
- throw 'Unimplemented ${node.runtimeType}: $node'; |
- } |
- |
- _visit(AstNode node) { |
+ T _visit<T extends JS.Node>(AstNode node) { |
if (node == null) return null; |
var result = node.accept(this); |
- return result is JS.Node ? annotate(result, node) : result; |
+ return result != null ? annotate(result, node) : null; |
} |
// TODO(jmesserly): we should make sure this only returns JS AST nodes. |
@@ -5610,6 +5655,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
} |
var _forwardingCache = new HashMap<Element, Map<String, ExecutableElement>>(); |
+ |
Element _lookupForwardedMember(ClassElement element, String name) { |
// We only care about public methods. |
if (name.startsWith('_')) return null; |
@@ -5837,6 +5883,129 @@ class CodeGenerator extends GeneralizingAstVisitor |
return path.endsWith(".template.dart"); |
} |
+ |
+ _unreachable(AstNode node) { |
+ throw new UnsupportedError( |
+ 'tried to generate an unreachable node: `$node`'); |
+ } |
+ |
+ /// Unused, see methods for emitting declarations. |
+ @override |
+ visitAnnotation(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitFieldInitializers]. |
+ @override |
+ visitAssertInitializer(node) => _unreachable(node); |
+ |
+ /// Not visited, but maybe they should be? |
+ /// See <https://github.com/dart-lang/sdk/issues/29347> |
+ @override |
+ visitComment(node) => _unreachable(node); |
+ |
+ /// Not visited, but maybe they should be? |
+ /// See <https://github.com/dart-lang/sdk/issues/29347> |
+ @override |
+ visitCommentReference(node) => _unreachable(node); |
+ |
+ /// Unused, handled by imports/exports. |
+ @override |
+ visitConfiguration(node) => _unreachable(node); |
+ |
+ /// Unusued, see [_emitConstructor]. |
+ @override |
+ visitConstructorDeclaration(node) => _unreachable(node); |
+ |
+ /// Unusued, see [_emitFieldInitializers]. |
+ @override |
+ visitConstructorFieldInitializer(node) => _unreachable(node); |
+ |
+ /// Unusued. Handled in [visitForEachStatement]. |
+ @override |
+ visitDeclaredIdentifier(node) => _unreachable(node); |
+ |
+ /// Unused, handled by imports/exports. |
+ @override |
+ visitDottedName(node) => _unreachable(node); |
+ |
+ /// Unused, handled by [visitEnumDeclaration]. |
+ @override |
+ visitEnumConstantDeclaration(node) => _unreachable(node); // see |
+ |
+ /// Unused, see [_emitClassHeritage]. |
+ @override |
+ visitExtendsClause(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitFormalParameterList]. |
+ @override |
+ visitFormalParameterList(node) => _unreachable(node); |
+ |
+ /// Unused, handled by imports/exports. |
+ @override |
+ visitShowCombinator(node) => _unreachable(node); |
+ |
+ /// Unused, handled by imports/exports. |
+ @override |
+ visitHideCombinator(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitClassHeritage]. |
+ @override |
+ visitImplementsClause(node) => _unreachable(node); |
+ |
+ /// Unused, handled by [visitStringInterpolation]. |
+ @override |
+ visitInterpolationString(node) => _unreachable(node); |
+ |
+ /// Unused, labels are handled by containing statements. |
+ @override |
+ visitLabel(node) => _unreachable(node); |
+ |
+ /// Unused, handled by imports/exports. |
+ @override |
+ visitLibraryIdentifier(node) => _unreachable(node); |
+ |
+ /// Unused, see [visitMapLiteral]. |
+ @override |
+ visitMapLiteralEntry(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitMethodDeclaration]. |
+ @override |
+ visitMethodDeclaration(node) => _unreachable(node); |
+ |
+ /// Unused, these are not visited. |
+ @override |
+ visitNativeClause(node) => _unreachable(node); |
+ |
+ /// Unused, these are not visited. |
+ @override |
+ visitNativeFunctionBody(node) => _unreachable(node); |
+ |
+ /// Unused, handled by [_emitConstructor]. |
+ @override |
+ visitSuperConstructorInvocation(node) => _unreachable(node); |
+ |
+ /// Unused, this can be handled when emitting the module if needed. |
+ @override |
+ visitScriptTag(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitType]. |
+ @override |
+ visitTypeArgumentList(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitType]. |
+ @override |
+ visitTypeParameter(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitType]. |
+ @override |
+ visitGenericFunctionType(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitType]. |
+ @override |
+ visitTypeParameterList(node) => _unreachable(node); |
+ |
+ /// Unused, see [_emitClassHeritage]. |
+ @override |
+ visitWithClause(node) => _unreachable(node); |
} |
/// Choose a canonical name from the [library] element. |