Esempio n. 1
0
        private void WeaveId(ModelWeavingContext context)
        {
            if (context.IdPropDef != null &&
                context.IdPropDef.PropertyType.Resolve() != context.ImportReference(typeof(Guid)).Resolve())
            {
                LogError($"[Id] property must have a {typeof(Guid).FullName} getter: {context.IdPropDef.FullName}");
            }

            // if id property doesn't have a setter, try to add one
            if (context.IdPropDef != null)
            {
                LogInfo($"Upserting [Id] property setter: {context.IdPropDef.FullName} ");

                var idBackingField = context
                                     .IdPropDef
                                     ?.GetMethod
                                     ?.Body
                                     ?.Instructions
                                     ?.SingleOrDefault(x => x.OpCode == OpCodes.Ldfld)
                                     ?.Operand as FieldReference;

                var setter = context.IdPropDef.SetMethod;
                if (setter == null)
                {
                    setter = new MethodDefinition(
                        $"set_{context.IdPropDef.Name}",
                        MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                        context.ImportReference(TypeSystem.Void));
                    setter.Parameters.Add(
                        new ParameterDefinition(
                            "value",
                            ParameterAttributes.None,
                            context.IdPropDef.PropertyType));
                    setter.SemanticsAttributes = MethodSemanticsAttributes.Setter;
                    context.Methods.Add(setter);
                    context.IdPropDef.SetMethod = setter;
                }
                else
                {
                    setter.Body.Instructions.Clear();
                }
                var proc = setter.Body.GetILProcessor();

                var ret = proc.Create(OpCodes.Ret);

                proc.Emit(OpCodes.Ldarg_0); // load 'this' onto stack
                proc.Emit(OpCodes.Callvirt, context.SessionManagedProperty.GetMethod);
                proc.Emit(OpCodes.Brtrue_S, ret);

                proc.Emit(OpCodes.Ldarg_0);               // load 'this' onto stack
                proc.Emit(OpCodes.Ldarg_1);               // load 'value' onto stack
                proc.Emit(OpCodes.Stfld, idBackingField); // this.backingField = value;
                proc.Append(ret);                         // return

                LogInfo($"Successfully added updated setter for {context.IdPropDef}");
            }
        }
Esempio n. 2
0
        //TODO: this might be the fugliest algo ever written.  maybe clean this up
        private IEnumerable <string> GetEagerRelationships(
            ModelWeavingContext context,
            TypeDefinition type,
            string path,
            TypeDefinition[] pathTypes)
        {
            var eagerRltns = type.Properties
                             .Where(x => x.CustomAttributes
                                    .Where(attr =>
                                           attr.AttributeType.Resolve() == context.ImportReference(_hasOneAttributeTypeDef).Resolve() ||
                                           attr.AttributeType.Resolve() == context.ImportReference(_hasManyAttributeTypeDef).Resolve())
                                    .Where(attr => attr.HasConstructorArguments)
                                    .SelectMany(attr => attr.ConstructorArguments
                                                .Where(arg => arg.Type.Resolve() == context.ImportReference(_loadStrategyTypeDef).Resolve()))
                                    .Any(arg => (int)arg.Value == 1)) // eager
                             .Where(eagerProp =>
            {
                var eagerPropAttr = eagerProp.CustomAttributes.ContainsAttribute(Constants.Attributes.HasOne)
                        ? Constants.Attributes.HasOne
                        : Constants.Attributes.HasMany;
                var eagerPropName = eagerProp.JsonApiName(TypeSystem, eagerPropAttr);
                var eagerPropType = eagerProp.PropertyType.Resolve();
                var nextPath      = string.IsNullOrEmpty(path)
                        ? eagerPropName
                        : $"{path}.{eagerPropName}";
                var typeVisited = pathTypes.Contains(eagerPropType);
                if (typeVisited)
                {
                    LogWarning($"Potential circular reference detected and omitted from eager load: {eagerProp.PropertyType.Resolve().FullName}::{nextPath}");
                }
                return(!typeVisited);
            });

            if (eagerRltns.Any())
            {
                return(eagerRltns.SelectMany(x =>
                {
                    var eagerPropAttr = x.CustomAttributes.ContainsAttribute(Constants.Attributes.HasOne)
                            ? Constants.Attributes.HasOne
                            : Constants.Attributes.HasMany;
                    var eagerPropName = x.JsonApiName(TypeSystem, eagerPropAttr);
                    var eagerPropType = x.PropertyType.Resolve();
                    var nextPath = string.IsNullOrEmpty(path)
                            ? eagerPropName
                            : $"{path}.{eagerPropName}";
                    return GetEagerRelationships(
                        context,
                        x.PropertyType.Resolve(),
                        nextPath,
                        pathTypes.Concat(new[] { eagerPropType }).ToArray());
                })
                       .ToArray());
            }

            return(new[] { path });
        }
Esempio n. 3
0
        private void AddSessionManagedProperty(ModelWeavingContext context)
        {
            var propertyName = "__argo__generated_SessionManaged";

            var getter = new MethodDefinition(
                $"get_{propertyName}",
                MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                context.ImportReference(typeof(bool)))
            {
                SemanticsAttributes = MethodSemanticsAttributes.Getter
            };

            var proc = getter.Body.GetILProcessor();

            proc.Body.Variables.Add(new VariableDefinition(context.ImportReference(typeof(bool))));

            var load0     = proc.Create(OpCodes.Ldc_I4_0);
            var storeLoc0 = proc.Create(OpCodes.Stloc_0);

            var loadRet = proc.Create(OpCodes.Ldloc_0);

            proc.Emit(OpCodes.Nop);

            //====== this.__argo__generated_session != null
            proc.Emit(OpCodes.Ldarg_0);
            proc.Emit(OpCodes.Ldfld, context.SessionField);
            proc.Emit(OpCodes.Brfalse_S, load0);
            //=============================================

            //====== !this.__argo__generated_session.Disposed
            proc.Emit(OpCodes.Ldarg_0);                                                    // push 'this' onto stack
            proc.Emit(OpCodes.Ldfld, context.SessionField);                                // push 'this.__argo__generated_session' onto stack
            proc.Emit(OpCodes.Callvirt, context.ImportReference(_session_DisposedGetter)); // push 'Disposed' value onto stack
            proc.Emit(OpCodes.Ldc_I4_0);
            proc.Emit(OpCodes.Ceq);
            proc.Emit(OpCodes.Br_S, storeLoc0);
            //===============================================

            proc.Append(load0);
            proc.Append(storeLoc0);
            proc.Emit(OpCodes.Br_S, loadRet);
            proc.Append(loadRet);
            proc.Emit(OpCodes.Ret);

            context.SessionManagedProperty = new PropertyDefinition(
                propertyName,
                PropertyAttributes.None,
                context.ImportReference(typeof(bool)))
            {
                GetMethod = getter
            };

            context.Methods.Add(getter);
            context.Properties.Add(context.SessionManagedProperty);
        }
Esempio n. 4
0
        private void WeaveHasManys(ModelWeavingContext context)
        {
            if (_session_GetGenericEnumerable == null ||
                _session_GetGenericCollection == null)
            {
                throw new Exception("Argo relationship weaving failed unexpectedly");
            }

            foreach (var propertyDef in context.MappedHasManys)
            {
                var propertyTypeRef = propertyDef.PropertyType;
                var propertyTypeDef = propertyTypeRef.Resolve();

                MethodReference getRltnMethRef;

                if (propertyTypeDef == context.ImportReference(typeof(IEnumerable <>)).Resolve())
                {
                    getRltnMethRef = _session_GetGenericEnumerable;
                }
                else if (propertyTypeDef == context.ImportReference(typeof(ICollection <>)).Resolve())
                {
                    getRltnMethRef = _session_GetGenericCollection;
                }
                else
                {
                    LogError($"Argo encountered a HasMany relationship on non IEnumerable<T> or ICollection<T> property {propertyDef.FullName}");
                    continue;
                }

                // get the backing field
                var backingField = propertyDef.BackingField();

                if (backingField == null)
                {
                    LogError($"Failed to load backing field for property {propertyDef.FullName}");
                    continue;
                }

                // find the rltnName, if there is one
                var rltnName = propertyDef.JsonApiName(TypeSystem, Constants.Attributes.HasMany);

                // find property generic element type
                var elementTypeDef = ((GenericInstanceType)propertyTypeRef).GenericArguments.First().Resolve();

                LogInfo($"\tWeaving {propertyDef} => {rltnName}");

                WeaveRltnGetter(context, backingField, propertyDef, elementTypeDef, getRltnMethRef, rltnName);
            }
        }
Esempio n. 5
0
        private void WeaveReferenceGetter(
            ModelWeavingContext context,
            FieldReference backingField,
            FieldReference backingFieldInitialized,
            PropertyDefinition refPropDef,
            string attrName)
        {
            // supply generic type arguments to template
            var sessionGetAttr = _session_GetReference.MakeGenericMethod(context.ModelTypeDef, refPropDef.PropertyType);

            // get
            // {
            //   if (this.__argo__generated_session != null && !this.<[PropName]>k__BackingFieldInitialized)
            //   {
            //     this.<[PropName]>k__BackingField = this.__argo__generated_session.GetReference<[ModelType], [ReturnType]>(this, "[AttrName]");
            //     this.<[PropName]>k__BackingFieldInitialized = true;
            //   }
            //   return this.<[PropName]>k__BackingField;
            // }
            refPropDef.GetMethod.Body.Instructions.Clear();
            var proc = refPropDef.GetMethod.Body.GetILProcessor();

            var returnField = proc.Create(OpCodes.Ldarg_0);

            proc.Emit(OpCodes.Ldarg_0);                        // load 'this' onto stack
            proc.Emit(OpCodes.Ldfld, context.SessionField);    // load __argo__generated_session field from 'this'
            proc.Emit(OpCodes.Brfalse, returnField);           // if __argo__generated_session != null continue, else returnField

            proc.Emit(OpCodes.Ldarg_0);                        // load 'this' onto stack
            proc.Emit(OpCodes.Ldfld, backingFieldInitialized); // load <[PropName]>k__BackingFieldInitialized from 'this'
            proc.Emit(OpCodes.Brtrue, returnField);            // !this.<[PropName]>k__BackingFieldInitialized continue, else returnField

            proc.Emit(OpCodes.Ldarg_0);                        // load 'this' to reference backing field

            proc.Emit(OpCodes.Ldarg_0);                        // load 'this' onto stack to reference session field
            proc.Emit(OpCodes.Ldfld, context.SessionField);    // load __argo__generated_session field from 'this'
            proc.Emit(OpCodes.Ldarg_0);                        // load 'this'
            proc.Emit(OpCodes.Ldstr, attrName);                // load attrName onto stack
            proc.Emit(OpCodes.Callvirt, context.ImportReference(
                          sessionGetAttr,
                          refPropDef.PropertyType.IsGenericParameter
                    ? context.ModelTypeDef
                    : null));                                  // invoke session.GetReference(..)
            proc.Emit(OpCodes.Stfld, backingField);            // store return value in 'this'.<backing field>

            proc.Emit(OpCodes.Ldarg_0);                        // load 'this' onto stack
            proc.Emit(OpCodes.Ldc_I4_1);                       // load true (1) onto stack
            proc.Emit(OpCodes.Stfld, backingFieldInitialized); // store true in 'this'.<backing field>Initialized

            proc.Append(returnField);                          // load 'this' onto stack
            proc.Emit(OpCodes.Ldfld, backingField);            // load 'this'.<backing field>
            proc.Emit(OpCodes.Ret);                            // return
        }
Esempio n. 6
0
        private void WeaveHasManyIds(ModelWeavingContext context)
        {
            if (_session_GetRelationshipIds == null)
            {
                throw new Exception("Argo relationship id weaving failed unexpectedly");
            }

            foreach (var propertyDef in context.MappedHasManyIds)
            {
                if (propertyDef.PropertyType.Resolve() != context.ImportReference(typeof(IEnumerable)).Resolve() &&
                    propertyDef.PropertyType.Resolve() != context.ImportReference(typeof(IEnumerable <Guid>)).Resolve())
                {
                    LogError($"[HasManyIds] property must have a {typeof(IEnumerable).FullName} or {typeof(IEnumerable<Guid>).FullName} getter: {propertyDef.FullName}");
                }
                if (propertyDef.SetMethod != null)
                {
                    LogError($"[HasManyIds] property must not have a setter");
                }

                // get the backing field
                var backingField = propertyDef
                                   ?.GetMethod
                                   ?.Body
                                   ?.Instructions
                                   ?.SingleOrDefault(x => x.OpCode == OpCodes.Ldfld)
                                   ?.Operand as FieldReference;

                if (backingField == null)
                {
                    throw new Exception($"Failed to load backing field for property {propertyDef.FullName}");
                }

                // find the attrName, if there is one
                var attrName = propertyDef.JsonApiName(TypeSystem, Constants.Attributes.HasManyIds);

                LogInfo($"\tWeaving {propertyDef} => {attrName}");

                WeaveRelationshipIdsGetter(context, backingField, propertyDef, attrName);
            }
        }
Esempio n. 7
0
        private static FieldDefinition AddField(
            string fieldName,
            TypeReference fieldType,
            FieldAttributes attributes,
            ModelWeavingContext context)
        {
            var fieldDef = new FieldDefinition(
                $"__argo__generated_{fieldName}",
                attributes,
                context.ImportReference(fieldType));

            context.Fields.Add(fieldDef);
            return(fieldDef);
        }
Esempio n. 8
0
        private static void WeaveRltnGetter(
            ModelWeavingContext context,
            FieldReference backingField,
            PropertyDefinition rltnPropDef,
            TypeReference elementTypeDef,
            MethodReference sessionGetRltnGeneric,
            string rltnName)
        {
            // supply generic type args to template
            var sessionGetRltn = sessionGetRltnGeneric.MakeGenericMethod(
                context.ModelTypeDef,
                elementTypeDef);

            rltnPropDef.GetMethod.Body.Instructions.Clear();

            var proc = rltnPropDef.GetMethod.Body.GetILProcessor();

            var endif = proc.Create(OpCodes.Ldarg_0);

            // TODO: this isn't thread safe - consider generating a Lazy in the ctor and invoking it here
            proc.Emit(OpCodes.Ldarg_0);
            proc.Emit(OpCodes.Ldfld, context.SessionField);
            proc.Emit(OpCodes.Brfalse_S, endif);
            proc.Emit(OpCodes.Ldarg_0);
            proc.Emit(OpCodes.Ldfld, backingField);
            proc.Emit(OpCodes.Brtrue_S, endif);

            proc.Emit(OpCodes.Ldarg_0);

            proc.Emit(OpCodes.Ldarg_0);                     // load 'this' onto stack to reference session field
            proc.Emit(OpCodes.Ldfld, context.SessionField); // load __argo__generated_session field from 'this'
            proc.Emit(OpCodes.Ldarg_0);                     // load 'this'
            proc.Emit(OpCodes.Ldstr, rltnName);             // load attrName onto stack
            proc.Emit(OpCodes.Callvirt, context.ImportReference(
                          sessionGetRltn,
                          rltnPropDef.PropertyType.IsGenericParameter
                    ? context.ModelTypeDef
                    : null));                       // invoke session.GetAttribute(..)

            proc.Emit(OpCodes.Stfld, backingField); // store return value in 'this'.<backing field>

            proc.Append(endif);
            proc.Emit(OpCodes.Ldfld, backingField);
            proc.Emit(OpCodes.Ret);
        }
Esempio n. 9
0
        private void WeaveAttributeFieldInitializers(
            ModelWeavingContext context,
            ILProcessor proc,
            IEnumerable <PropertyDefinition> attrPropDefs)
        {
            foreach (var attrPropDef in attrPropDefs)
            {
                // supply generic type arguments to template
                var sessionGetAttr = _session_GetAttribute
                                     .MakeGenericMethod(context.ModelTypeDef, attrPropDef.PropertyType);

                var backingField = attrPropDef.BackingField();

                if (backingField == null)
                {
                    throw new Exception($"Failed to load backing field for property {attrPropDef?.FullName}");
                }

                var propAttr = attrPropDef.CustomAttributes.GetAttribute(Constants.Attributes.Property);
                var attrName = propAttr.ConstructorArguments
                               .Select(x => x.Value as string)
                               .SingleOrDefault() ?? attrPropDef.Name.Camelize();

                proc.Emit(OpCodes.Ldarg_0);

                proc.Emit(OpCodes.Ldarg_0);                     // load 'this' onto stack to reference session field
                proc.Emit(OpCodes.Ldfld, context.SessionField); // load __argo__generated_session field from 'this'
                proc.Emit(OpCodes.Ldarg_0);                     // load 'this'
                proc.Emit(OpCodes.Ldstr, attrName);             // load attrName onto stack
                proc.Emit(OpCodes.Callvirt, context.ImportReference(
                              sessionGetAttr,
                              attrPropDef.PropertyType.IsGenericParameter
                        ? context.ModelTypeDef
                        : null));                       // invoke session.GetAttribute(..)

                proc.Emit(OpCodes.Stfld, backingField); // store return value in 'this'.<backing field>
            }
        }
Esempio n. 10
0
        private void AddInitialize(ModelWeavingContext context)
        {
            // public void __argo__generated_Initialize(IResourceIdentifier resource, IModelSession session)
            // {
            //   this.__argo__generated_Resource = resource;
            //   this.__argo__generated_session = session;
            //   this.__argo__generated_includePath = "model.include.path";
            //   this.Id = __argo__generated_session.GetId<TModel>();
            //  }
            var initialize = new MethodDefinition(
                "__argo__generated_Initialize",
                MethodAttributes.Public,
                TypeSystem.Void);

            initialize.Parameters.Add(
                new ParameterDefinition(
                    "resource",
                    ParameterAttributes.None,
                    context.ImportReference(_resourceIdentifierTypeDef)));
            initialize.Parameters.Add(
                new ParameterDefinition(
                    "session",
                    ParameterAttributes.None,
                    context.ImportReference(_sessionTypeDef)));

            var idBackingField = context
                                 .IdPropDef
                                 ?.GetMethod
                                 ?.Body
                                 ?.Instructions
                                 ?.SingleOrDefault(x => x.OpCode == OpCodes.Ldfld)
                                 ?.Operand as FieldReference;
            // supply generic type arguments to template
            var sessionGetId = _session_GetId.MakeGenericMethod(context.ModelTypeDef);

            var proc = initialize.Body.GetILProcessor();

            proc.Emit(OpCodes.Ldarg_0); // load 'this' onto stack
            proc.Emit(OpCodes.Ldarg_1); // load arg 'resource' onto stack
            proc.Emit(OpCodes.Callvirt, context.ResourcePropDef.SetMethod);

            proc.Emit(OpCodes.Ldarg_0);                     // load 'this' onto stack
            proc.Emit(OpCodes.Ldarg_2);                     // load arg 'session' onto stack
            proc.Emit(OpCodes.Stfld, context.SessionField); // this.__argo__generated_session = session;

            proc.Emit(OpCodes.Ldarg_0);                     // load 'this' onto stack
            proc.Emit(OpCodes.Ldarg_0);                     // load 'this' onto stack
            proc.Emit(OpCodes.Ldfld, context.SessionField); // load this.__argo__generated_session
            proc.Emit(OpCodes.Ldarg_0);                     // load 'this' onto stack
            proc.Emit(OpCodes.Callvirt, context.ImportReference(sessionGetId));
            proc.Emit(OpCodes.Stfld, idBackingField);       // this.<Id>K_backingField = this.__argo__generated_session.GetId<TModel>();

            // this._attrBackingField = this.__argo__generated_session.GetAttribute
            WeaveAttributeFieldInitializers(context, proc, context.MappedAttributes);

            // this._attrBackingField = this.__argo__generated_session.GetMeta
            WeaveMetaFieldInitializers(context, proc, context.MappedMeta);

            proc.Emit(OpCodes.Ret); // return

            context.Methods.Add(initialize);
        }
Esempio n. 11
0
        private PropertyDefinition AddAutoProperty(string propertyName, ModelWeavingContext context)
        {
            var backingField = new FieldDefinition(
                $"<{propertyName}>k__BackingField",
                FieldAttributes.Private,
                context.ImportReference(_resourceIdentifierTypeDef));

            backingField.CustomAttributes.Add(new CustomAttribute(context.ImportReference(_compilerGeneratedAttribute)));
            backingField.CustomAttributes.Add(new CustomAttribute(context.ImportReference(_debuggerBrowsableAttribute))
            {
                ConstructorArguments =
                {
                    new CustomAttributeArgument(
                        context.ImportReference(_debuggerBrowsableStateTypeDef),
                        DebuggerBrowsableState.Collapsed)
                }
            });

            var getter = new MethodDefinition(
                $"get_{propertyName}",
                MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                context.ImportReference(_resourceIdentifierTypeDef))
            {
                SemanticsAttributes = MethodSemanticsAttributes.Getter
            };

            var getterProc = getter.Body.GetILProcessor();

            getterProc.Emit(OpCodes.Ldarg_0);             // load 'this' onto stack
            getterProc.Emit(OpCodes.Ldfld, backingField); // load <__argo__generated_Resource>k__BackingField onto stack
            getterProc.Emit(OpCodes.Ret);                 // return this.<__argo__generated_Resource>k__BackingField;

            var setter = new MethodDefinition(
                $"set_{propertyName}",
                MethodAttributes.Private | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
                context.ImportReference(TypeSystem.Void))
            {
                SemanticsAttributes = MethodSemanticsAttributes.Setter,
                Parameters          =
                {
                    new ParameterDefinition(
                        "value",
                        ParameterAttributes.None,
                        context.ImportReference(_resourceIdentifierTypeDef))
                }
            };

            var setterProc = setter.Body.GetILProcessor();

            setterProc.Emit(OpCodes.Ldarg_0);             // load 'this' onto stack
            setterProc.Emit(OpCodes.Ldarg_1);             // load 'value' onto stack
            setterProc.Emit(OpCodes.Stfld, backingField); // this.<__argo__generated_Resource>k__BackingField = value;
            setterProc.Emit(OpCodes.Ret);                 // return;

            var propDef = new PropertyDefinition(
                propertyName,
                PropertyAttributes.None,
                context.ImportReference(_resourceIdentifierTypeDef))
            {
                GetMethod = getter,
                SetMethod = setter
            };

            context.Fields.Add(backingField);
            context.Methods.Add(getter);
            context.Methods.Add(setter);
            context.Properties.Add(propDef);

            return(propDef);
        }