Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
CAP
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tsai
CAP
Commits
ab0ccc92
Commit
ab0ccc92
authored
Aug 21, 2017
by
yangxiaodong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add ObjectMethodExecutor
parent
af9d22ed
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
787 additions
and
0 deletions
+787
-0
AwaitableInfo.cs
...etCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs
+127
-0
CoercedAwaitableInfo.cs
...CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs
+55
-0
ObjectMethodExecutor.cs
...CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs
+340
-0
ObjectMethodExecutorAwaitable.cs
...nal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs
+114
-0
ObjectMethodExecutorFSharpSupport.cs
...ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs
+151
-0
No files found.
src/DotNetCore.CAP/Internal/ObjectMethodExecutor/AwaitableInfo.cs
0 → 100644
View file @
ab0ccc92
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using
System
;
using
System.Linq
;
using
System.Reflection
;
using
System.Runtime.CompilerServices
;
namespace
Microsoft.Extensions.Internal
{
internal
struct
AwaitableInfo
{
public
Type
AwaiterType
{
get
;
}
public
PropertyInfo
AwaiterIsCompletedProperty
{
get
;
}
public
MethodInfo
AwaiterGetResultMethod
{
get
;
}
public
MethodInfo
AwaiterOnCompletedMethod
{
get
;
}
public
MethodInfo
AwaiterUnsafeOnCompletedMethod
{
get
;
}
public
Type
ResultType
{
get
;
}
public
MethodInfo
GetAwaiterMethod
{
get
;
}
public
AwaitableInfo
(
Type
awaiterType
,
PropertyInfo
awaiterIsCompletedProperty
,
MethodInfo
awaiterGetResultMethod
,
MethodInfo
awaiterOnCompletedMethod
,
MethodInfo
awaiterUnsafeOnCompletedMethod
,
Type
resultType
,
MethodInfo
getAwaiterMethod
)
{
AwaiterType
=
awaiterType
;
AwaiterIsCompletedProperty
=
awaiterIsCompletedProperty
;
AwaiterGetResultMethod
=
awaiterGetResultMethod
;
AwaiterOnCompletedMethod
=
awaiterOnCompletedMethod
;
AwaiterUnsafeOnCompletedMethod
=
awaiterUnsafeOnCompletedMethod
;
ResultType
=
resultType
;
GetAwaiterMethod
=
getAwaiterMethod
;
}
public
static
bool
IsTypeAwaitable
(
Type
type
,
out
AwaitableInfo
awaitableInfo
)
{
// Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347
// Awaitable must have method matching "object GetAwaiter()"
var
getAwaiterMethod
=
type
.
GetRuntimeMethods
().
FirstOrDefault
(
m
=>
m
.
Name
.
Equals
(
"GetAwaiter"
,
StringComparison
.
OrdinalIgnoreCase
)
&&
m
.
GetParameters
().
Length
==
0
&&
m
.
ReturnType
!=
null
);
if
(
getAwaiterMethod
==
null
)
{
awaitableInfo
=
default
(
AwaitableInfo
);
return
false
;
}
var
awaiterType
=
getAwaiterMethod
.
ReturnType
;
// Awaiter must have property matching "bool IsCompleted { get; }"
var
isCompletedProperty
=
awaiterType
.
GetRuntimeProperties
().
FirstOrDefault
(
p
=>
p
.
Name
.
Equals
(
"IsCompleted"
,
StringComparison
.
OrdinalIgnoreCase
)
&&
p
.
PropertyType
==
typeof
(
bool
)
&&
p
.
GetMethod
!=
null
);
if
(
isCompletedProperty
==
null
)
{
awaitableInfo
=
default
(
AwaitableInfo
);
return
false
;
}
// Awaiter must implement INotifyCompletion
var
awaiterInterfaces
=
awaiterType
.
GetInterfaces
();
var
implementsINotifyCompletion
=
awaiterInterfaces
.
Any
(
t
=>
t
==
typeof
(
INotifyCompletion
));
if
(!
implementsINotifyCompletion
)
{
awaitableInfo
=
default
(
AwaitableInfo
);
return
false
;
}
// INotifyCompletion supplies a method matching "void OnCompleted(Action action)"
var
iNotifyCompletionMap
=
awaiterType
.
GetTypeInfo
()
.
GetRuntimeInterfaceMap
(
typeof
(
INotifyCompletion
));
var
onCompletedMethod
=
iNotifyCompletionMap
.
InterfaceMethods
.
Single
(
m
=>
m
.
Name
.
Equals
(
"OnCompleted"
,
StringComparison
.
OrdinalIgnoreCase
)
&&
m
.
ReturnType
==
typeof
(
void
)
&&
m
.
GetParameters
().
Length
==
1
&&
m
.
GetParameters
()[
0
].
ParameterType
==
typeof
(
Action
));
// Awaiter optionally implements ICriticalNotifyCompletion
var
implementsICriticalNotifyCompletion
=
awaiterInterfaces
.
Any
(
t
=>
t
==
typeof
(
ICriticalNotifyCompletion
));
MethodInfo
unsafeOnCompletedMethod
;
if
(
implementsICriticalNotifyCompletion
)
{
// ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)"
var
iCriticalNotifyCompletionMap
=
awaiterType
.
GetTypeInfo
()
.
GetRuntimeInterfaceMap
(
typeof
(
ICriticalNotifyCompletion
));
unsafeOnCompletedMethod
=
iCriticalNotifyCompletionMap
.
InterfaceMethods
.
Single
(
m
=>
m
.
Name
.
Equals
(
"UnsafeOnCompleted"
,
StringComparison
.
OrdinalIgnoreCase
)
&&
m
.
ReturnType
==
typeof
(
void
)
&&
m
.
GetParameters
().
Length
==
1
&&
m
.
GetParameters
()[
0
].
ParameterType
==
typeof
(
Action
));
}
else
{
unsafeOnCompletedMethod
=
null
;
}
// Awaiter must have method matching "void GetResult" or "T GetResult()"
var
getResultMethod
=
awaiterType
.
GetRuntimeMethods
().
FirstOrDefault
(
m
=>
m
.
Name
.
Equals
(
"GetResult"
)
&&
m
.
GetParameters
().
Length
==
0
);
if
(
getResultMethod
==
null
)
{
awaitableInfo
=
default
(
AwaitableInfo
);
return
false
;
}
awaitableInfo
=
new
AwaitableInfo
(
awaiterType
,
isCompletedProperty
,
getResultMethod
,
onCompletedMethod
,
unsafeOnCompletedMethod
,
getResultMethod
.
ReturnType
,
getAwaiterMethod
);
return
true
;
}
}
}
src/DotNetCore.CAP/Internal/ObjectMethodExecutor/CoercedAwaitableInfo.cs
0 → 100644
View file @
ab0ccc92
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using
System
;
using
System.Linq.Expressions
;
namespace
Microsoft.Extensions.Internal
{
internal
struct
CoercedAwaitableInfo
{
public
AwaitableInfo
AwaitableInfo
{
get
;
}
public
Expression
CoercerExpression
{
get
;
}
public
Type
CoercerResultType
{
get
;
}
public
bool
RequiresCoercion
=>
CoercerExpression
!=
null
;
public
CoercedAwaitableInfo
(
AwaitableInfo
awaitableInfo
)
{
AwaitableInfo
=
awaitableInfo
;
CoercerExpression
=
null
;
CoercerResultType
=
null
;
}
public
CoercedAwaitableInfo
(
Expression
coercerExpression
,
Type
coercerResultType
,
AwaitableInfo
coercedAwaitableInfo
)
{
CoercerExpression
=
coercerExpression
;
CoercerResultType
=
coercerResultType
;
AwaitableInfo
=
coercedAwaitableInfo
;
}
public
static
bool
IsTypeAwaitable
(
Type
type
,
out
CoercedAwaitableInfo
info
)
{
if
(
AwaitableInfo
.
IsTypeAwaitable
(
type
,
out
var
directlyAwaitableInfo
))
{
info
=
new
CoercedAwaitableInfo
(
directlyAwaitableInfo
);
return
true
;
}
// It's not directly awaitable, but maybe we can coerce it.
// Currently we support coercing FSharpAsync<T>.
if
(
ObjectMethodExecutorFSharpSupport
.
TryBuildCoercerFromFSharpAsyncToAwaitable
(
type
,
out
var
coercerExpression
,
out
var
coercerResultType
))
{
if
(
AwaitableInfo
.
IsTypeAwaitable
(
coercerResultType
,
out
var
coercedAwaitableInfo
))
{
info
=
new
CoercedAwaitableInfo
(
coercerExpression
,
coercerResultType
,
coercedAwaitableInfo
);
return
true
;
}
}
info
=
default
(
CoercedAwaitableInfo
);
return
false
;
}
}
}
src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutor.cs
0 → 100644
View file @
ab0ccc92
This diff is collapsed.
Click to expand it.
src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorAwaitable.cs
0 → 100644
View file @
ab0ccc92
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using
System
;
using
System.Runtime.CompilerServices
;
namespace
Microsoft.Extensions.Internal
{
/// <summary>
/// Provides a common awaitable structure that <see cref="ObjectMethodExecutor.ExecuteAsync"/> can
/// return, regardless of whether the underlying value is a System.Task, an FSharpAsync, or an
/// application-defined custom awaitable.
/// </summary>
internal
struct
ObjectMethodExecutorAwaitable
{
private
readonly
object
_customAwaitable
;
private
readonly
Func
<
object
,
object
>
_getAwaiterMethod
;
private
readonly
Func
<
object
,
bool
>
_isCompletedMethod
;
private
readonly
Func
<
object
,
object
>
_getResultMethod
;
private
readonly
Action
<
object
,
Action
>
_onCompletedMethod
;
private
readonly
Action
<
object
,
Action
>
_unsafeOnCompletedMethod
;
// Perf note: since we're requiring the customAwaitable to be supplied here as an object,
// this will trigger a further allocation if it was a value type (i.e., to box it). We can't
// fix this by making the customAwaitable type generic, because the calling code typically
// does not know the type of the awaitable/awaiter at compile-time anyway.
//
// However, we could fix it by not passing the customAwaitable here at all, and instead
// passing a func that maps directly from the target object (e.g., controller instance),
// target method (e.g., action method info), and params array to the custom awaiter in the
// GetAwaiter() method below. In effect, by delaying the actual method call until the
// upstream code calls GetAwaiter on this ObjectMethodExecutorAwaitable instance.
// This optimization is not currently implemented because:
// [1] It would make no difference when the awaitable was an object type, which is
// by far the most common scenario (e.g., System.Task<T>).
// [2] It would be complex - we'd need some kind of object pool to track all the parameter
// arrays until we needed to use them in GetAwaiter().
// We can reconsider this in the future if there's a need to optimize for ValueTask<T>
// or other value-typed awaitables.
public
ObjectMethodExecutorAwaitable
(
object
customAwaitable
,
Func
<
object
,
object
>
getAwaiterMethod
,
Func
<
object
,
bool
>
isCompletedMethod
,
Func
<
object
,
object
>
getResultMethod
,
Action
<
object
,
Action
>
onCompletedMethod
,
Action
<
object
,
Action
>
unsafeOnCompletedMethod
)
{
_customAwaitable
=
customAwaitable
;
_getAwaiterMethod
=
getAwaiterMethod
;
_isCompletedMethod
=
isCompletedMethod
;
_getResultMethod
=
getResultMethod
;
_onCompletedMethod
=
onCompletedMethod
;
_unsafeOnCompletedMethod
=
unsafeOnCompletedMethod
;
}
public
Awaiter
GetAwaiter
()
{
var
customAwaiter
=
_getAwaiterMethod
(
_customAwaitable
);
return
new
Awaiter
(
customAwaiter
,
_isCompletedMethod
,
_getResultMethod
,
_onCompletedMethod
,
_unsafeOnCompletedMethod
);
}
public
struct
Awaiter
:
ICriticalNotifyCompletion
{
private
readonly
object
_customAwaiter
;
private
readonly
Func
<
object
,
bool
>
_isCompletedMethod
;
private
readonly
Func
<
object
,
object
>
_getResultMethod
;
private
readonly
Action
<
object
,
Action
>
_onCompletedMethod
;
private
readonly
Action
<
object
,
Action
>
_unsafeOnCompletedMethod
;
public
Awaiter
(
object
customAwaiter
,
Func
<
object
,
bool
>
isCompletedMethod
,
Func
<
object
,
object
>
getResultMethod
,
Action
<
object
,
Action
>
onCompletedMethod
,
Action
<
object
,
Action
>
unsafeOnCompletedMethod
)
{
_customAwaiter
=
customAwaiter
;
_isCompletedMethod
=
isCompletedMethod
;
_getResultMethod
=
getResultMethod
;
_onCompletedMethod
=
onCompletedMethod
;
_unsafeOnCompletedMethod
=
unsafeOnCompletedMethod
;
}
public
bool
IsCompleted
=>
_isCompletedMethod
(
_customAwaiter
);
public
object
GetResult
()
=>
_getResultMethod
(
_customAwaiter
);
public
void
OnCompleted
(
Action
continuation
)
{
_onCompletedMethod
(
_customAwaiter
,
continuation
);
}
public
void
UnsafeOnCompleted
(
Action
continuation
)
{
// If the underlying awaitable implements ICriticalNotifyCompletion, use its UnsafeOnCompleted.
// If not, fall back on using its OnCompleted.
//
// Why this is safe:
// - Implementing ICriticalNotifyCompletion is a way of saying the caller can choose whether it
// needs the execution context to be preserved (which it signals by calling OnCompleted), or
// that it doesn't (which it signals by calling UnsafeOnCompleted). Obviously it's faster *not*
// to preserve and restore the context, so we prefer that where possible.
// - If a caller doesn't need the execution context to be preserved and hence calls UnsafeOnCompleted,
// there's no harm in preserving it anyway - it's just a bit of wasted cost. That's what will happen
// if a caller sees that the proxy implements ICriticalNotifyCompletion but the proxy chooses to
// pass the call on to the underlying awaitable's OnCompleted method.
var
underlyingMethodToUse
=
_unsafeOnCompletedMethod
??
_onCompletedMethod
;
underlyingMethodToUse
(
_customAwaiter
,
continuation
);
}
}
}
}
\ No newline at end of file
src/DotNetCore.CAP/Internal/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs
0 → 100644
View file @
ab0ccc92
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using
System
;
using
System.Linq
;
using
System.Linq.Expressions
;
using
System.Reflection
;
using
System.Threading
;
using
System.Threading.Tasks
;
namespace
Microsoft.Extensions.Internal
{
/// <summary>
/// Helper for detecting whether a given type is FSharpAsync`1, and if so, supplying
/// an <see cref="Expression"/> for mapping instances of that type to a C# awaitable.
/// </summary>
/// <remarks>
/// The main design goal here is to avoid taking a compile-time dependency on
/// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references
/// to FSharp types have to be constructed dynamically at runtime.
/// </remarks>
internal
static
class
ObjectMethodExecutorFSharpSupport
{
private
static
object
_fsharpValuesCacheLock
=
new
object
();
private
static
Assembly
_fsharpCoreAssembly
;
private
static
MethodInfo
_fsharpAsyncStartAsTaskGenericMethod
;
private
static
PropertyInfo
_fsharpOptionOfTaskCreationOptionsNoneProperty
;
private
static
PropertyInfo
_fsharpOptionOfCancellationTokenNoneProperty
;
public
static
bool
TryBuildCoercerFromFSharpAsyncToAwaitable
(
Type
possibleFSharpAsyncType
,
out
Expression
coerceToAwaitableExpression
,
out
Type
awaitableType
)
{
var
methodReturnGenericType
=
possibleFSharpAsyncType
.
IsGenericType
?
possibleFSharpAsyncType
.
GetGenericTypeDefinition
()
:
null
;
if
(!
IsFSharpAsyncOpenGenericType
(
methodReturnGenericType
))
{
coerceToAwaitableExpression
=
null
;
awaitableType
=
null
;
return
false
;
}
var
awaiterResultType
=
possibleFSharpAsyncType
.
GetGenericArguments
().
Single
();
awaitableType
=
typeof
(
Task
<>).
MakeGenericType
(
awaiterResultType
);
// coerceToAwaitableExpression = (object fsharpAsync) =>
// {
// return (object)FSharpAsync.StartAsTask<TResult>(
// (Microsoft.FSharp.Control.FSharpAsync<TResult>)fsharpAsync,
// FSharpOption<TaskCreationOptions>.None,
// FSharpOption<CancellationToken>.None);
// };
var
startAsTaskClosedMethod
=
_fsharpAsyncStartAsTaskGenericMethod
.
MakeGenericMethod
(
awaiterResultType
);
var
coerceToAwaitableParam
=
Expression
.
Parameter
(
typeof
(
object
));
coerceToAwaitableExpression
=
Expression
.
Lambda
(
Expression
.
Convert
(
Expression
.
Call
(
startAsTaskClosedMethod
,
Expression
.
Convert
(
coerceToAwaitableParam
,
possibleFSharpAsyncType
),
Expression
.
MakeMemberAccess
(
null
,
_fsharpOptionOfTaskCreationOptionsNoneProperty
),
Expression
.
MakeMemberAccess
(
null
,
_fsharpOptionOfCancellationTokenNoneProperty
)),
typeof
(
object
)),
coerceToAwaitableParam
);
return
true
;
}
private
static
bool
IsFSharpAsyncOpenGenericType
(
Type
possibleFSharpAsyncGenericType
)
{
var
typeFullName
=
possibleFSharpAsyncGenericType
?.
FullName
;
if
(!
string
.
Equals
(
typeFullName
,
"Microsoft.FSharp.Control.FSharpAsync`1"
,
StringComparison
.
Ordinal
))
{
return
false
;
}
lock
(
_fsharpValuesCacheLock
)
{
if
(
_fsharpCoreAssembly
!=
null
)
{
// Since we've already found the real FSharpAsync.Core assembly, we just have
// to check that the supplied FSharpAsync`1 type is the one from that assembly.
return
possibleFSharpAsyncGenericType
.
Assembly
==
_fsharpCoreAssembly
;
}
else
{
// We'll keep trying to find the FSharp types/values each time any type called
// FSharpAsync`1 is supplied.
return
TryPopulateFSharpValueCaches
(
possibleFSharpAsyncGenericType
);
}
}
}
private
static
bool
TryPopulateFSharpValueCaches
(
Type
possibleFSharpAsyncGenericType
)
{
var
assembly
=
possibleFSharpAsyncGenericType
.
Assembly
;
var
fsharpOptionType
=
assembly
.
GetType
(
"Microsoft.FSharp.Core.FSharpOption`1"
);
var
fsharpAsyncType
=
assembly
.
GetType
(
"Microsoft.FSharp.Control.FSharpAsync"
);
if
(
fsharpOptionType
==
null
||
fsharpAsyncType
==
null
)
{
return
false
;
}
// Get a reference to FSharpOption<TaskCreationOptions>.None
var
fsharpOptionOfTaskCreationOptionsType
=
fsharpOptionType
.
MakeGenericType
(
typeof
(
TaskCreationOptions
));
_fsharpOptionOfTaskCreationOptionsNoneProperty
=
fsharpOptionOfTaskCreationOptionsType
.
GetTypeInfo
()
.
GetRuntimeProperty
(
"None"
);
// Get a reference to FSharpOption<CancellationToken>.None
var
fsharpOptionOfCancellationTokenType
=
fsharpOptionType
.
MakeGenericType
(
typeof
(
CancellationToken
));
_fsharpOptionOfCancellationTokenNoneProperty
=
fsharpOptionOfCancellationTokenType
.
GetTypeInfo
()
.
GetRuntimeProperty
(
"None"
);
// Get a reference to FSharpAsync.StartAsTask<>
var
fsharpAsyncMethods
=
fsharpAsyncType
.
GetRuntimeMethods
()
.
Where
(
m
=>
m
.
Name
.
Equals
(
"StartAsTask"
,
StringComparison
.
Ordinal
));
foreach
(
var
candidateMethodInfo
in
fsharpAsyncMethods
)
{
var
parameters
=
candidateMethodInfo
.
GetParameters
();
if
(
parameters
.
Length
==
3
&&
TypesHaveSameIdentity
(
parameters
[
0
].
ParameterType
,
possibleFSharpAsyncGenericType
)
&&
parameters
[
1
].
ParameterType
==
fsharpOptionOfTaskCreationOptionsType
&&
parameters
[
2
].
ParameterType
==
fsharpOptionOfCancellationTokenType
)
{
// This really does look like the correct method (and hence assembly).
_fsharpAsyncStartAsTaskGenericMethod
=
candidateMethodInfo
;
_fsharpCoreAssembly
=
assembly
;
break
;
}
}
return
_fsharpCoreAssembly
!=
null
;
}
private
static
bool
TypesHaveSameIdentity
(
Type
type1
,
Type
type2
)
{
return
type1
.
Assembly
==
type2
.
Assembly
&&
string
.
Equals
(
type1
.
Namespace
,
type2
.
Namespace
,
StringComparison
.
Ordinal
)
&&
string
.
Equals
(
type1
.
Name
,
type2
.
Name
,
StringComparison
.
Ordinal
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment