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

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

Issue 1484263002: Use destructuring assignments for named parameters (#180) (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years 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 | « lib/src/closure/closure_annotator.dart ('k') | lib/src/js/builder.dart » ('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 91b8c018766099a7ae1822a9b7501dbd96a481d4..7f0f5b4b43d4bea3aec0470587e3267d247e99b7 100644
--- a/lib/src/codegen/js_codegen.dart
+++ b/lib/src/codegen/js_codegen.dart
@@ -35,6 +35,7 @@ import 'js_interop.dart';
import 'js_names.dart' as JS;
import 'js_metalet.dart' as JS;
import 'js_module_item_order.dart';
+import 'js_names.dart';
import 'js_printer.dart' show writeJsLibrary;
import 'side_effect_analysis.dart';
import 'package:collection/equality.dart';
@@ -942,9 +943,11 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
// Pass along all arguments verbatim, and let the callee handle them.
// TODO(jmesserly): we'll need something different once we have
// rest/spread support, but this should work for now.
- var params = _visit(node.parameters);
+ var params =
+ _emitFormalParameterList(node.parameters, allowDestructuring: false);
+
var fun = js.call('function(#) { return $newKeyword #(#); }',
- [params, _visit(redirect), params,]) as JS.Fun;
+ [params, _visit(redirect), params]) as JS.Fun;
return annotate(
new JS.Method(name, fun, isStatic: true)..sourceInformation = node,
node.element);
@@ -1205,21 +1208,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
var jsParam = _visit(param.identifier);
if (param.kind == ParameterKind.NAMED) {
- // Parameters will be passed using their real names, not the (possibly
- // renamed) local variable.
- var paramName = js.string(param.identifier.name, "'");
-
- // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming
- // (either test if `obj.prop !== void 0`, or use JSCompiler_renameProperty).
- body.add(js.statement('let # = # && # in # ? #.# : #;', [
- jsParam,
- _namedArgTemp,
- paramName,
- _namedArgTemp,
- _namedArgTemp,
- paramName,
- _defaultParamValue(param),
- ]));
+ if (!_isDestructurableNamedParam(param)) {
+ // Parameters will be passed using their real names, not the (possibly
+ // renamed) local variable.
+ var paramName = js.string(param.identifier.name, "'");
+
+ // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
+ body.add(js.statement('let # = # && # in # ? #.# : #;', [
+ jsParam,
+ _namedArgTemp,
+ paramName,
+ _namedArgTemp,
+ _namedArgTemp,
+ paramName,
+ _defaultParamValue(param),
+ ]));
+ }
} else if (param.kind == ParameterKind.POSITIONAL) {
body.add(js.statement('if (# === void 0) # = #;',
[jsParam, jsParam, _defaultParamValue(param)]));
@@ -1422,17 +1426,22 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
if (parent is FunctionDeclaration) {
return _emitFunctionBody(params, node.body);
} else {
- String code;
+ // 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);
+
+ String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }';
JS.Node jsBody;
var body = node.body;
if (body.isGenerator || body.isAsynchronous) {
- code = '(#) => #';
jsBody = _emitGeneratorFunctionBody(params, body);
} else if (body is ExpressionFunctionBody) {
- code = '(#) => #';
jsBody = _visit(body.expression);
} else {
- code = '(#) => { #; }';
+ code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }';
jsBody = _visit(body);
}
var clos = js.call(code, [params, jsBody]);
@@ -1973,19 +1982,80 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator {
_propertyName(node.name.label.name), _visit(node.expression));
}
+ bool _isNamedParam(FormalParameter param) =>
+ param.kind == ParameterKind.NAMED;
+
+ /// We cannot destructure named params that clash with JS reserved names:
+ /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392.
+ bool _isDestructurableNamedParam(FormalParameter param) =>
+ _isNamedParam(param) && !invalidVariableName(param.identifier.name);
+
@override
- List<JS.Parameter> visitFormalParameterList(FormalParameterList node) {
+ List<JS.Parameter> visitFormalParameterList(FormalParameterList node) =>
+ _emitFormalParameterList(node);
+
+ List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
+ {bool allowDestructuring: true}) {
var result = <JS.Parameter>[];
+
+ var namedVars = <JS.DestructuredVariable>[];
+ var destructure = allowDestructuring &&
+ node.parameters.where(_isNamedParam).every(_isDestructurableNamedParam);
+ var hasNamedArgsConflictingWithObjectProperties = false;
+ var needsOpts = false;
+
for (FormalParameter param in node.parameters) {
if (param.kind == ParameterKind.NAMED) {
- result.add(_namedArgTemp);
- break;
+ if (destructure) {
+ if (_jsObjectProperties.contains(param.identifier.name)) {
+ hasNamedArgsConflictingWithObjectProperties = true;
+ }
+ namedVars.add(new JS.DestructuredVariable(
+ name: _visit(param.identifier),
+ defaultValue: _defaultParamValue(param)));
+ } else {
+ needsOpts = true;
+ }
+ } else {
+ result.add(_visit(param));
}
- result.add(_visit(param));
+ }
+
+ if (needsOpts) {
+ result.add(_namedArgTemp);
+ } else if (namedVars.isNotEmpty) {
+ // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so
+ // in case there are conflicting names we create an object without
+ // any prototype.
+ var defaultOpts = hasNamedArgsConflictingWithObjectProperties
+ ? js.call('Object.create(null)')
+ : js.call('{}');
+ result.add(new JS.DestructuredVariable(
+ structure: new JS.ObjectBindingPattern(namedVars),
+ defaultValue: defaultOpts));
}
return result;
}
+ /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`):
+ /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-object-prototype-object
+ /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties-of-the-object.prototype-object
+ static final Set<String> _jsObjectProperties = new Set<String>()
+ ..addAll([
+ "constructor",
+ "toString",
+ "toLocaleString",
+ "valueOf",
+ "hasOwnProperty",
+ "isPrototypeOf",
+ "propertyIsEnumerable",
+ "__defineGetter__",
+ "__lookupGetter__",
+ "__defineSetter__",
+ "__lookupSetter__",
+ "__proto__"
+ ]);
+
@override
JS.Statement visitExpressionStatement(ExpressionStatement node) =>
_visit(node.expression).toStatement();
« no previous file with comments | « lib/src/closure/closure_annotator.dart ('k') | lib/src/js/builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698