Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
StackExchange.Redis
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
StackExchange.Redis
Commits
1aeee1ca
Commit
1aeee1ca
authored
Jul 07, 2018
by
Marc Gravell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
replace IL-emit in scripting engine with Expression trees; remove dependency and work on UWP
parent
2629e253
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
81 additions
and
179 deletions
+81
-179
StackExchange.Redis.csproj
StackExchange.Redis/StackExchange.Redis.csproj
+1
-2
RedisKey.cs
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
+5
-1
ScriptParameterMapper.cs
...change.Redis/StackExchange/Redis/ScriptParameterMapper.cs
+75
-173
TaskSource.cs
StackExchange.Redis/StackExchange/Redis/TaskSource.cs
+0
-3
No files found.
StackExchange.Redis/StackExchange.Redis.csproj
View file @
1aeee1ca
...
@@ -14,11 +14,10 @@
...
@@ -14,11 +14,10 @@
</PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug' and '$(Computername)'=='OCHO'">
<PropertyGroup Condition="'$(Configuration)' == 'Debug' and '$(Computername)'=='OCHO'">
<
DefineConstants>$(DefineConstants);LOGOUTPUT</DefineConstants
>
<
!--<DefineConstants>$(DefineConstants);LOGOUTPUT</DefineConstants>--
>
</PropertyGroup>
</PropertyGroup>
<ItemGroup>
<ItemGroup>
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
<PackageReference Include="System.IO.Pipelines" Version="$(CoreFxVersion)" />
<PackageReference Include="System.IO.Pipelines" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="$(CoreFxVersion)" />
</ItemGroup>
</ItemGroup>
...
...
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
View file @
1aeee1ca
...
@@ -157,7 +157,11 @@ public override int GetHashCode()
...
@@ -157,7 +157,11 @@ public override int GetHashCode()
/// </summary>
/// </summary>
public
override
string
ToString
()
=>
((
string
)
this
)
??
"(null)"
;
public
override
string
ToString
()
=>
((
string
)
this
)
??
"(null)"
;
internal
RedisValue
AsRedisValue
()
=>
(
byte
[])
this
;
internal
RedisValue
AsRedisValue
()
{
if
(
keyPrefix
==
null
&&
keyValue
is
string
)
return
(
string
)
keyValue
;
return
(
byte
[])
this
;
}
internal
void
AssertNotNull
()
internal
void
AssertNotNull
()
{
{
...
...
StackExchange.Redis/StackExchange/Redis/ScriptParameterMapper.cs
View file @
1aeee1ca
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Linq
;
using
System.Linq.Expressions
;
using
System.Reflection
;
using
System.Reflection
;
using
System.Reflection.Emit
;
using
System.Text
;
using
System.Text
;
using
System.Text.RegularExpressions
;
using
System.Text.RegularExpressions
;
...
@@ -106,85 +106,23 @@ private static string MakeOrdinalScriptWithoutKeys(string rawScript, string[] ar
...
@@ -106,85 +106,23 @@ private static string MakeOrdinalScriptWithoutKeys(string rawScript, string[] ar
return
ret
.
ToString
();
return
ret
.
ToString
();
}
}
private
static
void
LoadMember
(
ILGenerator
il
,
MemberInfo
member
)
private
static
Dictionary
<
Type
,
MethodInfo
>
_conversionOperators
;
static
ScriptParameterMapper
()
{
{
// stack starts:
var
tmp
=
new
Dictionary
<
Type
,
MethodInfo
>();
// T(*?)
foreach
(
var
method
in
typeof
(
RedisValue
).
GetMethods
(
BindingFlags
.
Public
|
BindingFlags
.
Static
))
if
(
member
is
FieldInfo
asField
)
{
{
il
.
Emit
(
OpCodes
.
Ldfld
,
asField
);
// typeof(member)
if
(
method
.
ReturnType
==
typeof
(
RedisValue
)
&&
return
;
(
method
.
Name
==
"op_Implicit"
||
method
.
Name
==
"op_Explicit"
))
}
if
(
member
is
PropertyInfo
asProp
)
{
{
var
getter
=
asProp
.
GetGetMethod
();
var
p
=
method
.
GetParameters
();
if
(
getter
.
IsVirtual
)
if
(
p
!=
null
&&
p
.
Length
==
1
)
{
{
il
.
Emit
(
OpCodes
.
Callvirt
,
getter
);
// typeof(member)
tmp
[
p
[
0
].
ParameterType
]
=
method
;
}
else
{
il
.
Emit
(
OpCodes
.
Call
,
getter
);
// typeof(member)
}
return
;
}
throw
new
Exception
(
"Should't be possible"
);
}
}
private
static
readonly
MethodInfo
RedisValue_FromInt
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
int
)
});
private
static
readonly
MethodInfo
RedisValue_FromNullableInt
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
int
?)
});
private
static
readonly
MethodInfo
RedisValue_FromLong
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
long
)
});
private
static
readonly
MethodInfo
RedisValue_FromNullableLong
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
long
?)
});
private
static
readonly
MethodInfo
RedisValue_FromDouble
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
double
)
});
private
static
readonly
MethodInfo
RedisValue_FromNullableDouble
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
double
?)
});
private
static
readonly
MethodInfo
RedisValue_FromString
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
string
)
});
private
static
readonly
MethodInfo
RedisValue_FromByteArray
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
byte
[])
});
private
static
readonly
MethodInfo
RedisValue_FromBool
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
bool
)
});
private
static
readonly
MethodInfo
RedisValue_FromNullableBool
=
typeof
(
RedisValue
).
GetMethod
(
"op_Implicit"
,
new
[]
{
typeof
(
bool
?)
});
private
static
readonly
MethodInfo
RedisKey_AsRedisValue
=
typeof
(
RedisKey
).
GetMethod
(
"AsRedisValue"
,
BindingFlags
.
NonPublic
|
BindingFlags
.
Instance
);
private
static
void
ConvertToRedisValue
(
MemberInfo
member
,
ILGenerator
il
,
LocalBuilder
needsPrefixBool
,
ref
LocalBuilder
redisKeyLoc
)
{
// stack starts:
// typeof(member)
var
t
=
member
is
FieldInfo
?
((
FieldInfo
)
member
).
FieldType
:
((
PropertyInfo
)
member
).
PropertyType
;
if
(
t
==
typeof
(
RedisValue
))
{
// They've already converted for us, don't do anything
return
;
}
}
if
(
t
==
typeof
(
RedisKey
))
{
redisKeyLoc
=
redisKeyLoc
??
il
.
DeclareLocal
(
typeof
(
RedisKey
));
PrefixIfNeeded
(
il
,
needsPrefixBool
,
ref
redisKeyLoc
);
// RedisKey
il
.
Emit
(
OpCodes
.
Stloc
,
redisKeyLoc
);
// --empty--
il
.
Emit
(
OpCodes
.
Ldloca
,
redisKeyLoc
);
// RedisKey*
il
.
Emit
(
OpCodes
.
Call
,
RedisKey_AsRedisValue
);
// RedisValue
return
;
}
}
_conversionOperators
=
tmp
;
MethodInfo
convertOp
=
null
;
if
(
t
==
typeof
(
int
))
convertOp
=
RedisValue_FromInt
;
if
(
t
==
typeof
(
int
?))
convertOp
=
RedisValue_FromNullableInt
;
if
(
t
==
typeof
(
long
))
convertOp
=
RedisValue_FromLong
;
if
(
t
==
typeof
(
long
?))
convertOp
=
RedisValue_FromNullableLong
;
if
(
t
==
typeof
(
double
))
convertOp
=
RedisValue_FromDouble
;
if
(
t
==
typeof
(
double
?))
convertOp
=
RedisValue_FromNullableDouble
;
if
(
t
==
typeof
(
string
))
convertOp
=
RedisValue_FromString
;
if
(
t
==
typeof
(
byte
[]))
convertOp
=
RedisValue_FromByteArray
;
if
(
t
==
typeof
(
bool
))
convertOp
=
RedisValue_FromBool
;
if
(
t
==
typeof
(
bool
?))
convertOp
=
RedisValue_FromNullableBool
;
il
.
Emit
(
OpCodes
.
Call
,
convertOp
);
// stack ends:
// RedisValue
}
}
/// <summary>
/// <summary>
...
@@ -249,28 +187,6 @@ public static bool IsValidParameterHash(Type t, LuaScript script, out string mis
...
@@ -249,28 +187,6 @@ public static bool IsValidParameterHash(Type t, LuaScript script, out string mis
return
true
;
return
true
;
}
}
private
static
void
PrefixIfNeeded
(
ILGenerator
il
,
LocalBuilder
needsPrefixBool
,
ref
LocalBuilder
redisKeyLoc
)
{
// top of stack is
// RedisKey
var
getVal
=
typeof
(
RedisKey
?).
GetProperty
(
"Value"
).
GetGetMethod
();
var
prepend
=
typeof
(
RedisKey
).
GetMethod
(
"Prepend"
);
var
doNothing
=
il
.
DefineLabel
();
redisKeyLoc
=
redisKeyLoc
??
il
.
DeclareLocal
(
typeof
(
RedisKey
));
il
.
Emit
(
OpCodes
.
Ldloc
,
needsPrefixBool
);
// RedisKey bool
il
.
Emit
(
OpCodes
.
Brfalse
,
doNothing
);
// RedisKey
il
.
Emit
(
OpCodes
.
Stloc
,
redisKeyLoc
);
// --empty--
il
.
Emit
(
OpCodes
.
Ldloca
,
redisKeyLoc
);
// RedisKey*
il
.
Emit
(
OpCodes
.
Ldarga_S
,
1
);
// RedisKey* RedisKey?*
il
.
Emit
(
OpCodes
.
Call
,
getVal
);
// RedisKey* RedisKey
il
.
Emit
(
OpCodes
.
Call
,
prepend
);
// RedisKey
il
.
MarkLabel
(
doNothing
);
// RedisKey
}
/// <summary>
/// <summary>
/// <para>Creates a Func that extracts parameters from the given type for use by a LuaScript.</para>
/// <para>Creates a Func that extracts parameters from the given type for use by a LuaScript.</para>
/// <para>
/// <para>
...
@@ -292,6 +208,18 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
...
@@ -292,6 +208,18 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
{
{
if
(!
IsValidParameterHash
(
t
,
script
,
out
_
,
out
_
))
throw
new
Exception
(
"Shouldn't be possible"
);
if
(!
IsValidParameterHash
(
t
,
script
,
out
_
,
out
_
))
throw
new
Exception
(
"Shouldn't be possible"
);
Expression
GetMember
(
Expression
root
,
MemberInfo
member
)
{
switch
(
member
.
MemberType
)
{
case
MemberTypes
.
Property
:
return
Expression
.
Property
(
root
,
(
PropertyInfo
)
member
);
case
MemberTypes
.
Field
:
return
Expression
.
Field
(
root
,
(
FieldInfo
)
member
);
default
:
throw
new
ArgumentException
(
nameof
(
member
));
}
}
var
keys
=
new
List
<
MemberInfo
>();
var
keys
=
new
List
<
MemberInfo
>();
var
args
=
new
List
<
MemberInfo
>();
var
args
=
new
List
<
MemberInfo
>();
...
@@ -306,100 +234,74 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
...
@@ -306,100 +234,74 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
{
{
keys
.
Add
(
member
);
keys
.
Add
(
member
);
}
}
else
if
(
memberType
!=
typeof
(
RedisValue
)
&&
!
_conversionOperators
.
ContainsKey
(
memberType
))
args
.
Add
(
member
);
}
var
nullableRedisKeyHasValue
=
typeof
(
RedisKey
?).
GetProperty
(
"HasValue"
).
GetGetMethod
();
var
dyn
=
new
DynamicMethod
(
"ParameterExtractor_"
+
t
.
FullName
+
"_"
+
script
.
OriginalScript
.
GetHashCode
(),
typeof
(
ScriptParameters
),
new
[]
{
typeof
(
object
),
typeof
(
RedisKey
?)
},
restrictedSkipVisibility
:
true
);
var
il
=
dyn
.
GetILGenerator
();
// only init'd if we use it
LocalBuilder
redisKeyLoc
=
null
;
var
loc
=
il
.
DeclareLocal
(
t
);
il
.
Emit
(
OpCodes
.
Ldarg_0
);
// object
if
(
t
.
IsValueType
)
{
{
il
.
Emit
(
OpCodes
.
Unbox_Any
,
t
);
// T
throw
new
InvalidCastException
(
$"There is no conversion available from
{
memberType
.
Name
}
to
{
nameof
(
RedisValue
)}
"
);
}
}
else
args
.
Add
(
member
);
{
il
.
Emit
(
OpCodes
.
Castclass
,
t
);
// T
}
}
il
.
Emit
(
OpCodes
.
Stloc
,
loc
);
// --empty--
var
needsKeyPrefixLoc
=
il
.
DeclareLocal
(
typeof
(
bool
));
il
.
Emit
(
OpCodes
.
Ldarga_S
,
1
);
// RedisKey?*
var
objUntyped
=
Expression
.
Parameter
(
typeof
(
object
),
"obj"
);
il
.
Emit
(
OpCodes
.
Call
,
nullableRedisKeyHasValue
);
// bool
var
objTyped
=
Expression
.
Convert
(
objUntyped
,
t
);
il
.
Emit
(
OpCodes
.
Stloc
,
needsKeyPrefixLoc
);
// --empty--
var
keyPrefix
=
Expression
.
Parameter
(
typeof
(
RedisKey
?),
"keyPrefix"
);
Expression
keysResult
,
valuesResult
;
MethodInfo
asRedisValue
=
null
;
Expression
[]
keysResultArr
=
null
;
if
(
keys
.
Count
==
0
)
if
(
keys
.
Count
==
0
)
{
{
// if there are no keys, don't allocate
// if there are no keys, don't allocate
il
.
Emit
(
OpCodes
.
Ldnull
);
// null
keysResult
=
Expression
.
Constant
(
null
,
typeof
(
RedisKey
[]));
}
}
else
else
{
{
il
.
Emit
(
OpCodes
.
Ldc_I4
,
keys
.
Count
);
// int
var
needsKeyPrefix
=
Expression
.
Property
(
keyPrefix
,
nameof
(
Nullable
<
RedisKey
>.
HasValue
));
il
.
Emit
(
OpCodes
.
Newarr
,
typeof
(
RedisKey
));
// RedisKey[]
var
keyPrefixValueArr
=
new
[]
{
Expression
.
Call
(
keyPrefix
,
}
nameof
(
Nullable
<
RedisKey
>.
GetValueOrDefault
),
null
,
null
)
};
var
prepend
=
typeof
(
RedisKey
).
GetMethod
(
nameof
(
RedisKey
.
Prepend
),
BindingFlags
.
Public
|
BindingFlags
.
Instance
);
asRedisValue
=
typeof
(
RedisKey
).
GetMethod
(
nameof
(
RedisKey
.
AsRedisValue
),
BindingFlags
.
NonPublic
|
BindingFlags
.
Instance
);
for
(
var
i
=
0
;
i
<
keys
.
Count
;
i
++)
keysResultArr
=
new
Expression
[
keys
.
Count
];
for
(
int
i
=
0
;
i
<
keysResultArr
.
Length
;
i
++)
{
{
il
.
Emit
(
OpCodes
.
Dup
);
// RedisKey[] RedisKey[]
var
member
=
GetMember
(
objTyped
,
keys
[
i
]);
il
.
Emit
(
OpCodes
.
Ldc_I4
,
i
);
// RedisKey[] RedisKey[] int
keysResultArr
[
i
]
=
Expression
.
Condition
(
needsKeyPrefix
,
if
(
t
.
IsValueType
)
Expression
.
Call
(
member
,
prepend
,
keyPrefixValueArr
),
{
member
);
il
.
Emit
(
OpCodes
.
Ldloca
,
loc
);
// RedisKey[] RedisKey[] int T*
}
else
{
il
.
Emit
(
OpCodes
.
Ldloc
,
loc
);
// RedisKey[] RedisKey[] int T
}
}
LoadMember
(
il
,
keys
[
i
]);
// RedisKey[] RedisKey[] int RedisKey
keysResult
=
Expression
.
NewArrayInit
(
typeof
(
RedisKey
),
keysResultArr
);
PrefixIfNeeded
(
il
,
needsKeyPrefixLoc
,
ref
redisKeyLoc
);
// RedisKey[] RedisKey[] int RedisKey
il
.
Emit
(
OpCodes
.
Stelem
,
typeof
(
RedisKey
));
// RedisKey[]
}
}
if
(
args
.
Count
==
0
)
if
(
args
.
Count
==
0
)
{
{
// if there are no args, don't allocate
// if there are no args, don't allocate
il
.
Emit
(
OpCodes
.
Ldnull
);
// RedisKey[] null
valuesResult
=
Expression
.
Constant
(
null
,
typeof
(
RedisValue
[]));
}
}
else
else
{
{
il
.
Emit
(
OpCodes
.
Ldc_I4
,
args
.
Count
);
// RedisKey[] int
valuesResult
=
Expression
.
NewArrayInit
(
typeof
(
RedisValue
),
args
.
Select
(
arg
=>
il
.
Emit
(
OpCodes
.
Newarr
,
typeof
(
RedisValue
));
// RedisKey[] RedisValue[]
}
for
(
var
i
=
0
;
i
<
args
.
Count
;
i
++)
{
il
.
Emit
(
OpCodes
.
Dup
);
// RedisKey[] RedisValue[] RedisValue[]
il
.
Emit
(
OpCodes
.
Ldc_I4
,
i
);
// RedisKey[] RedisValue[] RedisValue[] int
if
(
t
.
IsValueType
)
{
il
.
Emit
(
OpCodes
.
Ldloca
,
loc
);
// RedisKey[] RedisValue[] RedisValue[] int T*
}
else
{
{
il
.
Emit
(
OpCodes
.
Ldloc
,
loc
);
// RedisKey[] RedisValue[] RedisValue[] int T
var
member
=
GetMember
(
objTyped
,
arg
);
if
(
member
.
Type
==
typeof
(
RedisValue
))
return
member
;
// pass-thru
if
(
member
.
Type
==
typeof
(
RedisKey
))
{
// need to apply prefix (note we can re-use the body from earlier)
var
val
=
keysResultArr
[
keys
.
IndexOf
(
arg
)];
return
Expression
.
Call
(
val
,
asRedisValue
);
}
}
var
member
=
args
[
i
];
// otherwise: use the conversion operator
LoadMember
(
il
,
member
);
// RedisKey[] RedisValue[] RedisValue[] int memberType
var
conversion
=
_conversionOperators
[
member
.
Type
];
ConvertToRedisValue
(
member
,
il
,
needsKeyPrefixLoc
,
ref
redisKeyLoc
);
// RedisKey[] RedisValue[] RedisValue[] int RedisValue
return
Expression
.
Call
(
conversion
,
member
);
}));
il
.
Emit
(
OpCodes
.
Stelem
,
typeof
(
RedisValue
));
// RedisKey[] RedisValue[]
}
}
il
.
Emit
(
OpCodes
.
Newobj
,
ScriptParameters
.
Cons
);
// ScriptParameters
var
body
=
Expression
.
Lambda
<
Func
<
object
,
RedisKey
?,
ScriptParameters
>>(
il
.
Emit
(
OpCodes
.
Ret
);
// --empty--
Expression
.
New
(
ScriptParameters
.
Cons
,
keysResult
,
valuesResult
),
objUntyped
,
keyPrefix
);
var
ret
=
(
Func
<
object
,
RedisKey
?,
ScriptParameters
>)
dyn
.
CreateDelegate
(
typeof
(
Func
<
object
,
RedisKey
?,
ScriptParameters
>));
return
body
.
Compile
();
return
ret
;
}
}
}
}
}
}
StackExchange.Redis/StackExchange/Redis/TaskSource.cs
View file @
1aeee1ca
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
System
;
using
System.Reflection
;
using
System.Reflection.Emit
;
namespace
StackExchange.Redis
namespace
StackExchange.Redis
{
{
...
...
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