3
3
using System . Linq ;
4
4
using System . Linq . Expressions ;
5
5
using System . Reflection ;
6
+ using System . Text . RegularExpressions ;
6
7
using Serilog . Events ;
7
8
using Serilog . Expressions . Ast ;
8
9
using Serilog . Expressions . Compilation . Transformations ;
11
12
using Expression = Serilog . Expressions . Ast . Expression ;
12
13
using LambdaExpression = System . Linq . Expressions . LambdaExpression ;
13
14
using ParameterExpression = System . Linq . Expressions . ParameterExpression ;
15
+ using LX = System . Linq . Expressions . Expression ;
14
16
15
17
namespace Serilog . Expressions . Compilation . Linq
16
18
{
17
19
class LinqExpressionCompiler : SerilogExpressionTransformer < Expression < CompiledExpression > >
18
20
{
21
+ static readonly LinqExpressionCompiler Instance = new LinqExpressionCompiler ( ) ;
22
+
19
23
static readonly IDictionary < string , MethodInfo > OperatorMethods = typeof ( RuntimeOperators )
20
24
. GetTypeInfo ( )
21
25
. GetMethods ( BindingFlags . Static | BindingFlags . Public )
22
26
. ToDictionary ( m => m . Name , StringComparer . OrdinalIgnoreCase ) ;
23
27
24
- static readonly ConstructorInfo SequenceValueCtor =
25
- typeof ( SequenceValue ) . GetConstructor ( new [ ] { typeof ( IEnumerable < LogEventPropertyValue > ) } ) ;
28
+ static readonly MethodInfo ConstructSequenceValueMethod = typeof ( LinqExpressionCompiler )
29
+ . GetMethod ( nameof ( ConstructSequenceValue ) , BindingFlags . Static | BindingFlags . Public ) ;
30
+
31
+ static readonly MethodInfo CoerceToScalarBooleanMethod = typeof ( LinqExpressionCompiler )
32
+ . GetMethod ( nameof ( CoerceToScalarBoolean ) , BindingFlags . Static | BindingFlags . Public ) ;
26
33
27
- static readonly MethodInfo CoerceTrue = typeof ( LinqExpressionCompiler ) . GetMethod ( nameof ( CoerceToScalarBoolean ) , BindingFlags . Static | BindingFlags . Public ) ;
34
+ static readonly MethodInfo IndexOfMatchMethod = typeof ( LinqExpressionCompiler )
35
+ . GetMethod ( nameof ( IndexOfMatch ) , BindingFlags . Static | BindingFlags . Public ) ;
36
+
37
+ static readonly LogEventPropertyValue NegativeOne = new ScalarValue ( - 1 ) ;
38
+
39
+ public static LogEventPropertyValue ConstructSequenceValue ( LogEventPropertyValue [ ] elements )
40
+ {
41
+ // Avoid upsetting Serilog's (currently) fragile `SequenceValue.Render()`.
42
+ if ( elements . Any ( el => el == null ) )
43
+ return null ;
44
+ return new SequenceValue ( elements ) ;
45
+ }
28
46
29
47
public static LogEventPropertyValue CoerceToScalarBoolean ( LogEventPropertyValue value )
30
48
{
@@ -33,11 +51,24 @@ public static LogEventPropertyValue CoerceToScalarBoolean(LogEventPropertyValue
33
51
return RuntimeOperators . ScalarBoolean ( false ) ;
34
52
}
35
53
54
+ public static LogEventPropertyValue IndexOfMatch ( LogEventPropertyValue value , Regex regex )
55
+ {
56
+ if ( value is ScalarValue scalar &&
57
+ scalar . Value is string s )
58
+ {
59
+ var m = regex . Match ( s ) ;
60
+ if ( m . Success )
61
+ return new ScalarValue ( m . Index ) ;
62
+ return NegativeOne ;
63
+ }
64
+
65
+ return null ;
66
+ }
67
+
36
68
public static CompiledExpression Compile ( Expression expression )
37
69
{
38
70
if ( expression == null ) throw new ArgumentNullException ( nameof ( expression ) ) ;
39
- var compiler = new LinqExpressionCompiler ( ) ;
40
- return compiler . Transform ( expression ) . Compile ( ) ;
71
+ return Instance . Transform ( expression ) . Compile ( ) ;
41
72
}
42
73
43
74
protected override Expression < CompiledExpression > Transform ( CallExpression lx )
@@ -50,11 +81,11 @@ protected override Expression<CompiledExpression> Transform(CallExpression lx)
50
81
51
82
var operands = lx . Operands . Select ( Transform ) . ToArray ( ) ;
52
83
53
- var context = System . Linq . Expressions . Expression . Parameter ( typeof ( LogEvent ) ) ;
84
+ var context = LX . Parameter ( typeof ( LogEvent ) ) ;
54
85
55
86
var operandValues = operands . Select ( o => Splice ( o , context ) ) ;
56
87
var operandVars = new List < ParameterExpression > ( ) ;
57
- var rtn = System . Linq . Expressions . Expression . Label ( typeof ( LogEventPropertyValue ) ) ;
88
+ var rtn = LX . Label ( typeof ( LogEventPropertyValue ) ) ;
58
89
59
90
var statements = new List < System . Linq . Expressions . Expression > ( ) ;
60
91
var first = true ;
@@ -64,38 +95,38 @@ protected override Expression<CompiledExpression> Transform(CallExpression lx)
64
95
{
65
96
if ( shortCircuit )
66
97
{
67
- shortCircuitElse = System . Linq . Expressions . Expression . Call ( CoerceTrue , op ) ;
98
+ shortCircuitElse = LX . Call ( CoerceToScalarBooleanMethod , op ) ;
68
99
break ;
69
100
}
70
101
71
- var opam = System . Linq . Expressions . Expression . Variable ( typeof ( LogEventPropertyValue ) ) ;
102
+ var opam = LX . Variable ( typeof ( LogEventPropertyValue ) ) ;
72
103
operandVars . Add ( opam ) ;
73
- statements . Add ( System . Linq . Expressions . Expression . Assign ( opam , op ) ) ;
104
+ statements . Add ( LX . Assign ( opam , op ) ) ;
74
105
75
106
if ( first && Operators . SameOperator ( lx . OperatorName , Operators . OpAnd ) )
76
107
{
77
108
Expression < Func < LogEventPropertyValue , bool > > shortCircuitIf = v => ! ( v is ScalarValue ) || ! true . Equals ( ( ( ScalarValue ) v ) . Value ) ;
78
109
var scc = Splice ( shortCircuitIf , opam ) ;
79
- statements . Add ( System . Linq . Expressions . Expression . IfThen ( scc , System . Linq . Expressions . Expression . Return ( rtn , System . Linq . Expressions . Expression . Constant ( new ScalarValue ( false ) , typeof ( LogEventPropertyValue ) ) ) ) ) ;
110
+ statements . Add ( LX . IfThen ( scc , LX . Return ( rtn , LX . Constant ( new ScalarValue ( false ) , typeof ( LogEventPropertyValue ) ) ) ) ) ;
80
111
shortCircuit = true ;
81
112
}
82
113
83
114
if ( first && Operators . SameOperator ( lx . OperatorName , Operators . OpOr ) )
84
115
{
85
116
Expression < Func < LogEventPropertyValue , bool > > shortCircuitIf = v => v is ScalarValue && true . Equals ( ( ( ScalarValue ) v ) . Value ) ;
86
117
var scc = Splice ( shortCircuitIf , opam ) ;
87
- statements . Add ( System . Linq . Expressions . Expression . IfThen ( scc , System . Linq . Expressions . Expression . Return ( rtn , System . Linq . Expressions . Expression . Constant ( new ScalarValue ( true ) , typeof ( LogEventPropertyValue ) ) ) ) ) ;
118
+ statements . Add ( LX . IfThen ( scc , LX . Return ( rtn , LX . Constant ( new ScalarValue ( true ) , typeof ( LogEventPropertyValue ) ) ) ) ) ;
88
119
shortCircuit = true ;
89
120
}
90
121
91
122
first = false ;
92
123
}
93
124
94
- statements . Add ( System . Linq . Expressions . Expression . Return ( rtn , shortCircuitElse ?? System . Linq . Expressions . Expression . Call ( m , operandVars ) ) ) ;
95
- statements . Add ( System . Linq . Expressions . Expression . Label ( rtn , System . Linq . Expressions . Expression . Constant ( null , typeof ( LogEventPropertyValue ) ) ) ) ;
125
+ statements . Add ( LX . Return ( rtn , shortCircuitElse ?? LX . Call ( m , operandVars ) ) ) ;
126
+ statements . Add ( LX . Label ( rtn , LX . Constant ( null , typeof ( LogEventPropertyValue ) ) ) ) ;
96
127
97
- return System . Linq . Expressions . Expression . Lambda < CompiledExpression > (
98
- System . Linq . Expressions . Expression . Block ( typeof ( LogEventPropertyValue ) , operandVars , statements ) ,
128
+ return LX . Lambda < CompiledExpression > (
129
+ LX . Block ( typeof ( LogEventPropertyValue ) , operandVars , statements ) ,
99
130
context ) ;
100
131
}
101
132
@@ -104,31 +135,31 @@ protected override Expression<CompiledExpression> Transform(AccessorExpression s
104
135
var tgv = typeof ( LinqExpressionCompiler ) . GetTypeInfo ( ) . GetMethod ( nameof ( TryGetStructurePropertyValue ) , BindingFlags . Static | BindingFlags . Public ) ;
105
136
var recv = Transform ( spx . Receiver ) ;
106
137
107
- var context = System . Linq . Expressions . Expression . Parameter ( typeof ( LogEvent ) ) ;
138
+ var context = LX . Parameter ( typeof ( LogEvent ) ) ;
108
139
109
- var r = System . Linq . Expressions . Expression . Variable ( typeof ( object ) ) ;
110
- var str = System . Linq . Expressions . Expression . Variable ( typeof ( StructureValue ) ) ;
111
- var result = System . Linq . Expressions . Expression . Variable ( typeof ( LogEventPropertyValue ) ) ;
140
+ var r = LX . Variable ( typeof ( object ) ) ;
141
+ var str = LX . Variable ( typeof ( StructureValue ) ) ;
142
+ var result = LX . Variable ( typeof ( LogEventPropertyValue ) ) ;
112
143
113
- var sx3 = System . Linq . Expressions . Expression . Call ( tgv , str , System . Linq . Expressions . Expression . Constant ( spx . MemberName , typeof ( string ) ) , result ) ;
144
+ var sx3 = LX . Call ( tgv , str , LX . Constant ( spx . MemberName , typeof ( string ) ) , result ) ;
114
145
115
- var sx1 = System . Linq . Expressions . Expression . Condition ( sx3 ,
146
+ var sx1 = LX . Condition ( sx3 ,
116
147
result ,
117
- System . Linq . Expressions . Expression . Constant ( null , typeof ( LogEventPropertyValue ) ) ) ;
148
+ LX . Constant ( null , typeof ( LogEventPropertyValue ) ) ) ;
118
149
119
- var sx2 = System . Linq . Expressions . Expression . Block ( typeof ( LogEventPropertyValue ) ,
120
- System . Linq . Expressions . Expression . Assign ( str , System . Linq . Expressions . Expression . TypeAs ( r , typeof ( StructureValue ) ) ) ,
121
- System . Linq . Expressions . Expression . Condition ( System . Linq . Expressions . Expression . Equal ( str , System . Linq . Expressions . Expression . Constant ( null , typeof ( StructureValue ) ) ) ,
122
- System . Linq . Expressions . Expression . Constant ( null , typeof ( LogEventPropertyValue ) ) ,
150
+ var sx2 = LX . Block ( typeof ( LogEventPropertyValue ) ,
151
+ LX . Assign ( str , LX . TypeAs ( r , typeof ( StructureValue ) ) ) ,
152
+ LX . Condition ( LX . Equal ( str , LX . Constant ( null , typeof ( StructureValue ) ) ) ,
153
+ LX . Constant ( null , typeof ( LogEventPropertyValue ) ) ,
123
154
sx1 ) ) ;
124
155
125
- var assignR = System . Linq . Expressions . Expression . Assign ( r , Splice ( recv , context ) ) ;
126
- var getValue = System . Linq . Expressions . Expression . Condition ( System . Linq . Expressions . Expression . Equal ( r , System . Linq . Expressions . Expression . Constant ( null , typeof ( LogEventPropertyValue ) ) ) ,
127
- System . Linq . Expressions . Expression . Constant ( null , typeof ( LogEventPropertyValue ) ) ,
156
+ var assignR = LX . Assign ( r , Splice ( recv , context ) ) ;
157
+ var getValue = LX . Condition ( LX . Equal ( r , LX . Constant ( null , typeof ( LogEventPropertyValue ) ) ) ,
158
+ LX . Constant ( null , typeof ( LogEventPropertyValue ) ) ,
128
159
sx2 ) ;
129
160
130
- return System . Linq . Expressions . Expression . Lambda < CompiledExpression > (
131
- System . Linq . Expressions . Expression . Block ( typeof ( LogEventPropertyValue ) , new [ ] { r , str , result } , assignR , getValue ) ,
161
+ return LX . Lambda < CompiledExpression > (
162
+ LX . Block ( typeof ( LogEventPropertyValue ) , new [ ] { r , str , result } , assignR , getValue ) ,
132
163
context ) ;
133
164
}
134
165
@@ -165,6 +196,7 @@ protected override Expression<CompiledExpression> Transform(AmbientPropertyExpre
165
196
if ( px . PropertyName == BuiltInProperty . Properties )
166
197
return context => new StructureValue ( context . Properties . Select ( kvp => new LogEventProperty ( kvp . Key , kvp . Value ) ) , null ) ;
167
198
199
+ // Also @Undefined
168
200
return context => null ;
169
201
}
170
202
@@ -207,8 +239,8 @@ static LogEventPropertyValue NormalizeBuiltInProperty(string rawValue)
207
239
208
240
protected override Expression < CompiledExpression > Transform ( Ast . LambdaExpression lmx )
209
241
{
210
- var context = System . Linq . Expressions . Expression . Parameter ( typeof ( LogEvent ) ) ;
211
- var parms = lmx . Parameters . Select ( px => Tuple . Create ( px , System . Linq . Expressions . Expression . Parameter ( typeof ( LogEventPropertyValue ) , px . ParameterName ) ) ) . ToList ( ) ;
242
+ var context = LX . Parameter ( typeof ( LogEvent ) ) ;
243
+ var parms = lmx . Parameters . Select ( px => Tuple . Create ( px , LX . Parameter ( typeof ( LogEventPropertyValue ) , px . ParameterName ) ) ) . ToList ( ) ;
212
244
var body = Splice ( Transform ( lmx . Body ) , context ) ;
213
245
var paramSwitcher = new ExpressionConstantMapper ( parms . ToDictionary ( px => ( object ) px . Item1 , px => ( System . Linq . Expressions . Expression ) px . Item2 ) ) ;
214
246
var rewritten = paramSwitcher . Visit ( body ) ;
@@ -221,17 +253,17 @@ protected override Expression<CompiledExpression> Transform(Ast.LambdaExpression
221
253
else
222
254
throw new NotSupportedException ( "Unsupported lambda signature." ) ;
223
255
224
- var lambda = System . Linq . Expressions . Expression . Lambda ( delegateType , rewritten , parms . Select ( px => px . Item2 ) . ToArray ( ) ) ;
256
+ var lambda = LX . Lambda ( delegateType , rewritten ! , parms . Select ( px => px . Item2 ) . ToArray ( ) ) ;
225
257
226
- return System . Linq . Expressions . Expression . Lambda < CompiledExpression > ( lambda , context ) ;
258
+ return LX . Lambda < CompiledExpression > ( lambda , context ) ;
227
259
}
228
260
229
261
protected override Expression < CompiledExpression > Transform ( Ast . ParameterExpression prx )
230
262
{
231
263
// Will be within a lambda, which will subsequently sub-in the actual value
232
- var context = System . Linq . Expressions . Expression . Parameter ( typeof ( LogEvent ) ) ;
233
- var constant = System . Linq . Expressions . Expression . Constant ( prx , typeof ( object ) ) ;
234
- return System . Linq . Expressions . Expression . Lambda < CompiledExpression > ( constant , context ) ;
264
+ var context = LX . Parameter ( typeof ( LogEvent ) ) ;
265
+ var constant = LX . Constant ( prx , typeof ( object ) ) ;
266
+ return LX . Lambda < CompiledExpression > ( constant , context ) ;
235
267
}
236
268
237
269
protected override Expression < CompiledExpression > Transform ( IndexerWildcardExpression wx )
@@ -241,16 +273,24 @@ protected override Expression<CompiledExpression> Transform(IndexerWildcardExpre
241
273
242
274
protected override Expression < CompiledExpression > Transform ( ArrayExpression ax )
243
275
{
244
- var context = System . Linq . Expressions . Expression . Parameter ( typeof ( LogEvent ) ) ;
276
+ var context = LX . Parameter ( typeof ( LogEvent ) ) ;
245
277
var elements = ax . Elements . Select ( Transform ) . Select ( ex => Splice ( ex , context ) ) . ToArray ( ) ;
246
- var arr = System . Linq . Expressions . Expression . NewArrayInit ( typeof ( LogEventPropertyValue ) , elements ) ;
247
- var sv = System . Linq . Expressions . Expression . New ( SequenceValueCtor , System . Linq . Expressions . Expression . Convert ( arr , typeof ( IEnumerable < LogEventPropertyValue > ) ) ) ;
248
- return System . Linq . Expressions . Expression . Lambda < CompiledExpression > ( System . Linq . Expressions . Expression . Convert ( sv , typeof ( LogEventPropertyValue ) ) , context ) ;
278
+ var arr = LX . NewArrayInit ( typeof ( LogEventPropertyValue ) , elements ) ;
279
+ var sv = LX . Call ( ConstructSequenceValueMethod , arr ) ;
280
+ return LX . Lambda < CompiledExpression > ( sv , context ) ;
249
281
}
250
282
251
283
protected override Expression < CompiledExpression > Transform ( IndexerExpression ix )
252
284
{
253
285
return Transform ( new CallExpression ( Operators . OpElementAt , ix . Receiver , ix . Index ) ) ;
254
286
}
287
+
288
+ protected override Expression < CompiledExpression > Transform ( IndexOfMatchExpression mx )
289
+ {
290
+ var context = LX . Parameter ( typeof ( LogEvent ) ) ;
291
+ var rx = LX . Constant ( mx . Regex ) ;
292
+ var target = Splice ( Transform ( mx . Corpus ) , context ) ;
293
+ return LX . Lambda < CompiledExpression > ( LX . Call ( IndexOfMatchMethod , target , rx ) , context ) ;
294
+ }
255
295
}
256
296
}
0 commit comments