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