ObjectManager.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. //
  2. // System.Runtime.Serialization.ObjectManager.cs
  3. //
  4. // Author: Lluis Sanchez Gual ([email protected])
  5. //
  6. // (C) 2003 Lluis Sanchez Gual
  7. //
  8. //
  9. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Collections;
  32. using System.Reflection;
  33. namespace System.Runtime.Serialization
  34. {
  35. public class ObjectManager
  36. {
  37. // All objects are chained in the same order as they have been registered
  38. ObjectRecord _objectRecordChain = null;
  39. ObjectRecord _lastObjectRecord = null;
  40. ArrayList _deserializedRecords = new ArrayList();
  41. Hashtable _objectRecords = new Hashtable();
  42. bool _finalFixup = false;
  43. ISurrogateSelector _selector;
  44. StreamingContext _context;
  45. int _registeredObjectsCount = 0;
  46. public ObjectManager(ISurrogateSelector selector, StreamingContext context)
  47. {
  48. _selector = selector;
  49. _context = context;
  50. }
  51. public virtual void DoFixups()
  52. {
  53. _finalFixup = true;
  54. try
  55. {
  56. if (_registeredObjectsCount < _objectRecords.Count)
  57. throw new SerializationException ("There are some fixups that refer to objects that have not been registered");
  58. ObjectRecord last = _lastObjectRecord;
  59. bool firstCicle = true;
  60. // Solve al pending fixups of all objects
  61. ObjectRecord record = _objectRecordChain;
  62. while (record != null)
  63. {
  64. bool ready = !(record.IsUnsolvedObjectReference && firstCicle);
  65. if (ready) ready = record.DoFixups (true, this, true);
  66. if (ready) ready = record.LoadData(this, _selector, _context);
  67. ObjectRecord next;
  68. if (ready)
  69. {
  70. if (record.OriginalObject is IDeserializationCallback)
  71. _deserializedRecords.Add (record);
  72. next = record.Next;
  73. }
  74. else
  75. {
  76. // There must be an unresolved IObjectReference instance.
  77. // Chain the record at the end so it is solved later
  78. if ((record.ObjectInstance is IObjectReference) && !firstCicle)
  79. {
  80. if (record.Status == ObjectRecordStatus.ReferenceSolvingDelayed)
  81. throw new SerializationException ("The object with ID " + record.ObjectID + " could not be resolved");
  82. else
  83. record.Status = ObjectRecordStatus.ReferenceSolvingDelayed;
  84. }
  85. if (record != _lastObjectRecord) {
  86. next = record.Next;
  87. record.Next = null;
  88. _lastObjectRecord.Next = record;
  89. _lastObjectRecord = record;
  90. }
  91. else
  92. next = record;
  93. }
  94. if (record == last) firstCicle = false;
  95. record = next;
  96. }
  97. }
  98. finally
  99. {
  100. _finalFixup = false;
  101. }
  102. }
  103. internal ObjectRecord GetObjectRecord (long objectID)
  104. {
  105. ObjectRecord rec = (ObjectRecord)_objectRecords[objectID];
  106. if (rec == null)
  107. {
  108. if (_finalFixup) throw new SerializationException ("The object with Id " + objectID + " has not been registered");
  109. rec = new ObjectRecord();
  110. rec.ObjectID = objectID;
  111. _objectRecords[objectID] = rec;
  112. }
  113. if (!rec.IsRegistered && _finalFixup) throw new SerializationException ("The object with Id " + objectID + " has not been registered");
  114. return rec;
  115. }
  116. public virtual object GetObject (long objectID)
  117. {
  118. if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
  119. ObjectRecord rec = (ObjectRecord)_objectRecords[objectID];
  120. if (rec == null || !rec.IsRegistered) return null;
  121. else return rec.ObjectInstance;
  122. }
  123. public virtual void RaiseDeserializationEvent ()
  124. {
  125. for (int i = _deserializedRecords.Count-1; i >= 0; i--)
  126. {
  127. ObjectRecord record = (ObjectRecord) _deserializedRecords [i];
  128. IDeserializationCallback obj = record.OriginalObject as IDeserializationCallback;
  129. if (obj != null) obj.OnDeserialization (this);
  130. }
  131. }
  132. private void AddFixup (BaseFixupRecord record)
  133. {
  134. record.ObjectToBeFixed.ChainFixup (record, true);
  135. record.ObjectRequired.ChainFixup (record, false);
  136. }
  137. public virtual void RecordArrayElementFixup (long arrayToBeFixed, int index, long objectRequired)
  138. {
  139. if (arrayToBeFixed <= 0) throw new ArgumentOutOfRangeException("arrayToBeFixed","The arrayToBeFixed parameter is less than or equal to zero");
  140. if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
  141. ArrayFixupRecord record = new ArrayFixupRecord(GetObjectRecord(arrayToBeFixed), index, GetObjectRecord(objectRequired));
  142. AddFixup (record);
  143. }
  144. public virtual void RecordArrayElementFixup (long arrayToBeFixed, int[] indices, long objectRequired)
  145. {
  146. if (arrayToBeFixed <= 0) throw new ArgumentOutOfRangeException("arrayToBeFixed","The arrayToBeFixed parameter is less than or equal to zero");
  147. if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
  148. if (indices == null) throw new ArgumentNullException("indices");
  149. MultiArrayFixupRecord record = new MultiArrayFixupRecord (GetObjectRecord(arrayToBeFixed), indices, GetObjectRecord(objectRequired));
  150. AddFixup (record);
  151. }
  152. public virtual void RecordDelayedFixup (long objectToBeFixed, string memberName, long objectRequired)
  153. {
  154. if (objectToBeFixed <= 0) throw new ArgumentOutOfRangeException("objectToBeFixed","The objectToBeFixed parameter is less than or equal to zero");
  155. if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
  156. if (memberName == null) throw new ArgumentNullException("memberName");
  157. DelayedFixupRecord record = new DelayedFixupRecord (GetObjectRecord(objectToBeFixed), memberName, GetObjectRecord(objectRequired));
  158. AddFixup (record);
  159. }
  160. public virtual void RecordFixup (long objectToBeFixed, MemberInfo member, long objectRequired)
  161. {
  162. if (objectToBeFixed <= 0) throw new ArgumentOutOfRangeException("objectToBeFixed","The objectToBeFixed parameter is less than or equal to zero");
  163. if (objectRequired <= 0) throw new ArgumentOutOfRangeException("objectRequired","The objectRequired parameter is less than or equal to zero");
  164. if (member == null) throw new ArgumentNullException("member");
  165. FixupRecord record = new FixupRecord (GetObjectRecord(objectToBeFixed), member, GetObjectRecord(objectRequired));
  166. AddFixup (record);
  167. }
  168. private void RegisterObjectInternal (object obj, ObjectRecord record)
  169. {
  170. if (obj == null) throw new ArgumentNullException("obj");
  171. if (record.IsRegistered)
  172. {
  173. if (record.OriginalObject != obj) throw new SerializationException ("An object with Id " + record.ObjectID + " has already been registered");
  174. else return;
  175. }
  176. record.ObjectInstance = obj;
  177. record.OriginalObject = obj;
  178. if (obj is IObjectReference) record.Status = ObjectRecordStatus.ReferenceUnsolved;
  179. else record.Status = ObjectRecordStatus.ReferenceSolved;
  180. record.DoFixups (true, this, false);
  181. record.DoFixups (false, this, false);
  182. _registeredObjectsCount++;
  183. // Adds the object to the chain of registered objects. This chain
  184. // is needed to be able to to perform the final fixups in the right order
  185. if (_objectRecordChain == null)
  186. {
  187. _objectRecordChain = record;
  188. _lastObjectRecord = record;
  189. }
  190. else
  191. {
  192. _lastObjectRecord.Next = record;
  193. _lastObjectRecord = record;
  194. }
  195. }
  196. public virtual void RegisterObject (object obj, long objectID)
  197. {
  198. if (obj == null) throw new ArgumentNullException("obj", "The obj parameter is null.");
  199. if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
  200. RegisterObjectInternal (obj, GetObjectRecord (objectID));
  201. }
  202. public void RegisterObject (object obj, long objectID, SerializationInfo info)
  203. {
  204. if (obj == null) throw new ArgumentNullException("obj", "The obj parameter is null.");
  205. if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
  206. ObjectRecord record = GetObjectRecord (objectID);
  207. record.Info = info;
  208. RegisterObjectInternal (obj, record);
  209. }
  210. public void RegisterObject (object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member)
  211. {
  212. RegisterObject (obj, objectID, info, idOfContainingObj, member, null);
  213. }
  214. public void RegisterObject( object obj, long objectID, SerializationInfo info, long idOfContainingObj, MemberInfo member, int[] arrayIndex)
  215. {
  216. if (obj == null) throw new ArgumentNullException("obj", "The obj parameter is null.");
  217. if (objectID <= 0) throw new ArgumentOutOfRangeException("objectID","The objectID parameter is less than or equal to zero");
  218. ObjectRecord record = GetObjectRecord (objectID);
  219. record.Info = info;
  220. record.IdOfContainingObj = idOfContainingObj;
  221. record.Member = member;
  222. record.ArrayIndex = arrayIndex;
  223. RegisterObjectInternal (obj, record);
  224. }
  225. }
  226. // Fixup types. There is a fixup class for each fixup type.
  227. // BaseFixupRecord
  228. // Base class for all fixups
  229. internal abstract class BaseFixupRecord
  230. {
  231. public BaseFixupRecord(ObjectRecord objectToBeFixed, ObjectRecord objectRequired)
  232. {
  233. ObjectToBeFixed = objectToBeFixed;
  234. ObjectRequired = objectRequired;
  235. }
  236. public bool DoFixup (ObjectManager manager, bool strict)
  237. {
  238. if (ObjectToBeFixed.IsRegistered && ObjectRequired.IsInstanceReady)
  239. {
  240. FixupImpl (manager);
  241. return true;
  242. }
  243. else if (strict)
  244. {
  245. if (!ObjectToBeFixed.IsRegistered) throw new SerializationException ("An object with ID " + ObjectToBeFixed.ObjectID + " was included in a fixup, but it has not been registered");
  246. else if (!ObjectRequired.IsRegistered) throw new SerializationException ("An object with ID " + ObjectRequired.ObjectID + " was included in a fixup, but it has not been registered");
  247. else return false;
  248. }
  249. else
  250. return false;
  251. }
  252. protected abstract void FixupImpl (ObjectManager manager);
  253. internal protected ObjectRecord ObjectToBeFixed;
  254. internal protected ObjectRecord ObjectRequired;
  255. public BaseFixupRecord NextSameContainer;
  256. public BaseFixupRecord NextSameRequired;
  257. }
  258. // ArrayFixupRecord
  259. // Fixup for assigning a value to one position of an array
  260. internal class ArrayFixupRecord : BaseFixupRecord
  261. {
  262. int _index;
  263. public ArrayFixupRecord (ObjectRecord objectToBeFixed, int index, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
  264. _index = index;
  265. }
  266. protected override void FixupImpl (ObjectManager manager) {
  267. Array array = (Array)ObjectToBeFixed.ObjectInstance;
  268. array.SetValue (ObjectRequired.ObjectInstance, _index);
  269. }
  270. }
  271. // MultiArrayFixupRecord
  272. // Fixup for assigning a value to several positions of an array
  273. internal class MultiArrayFixupRecord : BaseFixupRecord
  274. {
  275. int[] _indices;
  276. public MultiArrayFixupRecord (ObjectRecord objectToBeFixed, int[] indices, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
  277. _indices = indices;
  278. }
  279. protected override void FixupImpl (ObjectManager manager) {
  280. ObjectToBeFixed.SetArrayValue (manager, ObjectRequired.ObjectInstance, _indices);
  281. }
  282. }
  283. // FixupRecord
  284. // Fixup for assigning a value to a member of an object
  285. internal class FixupRecord: BaseFixupRecord
  286. {
  287. public MemberInfo _member;
  288. public FixupRecord (ObjectRecord objectToBeFixed, MemberInfo member, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
  289. _member = member;
  290. }
  291. protected override void FixupImpl (ObjectManager manager) {
  292. ObjectToBeFixed.SetMemberValue (manager, _member, ObjectRequired.ObjectInstance);
  293. }
  294. }
  295. // DelayedFixupRecord
  296. // Fixup for assigning a value to a SerializationInfo of an object
  297. internal class DelayedFixupRecord: BaseFixupRecord
  298. {
  299. public string _memberName;
  300. public DelayedFixupRecord (ObjectRecord objectToBeFixed, string memberName, ObjectRecord objectRequired): base (objectToBeFixed, objectRequired) {
  301. _memberName = memberName;
  302. }
  303. protected override void FixupImpl (ObjectManager manager) {
  304. ObjectToBeFixed.SetMemberValue (manager, _memberName, ObjectRequired.ObjectInstance);
  305. }
  306. }
  307. // Object Record
  308. internal enum ObjectRecordStatus: byte { Unregistered, ReferenceUnsolved, ReferenceSolvingDelayed, ReferenceSolved }
  309. internal class ObjectRecord
  310. {
  311. public ObjectRecordStatus Status = ObjectRecordStatus.Unregistered;
  312. public object OriginalObject;
  313. public object ObjectInstance;
  314. public long ObjectID;
  315. public SerializationInfo Info;
  316. public long IdOfContainingObj;
  317. public MemberInfo Member;
  318. public int[] ArrayIndex;
  319. public BaseFixupRecord FixupChainAsContainer;
  320. public BaseFixupRecord FixupChainAsRequired;
  321. public ObjectRecord Next;
  322. public void SetMemberValue (ObjectManager manager, MemberInfo member, object value)
  323. {
  324. if (member is FieldInfo)
  325. ((FieldInfo)member).SetValue (ObjectInstance, value);
  326. else if (member is PropertyInfo)
  327. ((PropertyInfo)member).SetValue (ObjectInstance, value, null);
  328. else throw new SerializationException ("Cannot perform fixup");
  329. if (Member != null)
  330. {
  331. ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
  332. if (containerRecord.IsRegistered)
  333. containerRecord.SetMemberValue (manager, Member, ObjectInstance);
  334. }
  335. else if (ArrayIndex != null)
  336. {
  337. ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
  338. if (containerRecord.IsRegistered)
  339. containerRecord.SetArrayValue (manager, ObjectInstance, ArrayIndex);
  340. }
  341. }
  342. public void SetArrayValue (ObjectManager manager, object value, int[] indices)
  343. {
  344. ((Array)ObjectInstance).SetValue (value, indices);
  345. }
  346. public void SetMemberValue (ObjectManager manager, string memberName, object value)
  347. {
  348. if (Info == null) throw new SerializationException ("Cannot perform fixup");
  349. Info.AddValue (memberName, value, value.GetType());
  350. }
  351. public bool IsInstanceReady
  352. {
  353. // Returns true if this object is ready to be assigned to a parent object.
  354. get
  355. {
  356. if (!IsRegistered) return false;
  357. if (IsUnsolvedObjectReference) return false;
  358. // Embedded value objects cannot be assigned to their containers until fully completed
  359. if (Member != null && (HasPendingFixups || Info != null))
  360. return false;
  361. return true;
  362. }
  363. }
  364. public bool IsUnsolvedObjectReference
  365. {
  366. get {
  367. return Status != ObjectRecordStatus.ReferenceSolved;
  368. }
  369. }
  370. public bool IsRegistered
  371. {
  372. get {
  373. return Status != ObjectRecordStatus.Unregistered;
  374. }
  375. }
  376. public bool DoFixups (bool asContainer, ObjectManager manager, bool strict)
  377. {
  378. BaseFixupRecord prevFixup = null;
  379. BaseFixupRecord fixup = asContainer ? FixupChainAsContainer : FixupChainAsRequired;
  380. bool allFixed = true;
  381. while (fixup != null)
  382. {
  383. if (fixup.DoFixup (manager, strict))
  384. {
  385. UnchainFixup (fixup, prevFixup, asContainer);
  386. if (asContainer) fixup.ObjectRequired.RemoveFixup (fixup, false);
  387. else fixup.ObjectToBeFixed.RemoveFixup (fixup, true);
  388. }
  389. else
  390. {
  391. prevFixup = fixup;
  392. allFixed = false;
  393. }
  394. fixup = asContainer ? fixup.NextSameContainer : fixup.NextSameRequired;
  395. }
  396. return allFixed;
  397. }
  398. public void RemoveFixup (BaseFixupRecord fixupToRemove, bool asContainer)
  399. {
  400. BaseFixupRecord prevFixup = null;
  401. BaseFixupRecord fixup = asContainer ? FixupChainAsContainer : FixupChainAsRequired;
  402. while (fixup != null)
  403. {
  404. if (fixup == fixupToRemove)
  405. {
  406. UnchainFixup (fixup, prevFixup, asContainer);
  407. return;
  408. }
  409. prevFixup = fixup;
  410. fixup = asContainer ? fixup.NextSameContainer : fixup.NextSameRequired;
  411. }
  412. }
  413. private void UnchainFixup (BaseFixupRecord fixup, BaseFixupRecord prevFixup, bool asContainer)
  414. {
  415. if (prevFixup == null) {
  416. if (asContainer) FixupChainAsContainer = fixup.NextSameContainer;
  417. else FixupChainAsRequired = fixup.NextSameRequired;
  418. }
  419. else {
  420. if (asContainer) prevFixup.NextSameContainer = fixup.NextSameContainer;
  421. else prevFixup.NextSameRequired = fixup.NextSameRequired;
  422. }
  423. }
  424. public void ChainFixup (BaseFixupRecord fixup, bool asContainer)
  425. {
  426. if (asContainer)
  427. {
  428. fixup.NextSameContainer = FixupChainAsContainer;
  429. FixupChainAsContainer = fixup;
  430. }
  431. else
  432. {
  433. fixup.NextSameRequired = FixupChainAsRequired;
  434. FixupChainAsRequired = fixup;
  435. }
  436. }
  437. public bool LoadData (ObjectManager manager, ISurrogateSelector selector, StreamingContext context)
  438. {
  439. if (Info != null)
  440. {
  441. ISurrogateSelector foundSelector = null;
  442. ISerializationSurrogate surrogate = null;
  443. if (selector != null)
  444. surrogate = selector.GetSurrogate (ObjectInstance.GetType(), context, out foundSelector);
  445. if (surrogate != null) {
  446. surrogate.SetObjectData (ObjectInstance, Info, context, foundSelector);
  447. } else if (ObjectInstance is ISerializable) {
  448. object[] pars = new object[] {Info, context};
  449. ConstructorInfo con = ObjectInstance.GetType().GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof (SerializationInfo), typeof (StreamingContext) }, null );
  450. if (con == null) throw new SerializationException ("The constructor to deserialize an object of type " + ObjectInstance.GetType().FullName + " was not found.");
  451. con.Invoke (ObjectInstance, pars);
  452. } else {
  453. throw new SerializationException ("No surrogate selector was found for type " + ObjectInstance.GetType().FullName);
  454. }
  455. Info = null;
  456. }
  457. if (ObjectInstance is IObjectReference && Status != ObjectRecordStatus.ReferenceSolved)
  458. {
  459. try {
  460. ObjectInstance = ((IObjectReference)ObjectInstance).GetRealObject(context);
  461. Status = ObjectRecordStatus.ReferenceSolved;
  462. }
  463. catch (NullReferenceException) {
  464. // Give a second chance
  465. return false;
  466. }
  467. }
  468. if (Member != null)
  469. {
  470. // If this object is a value object embedded in another object, the parent
  471. // object must be updated
  472. ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
  473. containerRecord.SetMemberValue (manager, Member, ObjectInstance);
  474. }
  475. else if (ArrayIndex != null)
  476. {
  477. ObjectRecord containerRecord = manager.GetObjectRecord (IdOfContainingObj);
  478. containerRecord.SetArrayValue (manager, ObjectInstance, ArrayIndex);
  479. }
  480. return true;
  481. }
  482. public bool HasPendingFixups
  483. {
  484. get { return FixupChainAsContainer != null; }
  485. }
  486. }
  487. }