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 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug' and '$(Computername)'=='OCHO'">
<
DefineConstants>$(DefineConstants);LOGOUTPUT</DefineConstants
>
<
!--<DefineConstants>$(DefineConstants);LOGOUTPUT</DefineConstants>--
>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
<PackageReference Include="System.IO.Pipelines" Version="$(CoreFxVersion)" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="$(CoreFxVersion)" />
</ItemGroup>
...
...
StackExchange.Redis/StackExchange/Redis/RedisKey.cs
View file @
1aeee1ca
...
...
@@ -157,7 +157,11 @@ public override int GetHashCode()
/// </summary>
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
()
{
...
...
StackExchange.Redis/StackExchange/Redis/ScriptParameterMapper.cs
View file @
1aeee1ca
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Linq.Expressions
;
using
System.Reflection
;
using
System.Reflection.Emit
;
using
System.Text
;
using
System.Text.RegularExpressions
;
...
...
@@ -106,85 +106,23 @@ private static string MakeOrdinalScriptWithoutKeys(string rawScript, string[] ar
return
ret
.
ToString
();
}
private
static
void
LoadMember
(
ILGenerator
il
,
MemberInfo
member
)
private
static
Dictionary
<
Type
,
MethodInfo
>
_conversionOperators
;
static
ScriptParameterMapper
()
{
// stack starts:
// T(*?)
if
(
member
is
FieldInfo
asField
)
var
tmp
=
new
Dictionary
<
Type
,
MethodInfo
>();
foreach
(
var
method
in
typeof
(
RedisValue
).
GetMethods
(
BindingFlags
.
Public
|
BindingFlags
.
Static
))
{
il
.
Emit
(
OpCodes
.
Ldfld
,
asField
);
// typeof(member)
return
;
}
if
(
member
is
PropertyInfo
asProp
)
if
(
method
.
ReturnType
==
typeof
(
RedisValue
)
&&
(
method
.
Name
==
"op_Implicit"
||
method
.
Name
==
"op_Explicit"
))
{
var
getter
=
asProp
.
GetGetMethod
();
if
(
getter
.
IsVirtual
)
var
p
=
method
.
GetParameters
();
if
(
p
!=
null
&&
p
.
Length
==
1
)
{
il
.
Emit
(
OpCodes
.
Callvirt
,
getter
);
// typeof(member)
}
else
{
il
.
Emit
(
OpCodes
.
Call
,
getter
);
// typeof(member)
}
return
;
}
throw
new
Exception
(
"Should't be possible"
);
tmp
[
p
[
0
].
ParameterType
]
=
method
;
}
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
;
}
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
_conversionOperators
=
tmp
;
}
/// <summary>
...
...
@@ -249,28 +187,6 @@ public static bool IsValidParameterHash(Type t, LuaScript script, out string mis
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>
/// <para>Creates a Func that extracts parameters from the given type for use by a LuaScript.</para>
/// <para>
...
...
@@ -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"
);
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
args
=
new
List
<
MemberInfo
>();
...
...
@@ -306,100 +234,74 @@ private static void PrefixIfNeeded(ILGenerator il, LocalBuilder needsPrefixBool,
{
keys
.
Add
(
member
);
}
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
)
else
if
(
memberType
!=
typeof
(
RedisValue
)
&&
!
_conversionOperators
.
ContainsKey
(
memberType
))
{
il
.
Emit
(
OpCodes
.
Unbox_Any
,
t
);
// T
throw
new
InvalidCastException
(
$"There is no conversion available from
{
memberType
.
Name
}
to
{
nameof
(
RedisValue
)}
"
);
}
else
{
il
.
Emit
(
OpCodes
.
Castclass
,
t
);
// T
args
.
Add
(
member
);
}
il
.
Emit
(
OpCodes
.
Stloc
,
loc
);
// --empty--
var
needsKeyPrefixLoc
=
il
.
DeclareLocal
(
typeof
(
bool
));
il
.
Emit
(
OpCodes
.
Ldarga_S
,
1
);
// RedisKey?*
il
.
Emit
(
OpCodes
.
Call
,
nullableRedisKeyHasValue
);
// bool
il
.
Emit
(
OpCodes
.
Stloc
,
needsKeyPrefixLoc
);
// --empty--
var
objUntyped
=
Expression
.
Parameter
(
typeof
(
object
),
"obj"
);
var
objTyped
=
Expression
.
Convert
(
objUntyped
,
t
);
var
keyPrefix
=
Expression
.
Parameter
(
typeof
(
RedisKey
?),
"keyPrefix"
);
Expression
keysResult
,
valuesResult
;
MethodInfo
asRedisValue
=
null
;
Expression
[]
keysResultArr
=
null
;
if
(
keys
.
Count
==
0
)
{
// if there are no keys, don't allocate
il
.
Emit
(
OpCodes
.
Ldnull
);
// null
keysResult
=
Expression
.
Constant
(
null
,
typeof
(
RedisKey
[]));
}
else
{
il
.
Emit
(
OpCodes
.
Ldc_I4
,
keys
.
Count
);
// int
il
.
Emit
(
OpCodes
.
Newarr
,
typeof
(
RedisKey
));
// RedisKey[]
}
var
needsKeyPrefix
=
Expression
.
Property
(
keyPrefix
,
nameof
(
Nullable
<
RedisKey
>.
HasValue
));
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[]
il
.
Emit
(
OpCodes
.
Ldc_I4
,
i
);
// RedisKey[] RedisKey[] int
if
(
t
.
IsValueType
)
{
il
.
Emit
(
OpCodes
.
Ldloca
,
loc
);
// RedisKey[] RedisKey[] int T*
}
else
{
il
.
Emit
(
OpCodes
.
Ldloc
,
loc
);
// RedisKey[] RedisKey[] int T
var
member
=
GetMember
(
objTyped
,
keys
[
i
]);
keysResultArr
[
i
]
=
Expression
.
Condition
(
needsKeyPrefix
,
Expression
.
Call
(
member
,
prepend
,
keyPrefixValueArr
),
member
);
}
LoadMember
(
il
,
keys
[
i
]);
// RedisKey[] RedisKey[] int RedisKey
PrefixIfNeeded
(
il
,
needsKeyPrefixLoc
,
ref
redisKeyLoc
);
// RedisKey[] RedisKey[] int RedisKey
il
.
Emit
(
OpCodes
.
Stelem
,
typeof
(
RedisKey
));
// RedisKey[]
keysResult
=
Expression
.
NewArrayInit
(
typeof
(
RedisKey
),
keysResultArr
);
}
if
(
args
.
Count
==
0
)
{
// if there are no args, don't allocate
il
.
Emit
(
OpCodes
.
Ldnull
);
// RedisKey[] null
valuesResult
=
Expression
.
Constant
(
null
,
typeof
(
RedisValue
[]));
}
else
{
il
.
Emit
(
OpCodes
.
Ldc_I4
,
args
.
Count
);
// RedisKey[] int
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
valuesResult
=
Expression
.
NewArrayInit
(
typeof
(
RedisValue
),
args
.
Select
(
arg
=>
{
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
];
LoadMember
(
il
,
member
);
// RedisKey[] RedisValue[] RedisValue[] int memberType
ConvertToRedisValue
(
member
,
il
,
needsKeyPrefixLoc
,
ref
redisKeyLoc
);
// RedisKey[] RedisValue[] RedisValue[] int RedisValue
il
.
Emit
(
OpCodes
.
Stelem
,
typeof
(
RedisValue
));
// RedisKey[] RedisValue[]
// otherwise: use the conversion operator
var
conversion
=
_conversionOperators
[
member
.
Type
];
return
Expression
.
Call
(
conversion
,
member
);
}));
}
il
.
Emit
(
OpCodes
.
Newobj
,
ScriptParameters
.
Cons
);
// ScriptParameters
il
.
Emit
(
OpCodes
.
Ret
);
// --empty--
var
ret
=
(
Func
<
object
,
RedisKey
?,
ScriptParameters
>)
dyn
.
CreateDelegate
(
typeof
(
Func
<
object
,
RedisKey
?,
ScriptParameters
>));
return
ret
;
var
body
=
Expression
.
Lambda
<
Func
<
object
,
RedisKey
?,
ScriptParameters
>>(
Expression
.
New
(
ScriptParameters
.
Cons
,
keysResult
,
valuesResult
),
objUntyped
,
keyPrefix
);
return
body
.
Compile
();
}
}
}
StackExchange.Redis/StackExchange/Redis/TaskSource.cs
View file @
1aeee1ca
using
System.Threading.Tasks
;
using
System
;
using
System.Reflection
;
using
System.Reflection.Emit
;
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