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 HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 | 6 |
7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
8 import 'package:analyzer/dart/ast/ast.dart'; | 8 import 'package:analyzer/dart/ast/ast.dart'; |
9 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 9 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
10 import 'package:analyzer/dart/element/element.dart'; | 10 import 'package:analyzer/dart/element/element.dart'; |
(...skipping 1098 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1109 } else { | 1109 } else { |
1110 tMethods.add(property); | 1110 tMethods.add(property); |
1111 } | 1111 } |
1112 } | 1112 } |
1113 } | 1113 } |
1114 | 1114 |
1115 var tCtors = <JS.Property>[]; | 1115 var tCtors = <JS.Property>[]; |
1116 for (ConstructorDeclaration node in ctors) { | 1116 for (ConstructorDeclaration node in ctors) { |
1117 var memberName = _constructorName(node.element); | 1117 var memberName = _constructorName(node.element); |
1118 var element = node.element; | 1118 var element = node.element; |
1119 var parts = _emitFunctionTypeParts(element.type, node.parameters); | 1119 var parts = _emitFunctionTypeParts(element.type, |
| 1120 parameters: node.parameters.parameters); |
1120 var property = | 1121 var property = |
1121 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 1122 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
1122 tCtors.add(property); | 1123 tCtors.add(property); |
1123 } | 1124 } |
1124 | 1125 |
1125 JS.Property build(String name, List<JS.Property> elements) { | 1126 JS.Property build(String name, List<JS.Property> elements) { |
1126 var o = | 1127 var o = |
1127 new JS.ObjectInitializer(elements, multiline: elements.length > 1); | 1128 new JS.ObjectInitializer(elements, multiline: elements.length > 1); |
1128 var e = js.call('() => #', o); | 1129 var e = js.call('() => #', o); |
1129 return new JS.Property(_propertyName(name), e); | 1130 return new JS.Property(_propertyName(name), e); |
(...skipping 493 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1623 } else { | 1624 } else { |
1624 fn = _emitFunctionBody(node.element, node.parameters, node.body); | 1625 fn = _emitFunctionBody(node.element, node.parameters, node.body); |
1625 | 1626 |
1626 if (node.operatorKeyword != null && | 1627 if (node.operatorKeyword != null && |
1627 node.name.name == '[]=' && | 1628 node.name.name == '[]=' && |
1628 fn.params.isNotEmpty) { | 1629 fn.params.isNotEmpty) { |
1629 // []= methods need to return the value. We could also address this at | 1630 // []= methods need to return the value. We could also address this at |
1630 // call sites, but it's cleaner to instead transform the operator method
. | 1631 // call sites, but it's cleaner to instead transform the operator method
. |
1631 fn = _alwaysReturnLastParameter(fn); | 1632 fn = _alwaysReturnLastParameter(fn); |
1632 } | 1633 } |
| 1634 |
| 1635 fn = _makeGenericFunction(fn); |
1633 } | 1636 } |
1634 | 1637 |
1635 return annotate( | 1638 return annotate( |
1636 new JS.Method(_elementMemberName(node.element), fn, | 1639 new JS.Method(_elementMemberName(node.element), fn, |
1637 isGetter: node.isGetter, | 1640 isGetter: node.isGetter, |
1638 isSetter: node.isSetter, | 1641 isSetter: node.isSetter, |
1639 isStatic: node.isStatic), | 1642 isStatic: node.isStatic), |
1640 node, | 1643 node, |
1641 node.element); | 1644 node.element); |
1642 } | 1645 } |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1773 } | 1776 } |
1774 return _loader.isLoaded(type.element); | 1777 return _loader.isLoaded(type.element); |
1775 } | 1778 } |
1776 | 1779 |
1777 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, | 1780 JS.Expression _emitFunctionTagged(JS.Expression fn, DartType type, |
1778 {topLevel: false}) { | 1781 {topLevel: false}) { |
1779 var name = type.name; | 1782 var name = type.name; |
1780 var lazy = topLevel && !_typeIsLoaded(type); | 1783 var lazy = topLevel && !_typeIsLoaded(type); |
1781 | 1784 |
1782 if (type is FunctionType && (name == '' || name == null)) { | 1785 if (type is FunctionType && (name == '' || name == null)) { |
1783 if (type.returnType.isDynamic && | 1786 if (type.typeFormals.isEmpty && |
| 1787 type.returnType.isDynamic && |
1784 type.optionalParameterTypes.isEmpty && | 1788 type.optionalParameterTypes.isEmpty && |
1785 type.namedParameterTypes.isEmpty && | 1789 type.namedParameterTypes.isEmpty && |
1786 type.normalParameterTypes.every((t) => t.isDynamic)) { | 1790 type.normalParameterTypes.every((t) => t.isDynamic)) { |
1787 return js.call('dart.fn(#)', [fn]); | 1791 return js.call('dart.fn(#)', [fn]); |
1788 } | 1792 } |
1789 | 1793 |
1790 String code = lazy ? '() => dart.definiteFunctionType(#)' : '#'; | 1794 var parts = _emitFunctionTypeParts(type); |
1791 return js.call('dart.fn(#, $code)', [fn, _emitFunctionTypeParts(type)]); | 1795 if (lazy) { |
| 1796 return js.call( |
| 1797 'dart.lazyFn(#, () => #)', [fn, new JS.ArrayInitializer(parts)]); |
| 1798 } else { |
| 1799 return js.call('dart.fn(#, #)', [fn, parts]); |
| 1800 } |
1792 } | 1801 } |
1793 throw 'Function has non function type: $type'; | 1802 throw 'Function has non function type: $type'; |
1794 } | 1803 } |
1795 | 1804 |
1796 /// Emits an arrow FunctionExpression node. | 1805 /// Emits an arrow FunctionExpression node. |
1797 /// | 1806 /// |
1798 /// This should be used for all places in Dart's AST where FunctionExpression | 1807 /// This should be used for all places in Dart's AST where FunctionExpression |
1799 /// appears and the function is actually in an Expression context. These | 1808 /// appears and the function is actually in an Expression context. These |
1800 /// correspond to arrow functions in Dart. | 1809 /// correspond to arrow functions in Dart. |
1801 /// | 1810 /// |
(...skipping 14 matching lines...) Expand all Loading... |
1816 if (body is JS.Block) { | 1825 if (body is JS.Block) { |
1817 JS.Block block = body; | 1826 JS.Block block = body; |
1818 if (block.statements.length == 1) { | 1827 if (block.statements.length == 1) { |
1819 JS.Statement s = block.statements[0]; | 1828 JS.Statement s = block.statements[0]; |
1820 if (s is JS.Return) body = s.value; | 1829 if (s is JS.Return) body = s.value; |
1821 } | 1830 } |
1822 } | 1831 } |
1823 | 1832 |
1824 // Convert `function(...) { ... }` to `(...) => ...` | 1833 // Convert `function(...) { ... }` to `(...) => ...` |
1825 // This is for readability, but it also ensures correct `this` binding. | 1834 // This is for readability, but it also ensures correct `this` binding. |
1826 return annotate( | 1835 var fn = new JS.ArrowFun(f.params, body, |
1827 new JS.ArrowFun(f.params, body, | 1836 typeParams: f.typeParams, returnType: f.returnType); |
1828 typeParams: f.typeParams, returnType: f.returnType), | 1837 |
1829 node); | 1838 return annotate(_makeGenericFunction(fn), node); |
| 1839 } |
| 1840 |
| 1841 JS.FunctionExpression/*=T*/ _makeGenericFunction |
| 1842 /*<T extends JS.FunctionExpression>*/(JS.FunctionExpression/*=T*/ fn) { |
| 1843 if (fn.typeParams == null || fn.typeParams.isEmpty) return fn; |
| 1844 |
| 1845 // TODO(jmesserly): we could make these default to `dynamic`. |
| 1846 var typeParams = fn.typeParams; |
| 1847 if (fn is JS.ArrowFun) { |
| 1848 return new JS.ArrowFun(typeParams, fn); |
| 1849 } |
| 1850 var f = fn as JS.Fun; |
| 1851 return new JS.Fun( |
| 1852 typeParams, |
| 1853 new JS.Block([ |
| 1854 // Convert the function to an => function, to ensure `this` binding. |
| 1855 new JS.Return(new JS.ArrowFun(f.params, f.body, |
| 1856 typeParams: f.typeParams, returnType: f.returnType)) |
| 1857 ])); |
1830 } | 1858 } |
1831 | 1859 |
1832 /// Emits a non-arrow FunctionExpression node. | 1860 /// Emits a non-arrow FunctionExpression node. |
1833 /// | 1861 /// |
1834 /// This should be used for all places in Dart's AST where FunctionExpression | 1862 /// This should be used for all places in Dart's AST where FunctionExpression |
1835 /// appears but the function is not actually in an Expression context, such | 1863 /// appears but the function is not actually in an Expression context, such |
1836 /// as methods, properties, and top-level functions. | 1864 /// as methods, properties, and top-level functions. |
1837 /// | 1865 /// |
1838 /// Contrast with [visitFunctionExpression]. | 1866 /// Contrast with [visitFunctionExpression]. |
1839 JS.Fun _emitFunction(FunctionExpression node) { | 1867 JS.Fun _emitFunction(FunctionExpression node) { |
1840 var fn = _emitFunctionBody(node.element, node.parameters, node.body); | 1868 var fn = _emitFunctionBody(node.element, node.parameters, node.body); |
1841 return annotate(fn, node); | 1869 return annotate(_makeGenericFunction(fn), node); |
1842 } | 1870 } |
1843 | 1871 |
1844 JS.Fun _emitFunctionBody(ExecutableElement element, | 1872 JS.Fun _emitFunctionBody(ExecutableElement element, |
1845 FormalParameterList parameters, FunctionBody body) { | 1873 FormalParameterList parameters, FunctionBody body) { |
1846 var returnType = emitTypeRef(element.returnType); | 1874 FunctionType type = element.type; |
1847 | 1875 |
1848 // sync*, async, async* | 1876 // sync*, async, async* |
1849 if (element.isAsynchronous || element.isGenerator) { | 1877 if (element.isAsynchronous || element.isGenerator) { |
1850 return new JS.Fun( | 1878 return new JS.Fun( |
1851 visitFormalParameterList(parameters, destructure: false), | 1879 visitFormalParameterList(parameters, destructure: false), |
1852 js.statement('{ return #; }', | 1880 new JS.Block([ |
1853 [_emitGeneratorFunctionBody(element, parameters, body)]), | 1881 _emitGeneratorFunctionBody(element, parameters, body).toReturn() |
1854 returnType: returnType); | 1882 ]), |
| 1883 returnType: emitTypeRef(type.returnType)); |
1855 } | 1884 } |
| 1885 |
1856 // normal function (sync) | 1886 // normal function (sync) |
1857 return new JS.Fun(visitFormalParameterList(parameters), _visit(body), | 1887 return new JS.Fun(visitFormalParameterList(parameters), _visit(body), |
1858 typeParams: _emitTypeFormals(element.typeParameters), | 1888 typeParams: _emitTypeFormals(type.typeFormals), |
1859 returnType: returnType); | 1889 returnType: emitTypeRef(type.returnType)); |
1860 } | 1890 } |
1861 | 1891 |
1862 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, | 1892 JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, |
1863 FormalParameterList parameters, FunctionBody body) { | 1893 FormalParameterList parameters, FunctionBody body) { |
1864 var kind = element.isSynchronous ? 'sync' : 'async'; | 1894 var kind = element.isSynchronous ? 'sync' : 'async'; |
1865 if (element.isGenerator) kind += 'Star'; | 1895 if (element.isGenerator) kind += 'Star'; |
1866 | 1896 |
1867 // Transforms `sync*` `async` and `async*` function bodies | 1897 // Transforms `sync*` `async` and `async*` function bodies |
1868 // using ES6 generators. | 1898 // using ES6 generators. |
1869 // | 1899 // |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2054 var key = _propertyName(name); | 2084 var key = _propertyName(name); |
2055 var value = _emitTypeName(type); | 2085 var value = _emitTypeName(type); |
2056 properties.add(new JS.Property(key, value)); | 2086 properties.add(new JS.Property(key, value)); |
2057 }); | 2087 }); |
2058 return new JS.ObjectInitializer(properties); | 2088 return new JS.ObjectInitializer(properties); |
2059 } | 2089 } |
2060 | 2090 |
2061 /// Emit the pieces of a function type, as an array of return type, | 2091 /// Emit the pieces of a function type, as an array of return type, |
2062 /// regular args, and optional/named args. | 2092 /// regular args, and optional/named args. |
2063 List<JS.Expression> _emitFunctionTypeParts(FunctionType type, | 2093 List<JS.Expression> _emitFunctionTypeParts(FunctionType type, |
2064 [FormalParameterList parameterList]) { | 2094 {List<FormalParameter> parameters, bool lowerTypedef: false}) { |
2065 var parameters = parameterList?.parameters; | |
2066 var returnType = type.returnType; | |
2067 var parameterTypes = type.normalParameterTypes; | 2095 var parameterTypes = type.normalParameterTypes; |
2068 var optionalTypes = type.optionalParameterTypes; | 2096 var optionalTypes = type.optionalParameterTypes; |
2069 var namedTypes = type.namedParameterTypes; | 2097 var namedTypes = type.namedParameterTypes; |
2070 var rt = _emitTypeName(returnType); | 2098 var rt = _emitTypeName(type.returnType); |
2071 var ra = _emitTypeNames(parameterTypes, parameters); | 2099 var ra = _emitTypeNames(parameterTypes, parameters); |
2072 if (!namedTypes.isEmpty) { | 2100 |
| 2101 List<JS.Expression> typeParts; |
| 2102 if (namedTypes.isNotEmpty) { |
2073 assert(optionalTypes.isEmpty); | 2103 assert(optionalTypes.isEmpty); |
2074 // TODO(vsm): Pass in annotations here as well. | 2104 // TODO(vsm): Pass in annotations here as well. |
2075 var na = _emitTypeProperties(namedTypes); | 2105 var na = _emitTypeProperties(namedTypes); |
2076 return [rt, ra, na]; | 2106 typeParts = [rt, ra, na]; |
2077 } | 2107 } else if (optionalTypes.isNotEmpty) { |
2078 if (!optionalTypes.isEmpty) { | |
2079 assert(namedTypes.isEmpty); | 2108 assert(namedTypes.isEmpty); |
2080 var oa = _emitTypeNames( | 2109 var oa = _emitTypeNames( |
2081 optionalTypes, parameters?.sublist(parameterTypes.length)); | 2110 optionalTypes, parameters?.sublist(parameterTypes.length)); |
2082 return [rt, ra, oa]; | 2111 typeParts = [rt, ra, oa]; |
| 2112 } else { |
| 2113 typeParts = [rt, ra]; |
2083 } | 2114 } |
2084 return [rt, ra]; | 2115 |
| 2116 var typeFormals = type.typeFormals; |
| 2117 if (typeFormals.isNotEmpty && !lowerTypedef) { |
| 2118 // TODO(jmesserly): this is a suboptimal representation for universal |
| 2119 // function types (as callable functions). See discussion at: |
| 2120 // https://github.com/dart-lang/dev_compiler/issues/526 |
| 2121 var tf = _emitTypeFormals(typeFormals); |
| 2122 typeParts = [new JS.ArrowFun(tf, new JS.ArrayInitializer(typeParts))]; |
| 2123 } |
| 2124 return typeParts; |
2085 } | 2125 } |
2086 | 2126 |
2087 /// Emits a Dart [type] into code. | 2127 /// Emits a Dart [type] into code. |
2088 /// | 2128 /// |
2089 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a | 2129 /// If [lowerTypedef] is set, a typedef will be expanded as if it were a |
2090 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form | 2130 /// function type. Similarly if [lowerGeneric] is set, the `List$()` form |
2091 /// will be used instead of `List`. These flags are used when generating | 2131 /// will be used instead of `List`. These flags are used when generating |
2092 /// the definitions for typedefs and generic types, respectively. | 2132 /// the definitions for typedefs and generic types, respectively. |
2093 JS.Expression _emitTypeName(DartType type, | 2133 JS.Expression _emitTypeName(DartType type, |
2094 {bool lowerTypedef: false, bool lowerGeneric: false}) { | 2134 {bool lowerTypedef: false, bool lowerGeneric: false}) { |
2095 // The void and dynamic types are not defined in core. | 2135 // The void and dynamic types are not defined in core. |
2096 if (type.isVoid) { | 2136 if (type.isVoid) { |
2097 return js.call('dart.void'); | 2137 return js.call('dart.void'); |
2098 } else if (type.isDynamic) { | 2138 } else if (type.isDynamic) { |
2099 return js.call('dart.dynamic'); | 2139 return js.call('dart.dynamic'); |
2100 } else if (type.isBottom) { | 2140 } else if (type.isBottom) { |
2101 return js.call('dart.bottom'); | 2141 return js.call('dart.bottom'); |
2102 } | 2142 } |
2103 | 2143 |
2104 _loader.declareBeforeUse(type.element); | 2144 _loader.declareBeforeUse(type.element); |
2105 | 2145 |
2106 // TODO(jmesserly): like constants, should we hoist function types out of | 2146 // TODO(jmesserly): like constants, should we hoist function types out of |
2107 // methods? Similar issue with generic types. For all of these, we may want | 2147 // methods? Similar issue with generic types. For all of these, we may want |
2108 // to canonicalize them too, at least when inside the same library. | 2148 // to canonicalize them too, at least when inside the same library. |
2109 var name = type.name; | 2149 var name = type.name; |
2110 var element = type.element; | 2150 var element = type.element; |
2111 if (name == '' || name == null || lowerTypedef) { | 2151 if (name == '' || name == null || lowerTypedef) { |
2112 var parts = _emitFunctionTypeParts(type as FunctionType); | 2152 // TODO(jmesserly): should we change how typedefs work? They currently |
| 2153 // go through use similar logic as generic classes. This makes them |
| 2154 // different from universal function types. |
| 2155 var ft = type as FunctionType; |
| 2156 var parts = _emitFunctionTypeParts(ft, lowerTypedef: lowerTypedef); |
2113 return js.call('dart.functionType(#)', [parts]); | 2157 return js.call('dart.functionType(#)', [parts]); |
2114 } | 2158 } |
2115 // For now, reify generic method parameters as dynamic | |
2116 bool _isGenericTypeParameter(DartType type) => | |
2117 type is TypeParameterType && | |
2118 type.element.enclosingElement is! TypeDefiningElement; | |
2119 | |
2120 if (_isGenericTypeParameter(type)) { | |
2121 return js.call('dart.dynamic'); | |
2122 } | |
2123 | 2159 |
2124 if (type is TypeParameterType) { | 2160 if (type is TypeParameterType) { |
2125 return new JS.Identifier(name); | 2161 return new JS.Identifier(name); |
2126 } | 2162 } |
2127 | 2163 |
2128 if (type is ParameterizedType) { | 2164 if (type is ParameterizedType) { |
2129 var args = type.typeArguments; | 2165 var args = type.typeArguments; |
2130 Iterable jsArgs = null; | 2166 Iterable jsArgs = null; |
2131 if (args.any((a) => !a.isDynamic && !_isGenericTypeParameter(a))) { | 2167 if (args.any((a) => !a.isDynamic)) { |
2132 jsArgs = args.map(_emitTypeName); | 2168 jsArgs = args.map(_emitTypeName); |
2133 } else if (lowerGeneric) { | 2169 } else if (lowerGeneric) { |
2134 jsArgs = []; | 2170 jsArgs = []; |
2135 } | 2171 } |
2136 if (jsArgs != null) { | 2172 if (jsArgs != null) { |
2137 var genericName = _emitTopLevelName(element, suffix: '\$'); | 2173 var genericName = _emitTopLevelName(element, suffix: '\$'); |
2138 return js.call('#(#)', [genericName, jsArgs]); | 2174 return js.call('#(#)', [genericName, jsArgs]); |
2139 } | 2175 } |
2140 } | 2176 } |
2141 | 2177 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2261 return new JS.Block(stmts); | 2297 return new JS.Block(stmts); |
2262 } | 2298 } |
2263 | 2299 |
2264 @override | 2300 @override |
2265 JS.Block visitBlock(Block node) => | 2301 JS.Block visitBlock(Block node) => |
2266 new JS.Block(_visitList(node.statements) as List<JS.Statement>, | 2302 new JS.Block(_visitList(node.statements) as List<JS.Statement>, |
2267 isScope: true); | 2303 isScope: true); |
2268 | 2304 |
2269 @override | 2305 @override |
2270 visitMethodInvocation(MethodInvocation node) { | 2306 visitMethodInvocation(MethodInvocation node) { |
2271 if (node.operator != null && node.operator.lexeme == '?.') { | 2307 if (node.operator?.lexeme == '?.') { |
2272 return _emitNullSafe(node); | 2308 return _emitNullSafe(node); |
2273 } | 2309 } |
2274 | 2310 |
2275 var target = _getTarget(node); | |
2276 var result = _emitForeignJS(node); | 2311 var result = _emitForeignJS(node); |
2277 if (result != null) return result; | 2312 if (result != null) return result; |
2278 | 2313 |
2279 String code; | 2314 var target = _getTarget(node); |
2280 if (target == null || isLibraryPrefix(target)) { | 2315 if (target == null || isLibraryPrefix(target)) { |
2281 if (DynamicInvoke.get(node.methodName)) { | 2316 return _emitFunctionCall(node); |
2282 code = 'dart.dcall(#, #)'; | |
2283 } else { | |
2284 code = '#(#)'; | |
2285 } | |
2286 return js | |
2287 .call(code, [_visit(node.methodName), _visit(node.argumentList)]); | |
2288 } | 2317 } |
2289 | 2318 |
| 2319 return _emitMethodCall(target, node); |
| 2320 } |
| 2321 |
| 2322 /// Emits a (possibly generic) instance method call. |
| 2323 JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { |
2290 var type = getStaticType(target); | 2324 var type = getStaticType(target); |
2291 var name = node.methodName.name; | 2325 var name = node.methodName.name; |
2292 var element = node.methodName.staticElement; | 2326 var element = node.methodName.staticElement; |
2293 bool isStatic = element is ExecutableElement && element.isStatic; | 2327 bool isStatic = element is ExecutableElement && element.isStatic; |
2294 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 2328 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
2295 | 2329 |
| 2330 JS.Expression jsTarget = _visit(target); |
| 2331 var typeArgs = _emitInvokeTypeArguments(node); |
| 2332 List<JS.Expression> args = _visit(node.argumentList); |
2296 if (DynamicInvoke.get(target)) { | 2333 if (DynamicInvoke.get(target)) { |
2297 code = 'dart.dsend(#, #, #)'; | 2334 if (typeArgs != null) { |
2298 } else if (DynamicInvoke.get(node.methodName)) { | 2335 return js.call('dart.dgsend(#, [#], #, #)', |
| 2336 [jsTarget, typeArgs, memberName, args]); |
| 2337 } else { |
| 2338 return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); |
| 2339 } |
| 2340 } |
| 2341 if (_isObjectMemberCall(target, name)) { |
| 2342 // Object methods require a helper for null checks & native types. |
| 2343 assert(typeArgs == null); // Object methods don't take type args. |
| 2344 return js.call('dart.#(#, #)', [memberName, jsTarget, args]); |
| 2345 } |
| 2346 |
| 2347 jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
| 2348 if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
| 2349 |
| 2350 if (DynamicInvoke.get(node.methodName)) { |
2299 // This is a dynamic call to a statically known target. For example: | 2351 // This is a dynamic call to a statically known target. For example: |
2300 // class Foo { Function bar; } | 2352 // class Foo { Function bar; } |
2301 // new Foo().bar(); // dynamic call | 2353 // new Foo().bar(); // dynamic call |
2302 code = 'dart.dcall(#.#, #)'; | 2354 return js.call('dart.dcall(#, #)', [jsTarget, args]); |
2303 } else if (_isObjectMemberCall(target, name)) { | |
2304 // Object methods require a helper for null checks. | |
2305 return js.call('dart.#(#, #)', | |
2306 [memberName, _visit(target), _visit(node.argumentList)]); | |
2307 } else { | |
2308 code = '#.#(#)'; | |
2309 } | 2355 } |
2310 | 2356 |
2311 return js | 2357 return new JS.Call(jsTarget, args); |
2312 .call(code, [_visit(target), memberName, _visit(node.argumentList)]); | |
2313 } | 2358 } |
2314 | 2359 |
2315 /// Emits code for the `JS(...)` builtin. | 2360 /// Emits a function call, to a top-level function, local function, or |
| 2361 /// an expression. |
| 2362 JS.Expression _emitFunctionCall(InvocationExpression node) { |
| 2363 var fn = _visit(node.function); |
| 2364 var args = _visit(node.argumentList); |
| 2365 if (DynamicInvoke.get(node.function)) { |
| 2366 var typeArgs = _emitInvokeTypeArguments(node); |
| 2367 if (typeArgs != null) { |
| 2368 return js.call('dart.dgcall(#, [#], #)', [fn, typeArgs, args]); |
| 2369 } else { |
| 2370 return js.call('dart.dcall(#, #)', [fn, args]); |
| 2371 } |
| 2372 } else { |
| 2373 return new JS.Call(_applyInvokeTypeArguments(fn, node), args); |
| 2374 } |
| 2375 } |
| 2376 |
| 2377 JS.Expression _applyInvokeTypeArguments( |
| 2378 JS.Expression target, InvocationExpression node) { |
| 2379 var typeArgs = _emitInvokeTypeArguments(node); |
| 2380 if (typeArgs == null) return target; |
| 2381 return new JS.Call(target, typeArgs); |
| 2382 } |
| 2383 |
| 2384 List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) { |
| 2385 return _emitFunctionTypeArguments( |
| 2386 node.function.staticType, node.staticInvokeType); |
| 2387 } |
| 2388 |
| 2389 /// If `g` is a generic function type, and `f` is an instantiation of it, |
| 2390 /// then this will return the type arguments to apply, otherwise null. |
| 2391 List<JS.Expression> _emitFunctionTypeArguments(DartType g, DartType f) { |
| 2392 if (g is FunctionType && |
| 2393 g.typeFormals.isNotEmpty && |
| 2394 f is FunctionType && |
| 2395 f.typeFormals.isEmpty) { |
| 2396 return _recoverTypeArguments(g, f) |
| 2397 .map(_emitTypeName) |
| 2398 .toList(growable: false); |
| 2399 } |
| 2400 return null; |
| 2401 } |
| 2402 |
| 2403 /// Given a generic function type [g] and an instantiated function type [f], |
| 2404 /// find a list of type arguments TArgs such that `g<TArgs> == f`, |
| 2405 /// and return TArgs. |
| 2406 /// |
| 2407 /// This function must be called with type [f] that was instantiated from [g]. |
| 2408 Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) { |
| 2409 // TODO(jmesserly): this design is a bit unfortunate. It would be nice if |
| 2410 // resolution could simply create a synthetic type argument list. |
| 2411 assert(identical(g.element, f.element)); |
| 2412 assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty); |
| 2413 assert(g.typeFormals.length + g.typeArguments.length == |
| 2414 f.typeArguments.length); |
| 2415 |
| 2416 // Instantiation in Analyzer works like this: |
| 2417 // Given: |
| 2418 // {U/T} <S> T -> S |
| 2419 // Where {U/T} represents the typeArguments (U) and typeParameters (T) list, |
| 2420 // and <S> represents the typeFormals. |
| 2421 // |
| 2422 // Now instantiate([V]), and the result should be: |
| 2423 // {U/T, V/S} T -> S. |
| 2424 // |
| 2425 // Therefore, we can recover the typeArguments from our instantiated |
| 2426 // function. |
| 2427 return f.typeArguments.skip(g.typeArguments.length); |
| 2428 } |
| 2429 |
| 2430 /// Emits code for the `JS(...)` macro. |
2316 _emitForeignJS(MethodInvocation node) { | 2431 _emitForeignJS(MethodInvocation node) { |
2317 var e = node.methodName.staticElement; | 2432 var e = node.methodName.staticElement; |
2318 if (isInlineJS(e)) { | 2433 if (isInlineJS(e)) { |
2319 var args = node.argumentList.arguments; | 2434 var args = node.argumentList.arguments; |
2320 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 2435 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
2321 var code = args[1]; | 2436 var code = args[1]; |
2322 var templateArgs; | 2437 var templateArgs; |
2323 var source; | 2438 var source; |
2324 if (code is StringInterpolation) { | 2439 if (code is StringInterpolation) { |
2325 if (args.length > 2) { | 2440 if (args.length > 2) { |
(...skipping 18 matching lines...) Expand all Loading... |
2344 var result = template.instantiate(_visitList(templateArgs)); | 2459 var result = template.instantiate(_visitList(templateArgs)); |
2345 // `throw` is emitted as a statement by `parseForeignJS`. | 2460 // `throw` is emitted as a statement by `parseForeignJS`. |
2346 assert(result is JS.Expression || node.parent is ExpressionStatement); | 2461 assert(result is JS.Expression || node.parent is ExpressionStatement); |
2347 return result; | 2462 return result; |
2348 } | 2463 } |
2349 return null; | 2464 return null; |
2350 } | 2465 } |
2351 | 2466 |
2352 @override | 2467 @override |
2353 JS.Expression visitFunctionExpressionInvocation( | 2468 JS.Expression visitFunctionExpressionInvocation( |
2354 FunctionExpressionInvocation node) { | 2469 FunctionExpressionInvocation node) => |
2355 var code; | 2470 _emitFunctionCall(node); |
2356 if (DynamicInvoke.get(node.function)) { | |
2357 code = 'dart.dcall(#, #)'; | |
2358 } else { | |
2359 code = '#(#)'; | |
2360 } | |
2361 return js.call(code, [_visit(node.function), _visit(node.argumentList)]); | |
2362 } | |
2363 | 2471 |
2364 @override | 2472 @override |
2365 List<JS.Expression> visitArgumentList(ArgumentList node) { | 2473 List<JS.Expression> visitArgumentList(ArgumentList node) { |
2366 var args = <JS.Expression>[]; | 2474 var args = <JS.Expression>[]; |
2367 var named = <JS.Property>[]; | 2475 var named = <JS.Property>[]; |
2368 for (var arg in node.arguments) { | 2476 for (var arg in node.arguments) { |
2369 if (arg is NamedExpression) { | 2477 if (arg is NamedExpression) { |
2370 named.add(_visit(arg)); | 2478 named.add(_visit(arg)); |
2371 } else if (arg is MethodInvocation && isJsSpreadInvocation(arg)) { | 2479 } else if (arg is MethodInvocation && isJsSpreadInvocation(arg)) { |
2372 args.add( | 2480 args.add( |
(...skipping 1309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3682 throw 'Unimplemented ${node.runtimeType}: $node'; | 3790 throw 'Unimplemented ${node.runtimeType}: $node'; |
3683 } | 3791 } |
3684 | 3792 |
3685 _visit(AstNode node) { | 3793 _visit(AstNode node) { |
3686 if (node == null) return null; | 3794 if (node == null) return null; |
3687 var result = node.accept(this); | 3795 var result = node.accept(this); |
3688 if (result is JS.Node) result = annotate(result, node); | 3796 if (result is JS.Node) result = annotate(result, node); |
3689 return result; | 3797 return result; |
3690 } | 3798 } |
3691 | 3799 |
3692 // TODO(jmesserly): this will need to be a generic method, if we ever want to | 3800 List/*<T>*/ _visitList/*<T extends AstNode>*/(Iterable/*<T>*/ nodes) { |
3693 // self-host strong mode. | |
3694 List/*<T>*/ _visitList/*<T>*/(Iterable<AstNode> nodes) { | |
3695 if (nodes == null) return null; | 3801 if (nodes == null) return null; |
3696 var result = /*<T>*/ []; | 3802 var result = /*<T>*/ []; |
3697 for (var node in nodes) result.add(_visit(node)); | 3803 for (var node in nodes) result.add(_visit(node)); |
3698 return result; | 3804 return result; |
3699 } | 3805 } |
3700 | 3806 |
3701 /// Visits a list of expressions, creating a comma expression if needed in JS. | 3807 /// Visits a list of expressions, creating a comma expression if needed in JS. |
3702 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 3808 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
3703 if (nodes == null || nodes.isEmpty) return null; | 3809 if (nodes == null || nodes.isEmpty) return null; |
3704 return new JS.Expression.binary( | 3810 return new JS.Expression.binary( |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3822 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 3928 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
3823 | 3929 |
3824 /// Returns the canonical name to refer to the Dart library. | 3930 /// Returns the canonical name to refer to the Dart library. |
3825 JS.Identifier emitLibraryName(LibraryElement library) { | 3931 JS.Identifier emitLibraryName(LibraryElement library) { |
3826 // It's either one of the libraries in this module, or it's an import. | 3932 // It's either one of the libraries in this module, or it's an import. |
3827 return _libraries[library] ?? | 3933 return _libraries[library] ?? |
3828 _imports.putIfAbsent(library, | 3934 _imports.putIfAbsent(library, |
3829 () => new JS.TemporaryId(jsLibraryName(_buildRoot, library))); | 3935 () => new JS.TemporaryId(jsLibraryName(_buildRoot, library))); |
3830 } | 3936 } |
3831 | 3937 |
3832 JS.Node annotate(JS.Node node, AstNode original, [Element element]) { | 3938 JS.Node/*=T*/ annotate/*<T extends JS.Node>*/( |
| 3939 JS.Node/*=T*/ node, AstNode original, |
| 3940 [Element element]) { |
3833 if (options.closure && element != null) { | 3941 if (options.closure && element != null) { |
3834 node = node.withClosureAnnotation(closureAnnotationFor( | 3942 node = node.withClosureAnnotation(closureAnnotationFor( |
3835 node, original, element, namedArgumentTemp.name)); | 3943 node, original, element, namedArgumentTemp.name)); |
3836 } | 3944 } |
3837 return node..sourceInformation = original; | 3945 return node..sourceInformation = original; |
3838 } | 3946 } |
3839 | 3947 |
3840 /// Returns true if this is any kind of object represented by `Number` in JS. | 3948 /// Returns true if this is any kind of object represented by `Number` in JS. |
3841 /// | 3949 /// |
3842 /// In practice, this is 4 types: num, int, double, and JSNumber. | 3950 /// In practice, this is 4 types: num, int, double, and JSNumber. |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3949 } | 4057 } |
3950 | 4058 |
3951 bool isLibraryPrefix(Expression node) => | 4059 bool isLibraryPrefix(Expression node) => |
3952 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4060 node is SimpleIdentifier && node.staticElement is PrefixElement; |
3953 | 4061 |
3954 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4062 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
3955 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4063 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
3956 | 4064 |
3957 bool _isDartRuntime(LibraryElement l) => | 4065 bool _isDartRuntime(LibraryElement l) => |
3958 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4066 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
OLD | NEW |