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

Unified Diff: lib/src/codegen/js_codegen.dart

Issue 1729933002: simplify generation of functions (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 10 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 | « no previous file | test/codegen/expect/destructuring.js » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/codegen/js_codegen.dart
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart
index bdd9bf2209b5a47371d2cbbaed32cc32eff86bf1..e365f91060c3acf128389091de8651711455e9ed 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/codegen/js_codegen.dart
@@ -430,7 +430,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
var classExpr = new JS.ClassExpression(new JS.Identifier(type.name),
_classHeritage(classElem), _emitClassMethods(node, ctors, fields),
- typeParams: _emitTypeParams(classElem).toList(),
+ typeParams: _emitTypeFormals(classElem.typeParameters),
fields:
_emitFieldDeclarations(classElem, fields, staticFields).toList());
@@ -473,11 +473,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return result;
}
- Iterable<JS.Identifier> _emitTypeParams(TypeParameterizedElement e) sync* {
- if (!options.closure) return;
- for (var typeParam in e.typeParameters) {
- yield new JS.Identifier(typeParam.name);
- }
+ List<JS.Identifier> _emitTypeFormals(List<TypeParameterElement> typeFormals) {
+ if (!options.closure) return null;
+ return typeFormals
+ .map((t) => new JS.Identifier(t.name))
+ .toList(growable: false);
}
/// Emit field declarations for TypeScript & Closure's ES6_TYPED
@@ -1057,7 +1057,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// TODO(jmesserly): we'll need something different once we have
// rest/spread support, but this should work for now.
var params =
- _emitFormalParameterList(node.parameters, allowDestructuring: false);
+ visitFormalParameterList(node.parameters, destructure: false);
var fun = new JS.Fun(
params,
@@ -1072,7 +1072,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// available for use by top-level constant initializers.
ClassDeclaration cls = node.parent;
if (node.constKeyword != null) _loader.startTopLevel(cls.element);
- var params = _emitFormalParameterList(node.parameters);
+ var params = visitFormalParameterList(node.parameters);
if (node.constKeyword != null) _loader.finishTopLevel(cls.element);
// Factory constructors are essentially static methods.
@@ -1378,13 +1378,13 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
}
}
- JS.Fun _emitNativeFunctionBody(List<JS.Parameter> params,
- List<JS.Expression> paramRefs, MethodDeclaration node) {
+ JS.Fun _emitNativeFunctionBody(MethodDeclaration node) {
if (node.isStatic) {
// TODO(vsm): Do we need to handle this case?
return null;
}
+ var params = visitFormalParameterList(node.parameters, destructure: false);
String name = node.name.name;
var annotation = findAnnotation(node.element, isJsName);
if (annotation != null) {
@@ -1395,10 +1395,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return new JS.Fun(params, js.statement('{ return this.#; }', [name]));
} else if (node.isSetter) {
return new JS.Fun(
- params, js.statement('{ this.# = #; }', [name, paramRefs.last]));
+ params, js.statement('{ this.# = #; }', [name, params.last]));
} else {
return new JS.Fun(
- params, js.statement('{ return this.#(#); }', [name, paramRefs]));
+ params, js.statement('{ return this.#(#); }', [name, params]));
}
}
@@ -1407,40 +1407,21 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
return null;
}
- var params = _visit(node.parameters) as List<JS.Parameter>;
- if (params == null) params = <JS.Parameter>[];
- var paramRefs = _emitParameterReferences(node.parameters);
-
JS.Fun fn;
if (_externalOrNative(node)) {
- fn = _emitNativeFunctionBody(params, paramRefs, node);
+ fn = _emitNativeFunctionBody(node);
// TODO(vsm): Remove if / when we handle the static case above.
if (fn == null) return null;
} else {
- var typeParams = _emitTypeParams(node.element).toList();
- var returnType = emitTypeRef(node.element.returnType);
- fn = _emitFunctionBody(
- params, paramRefs, node.body, typeParams, returnType);
- }
-
- if (node.operatorKeyword != null &&
- node.name.name == '[]=' &&
- params.isNotEmpty) {
- // []= methods need to return the value. We could also address this at
- // call sites, but it's cleaner to instead transform the operator method.
- var returnValue = new JS.Return(params.last);
- var body = fn.body;
- if (JS.Return.foundIn(fn)) {
- // If a return is inside body, transform `(params) { body }` to
- // `(params) { (() => { body })(); return value; }`.
- // TODO(jmesserly): we could instead generate the return differently,
- // and avoid the immediately invoked function.
- body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement();
- }
- // Rewrite the function to include the return.
- fn = new JS.Fun(fn.params, new JS.Block([body, returnValue]),
- typeParams: fn.typeParams,
- returnType: fn.returnType)..sourceInformation = fn.sourceInformation;
+ fn = _emitFunctionBody(node.element, node.parameters, node.body);
+
+ if (node.operatorKeyword != null &&
+ node.name.name == '[]=' &&
+ fn.params.isNotEmpty) {
+ // []= methods need to return the value. We could also address this at
+ // call sites, but it's cleaner to instead transform the operator method.
+ fn = _alwaysReturnLastParameter(fn);
+ }
}
return annotate(
@@ -1452,6 +1433,26 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
node.element);
}
+ /// Transform the function so the last parameter is always returned.
+ ///
+ /// This is useful for indexed set methods, which otherwise would not have
+ /// the right return value in JS.
+ JS.Fun _alwaysReturnLastParameter(JS.Fun fn) {
+ var body = fn.body;
+ if (JS.Return.foundIn(fn)) {
+ // If a return is inside body, transform `(params) { body }` to
+ // `(params) { (() => { body })(); return value; }`.
+ // TODO(jmesserly): we could instead generate the return differently,
+ // and avoid the immediately invoked function.
+ body = new JS.Call(new JS.ArrowFun([], fn.body), []).toStatement();
+ }
+ // 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;
+ }
+
@override
JS.Statement visitFunctionDeclaration(FunctionDeclaration node) {
assert(node.parent is CompilationUnit);
@@ -1580,80 +1581,76 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
@override
JS.Expression visitFunctionExpression(FunctionExpression node) {
- var params = _visit(node.parameters) as List<JS.Parameter>;
- if (params == null) params = <JS.Parameter>[];
+ var params = visitFormalParameterList(node.parameters);
+ var body = node.body;
var parent = node.parent;
var inStmt = parent.parent is FunctionDeclarationStatement;
- var typeParams = _emitTypeParams(node.element).toList();
- var returnType = emitTypeRef(node.element.returnType);
if (parent is FunctionDeclaration) {
- var paramRefs = _emitParameterReferences(node.parameters);
- return _emitFunctionBody(
- params, paramRefs, node.body, typeParams, returnType);
+ return _emitFunctionBody(node.element, node.parameters, body);
+ }
+
+ JS.Node jsBody;
+ if (body.isGenerator || body.isAsynchronous) {
+ jsBody = _emitGeneratorFunctionBody(node.element, node.parameters, body);
+ } else if (body is ExpressionFunctionBody) {
+ jsBody = _visit(body.expression);
+ } else {
+ jsBody = _visit(body);
+ }
+
+ var type = node.element.type;
+ var typeFormals = _emitTypeFormals(type.typeFormals);
+ var returnType = emitTypeRef(type.returnType);
+
+ JS.FunctionExpression fn;
+ if (node.parameters.parameters
+ .every((p) => p.kind != ParameterKind.NAMED)) {
+ fn = new JS.ArrowFun(params, jsBody,
+ typeParams: typeFormals, returnType: returnType);
} else {
// Chrome Canary does not accept default values with destructuring in
// arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them
// with regular functions (e.g. `function({a} = {}) { return 1 }`).
// Note that Firefox accepts both syntaxes just fine.
// TODO(ochafik): Simplify this code when Chrome Canary catches up.
- var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam);
-
- JS.Node jsBody;
- var body = node.body;
- if (body.isGenerator || body.isAsynchronous) {
- var paramRefs = _emitParameterReferences(node.parameters);
- jsBody =
- _emitGeneratorFunctionBody(params, paramRefs, body, returnType);
- } else if (body is ExpressionFunctionBody) {
- jsBody = _visit(body.expression);
- } else {
- jsBody = _visit(body);
- }
- if (jsBody is JS.Expression && !canUseArrowFun) {
+
+ if (jsBody is JS.Expression) {
jsBody = js.statement("{ return #; }", [jsBody]);
}
- var clos = canUseArrowFun
- ? new JS.ArrowFun(params, jsBody,
- typeParams: typeParams, returnType: returnType)
- : new JS.Fun(params, jsBody,
- typeParams: typeParams, returnType: returnType);
- if (!inStmt) {
- var type = getStaticType(node);
- return _emitFunctionTagged(clos, type,
- topLevel: _executesAtTopLevel(node));
- }
- return clos;
+ fn = new JS.Fun(params, jsBody,
+ typeParams: typeFormals, returnType: returnType);
+ }
+
+ if (!inStmt) {
+ var type = getStaticType(node);
+ return _emitFunctionTagged(fn, type, topLevel: _executesAtTopLevel(node));
}
+ return fn;
}
- JS.Fun _emitFunctionBody(
- List<JS.Parameter> params,
- List<JS.Expression> paramRefs,
- FunctionBody body,
- List<JS.Identifier> typeParams,
- JS.TypeRef returnType) {
+ JS.Fun _emitFunctionBody(ExecutableElement element,
+ FormalParameterList parameters, FunctionBody body) {
+ var returnType = emitTypeRef(element.returnType);
+
// sync*, async, async*
- if (body.isAsynchronous || body.isGenerator) {
- // TODO(ochafik): Refine params: we don't need default values in the
- // nested function, so we'd need to generate a custom, simpler params
- // list here.
+ if (element.isAsynchronous || element.isGenerator) {
return new JS.Fun(
- params,
- js.statement('{ return #; }', [
- _emitGeneratorFunctionBody(params, paramRefs, body, returnType)
- ]),
+ visitFormalParameterList(parameters, destructure: false),
+ js.statement('{ return #; }',
+ [_emitGeneratorFunctionBody(element, parameters, body)]),
returnType: returnType);
}
// normal function (sync)
- return new JS.Fun(params, _visit(body),
- typeParams: typeParams, returnType: returnType);
+ return new JS.Fun(visitFormalParameterList(parameters), _visit(body),
+ typeParams: _emitTypeFormals(element.typeParameters),
+ returnType: returnType);
}
- JS.Expression _emitGeneratorFunctionBody(List<JS.Parameter> params,
- List<JS.Expression> paramRefs, FunctionBody body, JS.TypeRef returnType) {
- var kind = body.isSynchronous ? 'sync' : 'async';
- if (body.isGenerator) kind += 'Star';
+ JS.Expression _emitGeneratorFunctionBody(ExecutableElement element,
+ FormalParameterList parameters, FunctionBody body) {
+ var kind = element.isSynchronous ? 'sync' : 'async';
+ if (element.isGenerator) kind += 'Star';
// Transforms `sync*` `async` and `async*` function bodies
// using ES6 generators.
@@ -1687,25 +1684,28 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// `await` is generated as `yield`.
// runtime/_generators.js has an example of what the code is generated as.
var savedController = _asyncStarController;
- List jsParams;
+ List jsParams = visitFormalParameterList(parameters);
if (kind == 'asyncStar') {
_asyncStarController = new JS.TemporaryId('stream');
- jsParams = [_asyncStarController]..addAll(params);
+ jsParams.insert(0, _asyncStarController);
} else {
_asyncStarController = null;
- jsParams = params;
}
- JS.Expression gen = new JS.Fun(jsParams, _visit(body),
- isGenerator: true, returnType: returnType);
+ // Visit the body with our async* controller set.
+ var jsBody = _visit(body);
+ _asyncStarController = savedController;
+
+ DartType returnType = _getExpectedReturnType(element);
+ JS.Expression gen = new JS.Fun(jsParams, jsBody,
+ isGenerator: true, returnType: emitTypeRef(returnType));
if (JS.This.foundIn(gen)) {
gen = js.call('#.bind(this)', gen);
}
- _asyncStarController = savedController;
- var T = _emitTypeName(_getExpectedReturnType(body));
+ var T = _emitTypeName(returnType);
return js.call('dart.#(#)', [
kind,
- [gen, T]..addAll(paramRefs)
+ [gen, T]..addAll(visitFormalParameterList(parameters, destructure: false))
]);
}
@@ -2210,29 +2210,15 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
_propertyName(node.name.label.name), _visit(node.expression));
}
- bool _isNamedParam(FormalParameter param) =>
- param.kind == ParameterKind.NAMED;
-
@override
- List<JS.Parameter> visitFormalParameterList(FormalParameterList node) =>
- _emitFormalParameterList(node);
+ List<JS.Parameter> visitFormalParameterList(FormalParameterList node,
+ {bool destructure: true}) {
+ if (node == null) return [];
- // TODO(ochafik): Decouple Parameter from Identifier.
- List<JS.Expression> _emitParameterReferences(FormalParameterList node) =>
- node == null
- ? <JS.Expression>[]
- : _emitFormalParameterList(node, allowDestructuring: false)
- .map((JS.Parameter p) {
- if (p is JS.RestParameter) return new JS.Spread(p.parameter);
- return p as JS.Identifier;
- }).toList();
+ destructure = destructure && options.destructureNamedParams;
- List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
- {bool allowDestructuring: true}) {
var result = <JS.Parameter>[];
-
var namedVars = <JS.DestructuredVariable>[];
- var destructure = allowDestructuring && options.destructureNamedParams;
var hasNamedArgsConflictingWithObjectProperties = false;
var needsOpts = false;
@@ -3683,23 +3669,16 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
// TODO(leafp): Various analyzer pieces computed similar things.
// Share this logic somewhere?
- DartType _getExpectedReturnType(FunctionBody body) {
- FunctionType functionType;
- var parent = body.parent;
- if (parent is Declaration) {
- functionType = (parent.element as dynamic)?.type;
- } else {
- assert(parent is FunctionExpression);
- functionType = parent.staticType;
- }
+ DartType _getExpectedReturnType(ExecutableElement element) {
+ FunctionType functionType = element.type;
if (functionType == null) {
return DynamicTypeImpl.instance;
}
var type = functionType.returnType;
InterfaceType expectedType = null;
- if (body.isAsynchronous) {
- if (body.isGenerator) {
+ if (element.isAsynchronous) {
+ if (element.isGenerator) {
// Stream<T> -> T
expectedType = _types.streamType;
} else {
@@ -3708,7 +3687,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor
expectedType = _types.futureType;
}
} else {
- if (body.isGenerator) {
+ if (element.isGenerator) {
// Iterable<T> -> T
expectedType = _types.iterableType;
} else {
« no previous file with comments | « no previous file | test/codegen/expect/destructuring.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698