Index: lib/src/compiler/code_generator.dart |
diff --git a/lib/src/compiler/code_generator.dart b/lib/src/compiler/code_generator.dart |
index bfc7dc95e960e65c47c14f4b018e088ee168da51..8e1f4fb36536aaf466bc9a967c1747bf3b5ef93f 100644 |
--- a/lib/src/compiler/code_generator.dart |
+++ b/lib/src/compiler/code_generator.dart |
@@ -1116,7 +1116,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
for (ConstructorDeclaration node in ctors) { |
var memberName = _constructorName(node.element); |
var element = node.element; |
- var parts = _emitFunctionTypeParts(element.type, node.parameters); |
+ var parts = _emitFunctionTypeParts(element.type, |
+ parameters: node.parameters.parameters); |
var property = |
new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
tCtors.add(property); |
@@ -1630,6 +1631,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
// call sites, but it's cleaner to instead transform the operator method. |
fn = _alwaysReturnLastParameter(fn); |
} |
+ |
+ fn = _makeGenericFunction(fn); |
} |
return annotate( |
@@ -1780,15 +1783,21 @@ class CodeGenerator extends GeneralizingAstVisitor |
var lazy = topLevel && !_typeIsLoaded(type); |
if (type is FunctionType && (name == '' || name == null)) { |
- if (type.returnType.isDynamic && |
+ if (type.typeFormals.isEmpty && |
+ type.returnType.isDynamic && |
type.optionalParameterTypes.isEmpty && |
type.namedParameterTypes.isEmpty && |
type.normalParameterTypes.every((t) => t.isDynamic)) { |
return js.call('dart.fn(#)', [fn]); |
} |
- String code = lazy ? '() => dart.definiteFunctionType(#)' : '#'; |
- return js.call('dart.fn(#, $code)', [fn, _emitFunctionTypeParts(type)]); |
+ var parts = _emitFunctionTypeParts(type); |
+ if (lazy) { |
+ return js.call( |
+ 'dart.lazyFn(#, () => #)', [fn, new JS.ArrayInitializer(parts)]); |
+ } else { |
+ return js.call('dart.fn(#, #)', [fn, parts]); |
+ } |
} |
throw 'Function has non function type: $type'; |
} |
@@ -1823,10 +1832,29 @@ class CodeGenerator extends GeneralizingAstVisitor |
// Convert `function(...) { ... }` to `(...) => ...` |
// This is for readability, but it also ensures correct `this` binding. |
- return annotate( |
- new JS.ArrowFun(f.params, body, |
- typeParams: f.typeParams, returnType: f.returnType), |
- node); |
+ var fn = new JS.ArrowFun(f.params, body, |
+ typeParams: f.typeParams, returnType: f.returnType); |
+ |
+ return annotate(_makeGenericFunction(fn), node); |
+ } |
+ |
+ JS.FunctionExpression/*=T*/ _makeGenericFunction/*<T extends JS.FunctionExpression>*/( |
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
|
+ JS.FunctionExpression/*=T*/ fn) { |
+ if (fn.typeParams == null || fn.typeParams.isEmpty) return fn; |
+ |
+ // TODO(jmesserly): we could make these default to `dynamic`. |
+ var typeParams = fn.typeParams; |
+ if (fn is JS.ArrowFun) { |
+ return new JS.ArrowFun(typeParams, fn); |
+ } |
+ var f = fn as JS.Fun; |
+ 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:
*
|
+ typeParams, |
+ new JS.Block([ |
+ // Convert the function to an => function, to ensure `this` binding. |
+ new JS.Return(new JS.ArrowFun(f.params, f.body, |
+ typeParams: f.typeParams, returnType: f.returnType)) |
+ ])); |
} |
/// Emits a non-arrow FunctionExpression node. |
@@ -1838,25 +1866,27 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// Contrast with [visitFunctionExpression]. |
JS.Fun _emitFunction(FunctionExpression node) { |
var fn = _emitFunctionBody(node.element, node.parameters, node.body); |
- return annotate(fn, node); |
+ return annotate(_makeGenericFunction(fn), node); |
} |
JS.Fun _emitFunctionBody(ExecutableElement element, |
FormalParameterList parameters, FunctionBody body) { |
- var returnType = emitTypeRef(element.returnType); |
+ FunctionType type = element.type; |
// sync*, async, async* |
if (element.isAsynchronous || element.isGenerator) { |
return new JS.Fun( |
visitFormalParameterList(parameters, destructure: false), |
- js.statement('{ return #; }', |
- [_emitGeneratorFunctionBody(element, parameters, body)]), |
- returnType: returnType); |
+ new JS.Block([ |
+ _emitGeneratorFunctionBody(element, parameters, body).toReturn() |
+ ]), |
+ returnType: emitTypeRef(type.returnType)); |
} |
+ |
// normal function (sync) |
return new JS.Fun(visitFormalParameterList(parameters), _visit(body), |
- typeParams: _emitTypeFormals(element.typeParameters), |
- returnType: returnType); |
+ typeParams: _emitTypeFormals(type.typeFormals), |
+ returnType: emitTypeRef(type.returnType)); |
} |
JS.Expression _emitGeneratorFunctionBody(ExecutableElement element, |
@@ -2061,27 +2091,37 @@ class CodeGenerator extends GeneralizingAstVisitor |
/// Emit the pieces of a function type, as an array of return type, |
/// regular args, and optional/named args. |
List<JS.Expression> _emitFunctionTypeParts(FunctionType type, |
- [FormalParameterList parameterList]) { |
- var parameters = parameterList?.parameters; |
- var returnType = type.returnType; |
+ {List<FormalParameter> parameters, bool lowerTypedef: false}) { |
var parameterTypes = type.normalParameterTypes; |
var optionalTypes = type.optionalParameterTypes; |
var namedTypes = type.namedParameterTypes; |
- var rt = _emitTypeName(returnType); |
+ var rt = _emitTypeName(type.returnType); |
var ra = _emitTypeNames(parameterTypes, parameters); |
- if (!namedTypes.isEmpty) { |
+ |
+ List<JS.Expression> typeParts; |
+ if (namedTypes.isNotEmpty) { |
assert(optionalTypes.isEmpty); |
// TODO(vsm): Pass in annotations here as well. |
var na = _emitTypeProperties(namedTypes); |
- return [rt, ra, na]; |
- } |
- if (!optionalTypes.isEmpty) { |
+ typeParts = [rt, ra, na]; |
+ } else if (optionalTypes.isNotEmpty) { |
assert(namedTypes.isEmpty); |
var oa = _emitTypeNames( |
optionalTypes, parameters?.sublist(parameterTypes.length)); |
- return [rt, ra, oa]; |
+ typeParts = [rt, ra, oa]; |
+ } else { |
+ typeParts = [rt, ra]; |
+ } |
+ |
+ var typeFormals = type.typeFormals; |
+ if (typeFormals.isNotEmpty && !lowerTypedef) { |
+ // TODO(jmesserly): this is a suboptimal representation for universal |
+ // function types (as callable functions). See discussion at: |
+ // https://github.com/dart-lang/dev_compiler/issues/526 |
+ var tf = _emitTypeFormals(typeFormals); |
+ typeParts = [new JS.ArrowFun(tf, new JS.ArrayInitializer(typeParts))]; |
} |
- return [rt, ra]; |
+ return typeParts; |
} |
/// Emits a Dart [type] into code. |
@@ -2109,17 +2149,13 @@ class CodeGenerator extends GeneralizingAstVisitor |
var name = type.name; |
var element = type.element; |
if (name == '' || name == null || lowerTypedef) { |
- var parts = _emitFunctionTypeParts(type as FunctionType); |
+ // TODO(jmesserly): should we change how typedefs work? They currently |
+ // go through use similar logic as generic classes. This makes them |
+ // 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
|
+ var ft = type as FunctionType; |
+ var parts = _emitFunctionTypeParts(ft, lowerTypedef: lowerTypedef); |
return js.call('dart.functionType(#)', [parts]); |
} |
- // For now, reify generic method parameters as dynamic |
- bool _isGenericTypeParameter(DartType type) => |
- type is TypeParameterType && |
- type.element.enclosingElement is! TypeDefiningElement; |
- |
- if (_isGenericTypeParameter(type)) { |
- return js.call('dart.dynamic'); |
- } |
if (type is TypeParameterType) { |
return new JS.Identifier(name); |
@@ -2128,7 +2164,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
if (type is ParameterizedType) { |
var args = type.typeArguments; |
Iterable jsArgs = null; |
- if (args.any((a) => !a.isDynamic && !_isGenericTypeParameter(a))) { |
+ if (args.any((a) => !a.isDynamic)) { |
jsArgs = args.map(_emitTypeName); |
} else if (lowerGeneric) { |
jsArgs = []; |
@@ -2268,51 +2304,130 @@ class CodeGenerator extends GeneralizingAstVisitor |
@override |
visitMethodInvocation(MethodInvocation node) { |
- if (node.operator != null && node.operator.lexeme == '?.') { |
+ if (node.operator?.lexeme == '?.') { |
return _emitNullSafe(node); |
} |
- var target = _getTarget(node); |
var result = _emitForeignJS(node); |
if (result != null) return result; |
- String code; |
+ var target = _getTarget(node); |
if (target == null || isLibraryPrefix(target)) { |
- if (DynamicInvoke.get(node.methodName)) { |
- code = 'dart.dcall(#, #)'; |
- } else { |
- code = '#(#)'; |
- } |
- return js |
- .call(code, [_visit(node.methodName), _visit(node.argumentList)]); |
+ return _emitFunctionCall(node); |
} |
+ return _emitMethodCall(target, node); |
+ } |
+ |
+ /// Emits a (possibly generic) instance method call. |
+ JS.Expression _emitMethodCall(Expression target, MethodInvocation node) { |
var type = getStaticType(target); |
var name = node.methodName.name; |
var element = node.methodName.staticElement; |
bool isStatic = element is ExecutableElement && element.isStatic; |
var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
+ JS.Expression jsTarget = _visit(target); |
+ var typeArgs = _emitInvokeTypeArguments(node); |
+ List<JS.Expression> args = _visit(node.argumentList); |
if (DynamicInvoke.get(target)) { |
- code = 'dart.dsend(#, #, #)'; |
- } else if (DynamicInvoke.get(node.methodName)) { |
+ if (typeArgs != null) { |
+ return js.call('dart.dgsend(#, [#], #, #)', |
+ [jsTarget, typeArgs, memberName, args]); |
+ } else { |
+ return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]); |
+ } |
+ } |
+ if (_isObjectMemberCall(target, name)) { |
+ // Object methods require a helper for null checks & native types. |
+ assert(typeArgs == null); // Object methods don't take type args. |
+ return js.call('dart.#(#, #)', [memberName, jsTarget, args]); |
+ } |
+ |
+ jsTarget = new JS.PropertyAccess(jsTarget, memberName); |
+ if (typeArgs != null) jsTarget = new JS.Call(jsTarget, typeArgs); |
+ |
+ if (DynamicInvoke.get(node.methodName)) { |
// This is a dynamic call to a statically known target. For example: |
// class Foo { Function bar; } |
// new Foo().bar(); // dynamic call |
- code = 'dart.dcall(#.#, #)'; |
- } else if (_isObjectMemberCall(target, name)) { |
- // Object methods require a helper for null checks. |
- return js.call('dart.#(#, #)', |
- [memberName, _visit(target), _visit(node.argumentList)]); |
+ return js.call('dart.dcall(#, #)', [jsTarget, args]); |
+ } |
+ |
+ return new JS.Call(jsTarget, args); |
+ } |
+ |
+ /// Emits a function call, to a top-level function, local function, or |
+ /// an expression. |
+ JS.Expression _emitFunctionCall(InvocationExpression node) { |
+ var fn = _visit(node.function); |
+ var args = _visit(node.argumentList); |
+ if (DynamicInvoke.get(node.function)) { |
+ var typeArgs = _emitInvokeTypeArguments(node); |
+ if (typeArgs != null) { |
+ return js.call('dart.dgcall(#, [#], #)', [fn, typeArgs, args]); |
+ } else { |
+ return js.call('dart.dcall(#, #)', [fn, args]); |
+ } |
} else { |
- code = '#.#(#)'; |
+ return new JS.Call(_applyInvokeTypeArguments(fn, node), args); |
+ } |
+ } |
+ |
+ JS.Expression _applyInvokeTypeArguments( |
+ JS.Expression target, InvocationExpression node) { |
+ var typeArgs = _emitInvokeTypeArguments(node); |
+ if (typeArgs == null) return target; |
+ return new JS.Call(target, typeArgs); |
+ } |
+ |
+ List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) { |
+ return _emitFunctionTypeArguments( |
+ node.function.staticType, node.staticInvokeType); |
+ } |
+ |
+ /// If `g` is a generic function type, and `f` is an instantiation of it, |
+ /// then this will return the type arguments to apply, otherwise null. |
+ List<JS.Expression> _emitFunctionTypeArguments(DartType g, DartType f) { |
+ if (g is FunctionType && |
+ g.typeFormals.isNotEmpty && |
+ f is FunctionType && |
+ f.typeFormals.isEmpty) { |
+ return _recoverTypeArguments(g, f) |
+ .map(_emitTypeName) |
+ .toList(growable: false); |
} |
+ return null; |
+ } |
- return js |
- .call(code, [_visit(target), memberName, _visit(node.argumentList)]); |
+ /// Given a generic function type [g] and an instantiated function type [f], |
+ /// find a list of type arguments TArgs such that `g<TArgs> == f`, |
+ /// and return TArgs. |
+ /// |
+ /// This function must be called with type [f] that was instantiated from [g]. |
+ Iterable<DartType> _recoverTypeArguments(FunctionType g, FunctionType f) { |
+ // TODO(jmesserly): this design is a bit unfortunate. It would be nice if |
+ // resolution could simply create a synthetic type argument list. |
+ assert(identical(g.element, f.element)); |
+ assert(g.typeFormals.isNotEmpty && f.typeFormals.isEmpty); |
+ assert(g.typeFormals.length + g.typeArguments.length == |
+ f.typeArguments.length); |
+ |
+ // Instantiation in Analyzer works like this: |
+ // Given: |
+ // {U/T} <S> T -> S |
+ // Where {U/T} represents the typeArguments (U) and typeParameters (T) list, |
+ // and <S> represents the typeFormals. |
+ // |
+ // Now instantiate([V]), and the result should be: |
+ // {U/T, V/S} T -> S. |
+ // |
+ // Therefore, we can recover the typeArguments from our instantiated |
+ // function. |
+ return f.typeArguments.skip(g.typeArguments.length); |
} |
- /// Emits code for the `JS(...)` builtin. |
+ /// Emits code for the `JS(...)` macro. |
_emitForeignJS(MethodInvocation node) { |
var e = node.methodName.staticElement; |
if (isInlineJS(e)) { |
@@ -2351,15 +2466,8 @@ class CodeGenerator extends GeneralizingAstVisitor |
@override |
JS.Expression visitFunctionExpressionInvocation( |
- FunctionExpressionInvocation node) { |
- var code; |
- if (DynamicInvoke.get(node.function)) { |
- code = 'dart.dcall(#, #)'; |
- } else { |
- code = '#(#)'; |
- } |
- return js.call(code, [_visit(node.function), _visit(node.argumentList)]); |
- } |
+ FunctionExpressionInvocation node) => |
+ _emitFunctionCall(node); |
@override |
List<JS.Expression> visitArgumentList(ArgumentList node) { |
@@ -3689,9 +3797,7 @@ class CodeGenerator extends GeneralizingAstVisitor |
return result; |
} |
- // TODO(jmesserly): this will need to be a generic method, if we ever want to |
- // self-host strong mode. |
- List/*<T>*/ _visitList/*<T>*/(Iterable<AstNode> nodes) { |
+ List/*<T>*/ _visitList/*<T extends AstNode>*/(Iterable/*<T>*/ nodes) { |
if (nodes == null) return null; |
var result = /*<T>*/ []; |
for (var node in nodes) result.add(_visit(node)); |
@@ -3829,7 +3935,9 @@ class CodeGenerator extends GeneralizingAstVisitor |
() => new JS.TemporaryId(jsLibraryName(_buildRoot, library))); |
} |
- JS.Node annotate(JS.Node node, AstNode original, [Element element]) { |
+ JS.Node/*=T*/ annotate/*<T extends JS.Node>*/( |
+ JS.Node/*=T*/ node, AstNode original, |
+ [Element element]) { |
if (options.closure && element != null) { |
node = node.withClosureAnnotation(closureAnnotationFor( |
node, original, element, namedArgumentTemp.name)); |