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