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

Unified 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, 8 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/runtime/dart_sdk.js ('k') | lib/src/compiler/compiler.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..5ca2eeefe15d6b64e5bf03ca98029a01ce338fd9 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>*/(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(
+ 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.
+ 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));
« 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