@@ -1116,7 +1116,8 @@ class CodeGenerator extends GeneralizingAstVisitor
1116
1116
for (ConstructorDeclaration node in ctors) {
1117
1117
var memberName = _constructorName (node.element);
1118
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
1121
var property =
1121
1122
new JS .Property (memberName, new JS .ArrayInitializer (parts));
1122
1123
tCtors.add (property);
@@ -1630,6 +1631,8 @@ class CodeGenerator extends GeneralizingAstVisitor
1630
1631
// call sites, but it's cleaner to instead transform the operator method.
1631
1632
fn = _alwaysReturnLastParameter (fn);
1632
1633
}
1634
+
1635
+ fn = _makeGenericFunction (fn);
1633
1636
}
1634
1637
1635
1638
return annotate (
@@ -1780,15 +1783,21 @@ class CodeGenerator extends GeneralizingAstVisitor
1780
1783
var lazy = topLevel && ! _typeIsLoaded (type);
1781
1784
1782
1785
if (type is FunctionType && (name == '' || name == null )) {
1783
- if (type.returnType.isDynamic &&
1786
+ if (type.typeFormals.isEmpty &&
1787
+ type.returnType.isDynamic &&
1784
1788
type.optionalParameterTypes.isEmpty &&
1785
1789
type.namedParameterTypes.isEmpty &&
1786
1790
type.normalParameterTypes.every ((t) => t.isDynamic)) {
1787
1791
return js.call ('dart.fn(#)' , [fn]);
1788
1792
}
1789
1793
1790
- String code = lazy ? '() => dart.definiteFunctionType(#)' : '#' ;
1791
- return js.call ('dart.fn(#, $code )' , [fn, _emitFunctionTypeParts (type)]);
1794
+ var parts = _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
1802
throw 'Function has non function type: $type ' ;
1794
1803
}
@@ -1823,10 +1832,29 @@ class CodeGenerator extends GeneralizingAstVisitor
1823
1832
1824
1833
// Convert `function(...) { ... }` to `(...) => ...`
1825
1834
// This is for readability, but it also ensures correct `this` binding.
1826
- return annotate (
1827
- new JS .ArrowFun (f.params, body,
1828
- typeParams: f.typeParams, returnType: f.returnType),
1829
- node);
1835
+ var fn = new JS .ArrowFun (f.params, body,
1836
+ typeParams: f.typeParams, returnType: f.returnType);
1837
+
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
1860
/// Emits a non-arrow FunctionExpression node.
@@ -1838,25 +1866,27 @@ class CodeGenerator extends GeneralizingAstVisitor
1838
1866
/// Contrast with [visitFunctionExpression] .
1839
1867
JS .Fun _emitFunction (FunctionExpression node) {
1840
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
1872
JS .Fun _emitFunctionBody (ExecutableElement element,
1845
1873
FormalParameterList parameters, FunctionBody body) {
1846
- var returnType = emitTypeRef ( element.returnType) ;
1874
+ FunctionType type = element.type ;
1847
1875
1848
1876
// sync*, async, async*
1849
1877
if (element.isAsynchronous || element.isGenerator) {
1850
1878
return new JS .Fun (
1851
1879
visitFormalParameterList (parameters, destructure: false ),
1852
- js.statement ('{ return #; }' ,
1853
- [_emitGeneratorFunctionBody (element, parameters, body)]),
1854
- returnType: returnType);
1880
+ new JS .Block ([
1881
+ _emitGeneratorFunctionBody (element, parameters, body).toReturn ()
1882
+ ]),
1883
+ returnType: emitTypeRef (type.returnType));
1855
1884
}
1885
+
1856
1886
// normal function (sync)
1857
1887
return new JS .Fun (visitFormalParameterList (parameters), _visit (body),
1858
- typeParams: _emitTypeFormals (element.typeParameters ),
1859
- returnType: returnType);
1888
+ typeParams: _emitTypeFormals (type.typeFormals ),
1889
+ returnType: emitTypeRef (type. returnType) );
1860
1890
}
1861
1891
1862
1892
JS .Expression _emitGeneratorFunctionBody (ExecutableElement element,
@@ -2061,27 +2091,37 @@ class CodeGenerator extends GeneralizingAstVisitor
2061
2091
/// Emit the pieces of a function type, as an array of return type,
2062
2092
/// regular args, and optional/named args.
2063
2093
List <JS .Expression > _emitFunctionTypeParts (FunctionType type,
2064
- [FormalParameterList parameterList]) {
2065
- var parameters = parameterList? .parameters;
2066
- var returnType = type.returnType;
2094
+ {List <FormalParameter > parameters, bool lowerTypedef: false }) {
2067
2095
var parameterTypes = type.normalParameterTypes;
2068
2096
var optionalTypes = type.optionalParameterTypes;
2069
2097
var namedTypes = type.namedParameterTypes;
2070
- var rt = _emitTypeName (returnType);
2098
+ var rt = _emitTypeName (type. returnType);
2071
2099
var ra = _emitTypeNames (parameterTypes, parameters);
2072
- if (! namedTypes.isEmpty) {
2100
+
2101
+ List <JS .Expression > typeParts;
2102
+ if (namedTypes.isNotEmpty) {
2073
2103
assert (optionalTypes.isEmpty);
2074
2104
// TODO(vsm): Pass in annotations here as well.
2075
2105
var na = _emitTypeProperties (namedTypes);
2076
- return [rt, ra, na];
2077
- }
2078
- if (! optionalTypes.isEmpty) {
2106
+ typeParts = [rt, ra, na];
2107
+ } else if (optionalTypes.isNotEmpty) {
2079
2108
assert (namedTypes.isEmpty);
2080
2109
var oa = _emitTypeNames (
2081
2110
optionalTypes, parameters? .sublist (parameterTypes.length));
2082
- return [rt, ra, oa];
2111
+ typeParts = [rt, ra, oa];
2112
+ } else {
2113
+ typeParts = [rt, ra];
2114
+ }
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))];
2083
2123
}
2084
- return [rt, ra] ;
2124
+ return typeParts ;
2085
2125
}
2086
2126
2087
2127
/// Emits a Dart [type] into code.
@@ -2109,17 +2149,13 @@ class CodeGenerator extends GeneralizingAstVisitor
2109
2149
var name = type.name;
2110
2150
var element = type.element;
2111
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
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
2160
if (type is TypeParameterType ) {
2125
2161
return new JS .Identifier (name);
@@ -2128,7 +2164,7 @@ class CodeGenerator extends GeneralizingAstVisitor
2128
2164
if (type is ParameterizedType ) {
2129
2165
var args = type.typeArguments;
2130
2166
Iterable jsArgs = null ;
2131
- if (args.any ((a) => ! a.isDynamic && ! _isGenericTypeParameter (a) )) {
2167
+ if (args.any ((a) => ! a.isDynamic)) {
2132
2168
jsArgs = args.map (_emitTypeName);
2133
2169
} else if (lowerGeneric) {
2134
2170
jsArgs = [];
@@ -2268,51 +2304,130 @@ class CodeGenerator extends GeneralizingAstVisitor
2268
2304
2269
2305
@override
2270
2306
visitMethodInvocation (MethodInvocation node) {
2271
- if (node.operator != null && node. operator .lexeme == '?.' ) {
2307
+ if (node.operator ? .lexeme == '?.' ) {
2272
2308
return _emitNullSafe (node);
2273
2309
}
2274
2310
2275
- var target = _getTarget (node);
2276
2311
var result = _emitForeignJS (node);
2277
2312
if (result != null ) return result;
2278
2313
2279
- String code ;
2314
+ var target = _getTarget (node) ;
2280
2315
if (target == null || isLibraryPrefix (target)) {
2281
- if (DynamicInvoke .get (node.methodName)) {
2282
- code = 'dart.dcall(#, #)' ;
2283
- } else {
2284
- code = '#(#)' ;
2285
- }
2286
- return js
2287
- .call (code, [_visit (node.methodName), _visit (node.argumentList)]);
2316
+ return _emitFunctionCall (node);
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
2324
var type = getStaticType (target);
2291
2325
var name = node.methodName.name;
2292
2326
var element = node.methodName.staticElement;
2293
2327
bool isStatic = element is ExecutableElement && element.isStatic;
2294
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
2333
if (DynamicInvoke .get (target)) {
2297
- code = 'dart.dsend(#, #, #)' ;
2298
- } else if (DynamicInvoke .get (node.methodName)) {
2334
+ if (typeArgs != null ) {
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
2351
// This is a dynamic call to a statically known target. For example:
2300
2352
// class Foo { Function bar; }
2301
2353
// new Foo().bar(); // dynamic call
2302
- code = 'dart.dcall(#.#, #)' ;
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)]);
2354
+ return js.call ('dart.dcall(#, #)' , [jsTarget, args]);
2355
+ }
2356
+
2357
+ return new JS .Call (jsTarget, args);
2358
+ }
2359
+
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
+ }
2307
2372
} else {
2308
- code = '#.#(#)' ;
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 );
2309
2399
}
2400
+ return null ;
2401
+ }
2310
2402
2311
- return js
2312
- .call (code, [_visit (target), memberName, _visit (node.argumentList)]);
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);
2313
2428
}
2314
2429
2315
- /// Emits code for the `JS(...)` builtin .
2430
+ /// Emits code for the `JS(...)` macro .
2316
2431
_emitForeignJS (MethodInvocation node) {
2317
2432
var e = node.methodName.staticElement;
2318
2433
if (isInlineJS (e)) {
@@ -2351,15 +2466,8 @@ class CodeGenerator extends GeneralizingAstVisitor
2351
2466
2352
2467
@override
2353
2468
JS .Expression visitFunctionExpressionInvocation (
2354
- FunctionExpressionInvocation node) {
2355
- var code;
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
- }
2469
+ FunctionExpressionInvocation node) =>
2470
+ _emitFunctionCall (node);
2363
2471
2364
2472
@override
2365
2473
List <JS .Expression > visitArgumentList (ArgumentList node) {
@@ -3689,9 +3797,7 @@ class CodeGenerator extends GeneralizingAstVisitor
3689
3797
return result;
3690
3798
}
3691
3799
3692
- // TODO(jmesserly): this will need to be a generic method, if we ever want to
3693
- // self-host strong mode.
3694
- List /*<T>*/ _visitList/*<T>*/ (Iterable <AstNode > nodes) {
3800
+ List /*<T>*/ _visitList/*<T extends AstNode>*/ (Iterable /*<T>*/ nodes) {
3695
3801
if (nodes == null ) return null ;
3696
3802
var result = /*<T>*/ [];
3697
3803
for (var node in nodes) result.add (_visit (node));
@@ -3829,7 +3935,9 @@ class CodeGenerator extends GeneralizingAstVisitor
3829
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
3941
if (options.closure && element != null ) {
3834
3942
node = node.withClosureAnnotation (closureAnnotationFor (
3835
3943
node, original, element, namedArgumentTemp.name));
0 commit comments