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; |
} |