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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
6 | 6 |
7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
(...skipping 908 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
919 | 919 |
920 var name = _constructorName(node.element); | 920 var name = _constructorName(node.element); |
921 | 921 |
922 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 922 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
923 var redirect = node.redirectedConstructor; | 923 var redirect = node.redirectedConstructor; |
924 if (redirect != null) { | 924 if (redirect != null) { |
925 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; | 925 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; |
926 // Pass along all arguments verbatim, and let the callee handle them. | 926 // Pass along all arguments verbatim, and let the callee handle them. |
927 // TODO(jmesserly): we'll need something different once we have | 927 // TODO(jmesserly): we'll need something different once we have |
928 // rest/spread support, but this should work for now. | 928 // rest/spread support, but this should work for now. |
929 var params = _visit(node.parameters); | 929 var params = _visitFormalParameterList( |
930 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.
| |
931 | |
930 var fun = js.call('function(#) { return $newKeyword #(#); }', | 932 var fun = js.call('function(#) { return $newKeyword #(#); }', |
931 [params, _visit(redirect), params,]) as JS.Fun; | 933 [params, _visit(redirect), params]) as JS.Fun; |
932 return annotate( | 934 return annotate( |
933 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, | 935 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, |
934 node.element); | 936 node.element); |
935 } | 937 } |
936 | 938 |
937 // Factory constructors are essentially static methods. | 939 // Factory constructors are essentially static methods. |
938 if (node.factoryKeyword != null) { | 940 if (node.factoryKeyword != null) { |
939 var body = <JS.Statement>[]; | 941 var body = <JS.Statement>[]; |
940 var init = _emitArgumentInitializers(node, constructor: true); | 942 var init = _emitArgumentInitializers(node, constructor: true); |
941 if (init != null) body.add(init); | 943 if (init != null) body.add(init); |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1178 /// as generic type checks needed due to our covariance. | 1180 /// as generic type checks needed due to our covariance. |
1179 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { | 1181 JS.Statement _emitArgumentInitializers(node, {bool constructor: false}) { |
1180 // Constructor argument initializers are emitted earlier in the code, rather | 1182 // Constructor argument initializers are emitted earlier in the code, rather |
1181 // than always when we visit the function body, so we control it explicitly. | 1183 // than always when we visit the function body, so we control it explicitly. |
1182 if (node is ConstructorDeclaration != constructor) return null; | 1184 if (node is ConstructorDeclaration != constructor) return null; |
1183 | 1185 |
1184 var parameters = _parametersOf(node); | 1186 var parameters = _parametersOf(node); |
1185 if (parameters == null) return null; | 1187 if (parameters == null) return null; |
1186 | 1188 |
1187 var body = <JS.Statement>[]; | 1189 var body = <JS.Statement>[]; |
1190 | |
1191 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 :-)
| |
1192 new Map.fromIterable(parameters.parameters, | |
1193 value: (param) => _visit(param.identifier)); | |
1194 | |
1188 for (var param in parameters.parameters) { | 1195 for (var param in parameters.parameters) { |
1189 var jsParam = _visit(param.identifier); | 1196 var jsParam = paramNames[param]; |
1190 | 1197 |
1191 if (param.kind == ParameterKind.NAMED) { | 1198 if (param.kind == ParameterKind.NAMED) { |
1192 // Parameters will be passed using their real names, not the (possibly | 1199 if (!_isDestructurableNamedParam(param)) { |
1193 // renamed) local variable. | 1200 // Parameters will be passed using their real names, not the (possibly |
1194 var paramName = js.string(param.identifier.name, "'"); | 1201 // renamed) local variable. |
1202 var paramName = js.string(param.identifier.name, "'"); | |
1195 | 1203 |
1196 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming | 1204 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. |
1197 // (either test if `obj.prop !== void 0`, or use JSCompiler_renameProper ty). | 1205 body.add(js.statement('let # = # && # in # ? #.# : #;', [ |
1198 body.add(js.statement('let # = # && # in # ? #.# : #;', [ | 1206 jsParam, |
1199 jsParam, | 1207 _namedArgTemp, |
1200 _namedArgTemp, | 1208 paramName, |
1201 paramName, | 1209 _namedArgTemp, |
1202 _namedArgTemp, | 1210 _namedArgTemp, |
1203 _namedArgTemp, | 1211 paramName, |
1204 paramName, | 1212 _defaultParamValue(param), |
1205 _defaultParamValue(param), | 1213 ])); |
1206 ])); | 1214 } |
1207 } else if (param.kind == ParameterKind.POSITIONAL) { | 1215 } else if (param.kind == ParameterKind.POSITIONAL) { |
1208 body.add(js.statement('if (# === void 0) # = #;', | 1216 body.add(js.statement('if (# === void 0) # = #;', |
1209 [jsParam, jsParam, _defaultParamValue(param)])); | 1217 [jsParam, jsParam, _defaultParamValue(param)])); |
1210 } | 1218 } |
1211 | 1219 |
1212 // TODO(jmesserly): various problems here, see: | 1220 // TODO(jmesserly): various problems here, see: |
1213 // https://github.com/dart-lang/dev_compiler/issues/161 | 1221 // https://github.com/dart-lang/dev_compiler/issues/161 |
1214 var paramType = param.element.type; | 1222 var paramType = param.element.type; |
1215 if (!constructor && _hasTypeParameter(paramType)) { | 1223 if (!constructor && _hasTypeParameter(paramType)) { |
1216 body.add(js.statement( | 1224 body.add(js.statement( |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1347 @override | 1355 @override |
1348 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1356 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1349 var params = _visit(node.parameters) as List<JS.Parameter>; | 1357 var params = _visit(node.parameters) as List<JS.Parameter>; |
1350 if (params == null) params = <JS.Parameter>[]; | 1358 if (params == null) params = <JS.Parameter>[]; |
1351 | 1359 |
1352 var parent = node.parent; | 1360 var parent = node.parent; |
1353 var inStmt = parent.parent is FunctionDeclarationStatement; | 1361 var inStmt = parent.parent is FunctionDeclarationStatement; |
1354 if (parent is FunctionDeclaration) { | 1362 if (parent is FunctionDeclaration) { |
1355 return _emitFunctionBody(params, node.body); | 1363 return _emitFunctionBody(params, node.body); |
1356 } else { | 1364 } else { |
1357 String code; | 1365 // Chrome Canary does not accept default values with destructuring in |
1366 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | |
1367 // with regular functions (e.g. `function({a} = {}) { return 1 }`). | |
1368 // Note that Firefox accepts both syntaxes just fine. | |
1369 // TODO(ochafik): Simplify this code when Chrome Canary catches up. | |
1370 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); | |
1371 | |
1372 String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }'; | |
1358 JS.Node jsBody; | 1373 JS.Node jsBody; |
1359 var body = node.body; | 1374 var body = node.body; |
1360 if (body.isGenerator || body.isAsynchronous) { | 1375 if (body.isGenerator || body.isAsynchronous) { |
1361 code = '(#) => #'; | |
1362 jsBody = _emitGeneratorFunctionBody(params, body); | 1376 jsBody = _emitGeneratorFunctionBody(params, body); |
1363 } else if (body is ExpressionFunctionBody) { | 1377 } else if (body is ExpressionFunctionBody) { |
1364 code = '(#) => #'; | |
1365 jsBody = _visit(body.expression); | 1378 jsBody = _visit(body.expression); |
1366 } else { | 1379 } else { |
1367 code = '(#) => { #; }'; | 1380 code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }'; |
1368 jsBody = _visit(body); | 1381 jsBody = _visit(body); |
1369 } | 1382 } |
1370 var clos = js.call(code, [params, jsBody]); | 1383 var clos = js.call(code, [params, jsBody]); |
1371 if (!inStmt) { | 1384 if (!inStmt) { |
1372 var type = getStaticType(node); | 1385 var type = getStaticType(node); |
1373 return _emitFunctionTagged(clos, type, | 1386 return _emitFunctionTagged(clos, type, |
1374 topLevel: _executesAtTopLevel(node)); | 1387 topLevel: _executesAtTopLevel(node)); |
1375 } | 1388 } |
1376 return clos; | 1389 return clos; |
1377 } | 1390 } |
(...skipping 520 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1898 return args; | 1911 return args; |
1899 } | 1912 } |
1900 | 1913 |
1901 @override | 1914 @override |
1902 JS.Property visitNamedExpression(NamedExpression node) { | 1915 JS.Property visitNamedExpression(NamedExpression node) { |
1903 assert(node.parent is ArgumentList); | 1916 assert(node.parent is ArgumentList); |
1904 return new JS.Property( | 1917 return new JS.Property( |
1905 _propertyName(node.name.label.name), _visit(node.expression)); | 1918 _propertyName(node.name.label.name), _visit(node.expression)); |
1906 } | 1919 } |
1907 | 1920 |
1921 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.
| |
1922 "constructor", | |
1923 "toString", | |
1924 "toLocaleString", | |
1925 "valueOf", | |
1926 "hasOwnProperty", | |
1927 "isPrototypeOf", | |
1928 "propertyIsEnumerable", | |
1929 "__defineGetter__", | |
1930 "__lookupGetter__", | |
1931 "__defineSetter__", | |
1932 "__lookupSetter__", | |
1933 "__proto__" | |
1934 ]); | |
1935 | |
1936 bool _isNamedParam(FormalParameter param) => | |
1937 param.kind == ParameterKind.NAMED; | |
1938 | |
1939 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!
| |
1940 var name = param.identifier.name; | |
1941 return _isNamedParam(param) && | |
1942 !_jsObjectProperties.contains(name) && | |
1943 name != 'super'; | |
1944 } | |
1945 | |
1946 _namedParams(FormalParameterList list) => | |
1947 list.parameters.where(_isNamedParam); | |
1948 | |
1949 bool _willDestructureNamedParams(FormalParameterList list) { | |
1950 var named = _namedParams(list).toList(growable: false); | |
1951 return named.isNotEmpty && named.every(_isDestructurableNamedParam); | |
1952 } | |
1953 | |
1908 @override | 1954 @override |
1909 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 1955 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => |
1956 _visitFormalParameterList(node, allowDestructuring: true); | |
1957 | |
1958 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.
| |
1959 FormalParameterList node, {bool allowDestructuring}) { | |
1960 | |
1910 var result = <JS.Parameter>[]; | 1961 var result = <JS.Parameter>[]; |
1962 | |
1963 var namedVars = <JS.DestructuredVariable>[]; | |
1964 var canDestructure = allowDestructuring && _willDestructureNamedParams(node) ; | |
Jennifer Messerly
2015/12/01 02:10:27
long line
ochafik
2015/12/02 20:05:46
Acknowledged.
| |
1965 var needsOpts = false; | |
1966 | |
1911 for (FormalParameter param in node.parameters) { | 1967 for (FormalParameter param in node.parameters) { |
1912 if (param.kind == ParameterKind.NAMED) { | 1968 if (param.kind == ParameterKind.NAMED) { |
1913 result.add(_namedArgTemp); | 1969 if (canDestructure) { |
1914 break; | 1970 namedVars.add(new JS.DestructuredVariable( |
1971 name: _visit(param.identifier), | |
1972 defaultValue: _defaultParamValue(param))); | |
1973 } else { | |
1974 needsOpts = true; | |
1975 } | |
1976 } else { | |
1977 result.add(_visit(param)); | |
1915 } | 1978 } |
1916 result.add(_visit(param)); | 1979 } |
1980 if (canDestructure) { | |
1981 result.add(new JS.DestructuredVariable( | |
1982 structure: new JS.ObjectDestructuring(namedVars), | |
1983 defaultValue: js.call('{}'))); | |
1984 } else if (needsOpts) { | |
1985 result.add(_namedArgTemp); | |
1917 } | 1986 } |
1918 return result; | 1987 return result; |
1919 } | 1988 } |
1920 | 1989 |
1921 @override | 1990 @override |
1922 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 1991 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
1923 _visit(node.expression).toStatement(); | 1992 _visit(node.expression).toStatement(); |
1924 | 1993 |
1925 @override | 1994 @override |
1926 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => | 1995 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => |
(...skipping 1420 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3347 | 3416 |
3348 /// A special kind of element created by the compiler, signifying a temporary | 3417 /// A special kind of element created by the compiler, signifying a temporary |
3349 /// variable. These objects use instance equality, and should be shared | 3418 /// variable. These objects use instance equality, and should be shared |
3350 /// everywhere in the tree where they are treated as the same variable. | 3419 /// everywhere in the tree where they are treated as the same variable. |
3351 class TemporaryVariableElement extends LocalVariableElementImpl { | 3420 class TemporaryVariableElement extends LocalVariableElementImpl { |
3352 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3421 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3353 | 3422 |
3354 int get hashCode => identityHashCode(this); | 3423 int get hashCode => identityHashCode(this); |
3355 bool operator ==(Object other) => identical(this, other); | 3424 bool operator ==(Object other) => identical(this, other); |
3356 } | 3425 } |
OLD | NEW |