Chromium Code Reviews| 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 |