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

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1677863002: Use default params when --destructure-named-params + fix renaming of reserved destructured params (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 unified diff | Download patch
« no previous file with comments | « no previous file | lib/src/js/nodes.dart » ('j') | lib/src/js/printer.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; 5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet;
6 6
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
8 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; 8 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator;
9 import 'package:analyzer/src/generated/constant.dart'; 9 import 'package:analyzer/src/generated/constant.dart';
10 import 'package:analyzer/src/generated/element.dart'; 10 import 'package:analyzer/src/generated/element.dart';
(...skipping 945 matching lines...) Expand 10 before | Expand all | Expand 10 after
956 var params = 956 var params =
957 _emitFormalParameterList(node.parameters, allowDestructuring: false); 957 _emitFormalParameterList(node.parameters, allowDestructuring: false);
958 958
959 var fun = js.call('function(#) { return $newKeyword #(#); }', 959 var fun = js.call('function(#) { return $newKeyword #(#); }',
960 [params, _visit(redirect), params]) as JS.Fun; 960 [params, _visit(redirect), params]) as JS.Fun;
961 return annotate( 961 return annotate(
962 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 962 new JS.Method(name, fun, isStatic: true)..sourceInformation = node,
963 node.element); 963 node.element);
964 } 964 }
965 965
966 // For const constructors we need to ensure default values are
967 // available for use by top-level constant initializers.
968 if (node.constKeyword != null)
Jennifer Messerly 2016/02/09 00:33:07 nit: use curly here. alternate idea: save node.el
ochafik 2016/02/09 01:51:19 Done (extracted the ClassDeclaration to a local va
969 _loader.startTopLevel(node.element.enclosingElement);
970 var params = _emitFormalParameterList(node.parameters);
971 if (node.constKeyword != null)
972 _loader.finishTopLevel(node.element.enclosingElement);
973
966 // Factory constructors are essentially static methods. 974 // Factory constructors are essentially static methods.
967 if (node.factoryKeyword != null) { 975 if (node.factoryKeyword != null) {
968 var body = <JS.Statement>[]; 976 var body = <JS.Statement>[];
969 var init = _emitArgumentInitializers(node, constructor: true); 977 var init = _emitArgumentInitializers(node, constructor: true);
970 if (init != null) body.add(init); 978 if (init != null) body.add(init);
971 body.add(_visit(node.body)); 979 body.add(_visit(node.body));
972 var fun = new JS.Fun( 980 var fun = new JS.Fun(params, new JS.Block(body));
973 _visit(node.parameters) as List<JS.Parameter>, new JS.Block(body));
974 return annotate( 981 return annotate(
975 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, 982 new JS.Method(name, fun, isStatic: true)..sourceInformation = node,
976 node.element); 983 node.element);
977 } 984 }
978 985
979 // Code generation for Object's constructor. 986 // Code generation for Object's constructor.
980 JS.Block body; 987 JS.Block body;
981 if (isObject && 988 if (isObject &&
982 node.body is EmptyFunctionBody && 989 node.body is EmptyFunctionBody &&
983 node.constKeyword != null && 990 node.constKeyword != null &&
(...skipping 18 matching lines...) Expand all
1002 return result === void 0 ? this : result; 1009 return result === void 0 ? this : result;
1003 }''') as JS.Block; 1010 }''') as JS.Block;
1004 } else { 1011 } else {
1005 body = _emitConstructorBody(node, fields); 1012 body = _emitConstructorBody(node, fields);
1006 } 1013 }
1007 1014
1008 // We generate constructors as initializer methods in the class; 1015 // We generate constructors as initializer methods in the class;
1009 // this allows use of `super` for instance methods/properties. 1016 // this allows use of `super` for instance methods/properties.
1010 // It also avoids V8 restrictions on `super` in default constructors. 1017 // It also avoids V8 restrictions on `super` in default constructors.
1011 return annotate( 1018 return annotate(
1012 new JS.Method(name, 1019 new JS.Method(name, new JS.Fun(params, body))..sourceInformation = node,
1013 new JS.Fun(_visit(node.parameters) as List<JS.Parameter>, body))
1014 ..sourceInformation = node,
1015 node.element); 1020 node.element);
1016 } 1021 }
1017 1022
1018 JS.Expression _constructorName(ConstructorElement ctor) { 1023 JS.Expression _constructorName(ConstructorElement ctor) {
1019 var name = ctor.name; 1024 var name = ctor.name;
1020 if (name != '') { 1025 if (name != '') {
1021 return _emitMemberName(name, isStatic: true); 1026 return _emitMemberName(name, isStatic: true);
1022 } 1027 }
1023 1028
1024 // Factory default constructors use `new` as their name, for readability 1029 // Factory default constructors use `new` as their name, for readability
1025 // Other default constructors use the class name, as they aren't called 1030 // Other default constructors use the class name, as they aren't called
1026 // from call sites, but rather from Object's constructor. 1031 // from call sites, but rather from Object's constructor.
1027 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning 1032 // TODO(jmesserly): revisit in the context of Dart metaclasses, and cleaning
1028 // up constructors to integrate more closely with ES6. 1033 // up constructors to integrate more closely with ES6.
1029 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name); 1034 return _propertyName(ctor.isFactory ? 'new' : ctor.enclosingElement.name);
1030 } 1035 }
1031 1036
1032 JS.Block _emitConstructorBody( 1037 JS.Block _emitConstructorBody(
1033 ConstructorDeclaration node, List<FieldDeclaration> fields) { 1038 ConstructorDeclaration node, List<FieldDeclaration> fields) {
1034 var body = <JS.Statement>[]; 1039 var body = <JS.Statement>[];
1035 1040
1036 // Generate optional/named argument value assignment. These can not have 1041 // Generate optional/named argument value assignment. These can not have
1037 // side effects, and may be used by the constructor's initializers, so it's 1042 // side effects, and may be used by the constructor's initializers, so it's
1038 // nice to do them first. 1043 // nice to do them first.
1039 // Also for const constructors we need to ensure default values are
1040 // available for use by top-level constant initializers.
1041 ClassDeclaration cls = node.parent; 1044 ClassDeclaration cls = node.parent;
1042 if (node.constKeyword != null) _loader.startTopLevel(cls.element); 1045 if (node.constKeyword != null) _loader.startTopLevel(cls.element);
1043 var init = _emitArgumentInitializers(node, constructor: true); 1046 var init = _emitArgumentInitializers(node, constructor: true);
1044 if (node.constKeyword != null) _loader.finishTopLevel(cls.element); 1047 if (node.constKeyword != null) _loader.finishTopLevel(cls.element);
1045 if (init != null) body.add(init); 1048 if (init != null) body.add(init);
1046 1049
1047 // Redirecting constructors: these are not allowed to have initializers, 1050 // Redirecting constructors: these are not allowed to have initializers,
1048 // and the redirecting ctor invocation runs before field initializers. 1051 // and the redirecting ctor invocation runs before field initializers.
1049 var redirectCall = node.initializers.firstWhere( 1052 var redirectCall = node.initializers.firstWhere(
1050 (i) => i is RedirectingConstructorInvocation, 1053 (i) => i is RedirectingConstructorInvocation,
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
1214 if (node is ConstructorDeclaration != constructor) return null; 1217 if (node is ConstructorDeclaration != constructor) return null;
1215 1218
1216 var parameters = _parametersOf(node); 1219 var parameters = _parametersOf(node);
1217 if (parameters == null) return null; 1220 if (parameters == null) return null;
1218 1221
1219 var body = <JS.Statement>[]; 1222 var body = <JS.Statement>[];
1220 for (var param in parameters.parameters) { 1223 for (var param in parameters.parameters) {
1221 var jsParam = _visit(param.identifier); 1224 var jsParam = _visit(param.identifier);
1222 1225
1223 if (param.kind == ParameterKind.NAMED) { 1226 if (param.kind == ParameterKind.NAMED) {
1224 if (!_isDestructurableNamedParam(param)) { 1227 if (!options.destructureNamedParams) {
1225 // Parameters will be passed using their real names, not the (possibly 1228 // Parameters will be passed using their real names, not the (possibly
1226 // renamed) local variable. 1229 // renamed) local variable.
1227 var paramName = js.string(param.identifier.name, "'"); 1230 var paramName = js.string(param.identifier.name, "'");
1228 1231
1229 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. 1232 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming.
1230 body.add(js.statement('let # = # && # in # ? #.# : #;', [ 1233 body.add(js.statement('let # = # && # in # ? #.# : #;', [
1231 jsParam, 1234 jsParam,
1232 _namedArgTemp, 1235 _namedArgTemp,
1233 paramName, 1236 paramName,
1234 _namedArgTemp, 1237 _namedArgTemp,
1235 _namedArgTemp, 1238 _namedArgTemp,
1236 paramName, 1239 paramName,
1237 _defaultParamValue(param), 1240 _defaultParamValue(param),
1238 ])); 1241 ]));
1239 } 1242 }
1240 } else if (param.kind == ParameterKind.POSITIONAL) { 1243 } else if (param.kind == ParameterKind.POSITIONAL &&
1244 !options.destructureNamedParams) {
1241 body.add(js.statement('if (# === void 0) # = #;', 1245 body.add(js.statement('if (# === void 0) # = #;',
1242 [jsParam, jsParam, _defaultParamValue(param)])); 1246 [jsParam, jsParam, _defaultParamValue(param)]));
1243 } 1247 }
1244 1248
1245 // TODO(jmesserly): various problems here, see: 1249 // TODO(jmesserly): various problems here, see:
1246 // https://github.com/dart-lang/dev_compiler/issues/161 1250 // https://github.com/dart-lang/dev_compiler/issues/161
1247 var paramType = param.element.type; 1251 var paramType = param.element.type;
1248 if (!constructor && _hasUnsoundTypeParameter(paramType)) { 1252 if (!constructor && _hasUnsoundTypeParameter(paramType)) {
1249 body.add(js 1253 body.add(js
1250 .statement('dart.as(#, #);', [jsParam, _emitTypeName(paramType)])); 1254 .statement('dart.as(#, #);', [jsParam, _emitTypeName(paramType)]));
(...skipping 786 matching lines...) Expand 10 before | Expand all | Expand 10 after
2037 @override 2041 @override
2038 JS.Property visitNamedExpression(NamedExpression node) { 2042 JS.Property visitNamedExpression(NamedExpression node) {
2039 assert(node.parent is ArgumentList); 2043 assert(node.parent is ArgumentList);
2040 return new JS.Property( 2044 return new JS.Property(
2041 _propertyName(node.name.label.name), _visit(node.expression)); 2045 _propertyName(node.name.label.name), _visit(node.expression));
2042 } 2046 }
2043 2047
2044 bool _isNamedParam(FormalParameter param) => 2048 bool _isNamedParam(FormalParameter param) =>
2045 param.kind == ParameterKind.NAMED; 2049 param.kind == ParameterKind.NAMED;
2046 2050
2047 /// We cannot destructure named params that clash with JS reserved names:
2048 /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392.
2049 bool _isDestructurableNamedParam(FormalParameter param) =>
2050 _isNamedParam(param) &&
2051 !invalidVariableName(param.identifier.name) &&
2052 options.destructureNamedParams;
2053
2054 @override 2051 @override
2055 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => 2052 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) =>
2056 _emitFormalParameterList(node); 2053 _emitFormalParameterList(node);
2057 2054
2058 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, 2055 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node,
2059 {bool allowDestructuring: true}) { 2056 {bool allowDestructuring: true}) {
2060 var result = <JS.Parameter>[]; 2057 var result = <JS.Parameter>[];
2061 2058
2062 var namedVars = <JS.DestructuredVariable>[]; 2059 var namedVars = <JS.DestructuredVariable>[];
2063 var destructure = allowDestructuring && 2060 var destructure = allowDestructuring && options.destructureNamedParams;
2064 node.parameters.where(_isNamedParam).every(_isDestructurableNamedParam);
2065 var hasNamedArgsConflictingWithObjectProperties = false; 2061 var hasNamedArgsConflictingWithObjectProperties = false;
2066 var needsOpts = false; 2062 var needsOpts = false;
2067 2063
2068 for (FormalParameter param in node.parameters) { 2064 for (FormalParameter param in node.parameters) {
2069 if (param.kind == ParameterKind.NAMED) { 2065 if (param.kind == ParameterKind.NAMED) {
2070 if (destructure) { 2066 if (destructure) {
2071 if (_jsObjectProperties.contains(param.identifier.name)) { 2067 if (_jsObjectProperties.contains(param.identifier.name)) {
2072 hasNamedArgsConflictingWithObjectProperties = true; 2068 hasNamedArgsConflictingWithObjectProperties = true;
2073 } 2069 }
2074 namedVars.add(new JS.DestructuredVariable( 2070 if (invalidVariableName(param.identifier.name)) {
Jennifer Messerly 2016/02/09 00:33:07 JS should handle this. It already understands whic
ochafik 2016/02/09 01:51:19 So, the logic here is a bit inconvenient: if the n
2075 name: _visit(param.identifier), 2071 namedVars.add(new JS.DestructuredVariable(
2076 defaultValue: _defaultParamValue(param))); 2072 name: js.string(param.identifier.name),
2073 structure:
2074 new JS.SimpleBindingPattern(_visit(param.identifier)),
2075 defaultValue: _defaultParamValue(param)));
2076 } else {
2077 namedVars.add(new JS.DestructuredVariable(
2078 name: _visit(param.identifier),
Jennifer Messerly 2016/02/09 00:33:07 (this might be moot with my comment above, but...)
ochafik 2016/02/09 01:51:20 Done.
2079 defaultValue: _defaultParamValue(param)));
2080 }
2077 } else { 2081 } else {
2078 needsOpts = true; 2082 needsOpts = true;
2079 } 2083 }
2080 } else { 2084 } else {
2081 result.add(_visit(param)); 2085 var jsParam = _visit(param);
2086 result.add(
2087 param is DefaultFormalParameter && options.destructureNamedParams
2088 ? new JS.DestructuredVariable(
2089 name: jsParam, defaultValue: _defaultParamValue(param))
Jennifer Messerly 2016/02/09 00:33:07 BTW, are "name" and "defaultValue" always supplied
ochafik 2016/02/09 01:51:20 Good question! They're not compulsory, although in
2090 : jsParam);
2082 } 2091 }
2083 } 2092 }
2084 2093
2085 if (needsOpts) { 2094 if (needsOpts) {
2086 result.add(_namedArgTemp); 2095 result.add(_namedArgTemp);
2087 } else if (namedVars.isNotEmpty) { 2096 } else if (namedVars.isNotEmpty) {
2088 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so 2097 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so
2089 // in case there are conflicting names we create an object without 2098 // in case there are conflicting names we create an object without
2090 // any prototype. 2099 // any prototype.
2091 var defaultOpts = hasNamedArgsConflictingWithObjectProperties 2100 var defaultOpts = hasNamedArgsConflictingWithObjectProperties
(...skipping 1520 matching lines...) Expand 10 before | Expand all | Expand 10 after
3612 3621
3613 /// A special kind of element created by the compiler, signifying a temporary 3622 /// A special kind of element created by the compiler, signifying a temporary
3614 /// variable. These objects use instance equality, and should be shared 3623 /// variable. These objects use instance equality, and should be shared
3615 /// everywhere in the tree where they are treated as the same variable. 3624 /// everywhere in the tree where they are treated as the same variable.
3616 class TemporaryVariableElement extends LocalVariableElementImpl { 3625 class TemporaryVariableElement extends LocalVariableElementImpl {
3617 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); 3626 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name);
3618 3627
3619 int get hashCode => identityHashCode(this); 3628 int get hashCode => identityHashCode(this);
3620 bool operator ==(Object other) => identical(this, other); 3629 bool operator ==(Object other) => identical(this, other);
3621 } 3630 }
OLDNEW
« no previous file with comments | « no previous file | lib/src/js/nodes.dart » ('j') | lib/src/js/printer.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698