Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit 4d27e3f

Browse files
author
John Messerly
committed
instantiate generic tear-offs, fixes #525
[email protected] Review URL: https://codereview.chromium.org/1930323004 .
1 parent 8a23cd3 commit 4d27e3f

File tree

8 files changed

+142
-41
lines changed

8 files changed

+142
-41
lines changed

karma.conf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ module.exports = function(config) {
8383
},
8484
},
8585

86-
browsers: ['ChromeCanary', 'Electron'],
86+
browsers: ['Chrome'],
8787

8888
// Continuous Integration mode
8989
// if true, Karma captures browsers, runs the tests and exits

lib/runtime/dart_sdk.js

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ dart_library.library('dart_sdk', null, /* Imports */[
142142
dart.tag(f, sig);
143143
return f;
144144
};
145+
dart.gbind = function(f, ...typeArgs) {
146+
let result = f(...typeArgs);
147+
let sig = dart._getRuntimeType(f)(...typeArgs);
148+
dart.tag(result, sig);
149+
return result;
150+
};
145151
dart._setMethodSignature = function(f, sigF) {
146152
dart.defineMemoizedGetter(f, dart._methodSig, () => {
147153
let sigObj = sigF();
@@ -462,7 +468,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
462468
}
463469
}
464470
if (ftype === void 0) {
465-
ftype = dart.read(f);
471+
ftype = dart._getRuntimeType(f);
466472
}
467473
if (!ftype) {
468474
if (typeArgs != null) {
@@ -490,10 +496,10 @@ dart_library.library('dart_sdk', null, /* Imports */[
490496
dart.throwNoSuchMethodFunc(obj, name, args, originalFunction);
491497
};
492498
dart.dcall = function(f, ...args) {
493-
return dart._checkAndCall(f, dart.read(f), void 0, null, args, 'call');
499+
return dart._checkAndCall(f, dart._getRuntimeType(f), void 0, null, args, 'call');
494500
};
495501
dart.dgcall = function(f, typeArgs, ...args) {
496-
return dart._checkAndCall(f, dart.read(f), void 0, typeArgs, args, 'call');
502+
return dart._checkAndCall(f, dart._getRuntimeType(f), void 0, typeArgs, args, 'call');
497503
};
498504
dart._callMethod = function(obj, name, typeArgs, args, displayName) {
499505
let symbol = dart._canonicalFieldName(obj, name, args, displayName);
@@ -758,7 +764,7 @@ dart_library.library('dart_sdk', null, /* Imports */[
758764
}
759765
return result;
760766
};
761-
dart.read = function(value) {
767+
dart._getRuntimeType = function(value) {
762768
return value[dart._runtimeType];
763769
};
764770
dart.tag = function(value, t) {
@@ -964,11 +970,11 @@ dart_library.library('dart_sdk', null, /* Imports */[
964970
return new dart.Typedef(name, closure);
965971
};
966972
dart.isDartType = function(type) {
967-
return dart.read(type) === core.Type;
973+
return dart._getRuntimeType(type) === core.Type;
968974
};
969975
dart.typeName = function(type) {
970976
if (type instanceof dart.TypeRep) return type.toString();
971-
let tag = dart.read(type);
977+
let tag = dart._getRuntimeType(type);
972978
if (tag === core.Type) {
973979
let name = type.name;
974980
let args = dart.getGenericArgs(type);

lib/src/compiler/code_generator.dart

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,11 @@ class CodeGenerator extends GeneralizingAstVisitor
475475
}
476476

477477
@override
478-
JS.Expression visitTypeName(TypeName node) => _emitTypeName(node.type);
478+
JS.Expression visitTypeName(TypeName node) {
479+
// TODO(jmesserly): should only happen for erroneous code.
480+
if (node.type == null) return js.call('dart.dynamic');
481+
return _emitTypeName(node.type);
482+
}
479483

480484
@override
481485
JS.Statement visitClassTypeAlias(ClassTypeAlias node) {
@@ -1479,7 +1483,7 @@ class CodeGenerator extends GeneralizingAstVisitor
14791483
for (var p in ctor.parameters.parameters) {
14801484
var element = p.element;
14811485
if (element is FieldFormalParameterElement) {
1482-
fields[element.field] = visitSimpleIdentifier(p.identifier);
1486+
fields[element.field] = _emitSimpleIdentifier(p.identifier);
14831487
}
14841488
}
14851489

@@ -1542,7 +1546,7 @@ class CodeGenerator extends GeneralizingAstVisitor
15421546

15431547
var body = <JS.Statement>[];
15441548
for (var param in parameters.parameters) {
1545-
var jsParam = visitSimpleIdentifier(param.identifier);
1549+
var jsParam = _emitSimpleIdentifier(param.identifier);
15461550

15471551
if (!options.destructureNamedParams) {
15481552
if (param.kind == ParameterKind.NAMED) {
@@ -1978,10 +1982,18 @@ class CodeGenerator extends GeneralizingAstVisitor
19781982
]);
19791983
}
19801984

1981-
/// Writes a simple identifier. This can handle implicit `this` as well as
1982-
/// going through the qualified library name if necessary.
1985+
/// Emits a simple identifier, including handling an inferred generic
1986+
/// function instantiation.
19831987
@override
19841988
JS.Expression visitSimpleIdentifier(SimpleIdentifier node) {
1989+
return _applyFunctionTypeArguments(
1990+
_emitSimpleIdentifier(node), node.staticElement, node.staticType);
1991+
}
1992+
1993+
/// Emits a simple identifier, handling implicit `this` as well as
1994+
/// going through the qualified library name if necessary, but *not* handling
1995+
/// inferred generic function instantiation.
1996+
JS.Expression _emitSimpleIdentifier(SimpleIdentifier node) {
19851997
var accessor = node.staticElement;
19861998
if (accessor == null) {
19871999
return js.commentExpression(
@@ -2334,8 +2346,8 @@ class CodeGenerator extends GeneralizingAstVisitor
23342346
List<JS.Expression> args = _visit(node.argumentList);
23352347
if (DynamicInvoke.get(target)) {
23362348
if (typeArgs != null) {
2337-
return js.call('dart.dgsend(#, [#], #, #)',
2338-
[jsTarget, typeArgs, memberName, args]);
2349+
return js.call('dart.dgsend(#, #, #, #)',
2350+
[jsTarget, new JS.ArrayInitializer(typeArgs), memberName, args]);
23392351
} else {
23402352
return js.call('dart.dsend(#, #, #)', [jsTarget, memberName, args]);
23412353
}
@@ -2367,7 +2379,8 @@ class CodeGenerator extends GeneralizingAstVisitor
23672379
if (DynamicInvoke.get(node.function)) {
23682380
var typeArgs = _emitInvokeTypeArguments(node);
23692381
if (typeArgs != null) {
2370-
return js.call('dart.dgcall(#, [#], #)', [fn, typeArgs, args]);
2382+
return js.call('dart.dgcall(#, #, #)',
2383+
[fn, new JS.ArrayInitializer(typeArgs), args]);
23712384
} else {
23722385
return js.call('dart.dcall(#, #)', [fn, args]);
23732386
}
@@ -2385,19 +2398,26 @@ class CodeGenerator extends GeneralizingAstVisitor
23852398

23862399
List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) {
23872400
return _emitFunctionTypeArguments(
2388-
node.function.staticType, node.staticInvokeType);
2401+
node.function.staticType, node.staticInvokeType, node.typeArguments);
23892402
}
23902403

23912404
/// If `g` is a generic function type, and `f` is an instantiation of it,
23922405
/// then this will return the type arguments to apply, otherwise null.
2393-
List<JS.Expression> _emitFunctionTypeArguments(DartType g, DartType f) {
2406+
List<JS.Expression> _emitFunctionTypeArguments(DartType g, DartType f,
2407+
[TypeArgumentList typeArgs]) {
23942408
if (g is FunctionType &&
23952409
g.typeFormals.isNotEmpty &&
23962410
f is FunctionType &&
23972411
f.typeFormals.isEmpty) {
23982412
return _recoverTypeArguments(g, f)
23992413
.map(_emitTypeName)
24002414
.toList(growable: false);
2415+
} else if (typeArgs != null) {
2416+
// Dynamic calls may have type arguments, even though the function types
2417+
// are not known.
2418+
// TODO(jmesserly): seems to be mostly broken in Analyzer at the moment:
2419+
// https://github.com/dart-lang/sdk/issues/26368
2420+
return typeArgs.arguments.map(visitTypeName).toList(growable: false);
24012421
}
24022422
return null;
24032423
}
@@ -3365,7 +3385,7 @@ class CodeGenerator extends GeneralizingAstVisitor
33653385
if (isLibraryPrefix(node.prefix)) {
33663386
return _visit(node.identifier);
33673387
} else {
3368-
return _emitAccess(node.prefix, node.identifier);
3388+
return _emitAccess(node.prefix, node.identifier, node.staticType);
33693389
}
33703390
}
33713391

@@ -3374,7 +3394,7 @@ class CodeGenerator extends GeneralizingAstVisitor
33743394
if (node.operator.lexeme == '?.') {
33753395
return _emitNullSafe(node);
33763396
}
3377-
return _emitAccess(_getTarget(node), node.propertyName);
3397+
return _emitAccess(_getTarget(node), node.propertyName, node.staticType);
33783398
}
33793399

33803400
JS.Expression _emitNullSafe(Expression node) {
@@ -3451,8 +3471,9 @@ class CodeGenerator extends GeneralizingAstVisitor
34513471
}
34523472

34533473
/// Shared code for [PrefixedIdentifier] and [PropertyAccess].
3454-
JS.Expression _emitAccess(Expression target, SimpleIdentifier memberId) {
3455-
var member = memberId.staticElement;
3474+
JS.Expression _emitAccess(
3475+
Expression target, SimpleIdentifier memberId, DartType resultType) {
3476+
Element member = memberId.staticElement;
34563477
if (member is PropertyAccessorElement) {
34573478
member = (member as PropertyAccessorElement).variable;
34583479
}
@@ -3463,30 +3484,49 @@ class CodeGenerator extends GeneralizingAstVisitor
34633484
return js.call('dart.dload(#, #)', [_visit(target), name]);
34643485
}
34653486

3466-
if (target is SuperExpression &&
3467-
member is FieldElement &&
3468-
!member.isSynthetic) {
3487+
var jsTarget = _visit(target);
3488+
bool isSuper = jsTarget is JS.Super;
3489+
3490+
if (isSuper && member is FieldElement && !member.isSynthetic) {
34693491
// If super.x is actually a field, then x is an instance property since
34703492
// subclasses cannot override x.
3471-
return js.call('this.#', [name]);
3493+
jsTarget = new JS.This();
34723494
}
34733495

3474-
String code;
3496+
JS.Expression result;
34753497
if (member != null && member is MethodElement && !isStatic) {
34763498
// Tear-off methods: explicitly bind it.
3477-
if (target is SuperExpression) {
3478-
return js.call('dart.bind(this, #, #.#)', [name, _visit(target), name]);
3499+
if (isSuper) {
3500+
result = js.call('dart.bind(this, #, #.#)', [name, jsTarget, name]);
34793501
} else if (_isObjectMemberCall(target, memberId.name)) {
3480-
return js.call('dart.bind(#, #, dart.#)', [_visit(target), name, name]);
3502+
result = js.call('dart.bind(#, #, dart.#)', [jsTarget, name, name]);
3503+
} else {
3504+
result = js.call('dart.bind(#, #)', [jsTarget, name]);
34813505
}
3482-
code = 'dart.bind(#, #)';
34833506
} else if (_isObjectMemberCall(target, memberId.name)) {
3484-
return js.call('dart.#(#)', [name, _visit(target)]);
3507+
result = js.call('dart.#(#)', [name, jsTarget]);
34853508
} else {
3486-
code = '#.#';
3509+
result = js.call('#.#', [jsTarget, name]);
3510+
}
3511+
return _applyFunctionTypeArguments(result, member, resultType);
3512+
}
3513+
3514+
/// If this is an inferred instantiation of a generic function/method, this
3515+
/// will add the inferred type arguments.
3516+
JS.Expression _applyFunctionTypeArguments(
3517+
JS.Expression result, Element member, DartType instantiated) {
3518+
DartType type;
3519+
if (member is ExecutableElement) {
3520+
type = member.type;
3521+
} else if (member is VariableElement) {
3522+
type = member.type;
34873523
}
34883524

3489-
return js.call(code, [_visit(target), name]);
3525+
// TODO(jmesserly): handle explicitly passed type args.
3526+
if (type == null) return result;
3527+
var typeArgs = _emitFunctionTypeArguments(type, instantiated);
3528+
if (typeArgs == null) return result;
3529+
return js.call('dart.gbind(#, #)', [result, typeArgs]);
34903530
}
34913531

34923532
/// Emits a generic send, like an operator method.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:math' as math;
6+
import 'dart:math' show min; // <-- generic: <T extends num>(T, T) -> T
7+
import 'package:expect/expect.dart';
8+
9+
class C {
10+
/*=T*/ m/*<T extends num>*/(/*=T*/ x, /*=T*/ y) => min(x, y);
11+
int m2(int x, int y) => min(x, y);
12+
}
13+
14+
typedef int Int2Int2Int(int x, int y);
15+
16+
void _test(Int2Int2Int f) {
17+
int y = f(123, 456);
18+
Expect.equals(y, 123);
19+
// `f` doesn't take type args.
20+
Expect.throws(() => (f as dynamic)/*<int>*/(123, 456));
21+
}
22+
23+
void _testParam(/*=T*/ minFn/*<T extends num>*/(/*=T*/ x, /*=T*/ y)) {
24+
_test(minFn);
25+
}
26+
27+
main() {
28+
// Strong mode infers: `min<int>`
29+
// Test simple/prefixed identifiers and property access
30+
_test(min);
31+
_test(math.min);
32+
_test(new C().m);
33+
34+
// Test local function, variable, and parameter
35+
/*=T*/ m/*<T extends num>*/(/*=T*/ x, /*=T*/ y) => min(x, y);
36+
_test(m);
37+
final f = min;
38+
_test(f);
39+
_testParam(math.min);
40+
41+
// A few misc tests for methods
42+
Expect.equals(123, (new C() as dynamic).m/*<int>*/(123, 456));
43+
Expect.throws(() => (new C() as dynamic).m2/*<int>*/(123, 456));
44+
}

tool/input_sdk/private/ddc_runtime/classes.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,17 @@ bind(obj, name, f) => JS('', '''(() => {
170170
return $f;
171171
})()''');
172172

173+
/// Instantiate a generic method.
174+
///
175+
/// We need to apply the type arguments both to the function, as well as its
176+
/// associated function type.
177+
gbind(f, @rest typeArgs) {
178+
var result = JS('', '#(...#)', f, typeArgs);
179+
var sig = JS('', '#(...#)', _getRuntimeType(f), typeArgs);
180+
tag(result, sig);
181+
return result;
182+
}
183+
173184
// Set up the method signature field on the constructor
174185
_setMethodSignature(f, sigF) => JS('', '''(() => {
175186
$defineMemoizedGetter($f, $_methodSig, () => {

tool/input_sdk/private/ddc_runtime/operations.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => {
104104
// then it should have been a function valued field, so
105105
// get the type from the function.
106106
if ($ftype === void 0) {
107-
$ftype = $read($f);
107+
$ftype = $_getRuntimeType($f);
108108
}
109109
110110
if (!$ftype) {
@@ -150,12 +150,12 @@ _checkAndCall(f, ftype, obj, typeArgs, args, name) => JS('', '''(() => {
150150
$throwNoSuchMethodFunc($obj, $name, $args, originalFunction);
151151
})()''');
152152

153-
dcall(f, @rest args) =>
154-
_checkAndCall(f, read(f), JS('', 'void 0'), null, args, 'call');
153+
dcall(f, @rest args) => _checkAndCall(
154+
f, _getRuntimeType(f), JS('', 'void 0'), null, args, 'call');
155155

156156

157-
dgcall(f, typeArgs, @rest args) =>
158-
_checkAndCall(f, read(f), JS('', 'void 0'), typeArgs, args, 'call');
157+
dgcall(f, typeArgs, @rest args) => _checkAndCall(
158+
f, _getRuntimeType(f), JS('', 'void 0'), typeArgs, args, 'call');
159159

160160

161161
/// Shared code for dsend, dindex, and dsetindex.

tool/input_sdk/private/ddc_runtime/rtti.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ _nonPrimitiveRuntimeType(obj) => JS('', '''(() => {
148148
return result;
149149
})()''');
150150

151-
read(value) => JS('', '#[#]', value, _runtimeType);
151+
_getRuntimeType(value) => JS('', '#[#]', value, _runtimeType);
152152

153153
/// Tag the runtime type of [value] to be type [t].
154154
void tag(value, t) {

tool/input_sdk/private/ddc_runtime/types.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,13 +293,13 @@ definiteFunctionType(returnType, args, extra) =>
293293

294294
typedef(name, closure) => JS('', 'new #(#, #)', Typedef, name, closure);
295295

296-
bool isDartType(type) => JS('bool', '#(#) === #', read, type, Type);
296+
bool isDartType(type) => JS('bool', '#(#) === #', _getRuntimeType, type, Type);
297297

298298
typeName(type) => JS('', '''(() => {
299299
// Non-instance types
300300
if ($type instanceof $TypeRep) return $type.toString();
301301
// Instance types
302-
let tag = $read($type);
302+
let tag = $_getRuntimeType($type);
303303
if (tag === $Type) {
304304
let name = $type.name;
305305
let args = $getGenericArgs($type);

0 commit comments

Comments
 (0)