void GenerateApartFieldsProperties(IILDynamicType classImpl, Type interfaceType) { var apartFields = new Dictionary <string, IILField>(); var initializedFields = new Dictionary <string, IILField>(); var methods = RelationInfo.GetMethods(interfaceType); var properties = RelationInfo.GetProperties(interfaceType).ToArray(); foreach (var method in methods) { var name = method.Name; if (!name.StartsWith("get_") && !name.StartsWith("set_")) { continue; } IILField field; IILField initCheckField; var propName = RelationInfo.GetPersistentName(method.Name.Substring(4), properties); if (!_relationInfo.ApartFields.ContainsKey(propName)) { throw new BTDBException($"Invalid property name {propName}."); } if (!apartFields.TryGetValue(propName, out field)) { field = classImpl.DefineField("_" + propName, method.ReturnType, FieldAttributes.Private); apartFields[propName] = field; initCheckField = classImpl.DefineField("_initialized_" + propName, typeof(bool), FieldAttributes.Private); initializedFields[propName] = initCheckField; } else { initCheckField = initializedFields[propName]; } var reqMethod = classImpl.DefineMethod(method.Name, method.ReturnType, method.GetParameters().Select(pi => pi.ParameterType).ToArray(), MethodAttributes.Virtual | MethodAttributes.Public); if (name.StartsWith("set_")) { reqMethod.Generator.Ldarg(0).Ldarg(1).Stfld(field) .Ldarg(0).LdcI4(1).Stfld(initCheckField) .Ret(); } else { var initializedLabel = reqMethod.Generator.DefineLabel("initialized"); reqMethod.Generator .Ldarg(0).Ldfld(initCheckField) .Brtrue(initializedLabel) .Ldstr($"Cannot use uninitialized apart field {propName}") .Newobj(() => new BTDBException(null)) .Throw() .Mark(initializedLabel) .Ldarg(0).Ldfld(field).Ret(); } classImpl.DefineMethodOverride(reqMethod, method); } }
public Func <IObjectDBTransaction, T> Build(string relationName, Type relationDBManipulatorType) { var interfaceType = typeof(T); var classImpl = ILBuilder.Instance.NewType("Relation" + relationName, relationDBManipulatorType, new[] { interfaceType }); var constructorMethod = classImpl.DefineConstructor(new[] { typeof(IObjectDBTransaction), typeof(RelationInfo) }); var il = constructorMethod.Generator; // super.ctor(transaction, relationInfo); il.Ldarg(0).Ldarg(1).Ldarg(2).Call(relationDBManipulatorType.GetConstructor(new[] { typeof(IObjectDBTransaction), typeof(RelationInfo) })) .Ret(); GenerateApartFieldsProperties(classImpl, interfaceType); var methods = RelationInfo.GetMethods(interfaceType); foreach (var method in methods) { if (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")) { continue; } var reqMethod = classImpl.DefineMethod("_R_" + method.Name, method.ReturnType, method.GetParameters().Select(pi => pi.ParameterType).ToArray(), MethodAttributes.Virtual | MethodAttributes.Public); if (method.Name.StartsWith("RemoveBy")) { BuildRemoveByMethod(method, reqMethod, relationDBManipulatorType); } else if (method.Name.StartsWith("FindBy")) { BuildFindByMethod(method, reqMethod, relationDBManipulatorType); } else if (method.Name == "Contains") { BuildContainsMethod(method, reqMethod, relationDBManipulatorType); } else if (method.Name == "ListById") //list by primary key { BuildListByIdMethod(method, reqMethod); } else if (method.Name.StartsWith("ListBy", StringComparison.Ordinal)) //ListBy{Name}(tenantId, .., AdvancedEnumeratorParam) { BuildListByMethod(method, reqMethod); } else if (method.Name == "Insert") { BuildInsertMethod(method, reqMethod, relationDBManipulatorType); } else { BuildManipulatorCallWithSameParameters(method, reqMethod, relationDBManipulatorType); } reqMethod.Generator.Ret(); classImpl.DefineMethodOverride(reqMethod, method); } var classImplType = classImpl.CreateType(); return(BuildRelationCreatorInstance <T>(classImplType, relationName, _relationInfo)); }
public Func <IObjectDBTransaction, T> Build(string relationName, Type relationDBManipulatorType) { var interfaceType = typeof(T); var classImpl = ILBuilder.Instance.NewType("Relation" + relationName, relationDBManipulatorType, new[] { interfaceType }); var constructorMethod = classImpl.DefineConstructor(new[] { typeof(IObjectDBTransaction), typeof(RelationInfo) }); var il = constructorMethod.Generator; // super.ctor(transaction, relationInfo); il.Ldarg(0).Ldarg(1).Ldarg(2).Call(relationDBManipulatorType.GetConstructor(new[] { typeof(IObjectDBTransaction), typeof(RelationInfo) })) .Ret(); GenerateApartFieldsProperties(classImpl, interfaceType); var methods = RelationInfo.GetMethods(interfaceType); foreach (var method in methods) { if (method.Name.StartsWith("get_") || method.Name.StartsWith("set_")) { continue; } var reqMethod = classImpl.DefineMethod("_R_" + method.Name, method.ReturnType, method.GetParameters().Select(pi => pi.ParameterType).ToArray(), MethodAttributes.Virtual | MethodAttributes.Public); if (method.Name.StartsWith("RemoveBy") || method.Name.StartsWith("FindBy") || method.Name == "Contains") { SaveKeyBytesAndCallMethod(reqMethod.Generator, relationDBManipulatorType, method.Name, method.GetParameters(), method.ReturnType, _relationInfo.ApartFields); } else if (method.Name == "ListById") //list by primary key { var parameters = method.GetParameters(); var advEnumParamOrder = (ushort)parameters.Length; var advEnumParam = parameters[advEnumParamOrder - 1].ParameterType; var advEnumParamType = advEnumParam.GenericTypeArguments[0]; var emptyBufferLoc = reqMethod.Generator.DeclareLocal(typeof(ByteBuffer)); var prefixParamCount = method.GetParameters().Length - 1; var field = _relationInfo.ClientRelationVersionInfo.GetPrimaryKeyFields() .Skip(_relationInfo.ApartFields.Count + prefixParamCount).First(); reqMethod.Generator .Ldarg(0); SavePKListPrefixBytes(reqMethod.Generator, method.Name, method.GetParameters(), _relationInfo.ApartFields); reqMethod.Generator .LdcI4(prefixParamCount + _relationInfo.ApartFields.Count) .Ldarg(advEnumParamOrder).Ldfld(advEnumParam.GetField("Order")) .Ldarg(advEnumParamOrder).Ldfld(advEnumParam.GetField("StartProposition")); FillBufferWhenNotIgnoredKeyPropositionIl(advEnumParamOrder, field, emptyBufferLoc, advEnumParam.GetField("Start"), reqMethod.Generator); reqMethod.Generator .Ldarg(advEnumParamOrder).Ldfld(advEnumParam.GetField("EndProposition")); FillBufferWhenNotIgnoredKeyPropositionIl(advEnumParamOrder, field, emptyBufferLoc, advEnumParam.GetField("End"), reqMethod.Generator); if (typeof(IEnumerator <>).MakeGenericType(_relationInfo.ClientType).IsAssignableFrom(method.ReturnType)) { //return new RelationAdvancedEnumerator<T>(relationManipulator, // prefixBytes, prefixFieldCount, // order, // startKeyProposition, startKeyBytes, // endKeyProposition, endKeyBytes, secondaryKeyIndex); var enumType = typeof(RelationAdvancedEnumerator <>).MakeGenericType(_relationInfo.ClientType); var advancedEnumeratorCtor = enumType.GetConstructors()[0]; reqMethod.Generator.Newobj(advancedEnumeratorCtor); } else if (typeof(IOrderedDictionaryEnumerator <,>).MakeGenericType(advEnumParamType, _relationInfo.ClientType) .IsAssignableFrom(method.ReturnType)) { reqMethod.Generator .LdcI4(1); //init key reader //return new RelationAdvancedOrderedEnumerator<T>(relationManipulator, // prefixBytes, prefixFieldCount, // order, // startKeyProposition, startKeyBytes, // endKeyProposition, endKeyBytes, secondaryKeyIndex, initKeyReader); var enumType = typeof(RelationAdvancedOrderedEnumerator <,>).MakeGenericType(advEnumParamType, _relationInfo.ClientType); var advancedEnumeratorCtor = enumType.GetConstructors()[0]; reqMethod.Generator.Newobj(advancedEnumeratorCtor); } else { throw new BTDBException("Invalid method " + method.Name); } } else if (method.Name.StartsWith("ListBy", StringComparison.Ordinal)) //ListBy{Name}(tenantId, .., AdvancedEnumeratorParam) { var parameters = method.GetParameters(); var advEnumParamOrder = (ushort)parameters.Length; var advEnumParam = parameters[advEnumParamOrder - 1].ParameterType; var advEnumParamType = advEnumParam.GenericTypeArguments[0]; var emptyBufferLoc = reqMethod.Generator.DeclareLocal(typeof(ByteBuffer)); var secondaryKeyIndex = _relationInfo.ClientRelationVersionInfo.GetSecondaryKeyIndex(method.Name.Substring(6)); var prefixParamCount = method.GetParameters().Length - 1; var field = _relationInfo.ClientRelationVersionInfo.GetSecondaryKeyFields(secondaryKeyIndex) .Skip(_relationInfo.ApartFields.Count + prefixParamCount).First(); reqMethod.Generator .Ldarg(0); SaveListPrefixBytes(secondaryKeyIndex, reqMethod.Generator, method.Name, method.GetParameters(), _relationInfo.ApartFields); reqMethod.Generator .LdcI4(prefixParamCount + _relationInfo.ApartFields.Count) .Ldarg(advEnumParamOrder).Ldfld(advEnumParam.GetField("Order")) .Ldarg(advEnumParamOrder).Ldfld(advEnumParam.GetField("StartProposition")); FillBufferWhenNotIgnoredKeyPropositionIl(advEnumParamOrder, field, emptyBufferLoc, advEnumParam.GetField("Start"), reqMethod.Generator); reqMethod.Generator .Ldarg(advEnumParamOrder).Ldfld(advEnumParam.GetField("EndProposition")); FillBufferWhenNotIgnoredKeyPropositionIl(advEnumParamOrder, field, emptyBufferLoc, advEnumParam.GetField("End"), reqMethod.Generator); reqMethod.Generator .LdcI4((int)secondaryKeyIndex); if (typeof(IEnumerator <>).MakeGenericType(_relationInfo.ClientType).IsAssignableFrom(method.ReturnType)) { //return new RelationAdvancedSecondaryKeyEnumerator<T>(relationManipulator, // prefixBytes, prefixFieldCount, // order, // startKeyProposition, startKeyBytes, // endKeyProposition, endKeyBytes, secondaryKeyIndex); var enumType = typeof(RelationAdvancedSecondaryKeyEnumerator <>).MakeGenericType(_relationInfo.ClientType); var advancedEnumeratorCtor = enumType.GetConstructors()[0]; reqMethod.Generator.Newobj(advancedEnumeratorCtor); } else if (typeof(IOrderedDictionaryEnumerator <,>).MakeGenericType(advEnumParamType, _relationInfo.ClientType) .IsAssignableFrom(method.ReturnType)) { //return new RelationAdvancedOrderedSecondaryKeyEnumerator<T>(relationManipulator, // prefixBytes, prefixFieldCount, // order, // startKeyProposition, startKeyBytes, // endKeyProposition, endKeyBytes, secondaryKeyIndex); var enumType = typeof(RelationAdvancedOrderedSecondaryKeyEnumerator <,>).MakeGenericType(advEnumParamType, _relationInfo.ClientType); var advancedEnumeratorCtor = enumType.GetConstructors()[0]; reqMethod.Generator.Newobj(advancedEnumeratorCtor); } else { throw new BTDBException("Invalid method " + method.Name); } } else if (method.Name == "Insert") { var methodInfo = relationDBManipulatorType.GetMethod(method.Name); bool returningBoolVariant; var returnType = method.ReturnType; if (returnType == typeof(void)) { returningBoolVariant = false; } else if (returnType == typeof(bool)) { returningBoolVariant = true; } else { throw new BTDBException("Method Insert should be defined with void or bool return type."); } var methodParams = method.GetParameters(); CheckParameterCount(method.Name, 1, methodParams.Length); CheckParameterType(method.Name, 0, methodInfo.GetParameters()[0].ParameterType, methodParams[0].ParameterType); reqMethod.Generator .Ldarg(0) //this .Ldarg(1) .Callvirt(methodInfo); if (!returningBoolVariant) { var returnedTrueLabel = reqMethod.Generator.DefineLabel("returnedTrueLabel"); reqMethod.Generator .Brtrue(returnedTrueLabel) .Ldstr("Trying to insert duplicate key.") .Newobj(() => new BTDBException(null)) .Throw() .Mark(returnedTrueLabel); } } else //call the same method name with the same parameters { var methodParams = method.GetParameters(); int paramCount = methodParams.Length; var methodInfo = relationDBManipulatorType.GetMethod(method.Name); if (methodInfo == null) { throw new BTDBException($"Method {method} is not supported."); } CheckReturnType(method.Name, methodInfo.ReturnType, method.ReturnType); var calledMethodParams = methodInfo.GetParameters(); CheckParameterCount(method.Name, calledMethodParams.Length, methodParams.Length); for (int i = 0; i < methodParams.Length; i++) { CheckParameterType(method.Name, i, calledMethodParams[i].ParameterType, methodParams[i].ParameterType); } for (ushort i = 0; i <= paramCount; i++) { reqMethod.Generator.Ldarg(i); } reqMethod.Generator.Callvirt(methodInfo); } reqMethod.Generator.Ret(); classImpl.DefineMethodOverride(reqMethod, method); } var classImplType = classImpl.CreateType(); return(BuildRelationCreatorInstance <T>(classImplType, relationName, _relationInfo)); }