2023-11-21 23:05:03 +08:00

504 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace CPF.Windows.Json.Deserialize
{
[ExpressionBuildType(DeserializeBuildTypeEnum.KeyValueObject)]
internal class KeyValueObjectBuild : ExpressionJsonResolve
{
internal static BlockExpression Build(Type type)
{
List<Expression> methodCall = new List<Expression>();
LabelTarget returnValueLable = Expression.Label(type, "returnValue");
if (type.IsValueType)
{
/*
ReadObjLeft()
*/
methodCall.Add(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadObjLeft));
}
else
{
/*
if(_ReadNullOrObjLeft)
return null;/return default(ValueType)
*/
Expression ifReadNullOrObjLeftReturnNull1 = Expression.IfThen(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadNullOrObjLeft),
Expression.Return(returnValueLable, Expression.Constant(null, type)
));
methodCall.Add(ifReadNullOrObjLeftReturnNull1);
}
/*
Model m=new Model();
*/
NewExpression newCtor = null;
if (type.IsValueType)
{
List<Expression> ctorArgs = null;
var ctor = type.GetValueTypeCtor(ref ctorArgs);
if (ctor == null)
newCtor = Expression.New(type);
else
newCtor = Expression.New(ctor, ctorArgs);
}
else
{
if (type.IsInterface)
{
//newCtor = Expression.New((Type)typeof(InterfaceImplementation<>).MakeGenericType(type).GetField("Proxy").GetValue(null));
throw new NotSupportedException();
}
else
{
List<Expression> ctorArgs = null;
var ctor = type.GetClassCtor(ref ctorArgs);
newCtor = Expression.New(ctor, ctorArgs);
}
}
ParameterExpression newModel = Expression.Variable(type, "newModel");
methodCall.Add(Expression.Assign(newModel, newCtor));
/*
if(reader.ReadBoolObjRight)
return m;
*/
methodCall.Add(Expression.IfThen(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadBoolObjRight), Expression.Return(returnValueLable, newModel)));
/*
int moveNext=2;
*/
methodCall.Add(ExpressionMembers.MoveNextAssignOne); //Add moveNext localVirbale
/*
if(jsonDeserializeOption.IsFirstUpper)
while(moveNext-->0)
{ }
else if(jsonDeserializeOption.IsFirstLower)
while(moveNext-->0)
{ }
else
while(moveNext-->0)
{ }
*/
var charTries = type.GetCharTries();
if (charTries.Childrens.Count > 0)
methodCall.Add(GenerateWhile(newModel, type, charTries));
/*
reader.ReadObjRight()
*/
methodCall.Add(Expression.Call(ExpressionMembers.Reader, JsonReader._ReadObjRight));
/*
return newModel;
*/
methodCall.Add(Expression.Return(returnValueLable, newModel));
methodCall.Add(Expression.Label(returnValueLable, newModel));
return Expression.Block(new[] { ExpressionMembers.AfterFormatKey, ExpressionMembers.CurrentIdx, ExpressionMembers.IsValueFormat, ExpressionMembers.ValueLength, ExpressionMembers.FormatResult, ExpressionMembers.CharVariable, ExpressionMembers.MoveNext, ExpressionMembers.IsArrive, newModel }, methodCall);
}
private static BlockExpression GenerateWhile(ParameterExpression newModel, Type type, CharTries charTries)
{
//(state):0==The initial state does not change1==Capital letters2==Initial lowercase
/*
char c;
while(moveNext-->0)
{
}
*/
ParameterExpression firstChar = Expression.Variable(typeof(char), "firstChar");
LabelTarget tailOfMethod = Expression.Label(typeof(void), "tailOfMethod");
LabelTarget whileBreak = Expression.Label();
LoopExpression loopExpression = Expression.Loop(Expression.IfThenElse(ExpressionMembers.MoveNextDecrement,
ReturnFunc<Expression>(() =>
{
Expression[] expressions = new Expression[6];
/*
isArrive=false;
*/
expressions[0] = ExpressionMembers.IsArriveAssignFalse;
/*
2019-11-10 16:55:31,Add two configuration options of IgnoreJsonKeys and IsIgnoreExtraKeysInJSON
*/
expressions[1] = GenerateIgnoreKeys(type, tailOfMethod);
/*
* if(handler.option.GlobalKeyFormat!=null)
GenerateGlobalKeyFormat()
else
ReadKey()
*/
expressions[2] = Expression.IfThenElse(
Expression.NotEqual(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._GlobalKeyFormat), Expression.Constant(null, JsonDeserializeOption._GlobalKeyFormat.FieldType)),
GenerateGlobalKeyFormat(type, newModel, tailOfMethod),
GenerateKeyValueObjectReadKey(firstChar, charTries, newModel)
);
/*
if(!isArrive)
throw
*/
expressions[3] = Expression.IfThen(Expression.Equal(ExpressionMembers.IsArrive, Expression.Constant(false, typeof(bool))), Expression.Throw(Expression.New(JsonDeserializationTypeResolutionException._JsonDeserializationTypeResolutionExceptionCtor, ExpressionMembers.Reader, type.IsInterface ? (Expression)Expression.Constant(type, typeof(Type)) : Expression.Call(newModel, type.GetMethod("GetType")))));
/*
Label -> tailOfMethod:
*/
expressions[4] = Expression.Label(tailOfMethod);
/*
if(reader.ReadComma()==true)
moveNext++;
*/
expressions[5] = ExpressionMembers.IfReadBoolCommaIsTrueSoMoveNextIncrement;
return Expression.Block(expressions);
})
, Expression.Break(whileBreak)));
return Expression.Block(new[] { firstChar }, loopExpression, Expression.Label(whileBreak));
}
private static Expression GenerateIgnoreKeys(Type type, LabelTarget tailOfMethod)
{
#region
/*
if(jsonDeserializeHandler.Option.JsonCharacterReadState == JsonCharacterReadStateEnum.None && jsonDeserializeHandler.Option.GlobalKeyFormat == null)
{
string key = reader.ReadString();
if(IsIgnoreExtraKeysInJSON){
if(TypeKeysCache<T>.NotContains(key))
{
reader.ReadColon();
Reader.ReadObject();
goto tailOfMethod;
}
if(IgnoreJsonKeysHasValue)
{
if(IgnoreKey.Contains(key))
{
reader.ReadColon();
Reader.ReadObject();
goto tailOfMethod;
}
}
if(key!=null)
Rollback(key.Length+2);
}
*/
#endregion
ParameterExpression key = Expression.Variable(typeof(string), "key");
return Expression.IfThen(Expression.AndAlso(Expression.Equal(ExpressionMembers.JsonCharacterReadState, Expression.Constant(JsonCharacterReadStateEnum.None, typeof(JsonCharacterReadStateEnum))), ExpressionMembers.GlobalKeyFormatEqualNull),
Expression.Block(new[] { key },
//string key=reader.ReadString();
Expression.Assign(key, Expression.Call(ExpressionMembers.Reader, JsonReader._ReadString)),
//if(IsIgnoreExtraKeysInJSON)
IfIsIgnoreExtraKeysInJsonThenSkipObject(key, type, tailOfMethod),
//if(IgnoreJsonKeysHasValue)
IfIgnoreJsonKeysHasValueThenSkipObject(key, tailOfMethod),
//if(key!=null)
Expression.IfThen(Expression.NotEqual(key, Expression.Constant(null, typeof(string))),
//Rollback(key.Length+2);
Expression.Call(ExpressionMembers.Reader, JsonReader._Rollback, Expression.Add(Expression.Property(key, typeof(string).GetProperty("Length")), Expression.Constant(2, typeof(int))))
)
)
);
}
private static Expression IfIsIgnoreExtraKeysInJsonThenSkipObject(ParameterExpression key, Type type, LabelTarget tailOfMethod)
{
return Expression.IfThen(Expression.Equal(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._IsIgnoreExtraKeysInJSON), Expression.Constant(true, typeof(bool))),
//if(TypeKeysCache<T>.NotContains(key))
Expression.IfThen(Expression.Equal(Expression.Call(TypeUtils.GetContainsMethodInfo(type), key), Expression.Constant(true, typeof(bool))),
/*
reader.ReadColon();
Reader.SkipObject();
goto tailOfMethod;
*/
ReadColonAndSkipObjectAndGotoTailOfMethod(tailOfMethod)
)
);
}
private static Expression IfIgnoreJsonKeysHasValueThenSkipObject(ParameterExpression key, LabelTarget tailOfMethod)
{
return Expression.IfThen(Expression.Equal(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._IgnoreJsonKeysHasValue), Expression.Constant(true, typeof(bool))),
//if(IgnoreKey.Contains(key))
Expression.IfThen(Expression.Call(Expression.MakeMemberAccess(ExpressionMembers.JsonDeserializeOption, JsonDeserializeOption._IgnoreJsonKeys), JsonDeserializeOption._IgnoreJsonKeyContains, key),
/*
reader.ReadColon();
Reader.SkipObject();
goto tailOfMethod;
*/
ReadColonAndSkipObjectAndGotoTailOfMethod(tailOfMethod)
)
);
}
private static Expression ReadColonAndSkipObjectAndGotoTailOfMethod(LabelTarget tailOfMethod)
{
return Expression.Block(
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadColon),
Expression.Call(ExpressionMembers.Reader, JsonReader._SkipObj, ExpressionMembers.JsonDeserializeHandler),
Expression.Goto(tailOfMethod)
);
}
private static Expression GenerateGlobalKeyFormat(Type type, ParameterExpression newModel, LabelTarget tailOfMethod)
{
return Expression.Block(
/*
var afterFormatKey = handler.option.GlobalKeyFormat.Invoke(reader.ReadString(),type)
*/
Expression.Assign(ExpressionMembers.AfterFormatKey,
Expression.Call(ExpressionMembers.GlobalKeyFormat, JsonDeserializeOption._GlobalKeyFormatInvoke,
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadString), Expression.Constant(type, typeof(Type)))),
/*
if(IsIgnoreExtraKeysInJSON){
if(!modelKeys.Contains(afterFormatKey))
{
reader.ReadColon();
Reader.ReadObject();
goto zuihou;
}
if(IsOpenIgnoreKey)
if(IgnoreKey.Contains(key))
{
reader.ReadColon();
Reader.ReadObject();
goto zuihou;
}
*/
IfIsIgnoreExtraKeysInJsonThenSkipObject(ExpressionMembers.AfterFormatKey, type, tailOfMethod),
IfIgnoreJsonKeysHasValueThenSkipObject(ExpressionMembers.AfterFormatKey, tailOfMethod),
/*
reader.ReadColon()
*/
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadColon),
/*
Switch(afterFormatKey)
case 'Name':
ReadValue()....
*/
Expression.Switch(
typeof(void),
ExpressionMembers.AfterFormatKey,
null, null,
ReturnFunc(() =>
{
var members = type.GetModelMembers();
SwitchCase[] switchCases = new SwitchCase[members.Count];
for (int i = 0; i < members.Count; i++)
{
var item = members[i];
switchCases[i] = Expression.SwitchCase(GenerateKeyValueObjectReadValue(item.Value, newModel), Expression.Constant(item.Key));
}
return switchCases;
})
),
/*
isArrive=true;
*/
ExpressionMembers.IsArriveAssignTrue
);
}
private static Expression GenerateKeyValueObjectReadValue(MemberExtension member, ParameterExpression newModel)
{
if (!member.IsProperty || (member.IsProperty && member.PropertyInfo.CanWrite))
{
var valueFormatAttribute = member.MemberInfo.GetCustomAttribute<ValueFormatAttribute>() ?? member.MemberInfo.DeclaringType.GetCustomAttribute<ValueFormatAttribute>();
if (valueFormatAttribute != null)//part
{
return (GenerateValueFormatCode(Expression.Constant(valueFormatAttribute, typeof(ValueFormatAttribute)), ValueFormatAttribute._ReadValueFormat, new[] { ExpressionMembers.JsonRemoveQuoteAndSubstring, Expression.Constant(member.Type, typeof(Type)), ExpressionMembers.JsonDeserializeHandler, ExpressionMembers.IsValueFormat }, newModel, member));
}
else //global
{
/*
if( jsonDeserializeHandler.jsonDeserializeOption .globalValueFormat!=null)
{
}
else
model.N=ReadJson();
*/
return (Expression.IfThenElse(ExpressionMembers.GlobalValueFormatNotEqualNull, GenerateValueFormatCode(ExpressionMembers.GlobalValueFormat, JsonDeserializeOption._GlobalValueFormatInvoke, new[] { ExpressionMembers.JsonRemoveQuoteAndSubstring, Expression.Constant(member.Type, typeof(Type)), ExpressionMembers.JsonDeserializeHandler, ExpressionMembers.IsValueFormat }, newModel, member), Expression.Assign(Expression.MakeMemberAccess(newModel, member.MemberInfo), ExpressionMembers.GetMethodCall(member.Type))));
}
}
else
/* SkipObj(); */
return (Expression.Call(ExpressionMembers.Reader, JsonReader._SkipObj, ExpressionMembers.JsonDeserializeHandler));
}
private static BlockExpression GenerateValueFormatCode(Expression formatDeclareInstance, MethodInfo callFormat, Expression[] paras, ParameterExpression newModel, MemberExtension member)
{
Expression[] expressions = new Expression[6];
/*
reader.BeforAnnotation();
reader.RollBackChar()
*/
expressions[0] = Expression.Call(ExpressionMembers.Reader, JsonReader._BeforAnnotation);
expressions[1] = Expression.Call(ExpressionMembers.Reader, JsonReader._RollbackChar);
/*
currentIdx= Length - reader.Remaining
*/
expressions[2] = ExpressionMembers.CurrentIdxAssignReming;
/*
valueLength=reader.Skipobj()
*/
expressions[3] = (ExpressionMembers.ValueLengthAssignSkipObj);
/*
object formatResult = ValueFormat.ReadValueFormat( reader.Substring(currentIdx,valueLength) ,out isValueFormat);
*/
expressions[4] = (Expression.Assign(ExpressionMembers.FormatResult, Expression.Call(formatDeclareInstance, callFormat, paras)));
/*
if(isValueFormat==true)
model.N=(Convert)obj;
else
reader.Rollback(valueLength)
m.N=ReadJson();//string
*/
expressions[5] = (Expression.IfThenElse(ExpressionMembers.IsValueFormatEqualTrue, Expression.Assign(Expression.MakeMemberAccess(newModel, member.MemberInfo), Expression.Convert(ExpressionMembers.FormatResult, member.Type)), Expression.Block(Expression.Call(ExpressionMembers.Reader, JsonReader._Rollback, ExpressionMembers.ValueLength), Expression.Assign(Expression.MakeMemberAccess(newModel, member.MemberInfo), ExpressionMembers.GetMethodCall(member.Type)))));
return Expression.Block(expressions);
}
private static Expression GenerateKeyValueObjectReadKey(ParameterExpression firstChar, CharTries charTries, ParameterExpression newModel)
{
return
Expression.IfThenElse(
Expression.NotEqual(ExpressionMembers.JsonCharacterReadState, Expression.Constant(JsonCharacterReadStateEnum.IgnoreCase, typeof(JsonCharacterReadStateEnum))),
GenerateKeyValueObjectReadKeyWithInitial(firstChar, charTries, newModel),
GenerateKeyValueObjectReadKeyWithIgnoreCase(charTries, newModel));
}
private static Expression GenerateKeyValueObjectReadKeyWithInitial(ParameterExpression firstChar, CharTries charTries, ParameterExpression newModel)
{
return Expression.Block(
/*
_ReadQuotes();
*/
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadQuotes),
/*
switch (handler.Option.InitialReadState)
{
case InitialReadStateEnum.None:
firstChar = reader.GetChar();
break;
case InitialReadStateEnum.Upper:
firstChar = char.ToUpper(reader.GetChar());
break;
case InitialReadStateEnum.Lower:
firstChar = char.ToLower(reader.GetChar());
break;
}
*/
Expression.Switch(typeof(void), ExpressionMembers.JsonCharacterReadState,
null, null,
Expression.SwitchCase(Expression.Assign(firstChar, ExpressionMembers.GetChar), Expression.Constant(JsonCharacterReadStateEnum.None, typeof(JsonCharacterReadStateEnum))),
Expression.SwitchCase(Expression.Assign(firstChar, Expression.Call(typeof(char).GetMethod("ToUpper", new[] { typeof(char) }), ExpressionMembers.GetChar)), Expression.Constant(JsonCharacterReadStateEnum.InitialUpper, typeof(JsonCharacterReadStateEnum))),
Expression.SwitchCase(Expression.Assign(firstChar, Expression.Call(typeof(char).GetMethod("ToLower", new[] { typeof(char) }), ExpressionMembers.GetChar)), Expression.Constant(JsonCharacterReadStateEnum.InitialLower, typeof(JsonCharacterReadStateEnum)))
),
/*
Switch(firstChar)
case 'N'
Switch(getChar())
case 'a'
Switch(getChar())
case 'm'
Switch(getChar())
case 'e'
Switch(getChar())
case '"':
ReadValue()...
*/
GenerateSwitchCodeByChar(firstChar, charTries, newModel)
);
}
private static Expression GenerateKeyValueObjectReadKeyWithIgnoreCase(CharTries charTries, ParameterExpression newModel)
{
return Expression.Block(
/*
_ReadQuotes();
*/
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadQuotes),
/*
Switch(getChar())
case 'N'
Switch(getChar())
case 'a'
Switch(getChar())
case 'm'
Switch(getChar())
case 'e'
Switch(getChar())
case '"':
ReadValue()...
*/
GenerateSwitchCodeByChar(null, charTries, newModel, true)
);
}
private static SwitchExpression GenerateSwitchCodeByChar(ParameterExpression firstChar, CharTries charTries, ParameterExpression newModel, bool isToLower = false)
{
List<SwitchCase> switchCases = new List<SwitchCase>();
if (charTries.IsValue)
{
//case '"'
var caseQuotes = Expression.SwitchCase(
Expression.Block(
typeof(void),
/*
ReadColon();
*/
Expression.Call(ExpressionMembers.Reader, JsonReader._ReadColon),
GenerateKeyValueObjectReadValue(charTries.Member, newModel),
/*
isArrive=true;
*/
ExpressionMembers.IsArriveAssignTrue
)
, Expression.Constant('"', typeof(char)));
switchCases.Add(caseQuotes);
}
if (charTries.Childrens.Count > 0)
{
foreach (var item in charTries.Childrens)
{
char c = item.Val;
SwitchCase caseOrdinary = Expression.SwitchCase( //When there are two duplicate case items, the expression takes the first one automatically
GenerateSwitchCodeByChar(null, item, newModel, isToLower),
Expression.Constant(isToLower ? char.ToLower(c) : c, typeof(char)));
switchCases.Add(caseOrdinary);
}
}
return Expression.Switch(isToLower ? Expression.Call(typeof(char).GetMethod("ToLower", new[] { typeof(char) }), ExpressionMembers.GetChar) : firstChar ?? ExpressionMembers.GetChar, switchCases.ToArray());
}
}
}