@@ -35,14 +35,15 @@ public static Function FromCallback(IStore store, Delegate callback)
3535 var parameterKinds = new List < ValueKind > ( ) ;
3636 var resultKinds = new List < ValueKind > ( ) ;
3737
38- using var funcType = GetFunctionType ( callback . GetType ( ) , parameterKinds , resultKinds , allowCaller : true , allowTuple : true , out var hasCaller ) ;
38+ using var funcType = GetFunctionType ( callback . GetType ( ) , parameterKinds , resultKinds , allowCaller : true , allowTuple : true , out var hasCaller , out var returnsTuple ) ;
39+ var callbackInvokeMethod = callback . GetType ( ) . GetMethod ( nameof ( Action . Invoke ) ) ! ;
3940
4041 unsafe
4142 {
4243 Native . WasmtimeFuncCallback ? func = ( env , callerPtr , args , nargs , results , nresults ) =>
4344 {
4445 using var caller = new Caller ( callerPtr ) ;
45- return InvokeCallback ( callback , caller , hasCaller , args , ( int ) nargs , results , ( int ) nresults , resultKinds ) ;
46+ return InvokeCallback ( callback , callbackInvokeMethod , caller , hasCaller , args , ( int ) nargs , results , ( int ) nresults , resultKinds , returnsTuple ) ;
4647 } ;
4748
4849 Native . wasmtime_func_new (
@@ -1800,32 +1801,42 @@ private Function(IStore store, ExternFunc func, List<ValueKind> parameters, List
18001801 this . results = results ;
18011802 }
18021803
1803- private static IEnumerable < Type > EnumerateReturnTypes ( Type ? returnType )
1804+ private static IEnumerable < Type > EnumerateReturnTypes ( Type ? returnType , out bool isTuple )
18041805 {
1806+ isTuple = false ;
1807+
18051808 if ( returnType is null )
18061809 {
1807- yield break ;
1810+ return Array . Empty < Type > ( ) ;
18081811 }
18091812
18101813 if ( IsTuple ( returnType ) )
18111814 {
1812- foreach ( var type in returnType
1813- . GetGenericArguments ( )
1814- . SelectMany ( type =>
1815+ isTuple = true ;
1816+ return EnumerateTupleTypes ( returnType ) ;
1817+
1818+ static IEnumerable < Type > EnumerateTupleTypes ( Type tupleType )
1819+ {
1820+ foreach ( var ( typeArgument , idx ) in tupleType . GenericTypeArguments . Select ( ( e , idx ) => ( e , idx ) ) )
1821+ {
1822+ if ( idx is 7 && IsTuple ( typeArgument ) )
18151823 {
1816- if ( type . IsConstructedGenericType )
1824+ // Recursively enumerate the nested tuple's type arguments.
1825+ foreach ( var type in EnumerateTupleTypes ( typeArgument ) )
18171826 {
1818- return type . GenericTypeArguments ;
1827+ yield return type ;
18191828 }
1820- return Enumerable . Repeat ( type , 1 ) ;
1821- } ) )
1822- {
1823- yield return type ;
1829+ }
1830+ else
1831+ {
1832+ yield return typeArgument ;
1833+ }
1834+ }
18241835 }
18251836 }
18261837 else
18271838 {
1828- yield return returnType ;
1839+ return new Type [ ] { returnType } ;
18291840 }
18301841 }
18311842
@@ -1849,7 +1860,7 @@ private static bool IsTuple(Type type)
18491860 definition == typeof ( ValueTuple < , , , , , , , > ) ;
18501861 }
18511862
1852- internal static TypeHandle GetFunctionType ( Type type , List < ValueKind > parameters , List < ValueKind > results , bool allowCaller , bool allowTuple , out bool hasCaller )
1863+ internal static TypeHandle GetFunctionType ( Type type , List < ValueKind > parameters , List < ValueKind > results , bool allowCaller , bool allowTuple , out bool hasCaller , out bool returnsTuple )
18531864 {
18541865 if ( ! typeof ( Delegate ) . IsAssignableFrom ( type ) )
18551866 throw new ArgumentException ( "The specified type must be a Delegate type." ) ;
@@ -1886,7 +1897,7 @@ internal static TypeHandle GetFunctionType(Type type, List<ValueKind> parameters
18861897 parameters . Add ( kind ) ;
18871898 }
18881899
1889- results . AddRange ( EnumerateReturnTypes ( returnType ) . Select ( t =>
1900+ results . AddRange ( EnumerateReturnTypes ( returnType , out returnsTuple ) . Select ( t =>
18901901 {
18911902 if ( ! Value . TryGetKind ( t , out var kind ) )
18921903 {
@@ -1895,16 +1906,15 @@ internal static TypeHandle GetFunctionType(Type type, List<ValueKind> parameters
18951906 return kind ;
18961907 } ) ) ;
18971908
1898- // TODO: Once PR #161 is merged, uncomment this
1899- //if (returnsTuple &&!allowTuple)
1900- //{
1901- // throw new ArgumentException("Use a different overload that implicitly returns ValueTuple.");
1902- //}
1909+ if ( returnsTuple && ! allowTuple )
1910+ {
1911+ throw new ArgumentException ( "Use a different overload that implicitly returns ValueTuple." ) ;
1912+ }
19031913
19041914 return new Function . TypeHandle ( Function . Native . wasm_functype_new ( new ValueTypeArray ( parameters ) , new ValueTypeArray ( results ) ) ) ;
19051915 }
19061916
1907- internal unsafe static IntPtr InvokeCallback ( Delegate callback , Caller caller , bool passCaller , Value * args , int nargs , Value * results , int nresults , IReadOnlyList < ValueKind > resultKinds )
1917+ internal unsafe static IntPtr InvokeCallback ( Delegate callback , MethodInfo callbackInvokeMethod , Caller caller , bool passCaller , Value * args , int nargs , Value * results , int nresults , IReadOnlyList < ValueKind > resultKinds , bool returnsTuple )
19081918 {
19091919 try
19101920 {
@@ -1924,23 +1934,21 @@ internal unsafe static IntPtr InvokeCallback(Delegate callback, Caller caller, b
19241934
19251935 // NOTE: reflection is extremely slow for invoking methods. in the future, perhaps this could be replaced with
19261936 // source generators, system.linq.expressions, or generate IL with DynamicMethods or something
1927- var result = callback . Method . Invoke ( callback . Target , BindingFlags . DoNotWrapExceptions , null , invokeArgs , null ) ;
1937+ var result = callbackInvokeMethod . Invoke ( callback , BindingFlags . DoNotWrapExceptions , null , invokeArgs , null ) ;
19281938
1929- if ( resultKinds . Count > 0 )
1939+ if ( returnsTuple )
19301940 {
1931- var tuple = result as ITuple ;
1932- if ( tuple is null )
1941+ var tuple = ( ITuple ) result ! ;
1942+
1943+ for ( int i = 0 ; i < tuple . Length ; ++ i )
19331944 {
1934- results [ 0 ] = Value . FromObject ( result , resultKinds [ 0 ] ) ;
1935- }
1936- else
1937- {
1938- for ( int i = 0 ; i < tuple . Length ; ++ i )
1939- {
1940- results [ i ] = Value . FromObject ( tuple [ i ] , resultKinds [ i ] ) ;
1941- }
1945+ results [ i ] = Value . FromObject ( tuple [ i ] , resultKinds [ i ] ) ;
19421946 }
19431947 }
1948+ else if ( resultKinds . Count == 1 )
1949+ {
1950+ results [ 0 ] = Value . FromObject ( result , resultKinds [ 0 ] ) ;
1951+ }
19441952 return IntPtr . Zero ;
19451953 }
19461954 catch ( Exception ex )
0 commit comments