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