OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |