[release/8.0] [mono] Initialize module's image ALC for dynamic objects (#90912)
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Wed, 23 Aug 2023 19:51:40 +0000 (12:51 -0700)
committerGitHub <noreply@github.com>
Wed, 23 Aug 2023 19:51:40 +0000 (12:51 -0700)
* Use the image's ALC instead of the default one if exists

* Init module's image ALC

* Add runtime test for dynamic objects

* Fix lint

* Disable test on platforms that don't support dynamic code generation

---------

Co-authored-by: Milos Kotlar <kotlarmilos@gmail.com>
src/mono/mono/metadata/sre.c
src/tests/Loader/CustomAttributes/DynamicObjects.cs [new file with mode: 0644]
src/tests/Loader/CustomAttributes/DynamicObjects.csproj [new file with mode: 0644]
src/tests/issues.targets

index de94e8c..3f47f9e 100644 (file)
@@ -1289,6 +1289,7 @@ image_module_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *e
                 * determined at assembly save time.
                 */
                /*image = (MonoDynamicImage*)ab->dynamic_assembly->assembly.image; */
+               MonoAssemblyLoadContext *alc = mono_alc_get_default ();
                MonoStringHandle abname = MONO_HANDLE_NEW_GET (MonoString, ab, name);
                char *name = mono_string_handle_to_utf8 (abname, error);
                return_val_if_nok (error, FALSE);
@@ -1300,6 +1301,7 @@ image_module_basic_init (MonoReflectionModuleBuilderHandle moduleb, MonoError *e
                }
                MonoDynamicAssembly *dynamic_assembly = MONO_HANDLE_GETVAL (ab, dynamic_assembly);
                image = mono_dynamic_image_create (dynamic_assembly, name, fqname);
+               image->image.alc = alc;
 
                MONO_HANDLE_SETVAL (MONO_HANDLE_CAST (MonoReflectionModule, moduleb), image, MonoImage*, &image->image);
                MONO_HANDLE_SETVAL (moduleb, dynamic_image, MonoDynamicImage*, image);
diff --git a/src/tests/Loader/CustomAttributes/DynamicObjects.cs b/src/tests/Loader/CustomAttributes/DynamicObjects.cs
new file mode 100644 (file)
index 0000000..5b232d8
--- /dev/null
@@ -0,0 +1,107 @@
+using System;
+using System.Resources;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+
+using Xunit;
+
+#nullable disable
+
+namespace DynamicObjects {
+    public class M {
+        public const string ObjectRequiredMessage = "some string";
+        public static int Main() {
+            var instance = createObject();
+            var attrs = instance.GetType().GetProperty("prop1").GetCustomAttributes();
+
+            Assert.True(attrs.Count() == 2);
+            Assert.Equal(attrs.ElementAt(0).ToString(), "System.ComponentModel.DataAnnotations.DisplayAttribute");
+            Assert.Equal(attrs.ElementAt(1).ToString(), "System.ComponentModel.DataAnnotations.RequiredAttribute");
+            Assert.Equal(typeof(RequiredAttribute), attrs.ElementAt(1).GetType());
+            Assert.Equal(ObjectRequiredMessage, ((RequiredAttribute)attrs.ElementAt(1)).FormatErrorMessage("abc"));
+
+            Console.WriteLine("Success");
+            return 100;
+        }
+
+        public static object createObject () {
+            var an = new AssemblyName { Name = "TempAssembly" ,Version = new Version(1, 0, 0, 0) };
+            var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
+            var moduleBuilder = assemblyBuilder.DefineDynamicModule("TempWorkflowAssembly.dll");
+            var tb = moduleBuilder.DefineType("namespace.myclass"
+                                            , TypeAttributes.Public |
+                                            TypeAttributes.Class |
+                                            TypeAttributes.AnsiClass |
+                                            TypeAttributes.BeforeFieldInit
+                                            , typeof(object));
+
+            FieldBuilder fb = tb.DefineField("_prop1",
+                                                        typeof(string),
+                                                        FieldAttributes.Private);
+
+            var pb = tb.DefineProperty("prop1", PropertyAttributes.HasDefault, typeof(string), null);
+            MethodAttributes getSetAttr =
+                MethodAttributes.Public | MethodAttributes.SpecialName |
+                MethodAttributes.HideBySig;
+
+            // Define the "get" accessor method for prop1.
+            MethodBuilder custNameGetPropMthdBldr =
+                tb.DefineMethod("get_prop1",
+                                        getSetAttr,
+                                        typeof(string),
+                                        Type.EmptyTypes);
+
+            ILGenerator custNameGetIL = custNameGetPropMthdBldr.GetILGenerator();
+
+            custNameGetIL.Emit(OpCodes.Ldarg_0);
+            custNameGetIL.Emit(OpCodes.Ldfld, fb);
+            custNameGetIL.Emit(OpCodes.Ret);
+
+            // Define the "set" accessor method for prop1.
+            MethodBuilder custNameSetPropMthdBldr =
+                tb.DefineMethod("set_prop1",
+                                        getSetAttr,
+                                        null,
+                                        new Type[] { typeof(string) });
+
+            ILGenerator custNameSetIL = custNameSetPropMthdBldr.GetILGenerator();
+
+            custNameSetIL.Emit(OpCodes.Ldarg_0);
+            custNameSetIL.Emit(OpCodes.Ldarg_1);
+            custNameSetIL.Emit(OpCodes.Stfld, fb);
+            custNameSetIL.Emit(OpCodes.Ret);
+
+            // Last, we must map the two methods created above to our PropertyBuilder to
+            // their corresponding behaviors, "get" and "set" respectively.
+            pb.SetGetMethod(custNameGetPropMthdBldr);
+            pb.SetSetMethod(custNameSetPropMthdBldr);
+
+
+            ///create display attribute
+            var dat = typeof(DisplayAttribute);
+            CustomAttributeBuilder CAB = new CustomAttributeBuilder(dat.GetConstructor(new Type[0]), 
+                new object[0],
+                new PropertyInfo[1] { dat.GetProperty(nameof(DisplayAttribute.Name))}, 
+                new object[] { "property 1"});
+            pb.SetCustomAttribute(CAB);
+
+            // //create required attribute
+            var rat = typeof(RequiredAttribute);
+            CustomAttributeBuilder CABR = new CustomAttributeBuilder(rat.GetConstructor(new Type[0]),
+                                new object[0],
+                                new PropertyInfo[2] { rat.GetProperty(nameof(RequiredAttribute.ErrorMessageResourceType)),rat.GetProperty(nameof(RequiredAttribute.ErrorMessageResourceName))},
+                                new object[] {typeof(ValidationErrors), "ObjectRequired" });
+            pb.SetCustomAttribute(CABR);
+
+            var objectType = tb.CreateType();
+            return Activator.CreateInstance(objectType);
+        }
+    }
+
+    public class ValidationErrors {
+        public static string ObjectRequired => M.ObjectRequiredMessage; 
+    }
+
+}
diff --git a/src/tests/Loader/CustomAttributes/DynamicObjects.csproj b/src/tests/Loader/CustomAttributes/DynamicObjects.csproj
new file mode 100644 (file)
index 0000000..3e62bfd
--- /dev/null
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+</Project>
index 22178e4..bdccec8 100644 (file)
 
         <!-- Requires Debug build -->
         <ExcludeList Include="$(XunitTestBinBase)/JIT\Directed\debugging\poisoning\poison\*" />
+
+        <ExcludeList Include="$(XunitTestBinBase)/Loader/CustomAttributes/**">
+            <Issue>Dynamic code generation is not supported on this platform</Issue>
+        </ExcludeList>
     </ItemGroup>
 
     <!-- run.proj finds all the *.cmd/*.sh scripts in a test folder and creates corresponding test methods.
         <ExcludeList Include="$(XunitTestBinBase)/tracing/eventpipe/gcdump/**">
             <Issue>https://github.com/dotnet/runtime/issues/90012</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/Loader/CustomAttributes/**">
+            <Issue>Dynamic code generation is not supported on this platform</Issue>
+        </ExcludeList>
     </ItemGroup>
 
     <ItemGroup Condition="'$(TargetArchitecture)' == 'wasm' or '$(TargetsMobile)' == 'true'">