static void InjectOnCreateForCompiler(TypeDefinition typeDefinition) { // Turns out it's not trivial to inject some code that has to be run OnCreate of the system. // We cannot just create an OnCreate() method, because there might be a deriving class that also implements it. // That child method is probably not calling base.OnCreate(), but even when it is (!!) the c# compiler bakes base.OnCreate() // into a direct reference to whatever is the first baseclass to have OnCreate() at the time of compilation. So if we go // and inject an OnCreate() in this class later on, the child's base.OnCreate() call will actually bypass it. // // Instead what we do is add OnCreateForCompiler, hide it from intellisense, give you an error if wanna be that guy that goes // and implement it anyway, and then we inject a OnCreateForCompiler method into each and every ComponentSystem. The reason we have to emit it in // each and every system, and not just the ones where we have something to inject, is that when we emit these method, we need // to also emit base.OnCreateForCompiler(). However, when we are processing an user system type, we do not know yet if its baseclass // also needs an OnCreateForCompiler(). So we play it safe, and assume it does. So every OnCreateForCompiler() that we emit, // will assume its basetype also has an implementation and invoke that. if (typeDefinition.Name == nameof(ComponentSystemBase) && typeDefinition.Namespace == "Unity.Entities") { return; } var onCreateForCompilerName = EntitiesILHelpers.GetOnCreateForCompilerName(); var preExistingMethod = typeDefinition.Methods.FirstOrDefault(m => m.Name == onCreateForCompilerName); if (preExistingMethod != null) { UserError.DC0026($"It's not allowed to implement {onCreateForCompilerName}'", preExistingMethod).Throw(); } EntitiesILHelpers.GetOrMakeOnCreateForCompilerMethodFor(typeDefinition); }
private static MethodDefinition InjectOnCreateForCompiler(TypeDefinition typeDefinition) { // Turns out it's not trivial to inject some code that has to be run OnCreate of the system. // We cannot just create an OnCreate() method, because there might be a deriving class that also implements it. // That child method is probably not calling base.OnCreate(), but even when it is (!!) the c# compiler bakes base.OnCreate() // into a direct reference to whatever is the first baseclass to have OnCreate() at the time of compilation. So if we go // and inject an OnCreate() in this class later on, the child's base.OnCreate() call will actually bypass it. // // Instead what we do is add OnCreateForCompiler, hide it from intellisense, give you an error if wanna be that guy that goes // and implement it anyway, and then we inject a OnCreateForCompiler method into each and every ComponentSystem. The reason we have to emit it in // each and every system, and not just the ones where we have something to inject, is that when we emit these method, we need // to also emit base.OnCreateForCompiler(). However, when we are processing an user system type, we do not know yet if its baseclass // also needs an OnCreateForCompiler(). So we play it safe, and assume it does. So every OnCreateForCompiler() that we emit, // will assume its basetype also has an implementation and invoke that. if (typeDefinition.Name == nameof(ComponentSystemBase) && typeDefinition.Namespace == "Unity.Entities") { return(null); } var name = OnCreateForCompilerName; var pre_existing_method = typeDefinition.Methods.FirstOrDefault(m => m.Name == name); if (pre_existing_method != null) { UserError.DC0026($"It's not allowed to implement {OnCreateForCompilerName}'", pre_existing_method).Throw(); } var typeSystemVoid = typeDefinition.Module.TypeSystem.Void; var newMethod = new MethodDefinition(name, MethodAttributes.FamORAssem | MethodAttributes.Virtual | MethodAttributes.HideBySig, typeSystemVoid); typeDefinition.Methods.Add(newMethod); var ilProcessor = newMethod.Body.GetILProcessor(); ilProcessor.Emit(OpCodes.Ldarg_0); ilProcessor.Emit(OpCodes.Call, new MethodReference(name, typeSystemVoid, typeDefinition.BaseType) { HasThis = true }); ilProcessor.Emit(OpCodes.Ret); return(newMethod); }