Chromium Code Reviews| Index: lib/src/codegen/js_codegen.dart | 
| diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart | 
| index 0dd62fd6d66a560036728d90a15671685a87c050..723102570e699d9dd93854eae83186da7a8f3c06 100644 | 
| --- a/lib/src/codegen/js_codegen.dart | 
| +++ b/lib/src/codegen/js_codegen.dart | 
| @@ -926,9 +926,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 = _visitFormalParameterList( | 
| + node.parameters, allowDestructuring: false); | 
| 
 
Jennifer Messerly
2015/12/01 02:10:27
yeah, FYI, this makes total sense.
 
ochafik
2015/12/02 20:05:46
Acknowledged.
 
 | 
| + | 
| 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); | 
| @@ -1185,25 +1187,31 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { | 
| if (parameters == null) return null; | 
| var body = <JS.Statement>[]; | 
| + | 
| + Map<FormalParameter, JS.Identifier> paramNames = | 
| 
 
Jennifer Messerly
2015/12/01 02:10:27
is this change still needed? the map doesn't seem
 
ochafik
2015/12/02 20:05:46
Indeed, removed :-)
 
 | 
| + new Map.fromIterable(parameters.parameters, | 
| + value: (param) => _visit(param.identifier)); | 
| + | 
| for (var param in parameters.parameters) { | 
| - var jsParam = _visit(param.identifier); | 
| + var jsParam = paramNames[param]; | 
| 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)])); | 
| @@ -1354,17 +1362,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]); | 
| @@ -1905,15 +1918,71 @@ class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { | 
| _propertyName(node.name.label.name), _visit(node.expression)); | 
| } | 
| + static final Set<String> _jsObjectProperties = new Set<String>()..addAll([ | 
| 
 
Jennifer Messerly
2015/12/01 02:10:27
it'd be good to cite where this list comes from.
 
ochafik
2015/12/02 20:05:46
Done.
 
 | 
| + "constructor", | 
| + "toString", | 
| + "toLocaleString", | 
| + "valueOf", | 
| + "hasOwnProperty", | 
| + "isPrototypeOf", | 
| + "propertyIsEnumerable", | 
| + "__defineGetter__", | 
| + "__lookupGetter__", | 
| + "__defineSetter__", | 
| + "__lookupSetter__", | 
| + "__proto__" | 
| + ]); | 
| + | 
| + bool _isNamedParam(FormalParameter param) => | 
| + param.kind == ParameterKind.NAMED; | 
| + | 
| + bool _isDestructurableNamedParam(FormalParameter param) { | 
| 
 
Jennifer Messerly
2015/12/01 02:10:27
I think we can allow destructing always and just u
 
ochafik
2015/12/02 20:05:46
Oh sweet, thanks! Done. (removes lots of nonsense!
 
 | 
| + var name = param.identifier.name; | 
| + return _isNamedParam(param) && | 
| + !_jsObjectProperties.contains(name) && | 
| + name != 'super'; | 
| + } | 
| + | 
| + _namedParams(FormalParameterList list) => | 
| + list.parameters.where(_isNamedParam); | 
| + | 
| + bool _willDestructureNamedParams(FormalParameterList list) { | 
| + var named = _namedParams(list).toList(growable: false); | 
| + return named.isNotEmpty && named.every(_isDestructurableNamedParam); | 
| + } | 
| + | 
| @override | 
| - List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 
| + List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => | 
| + _visitFormalParameterList(node, allowDestructuring: true); | 
| + | 
| + List<JS.Parameter> _visitFormalParameterList( | 
| 
 
Jennifer Messerly
2015/12/01 02:10:27
for better or worse, we have an "_emit" naming con
 
ochafik
2015/12/02 20:05:46
Done.
 
 | 
| + FormalParameterList node, {bool allowDestructuring}) { | 
| + | 
| var result = <JS.Parameter>[]; | 
| + | 
| + var namedVars = <JS.DestructuredVariable>[]; | 
| + var canDestructure = allowDestructuring && _willDestructureNamedParams(node); | 
| 
 
Jennifer Messerly
2015/12/01 02:10:27
long line
 
ochafik
2015/12/02 20:05:46
Acknowledged.
 
 | 
| + var needsOpts = false; | 
| + | 
| for (FormalParameter param in node.parameters) { | 
| if (param.kind == ParameterKind.NAMED) { | 
| - result.add(_namedArgTemp); | 
| - break; | 
| + if (canDestructure) { | 
| + 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 (canDestructure) { | 
| + result.add(new JS.DestructuredVariable( | 
| + structure: new JS.ObjectDestructuring(namedVars), | 
| + defaultValue: js.call('{}'))); | 
| + } else if (needsOpts) { | 
| + result.add(_namedArgTemp); | 
| } | 
| return result; | 
| } |