PropertySiteProcessor.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. using System;
  2. using Mono.CecilX;
  3. using Mono.CecilX.Cil;
  4. namespace Mirror.Weaver
  5. {
  6. public static class PropertySiteProcessor
  7. {
  8. public static void Process(ModuleDefinition moduleDef)
  9. {
  10. DateTime startTime = DateTime.Now;
  11. //Search through the types
  12. foreach (TypeDefinition td in moduleDef.Types)
  13. {
  14. if (td.IsClass)
  15. {
  16. ProcessSiteClass(td);
  17. }
  18. }
  19. Console.WriteLine(" ProcessSitesModule " + moduleDef.Name + " elapsed time:" + (DateTime.Now - startTime));
  20. }
  21. static void ProcessSiteClass(TypeDefinition td)
  22. {
  23. //Console.WriteLine(" ProcessSiteClass " + td);
  24. foreach (MethodDefinition md in td.Methods)
  25. {
  26. ProcessSiteMethod(md);
  27. }
  28. foreach (TypeDefinition nested in td.NestedTypes)
  29. {
  30. ProcessSiteClass(nested);
  31. }
  32. }
  33. static void ProcessSiteMethod(MethodDefinition md)
  34. {
  35. // process all references to replaced members with properties
  36. //Weaver.DLog(td, " ProcessSiteMethod " + md);
  37. if (md.Name == ".cctor" ||
  38. md.Name == NetworkBehaviourProcessor.ProcessedFunctionName ||
  39. md.Name.StartsWith(Weaver.InvokeRpcPrefix))
  40. return;
  41. if (md.IsAbstract)
  42. {
  43. return;
  44. }
  45. if (md.Body != null && md.Body.Instructions != null)
  46. {
  47. for (int iCount = 0; iCount < md.Body.Instructions.Count;)
  48. {
  49. Instruction instr = md.Body.Instructions[iCount];
  50. iCount += ProcessInstruction(md, instr, iCount);
  51. }
  52. }
  53. }
  54. // replaces syncvar write access with the NetworkXYZ.get property calls
  55. static void ProcessInstructionSetterField(MethodDefinition md, Instruction i, FieldDefinition opField)
  56. {
  57. // don't replace property call sites in constructors
  58. if (md.Name == ".ctor")
  59. return;
  60. // does it set a field that we replaced?
  61. if (Weaver.WeaveLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement))
  62. {
  63. //replace with property
  64. //DLog(td, " replacing " + md.Name + ":" + i);
  65. i.OpCode = OpCodes.Call;
  66. i.Operand = replacement;
  67. //DLog(td, " replaced " + md.Name + ":" + i);
  68. }
  69. }
  70. // replaces syncvar read access with the NetworkXYZ.get property calls
  71. static void ProcessInstructionGetterField(MethodDefinition md, Instruction i, FieldDefinition opField)
  72. {
  73. // don't replace property call sites in constructors
  74. if (md.Name == ".ctor")
  75. return;
  76. // does it set a field that we replaced?
  77. if (Weaver.WeaveLists.replacementGetterProperties.TryGetValue(opField, out MethodDefinition replacement))
  78. {
  79. //replace with property
  80. //DLog(td, " replacing " + md.Name + ":" + i);
  81. i.OpCode = OpCodes.Call;
  82. i.Operand = replacement;
  83. //DLog(td, " replaced " + md.Name + ":" + i);
  84. }
  85. }
  86. static int ProcessInstruction(MethodDefinition md, Instruction instr, int iCount)
  87. {
  88. if (instr.OpCode == OpCodes.Stfld && instr.Operand is FieldDefinition opFieldst)
  89. {
  90. // this instruction sets the value of a field. cache the field reference.
  91. ProcessInstructionSetterField(md, instr, opFieldst);
  92. }
  93. if (instr.OpCode == OpCodes.Ldfld && instr.Operand is FieldDefinition opFieldld)
  94. {
  95. // this instruction gets the value of a field. cache the field reference.
  96. ProcessInstructionGetterField(md, instr, opFieldld);
  97. }
  98. if (instr.OpCode == OpCodes.Ldflda && instr.Operand is FieldDefinition opFieldlda)
  99. {
  100. // loading a field by reference, watch out for initobj instruction
  101. // see https://github.com/vis2k/Mirror/issues/696
  102. return ProcessInstructionLoadAddress(md, instr, opFieldlda, iCount);
  103. }
  104. return 1;
  105. }
  106. static int ProcessInstructionLoadAddress(MethodDefinition md, Instruction instr, FieldDefinition opField, int iCount)
  107. {
  108. // don't replace property call sites in constructors
  109. if (md.Name == ".ctor")
  110. return 1;
  111. // does it set a field that we replaced?
  112. if (Weaver.WeaveLists.replacementSetterProperties.TryGetValue(opField, out MethodDefinition replacement))
  113. {
  114. // we have a replacement for this property
  115. // is the next instruction a initobj?
  116. Instruction nextInstr = md.Body.Instructions[iCount + 1];
  117. if (nextInstr.OpCode == OpCodes.Initobj)
  118. {
  119. // we need to replace this code with:
  120. // var tmp = new MyStruct();
  121. // this.set_Networkxxxx(tmp);
  122. ILProcessor worker = md.Body.GetILProcessor();
  123. VariableDefinition tmpVariable = new VariableDefinition(opField.FieldType);
  124. md.Body.Variables.Add(tmpVariable);
  125. worker.InsertBefore(instr, worker.Create(OpCodes.Ldloca, tmpVariable));
  126. worker.InsertBefore(instr, worker.Create(OpCodes.Initobj, opField.FieldType));
  127. worker.InsertBefore(instr, worker.Create(OpCodes.Ldloc, tmpVariable));
  128. worker.InsertBefore(instr, worker.Create(OpCodes.Call, replacement));
  129. worker.Remove(instr);
  130. worker.Remove(nextInstr);
  131. return 4;
  132. }
  133. }
  134. return 1;
  135. }
  136. }
  137. }