Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Unified Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

Issue 2822633003: fix #29346, ensure all nodes are implemented by DDC's code generator (Closed)
Patch Set: fix Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/lib/src/js_ast/nodes.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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.
« no previous file with comments | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/lib/src/js_ast/nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698