Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: lib/src/compiler/code_generator.dart

Issue 1926283002: implement generic method runtime behavior, fixes #301 (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/runtime/dart_sdk.js ('k') | lib/src/compiler/compiler.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
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
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';
OLDNEW
« no previous file with comments | « lib/runtime/dart_sdk.js ('k') | lib/src/compiler/compiler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698