@@ -55,14 +55,20 @@ bind(obj, name, method) {
55
55
// TODO(jmesserly): canonicalize tearoffs.
56
56
JS ('' , '#._boundObject = #' , f, obj);
57
57
JS ('' , '#._boundMethod = #' , f, method);
58
- JS (
59
- '' ,
60
- '#[#] = #' ,
61
- f,
62
- JS_GET_FLAG ('NEW_RUNTIME_TYPES' )
63
- ? JS_GET_NAME (JsGetName .SIGNATURE_NAME )
64
- : _runtimeType,
65
- getMethodType (getType (obj), name));
58
+ var objType = getType (obj);
59
+ var methodType = getMethodType (objType, name);
60
+ if (JS_GET_FLAG ('NEW_RUNTIME_TYPES' )) {
61
+ if (rti.isGenericFunctionType (methodType)) {
62
+ // Attach the default type argument values to the new function in case
63
+ // they are needed for a dynamic call.
64
+ var defaultTypeArgs = getMethodDefaultTypeArgs (objType, name);
65
+ JS ('' , '#._defaultTypeArgs = #' , f, defaultTypeArgs);
66
+ }
67
+ JS ('' , '#[#] = #' , f, JS_GET_NAME (JsGetName .SIGNATURE_NAME ), methodType);
68
+ } else {
69
+ JS ('' , '#[#] = #' , f, _runtimeType, methodType);
70
+ }
71
+
66
72
return f;
67
73
}
68
74
@@ -77,21 +83,25 @@ bind(obj, name, method) {
77
83
/// a native type/interface with `call` .
78
84
bindCall (obj, name) {
79
85
if (obj == null ) return null ;
80
- var ftype = getMethodType (getType (obj), name);
86
+ var objType = getType (obj);
87
+ var ftype = getMethodType (objType, name);
81
88
if (ftype == null ) return null ;
82
89
var method = JS ('' , '#[#]' , obj, name);
83
90
var f = JS ('' , '#.bind(#)' , method, obj);
84
91
// TODO(jmesserly): canonicalize tearoffs.
85
92
JS ('' , '#._boundObject = #' , f, obj);
86
93
JS ('' , '#._boundMethod = #' , f, method);
87
- JS (
88
- '' ,
89
- '#[#] = #' ,
90
- f,
91
- JS_GET_FLAG ('NEW_RUNTIME_TYPES' )
92
- ? JS_GET_NAME (JsGetName .SIGNATURE_NAME )
93
- : _runtimeType,
94
- ftype);
94
+ if (JS_GET_FLAG ('NEW_RUNTIME_TYPES' )) {
95
+ JS ('' , '#[#] = #' , f, JS_GET_NAME (JsGetName .SIGNATURE_NAME ), ftype);
96
+ if (rti.isGenericFunctionType (ftype)) {
97
+ // Attach the default type argument values to the new function in case
98
+ // they are needed for a dynamic call.
99
+ var defaultTypeArgs = getMethodDefaultTypeArgs (objType, name);
100
+ JS ('' , '#._defaultTypeArgs = #' , f, defaultTypeArgs);
101
+ }
102
+ } else {
103
+ JS ('' , '#[#] = #' , f, _runtimeType, ftype);
104
+ }
95
105
return f;
96
106
}
97
107
@@ -100,22 +110,31 @@ bindCall(obj, name) {
100
110
/// We need to apply the type arguments both to the function, as well as its
101
111
/// associated function type.
102
112
gbind (f, @rest List <Object > typeArgs) {
113
+ var instantiatedType;
103
114
if (JS_GET_FLAG ('NEW_RUNTIME_TYPES' )) {
104
- throw 'TODO: Support tearing off generic methods' ;
115
+ Object fnType = JS ('!' , '#[#]' , f, JS_GET_NAME (JsGetName .SIGNATURE_NAME ));
116
+ // TODO(nshahan): The old type system checks type arguments against the
117
+ // bounds here but why? Is it possible to reach this code without knowing
118
+ // if the provided type args are valid or not?
119
+ var instantiationBinding =
120
+ rti.bindingRtiFromList (JS <JSArray >('!' , '#' , typeArgs));
121
+ instantiatedType = rti.instantiatedGenericFunctionType (
122
+ JS <rti.Rti >('!' , '#' , fnType), instantiationBinding);
105
123
} else {
106
124
GenericFunctionType type = JS ('!' , '#[#]' , f, _runtimeType);
107
125
type.checkBounds (typeArgs);
108
- // Create a JS wrapper function that will also pass the type arguments.
109
- var result =
110
- JS ('' , '(...args) => #.apply(null, #.concat(args))' , f, typeArgs);
111
- // Tag the wrapper with the original function to be used for equality
112
- // checks.
113
- JS ('' , '#["_originalFn"] = #' , result, f);
114
- JS ('' , '#["_typeArgs"] = #' , result, constList (typeArgs, Object ));
115
-
116
- // Tag the wrapper with the instantiated function type.
117
- return fn (result, type.instantiate (typeArgs));
126
+ instantiatedType = type.instantiate (typeArgs);
118
127
}
128
+ // Create a JS wrapper function that will also pass the type arguments.
129
+ var result =
130
+ JS ('' , '(...args) => #.apply(null, #.concat(args))' , f, typeArgs);
131
+ // Tag the wrapper with the original function to be used for equality
132
+ // checks.
133
+ JS ('' , '#["_originalFn"] = #' , result, f);
134
+ JS ('' , '#["_typeArgs"] = #' , result, constList (typeArgs, Object ));
135
+
136
+ // Tag the wrapper with the instantiated function type.
137
+ return fn (result, instantiatedType);
119
138
}
120
139
121
140
dloadRepl (obj, field) => dload (obj, replNameLookup (obj, field));
@@ -463,16 +482,63 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) {
463
482
return JS ('' , '#.apply(#, #)' , f, obj, args);
464
483
}
465
484
466
- // Apply type arguments
485
+ // Apply type arguments if needed.
467
486
if (JS_GET_FLAG ('NEW_RUNTIME_TYPES' )) {
468
487
if (rti.isGenericFunctionType (ftype)) {
469
- throw 'TODO: Support dynamic calls of functions with generic type '
470
- 'arguments.' ;
488
+ var typeParameterBounds = rti.getGenericFunctionBounds (ftype);
489
+ var typeParameterCount = JS <int >('!' , '#.length' , typeParameterBounds);
490
+ if (typeArgs == null ) {
491
+ // No type arguments were provided so they will take on their default
492
+ // values that are attached to generic function tearoffs for this
493
+ // purpose.
494
+ //
495
+ // Note the default value is not always equivalent to the bound for a
496
+ // given type parameter. The bound can reference other type parameters
497
+ // and contain infinite cycles where the default value is determined
498
+ // with an algorithm that will terminate. This means that the default
499
+ // values will need to be checked against the instantiated bounds just
500
+ // like any other type arguments.
501
+ typeArgs = JS ('!' , '#._defaultTypeArgs' , f);
502
+ }
503
+ var typeArgCount = JS <int >('!' , '#.length' , typeArgs);
504
+ if (typeArgCount != typeParameterCount) {
505
+ return callNSM ('Dynamic call with incorrect number of type arguments. '
506
+ 'Expected: $typeParameterCount Actual: $typeArgCount ' );
507
+ } else {
508
+ // Check the provided type arguments against the instantiated bounds.
509
+ for (var i = 0 ; i < typeParameterCount; i++ ) {
510
+ var bound = JS <rti.Rti >('!' , '#[#]' , typeParameterBounds, i);
511
+ var typeArg = JS <rti.Rti >('!' , '#[#]' , typeArgs, i);
512
+ // TODO(nshahan): Skip type checks when the bound is a top type once
513
+ // there is no longer any warnings/errors in weak null safety mode.
514
+ if (bound != typeArg) {
515
+ var instantiatedBound = rti.substitute (bound, typeArgs);
516
+ var validSubtype = compileTimeFlag ('soundNullSafety' )
517
+ ? rti.isSubtype (JS_EMBEDDED_GLOBAL ('' , RTI_UNIVERSE ), typeArg,
518
+ instantiatedBound)
519
+ : _isSubtypeWithWarning (typeArg, instantiatedBound);
520
+ if (! validSubtype) {
521
+ throwTypeError ("The type '${rti .rtiToString (typeArg )}' "
522
+ "is not a subtype of the type variable bound "
523
+ "'${rti .rtiToString (instantiatedBound )}' "
524
+ "of type variable 'T${i + 1 }' "
525
+ "in '${rti .rtiToString (ftype )}'." );
526
+ }
527
+ }
528
+ }
529
+ }
530
+ var instantiationBinding =
531
+ rti.bindingRtiFromList (JS <JSArray >('!' , '#' , typeArgs));
532
+ ftype = rti.instantiatedGenericFunctionType (
533
+ JS <rti.Rti >('!' , '#' , ftype), instantiationBinding);
534
+ } else if (typeArgs != null ) {
535
+ return callNSM ('Dynamic call with unexpected type arguments. '
536
+ 'Expected: 0 Actual: ${JS <int >('!' , '#.length' , typeArgs )}' );
471
537
}
472
538
} else {
539
+ // Old runtime types.
473
540
if (_jsInstanceOf (ftype, GenericFunctionType )) {
474
541
var formalCount = JS <int >('!' , '#.formalCount' , ftype);
475
-
476
542
if (typeArgs == null ) {
477
543
typeArgs = JS <List >('!' , '#.instantiateDefaultBounds()' , ftype);
478
544
} else if (JS <bool >('!' , '#.length != #' , typeArgs, formalCount)) {
@@ -564,6 +630,12 @@ callMethod(obj, name, typeArgs, args, named, displayName) {
564
630
var f = obj != null ? JS ('' , '#[#]' , obj, symbol) : null ;
565
631
var type = getType (obj);
566
632
var ftype = getMethodType (type, symbol);
633
+ if (JS_GET_FLAG ('NEW_RUNTIME_TYPES' )) {
634
+ if (ftype != null && rti.isGenericFunctionType (ftype) && typeArgs == null ) {
635
+ // No type arguments were provided, use the default values in this call.
636
+ typeArgs = getMethodDefaultTypeArgs (type, name);
637
+ }
638
+ }
567
639
// No such method if dart object and ftype is missing.
568
640
return _checkAndCall (f, ftype, obj, typeArgs, args, named, displayName);
569
641
}
@@ -667,6 +739,45 @@ cast(obj, type) {
667
739
}
668
740
}
669
741
742
+ /// Returns `true` if [t1] is a subtype of [t2] .
743
+ ///
744
+ /// This method should only be called when running with weak null safety.
745
+ ///
746
+ /// Will produce a warning/error (if enabled) when the subtype passes but would
747
+ /// fail in sound null safety.
748
+ ///
749
+ /// Currently only called from _checkAndCall to test type arguments applied to
750
+ /// dynamic method calls.
751
+ // TODO(48585) Revise argument types after removing old type representation.
752
+ @notNull
753
+ bool _isSubtypeWithWarning (@notNull t1, @notNull t2) {
754
+ // Avoid classes from the rti library appearing unless they are used.
755
+ if (JS_GET_FLAG ('NEW_RUNTIME_TYPES' )) {
756
+ var t1Rti = JS <rti.Rti >('!' , '#' , t1);
757
+ var t2Rti = JS <rti.Rti >('!' , '#' , t2);
758
+ var legacyErasedRecipe = rti.Rti .getLegacyErasedRecipe (t2Rti);
759
+ var legacyErasedType = rti.findType (legacyErasedRecipe);
760
+ legacyTypeChecks = false ;
761
+ var validSubtype = rti.isSubtype (
762
+ JS_EMBEDDED_GLOBAL ('' , RTI_UNIVERSE ), t1Rti, legacyErasedType);
763
+ legacyTypeChecks = true ;
764
+ if (validSubtype) return true ;
765
+ validSubtype =
766
+ rti.isSubtype (JS_EMBEDDED_GLOBAL ('' , RTI_UNIVERSE ), t1Rti, t2Rti);
767
+ if (validSubtype) {
768
+ // Subtype check passes put would fail with sound null safety.
769
+ _nullWarn ('${rti .createRuntimeType (t1Rti )} '
770
+ 'is not a subtype of '
771
+ '${rti .createRuntimeType (t2Rti )}.' );
772
+ }
773
+ return validSubtype;
774
+ } else {
775
+ // Should never be reached because this method isn't called when using old
776
+ // runtime type representation.
777
+ throwUnimplementedInOldRti ();
778
+ }
779
+ }
780
+
670
781
bool test (bool ? obj) {
671
782
if (obj == null ) throw BooleanConversionAssertionError ();
672
783
return obj;
0 commit comments