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 17 matching lines...) Expand all Loading... |
28 import '../info.dart'; | 28 import '../info.dart'; |
29 import '../options.dart' show CodegenOptions; | 29 import '../options.dart' show CodegenOptions; |
30 import '../utils.dart'; | 30 import '../utils.dart'; |
31 | 31 |
32 import 'code_generator.dart'; | 32 import 'code_generator.dart'; |
33 import 'js_field_storage.dart'; | 33 import 'js_field_storage.dart'; |
34 import 'js_interop.dart'; | 34 import 'js_interop.dart'; |
35 import 'js_names.dart' as JS; | 35 import 'js_names.dart' as JS; |
36 import 'js_metalet.dart' as JS; | 36 import 'js_metalet.dart' as JS; |
37 import 'js_module_item_order.dart'; | 37 import 'js_module_item_order.dart'; |
| 38 import 'js_names.dart'; |
38 import 'js_printer.dart' show writeJsLibrary; | 39 import 'js_printer.dart' show writeJsLibrary; |
39 import 'side_effect_analysis.dart'; | 40 import 'side_effect_analysis.dart'; |
40 import 'package:collection/equality.dart'; | 41 import 'package:collection/equality.dart'; |
41 | 42 |
42 // Various dynamic helpers we call. | 43 // Various dynamic helpers we call. |
43 // If renaming these, make sure to check other places like the | 44 // If renaming these, make sure to check other places like the |
44 // _runtime.js file and comments. | 45 // _runtime.js file and comments. |
45 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 46 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
46 // import and generate calls to, rather than dart_runtime.js | 47 // import and generate calls to, rather than dart_runtime.js |
47 const DPUT = 'dput'; | 48 const DPUT = 'dput'; |
(...skipping 887 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
935 | 936 |
936 var name = _constructorName(node.element); | 937 var name = _constructorName(node.element); |
937 | 938 |
938 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; | 939 // Wacky factory redirecting constructors: factory Foo.q(x, y) = Bar.baz; |
939 var redirect = node.redirectedConstructor; | 940 var redirect = node.redirectedConstructor; |
940 if (redirect != null) { | 941 if (redirect != null) { |
941 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; | 942 var newKeyword = redirect.staticElement.isFactory ? '' : 'new'; |
942 // Pass along all arguments verbatim, and let the callee handle them. | 943 // Pass along all arguments verbatim, and let the callee handle them. |
943 // TODO(jmesserly): we'll need something different once we have | 944 // TODO(jmesserly): we'll need something different once we have |
944 // rest/spread support, but this should work for now. | 945 // rest/spread support, but this should work for now. |
945 var params = _visit(node.parameters); | 946 var params = |
| 947 _emitFormalParameterList(node.parameters, allowDestructuring: false); |
| 948 |
946 var fun = js.call('function(#) { return $newKeyword #(#); }', | 949 var fun = js.call('function(#) { return $newKeyword #(#); }', |
947 [params, _visit(redirect), params,]) as JS.Fun; | 950 [params, _visit(redirect), params]) as JS.Fun; |
948 return annotate( | 951 return annotate( |
949 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, | 952 new JS.Method(name, fun, isStatic: true)..sourceInformation = node, |
950 node.element); | 953 node.element); |
951 } | 954 } |
952 | 955 |
953 // Factory constructors are essentially static methods. | 956 // Factory constructors are essentially static methods. |
954 if (node.factoryKeyword != null) { | 957 if (node.factoryKeyword != null) { |
955 var body = <JS.Statement>[]; | 958 var body = <JS.Statement>[]; |
956 var init = _emitArgumentInitializers(node, constructor: true); | 959 var init = _emitArgumentInitializers(node, constructor: true); |
957 if (init != null) body.add(init); | 960 if (init != null) body.add(init); |
(...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1198 if (node is ConstructorDeclaration != constructor) return null; | 1201 if (node is ConstructorDeclaration != constructor) return null; |
1199 | 1202 |
1200 var parameters = _parametersOf(node); | 1203 var parameters = _parametersOf(node); |
1201 if (parameters == null) return null; | 1204 if (parameters == null) return null; |
1202 | 1205 |
1203 var body = <JS.Statement>[]; | 1206 var body = <JS.Statement>[]; |
1204 for (var param in parameters.parameters) { | 1207 for (var param in parameters.parameters) { |
1205 var jsParam = _visit(param.identifier); | 1208 var jsParam = _visit(param.identifier); |
1206 | 1209 |
1207 if (param.kind == ParameterKind.NAMED) { | 1210 if (param.kind == ParameterKind.NAMED) { |
1208 // Parameters will be passed using their real names, not the (possibly | 1211 if (!_isDestructurableNamedParam(param)) { |
1209 // renamed) local variable. | 1212 // Parameters will be passed using their real names, not the (possibly |
1210 var paramName = js.string(param.identifier.name, "'"); | 1213 // renamed) local variable. |
| 1214 var paramName = js.string(param.identifier.name, "'"); |
1211 | 1215 |
1212 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming | 1216 // TODO(ochafik): Fix `'prop' in obj` to please Closure's renaming. |
1213 // (either test if `obj.prop !== void 0`, or use JSCompiler_renameProper
ty). | 1217 body.add(js.statement('let # = # && # in # ? #.# : #;', [ |
1214 body.add(js.statement('let # = # && # in # ? #.# : #;', [ | 1218 jsParam, |
1215 jsParam, | 1219 _namedArgTemp, |
1216 _namedArgTemp, | 1220 paramName, |
1217 paramName, | 1221 _namedArgTemp, |
1218 _namedArgTemp, | 1222 _namedArgTemp, |
1219 _namedArgTemp, | 1223 paramName, |
1220 paramName, | 1224 _defaultParamValue(param), |
1221 _defaultParamValue(param), | 1225 ])); |
1222 ])); | 1226 } |
1223 } else if (param.kind == ParameterKind.POSITIONAL) { | 1227 } else if (param.kind == ParameterKind.POSITIONAL) { |
1224 body.add(js.statement('if (# === void 0) # = #;', | 1228 body.add(js.statement('if (# === void 0) # = #;', |
1225 [jsParam, jsParam, _defaultParamValue(param)])); | 1229 [jsParam, jsParam, _defaultParamValue(param)])); |
1226 } | 1230 } |
1227 | 1231 |
1228 // TODO(jmesserly): various problems here, see: | 1232 // TODO(jmesserly): various problems here, see: |
1229 // https://github.com/dart-lang/dev_compiler/issues/161 | 1233 // https://github.com/dart-lang/dev_compiler/issues/161 |
1230 var paramType = param.element.type; | 1234 var paramType = param.element.type; |
1231 if (!constructor && _hasTypeParameter(paramType)) { | 1235 if (!constructor && _hasTypeParameter(paramType)) { |
1232 body.add(js.statement( | 1236 body.add(js.statement( |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1415 @override | 1419 @override |
1416 JS.Expression visitFunctionExpression(FunctionExpression node) { | 1420 JS.Expression visitFunctionExpression(FunctionExpression node) { |
1417 var params = _visit(node.parameters) as List<JS.Parameter>; | 1421 var params = _visit(node.parameters) as List<JS.Parameter>; |
1418 if (params == null) params = <JS.Parameter>[]; | 1422 if (params == null) params = <JS.Parameter>[]; |
1419 | 1423 |
1420 var parent = node.parent; | 1424 var parent = node.parent; |
1421 var inStmt = parent.parent is FunctionDeclarationStatement; | 1425 var inStmt = parent.parent is FunctionDeclarationStatement; |
1422 if (parent is FunctionDeclaration) { | 1426 if (parent is FunctionDeclaration) { |
1423 return _emitFunctionBody(params, node.body); | 1427 return _emitFunctionBody(params, node.body); |
1424 } else { | 1428 } else { |
1425 String code; | 1429 // Chrome Canary does not accept default values with destructuring in |
| 1430 // arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them |
| 1431 // with regular functions (e.g. `function({a} = {}) { return 1 }`). |
| 1432 // Note that Firefox accepts both syntaxes just fine. |
| 1433 // TODO(ochafik): Simplify this code when Chrome Canary catches up. |
| 1434 var canUseArrowFun = !node.parameters.parameters.any(_isNamedParam); |
| 1435 |
| 1436 String code = canUseArrowFun ? '(#) => #' : 'function(#) { return # }'; |
1426 JS.Node jsBody; | 1437 JS.Node jsBody; |
1427 var body = node.body; | 1438 var body = node.body; |
1428 if (body.isGenerator || body.isAsynchronous) { | 1439 if (body.isGenerator || body.isAsynchronous) { |
1429 code = '(#) => #'; | |
1430 jsBody = _emitGeneratorFunctionBody(params, body); | 1440 jsBody = _emitGeneratorFunctionBody(params, body); |
1431 } else if (body is ExpressionFunctionBody) { | 1441 } else if (body is ExpressionFunctionBody) { |
1432 code = '(#) => #'; | |
1433 jsBody = _visit(body.expression); | 1442 jsBody = _visit(body.expression); |
1434 } else { | 1443 } else { |
1435 code = '(#) => { #; }'; | 1444 code = canUseArrowFun ? '(#) => { #; }' : 'function(#) { #; }'; |
1436 jsBody = _visit(body); | 1445 jsBody = _visit(body); |
1437 } | 1446 } |
1438 var clos = js.call(code, [params, jsBody]); | 1447 var clos = js.call(code, [params, jsBody]); |
1439 if (!inStmt) { | 1448 if (!inStmt) { |
1440 var type = getStaticType(node); | 1449 var type = getStaticType(node); |
1441 return _emitFunctionTagged(clos, type, | 1450 return _emitFunctionTagged(clos, type, |
1442 topLevel: _executesAtTopLevel(node)); | 1451 topLevel: _executesAtTopLevel(node)); |
1443 } | 1452 } |
1444 return clos; | 1453 return clos; |
1445 } | 1454 } |
(...skipping 520 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1966 return args; | 1975 return args; |
1967 } | 1976 } |
1968 | 1977 |
1969 @override | 1978 @override |
1970 JS.Property visitNamedExpression(NamedExpression node) { | 1979 JS.Property visitNamedExpression(NamedExpression node) { |
1971 assert(node.parent is ArgumentList); | 1980 assert(node.parent is ArgumentList); |
1972 return new JS.Property( | 1981 return new JS.Property( |
1973 _propertyName(node.name.label.name), _visit(node.expression)); | 1982 _propertyName(node.name.label.name), _visit(node.expression)); |
1974 } | 1983 } |
1975 | 1984 |
| 1985 bool _isNamedParam(FormalParameter param) => |
| 1986 param.kind == ParameterKind.NAMED; |
| 1987 |
| 1988 /// We cannot destructure named params that clash with JS reserved names: |
| 1989 /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392. |
| 1990 bool _isDestructurableNamedParam(FormalParameter param) => |
| 1991 _isNamedParam(param) && !invalidVariableName(param.identifier.name); |
| 1992 |
1976 @override | 1993 @override |
1977 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) { | 1994 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => |
| 1995 _emitFormalParameterList(node); |
| 1996 |
| 1997 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, |
| 1998 {bool allowDestructuring: true}) { |
1978 var result = <JS.Parameter>[]; | 1999 var result = <JS.Parameter>[]; |
| 2000 |
| 2001 var namedVars = <JS.DestructuredVariable>[]; |
| 2002 var destructure = allowDestructuring && |
| 2003 node.parameters.where(_isNamedParam).every(_isDestructurableNamedParam); |
| 2004 var hasNamedArgsConflictingWithObjectProperties = false; |
| 2005 var needsOpts = false; |
| 2006 |
1979 for (FormalParameter param in node.parameters) { | 2007 for (FormalParameter param in node.parameters) { |
1980 if (param.kind == ParameterKind.NAMED) { | 2008 if (param.kind == ParameterKind.NAMED) { |
1981 result.add(_namedArgTemp); | 2009 if (destructure) { |
1982 break; | 2010 if (_jsObjectProperties.contains(param.identifier.name)) { |
| 2011 hasNamedArgsConflictingWithObjectProperties = true; |
| 2012 } |
| 2013 namedVars.add(new JS.DestructuredVariable( |
| 2014 name: _visit(param.identifier), |
| 2015 defaultValue: _defaultParamValue(param))); |
| 2016 } else { |
| 2017 needsOpts = true; |
| 2018 } |
| 2019 } else { |
| 2020 result.add(_visit(param)); |
1983 } | 2021 } |
1984 result.add(_visit(param)); | 2022 } |
| 2023 |
| 2024 if (needsOpts) { |
| 2025 result.add(_namedArgTemp); |
| 2026 } else if (namedVars.isNotEmpty) { |
| 2027 // Note: `var {valueOf} = {}` extracts `Object.prototype.valueOf`, so |
| 2028 // in case there are conflicting names we create an object without |
| 2029 // any prototype. |
| 2030 var defaultOpts = hasNamedArgsConflictingWithObjectProperties |
| 2031 ? js.call('Object.create(null)') |
| 2032 : js.call('{}'); |
| 2033 result.add(new JS.DestructuredVariable( |
| 2034 structure: new JS.ObjectBindingPattern(namedVars), |
| 2035 defaultValue: defaultOpts)); |
1985 } | 2036 } |
1986 return result; | 2037 return result; |
1987 } | 2038 } |
1988 | 2039 |
| 2040 /// See ES6 spec (and `Object.getOwnPropertyNames(Object.prototype)`): |
| 2041 /// http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-obje
ct-prototype-object |
| 2042 /// http://www.ecma-international.org/ecma-262/6.0/#sec-additional-properties-
of-the-object.prototype-object |
| 2043 static final Set<String> _jsObjectProperties = new Set<String>() |
| 2044 ..addAll([ |
| 2045 "constructor", |
| 2046 "toString", |
| 2047 "toLocaleString", |
| 2048 "valueOf", |
| 2049 "hasOwnProperty", |
| 2050 "isPrototypeOf", |
| 2051 "propertyIsEnumerable", |
| 2052 "__defineGetter__", |
| 2053 "__lookupGetter__", |
| 2054 "__defineSetter__", |
| 2055 "__lookupSetter__", |
| 2056 "__proto__" |
| 2057 ]); |
| 2058 |
1989 @override | 2059 @override |
1990 JS.Statement visitExpressionStatement(ExpressionStatement node) => | 2060 JS.Statement visitExpressionStatement(ExpressionStatement node) => |
1991 _visit(node.expression).toStatement(); | 2061 _visit(node.expression).toStatement(); |
1992 | 2062 |
1993 @override | 2063 @override |
1994 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => | 2064 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => |
1995 new JS.EmptyStatement(); | 2065 new JS.EmptyStatement(); |
1996 | 2066 |
1997 @override | 2067 @override |
1998 JS.Statement visitAssertStatement(AssertStatement node) => | 2068 JS.Statement visitAssertStatement(AssertStatement node) => |
(...skipping 1418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3417 | 3487 |
3418 /// A special kind of element created by the compiler, signifying a temporary | 3488 /// A special kind of element created by the compiler, signifying a temporary |
3419 /// variable. These objects use instance equality, and should be shared | 3489 /// variable. These objects use instance equality, and should be shared |
3420 /// everywhere in the tree where they are treated as the same variable. | 3490 /// everywhere in the tree where they are treated as the same variable. |
3421 class TemporaryVariableElement extends LocalVariableElementImpl { | 3491 class TemporaryVariableElement extends LocalVariableElementImpl { |
3422 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3492 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3423 | 3493 |
3424 int get hashCode => identityHashCode(this); | 3494 int get hashCode => identityHashCode(this); |
3425 bool operator ==(Object other) => identical(this, other); | 3495 bool operator ==(Object other) => identical(this, other); |
3426 } | 3496 } |
OLD | NEW |