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/*<T extends JS.FunctionExpres sion>*/( | |
vsm
2016/04/29 14:48:53
nit len
Jennifer Messerly
2016/04/29 17:18:44
fixed. I guess dartfmt can't help until we have ge
| |
1842 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( | |
vsm
2016/04/29 14:48:53
Could this be an ArrayFun?
Jennifer Messerly
2016/04/29 17:18:44
it preserves the kind of the incoming function:
*
| |
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. | |
vsm
2016/04/29 14:48:53
do you want to file a bug? is there a language op
Leaf
2016/04/29 15:55:03
There's active language discussion about how to so
Jennifer Messerly
2016/04/29 17:18:44
Haha, yeah. Funny when theory and implementation c
| |
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 |