RuntimeTypeModel.cs 88 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066
  1. #if !NO_RUNTIME
  2. using System;
  3. using System.Collections;
  4. using System.Text;
  5. #if FEAT_IKVM
  6. using Type = IKVM.Reflection.Type;
  7. using IKVM.Reflection;
  8. using IKVM.Reflection.Emit;
  9. #else
  10. using System.Reflection;
  11. #if FEAT_COMPILER
  12. using System.Reflection.Emit;
  13. #endif
  14. #endif
  15. using ProtoBuf.Serializers;
  16. using System.Threading;
  17. using System.IO;
  18. namespace ProtoBuf.Meta
  19. {
  20. /// <summary>
  21. /// Provides protobuf serialization support for a number of types that can be defined at runtime
  22. /// </summary>
  23. public sealed class RuntimeTypeModel : TypeModel
  24. {
  25. private ushort options;
  26. private const ushort
  27. OPTIONS_InferTagFromNameDefault = 1,
  28. OPTIONS_IsDefaultModel = 2,
  29. OPTIONS_Frozen = 4,
  30. OPTIONS_AutoAddMissingTypes = 8,
  31. #if FEAT_COMPILER && !FX11
  32. OPTIONS_AutoCompile = 16,
  33. #endif
  34. OPTIONS_UseImplicitZeroDefaults = 32,
  35. OPTIONS_AllowParseableTypes = 64,
  36. OPTIONS_AutoAddProtoContractTypesOnly = 128,
  37. OPTIONS_IncludeDateTimeKind = 256;
  38. private bool GetOption(ushort option)
  39. {
  40. return (options & option) == option;
  41. }
  42. private void SetOption(ushort option, bool value)
  43. {
  44. if (value) options |= option;
  45. else options &= (ushort)~option;
  46. }
  47. /// <summary>
  48. /// Global default that
  49. /// enables/disables automatic tag generation based on the existing name / order
  50. /// of the defined members. See <seealso cref="ProtoContractAttribute.InferTagFromName"/>
  51. /// for usage and <b>important warning</b> / explanation.
  52. /// You must set the global default before attempting to serialize/deserialize any
  53. /// impacted type.
  54. /// </summary>
  55. public bool InferTagFromNameDefault
  56. {
  57. get { return GetOption(OPTIONS_InferTagFromNameDefault); }
  58. set { SetOption(OPTIONS_InferTagFromNameDefault, value); }
  59. }
  60. /// <summary>
  61. /// Global default that determines whether types are considered serializable
  62. /// if they have [DataContract] / [XmlType]. With this enabled, <b>ONLY</b>
  63. /// types marked as [ProtoContract] are added automatically.
  64. /// </summary>
  65. public bool AutoAddProtoContractTypesOnly
  66. {
  67. get { return GetOption(OPTIONS_AutoAddProtoContractTypesOnly); }
  68. set { SetOption(OPTIONS_AutoAddProtoContractTypesOnly, value); }
  69. }
  70. /// <summary>
  71. /// Global switch that enables or disables the implicit
  72. /// handling of "zero defaults"; meanning: if no other default is specified,
  73. /// it assumes bools always default to false, integers to zero, etc.
  74. ///
  75. /// If this is disabled, no such assumptions are made and only *explicit*
  76. /// default values are processed. This is enabled by default to
  77. /// preserve similar logic to v1.
  78. /// </summary>
  79. public bool UseImplicitZeroDefaults
  80. {
  81. get {return GetOption(OPTIONS_UseImplicitZeroDefaults);}
  82. set {
  83. if (!value && GetOption(OPTIONS_IsDefaultModel))
  84. {
  85. throw new InvalidOperationException("UseImplicitZeroDefaults cannot be disabled on the default model");
  86. }
  87. SetOption(OPTIONS_UseImplicitZeroDefaults, value);
  88. }
  89. }
  90. /// <summary>
  91. /// Global switch that determines whether types with a <c>.ToString()</c> and a <c>Parse(string)</c>
  92. /// should be serialized as strings.
  93. /// </summary>
  94. public bool AllowParseableTypes
  95. {
  96. get { return GetOption(OPTIONS_AllowParseableTypes); }
  97. set { SetOption(OPTIONS_AllowParseableTypes, value); }
  98. }
  99. /// <summary>
  100. /// Global switch that determines whether DateTime serialization should include the <c>Kind</c> of the date/time.
  101. /// </summary>
  102. public bool IncludeDateTimeKind
  103. {
  104. get { return GetOption(OPTIONS_IncludeDateTimeKind); }
  105. set { SetOption(OPTIONS_IncludeDateTimeKind, value); }
  106. }
  107. /// <summary>
  108. /// Should the <c>Kind</c> be included on date/time values?
  109. /// </summary>
  110. protected internal override bool SerializeDateTimeKind()
  111. {
  112. return GetOption(OPTIONS_IncludeDateTimeKind);
  113. }
  114. private sealed class Singleton
  115. {
  116. private Singleton() { }
  117. internal static readonly RuntimeTypeModel Value = new RuntimeTypeModel(true);
  118. }
  119. /// <summary>
  120. /// The default model, used to support ProtoBuf.Serializer
  121. /// </summary>
  122. public static RuntimeTypeModel Default
  123. {
  124. get { return Singleton.Value; }
  125. }
  126. /// <summary>
  127. /// Returns a sequence of the Type instances that can be
  128. /// processed by this model.
  129. /// </summary>
  130. public IEnumerable GetTypes() { return types; }
  131. /// <summary>
  132. /// Suggest a .proto definition for the given type
  133. /// </summary>
  134. /// <param name="type">The type to generate a .proto definition for, or <c>null</c> to generate a .proto that represents the entire model</param>
  135. /// <returns>The .proto definition as a string</returns>
  136. public override string GetSchema(Type type)
  137. {
  138. BasicList requiredTypes = new BasicList();
  139. MetaType primaryType = null;
  140. bool isInbuiltType = false;
  141. if (type == null)
  142. { // generate for the entire model
  143. foreach(MetaType meta in types)
  144. {
  145. MetaType tmp = meta.GetSurrogateOrBaseOrSelf(false);
  146. if (!requiredTypes.Contains(tmp))
  147. { // ^^^ note that the type might have been added as a descendent
  148. requiredTypes.Add(tmp);
  149. CascadeDependents(requiredTypes, tmp);
  150. }
  151. }
  152. }
  153. else
  154. {
  155. Type tmp = Helpers.GetUnderlyingType(type);
  156. if (tmp != null) type = tmp;
  157. WireType defaultWireType;
  158. isInbuiltType = (ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out defaultWireType, false, false, false, false) != null);
  159. if (!isInbuiltType)
  160. {
  161. //Agenerate just relative to the supplied type
  162. int index = FindOrAddAuto(type, false, false, false);
  163. if (index < 0) throw new ArgumentException("The type specified is not a contract-type", "type");
  164. // get the required types
  165. primaryType = ((MetaType)types[index]).GetSurrogateOrBaseOrSelf(false);
  166. requiredTypes.Add(primaryType);
  167. CascadeDependents(requiredTypes, primaryType);
  168. }
  169. }
  170. // use the provided type's namespace for the "package"
  171. StringBuilder headerBuilder = new StringBuilder();
  172. string package = null;
  173. if (!isInbuiltType)
  174. {
  175. IEnumerable typesForNamespace = primaryType == null ? types : requiredTypes;
  176. foreach (MetaType meta in typesForNamespace)
  177. {
  178. if (meta.IsList) continue;
  179. string tmp = meta.Type.Namespace;
  180. if (!Helpers.IsNullOrEmpty(tmp))
  181. {
  182. if (tmp.StartsWith("System.")) continue;
  183. if (package == null)
  184. { // haven't seen any suggestions yet
  185. package = tmp;
  186. }
  187. else if (package == tmp)
  188. { // that's fine; a repeat of the one we already saw
  189. }
  190. else
  191. { // something else; have confliucting suggestions; abort
  192. package = null;
  193. break;
  194. }
  195. }
  196. }
  197. }
  198. if (!Helpers.IsNullOrEmpty(package))
  199. {
  200. headerBuilder.Append("package ").Append(package).Append(';');
  201. Helpers.AppendLine(headerBuilder);
  202. }
  203. bool requiresBclImport = false;
  204. StringBuilder bodyBuilder = new StringBuilder();
  205. // sort them by schema-name
  206. MetaType[] metaTypesArr = new MetaType[requiredTypes.Count];
  207. requiredTypes.CopyTo(metaTypesArr, 0);
  208. Array.Sort(metaTypesArr, MetaType.Comparer.Default);
  209. // write the messages
  210. if (isInbuiltType)
  211. {
  212. Helpers.AppendLine(bodyBuilder).Append("message ").Append(type.Name).Append(" {");
  213. MetaType.NewLine(bodyBuilder, 1).Append("optional ").Append(GetSchemaTypeName(type, DataFormat.Default, false, false, ref requiresBclImport))
  214. .Append(" value = 1;");
  215. Helpers.AppendLine(bodyBuilder).Append('}');
  216. }
  217. else
  218. {
  219. for (int i = 0; i < metaTypesArr.Length; i++)
  220. {
  221. MetaType tmp = metaTypesArr[i];
  222. if (tmp.IsList && tmp != primaryType) continue;
  223. tmp.WriteSchema(bodyBuilder, 0, ref requiresBclImport);
  224. }
  225. }
  226. if (requiresBclImport)
  227. {
  228. headerBuilder.Append("import \"bcl.proto\"; // schema for protobuf-net's handling of core .NET types");
  229. Helpers.AppendLine(headerBuilder);
  230. }
  231. return Helpers.AppendLine(headerBuilder.Append(bodyBuilder)).ToString();
  232. }
  233. private void CascadeDependents(BasicList list, MetaType metaType)
  234. {
  235. MetaType tmp;
  236. if (metaType.IsList)
  237. {
  238. Type itemType = TypeModel.GetListItemType(this, metaType.Type);
  239. WireType defaultWireType;
  240. IProtoSerializer coreSerializer = ValueMember.TryGetCoreSerializer(this, DataFormat.Default, itemType, out defaultWireType, false, false, false, false);
  241. if (coreSerializer == null)
  242. {
  243. int index = FindOrAddAuto(itemType, false, false, false);
  244. if (index >= 0)
  245. {
  246. tmp = ((MetaType)types[index]).GetSurrogateOrBaseOrSelf(false);
  247. if (!list.Contains(tmp))
  248. { // could perhaps also implement as a queue, but this should work OK for sane models
  249. list.Add(tmp);
  250. CascadeDependents(list, tmp);
  251. }
  252. }
  253. }
  254. }
  255. else
  256. {
  257. if (metaType.IsAutoTuple)
  258. {
  259. MemberInfo[] mapping;
  260. if(MetaType.ResolveTupleConstructor(metaType.Type, out mapping) != null)
  261. {
  262. for (int i = 0; i < mapping.Length; i++)
  263. {
  264. Type type = null;
  265. if (mapping[i] is PropertyInfo) type = ((PropertyInfo)mapping[i]).PropertyType;
  266. else if (mapping[i] is FieldInfo) type = ((FieldInfo)mapping[i]).FieldType;
  267. WireType defaultWireType;
  268. IProtoSerializer coreSerializer = ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out defaultWireType, false, false, false, false);
  269. if (coreSerializer == null)
  270. {
  271. int index = FindOrAddAuto(type, false, false, false);
  272. if (index >= 0)
  273. {
  274. tmp = ((MetaType)types[index]).GetSurrogateOrBaseOrSelf(false);
  275. if (!list.Contains(tmp))
  276. { // could perhaps also implement as a queue, but this should work OK for sane models
  277. list.Add(tmp);
  278. CascadeDependents(list, tmp);
  279. }
  280. }
  281. }
  282. }
  283. }
  284. }
  285. else
  286. {
  287. foreach (ValueMember member in metaType.Fields)
  288. {
  289. Type type = member.ItemType;
  290. if (type == null) type = member.MemberType;
  291. WireType defaultWireType;
  292. IProtoSerializer coreSerializer = ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out defaultWireType, false, false, false, false);
  293. if (coreSerializer == null)
  294. {
  295. // is an interesting type
  296. int index = FindOrAddAuto(type, false, false, false);
  297. if (index >= 0)
  298. {
  299. tmp = ((MetaType)types[index]).GetSurrogateOrBaseOrSelf(false);
  300. if (!list.Contains(tmp))
  301. { // could perhaps also implement as a queue, but this should work OK for sane models
  302. list.Add(tmp);
  303. CascadeDependents(list, tmp);
  304. }
  305. }
  306. }
  307. }
  308. }
  309. if (metaType.HasSubtypes)
  310. {
  311. foreach (SubType subType in metaType.GetSubtypes())
  312. {
  313. tmp = subType.DerivedType.GetSurrogateOrSelf(); // note: exclude base-types!
  314. if (!list.Contains(tmp))
  315. {
  316. list.Add(tmp);
  317. CascadeDependents(list, tmp);
  318. }
  319. }
  320. }
  321. tmp = metaType.BaseType;
  322. if (tmp != null) tmp = tmp.GetSurrogateOrSelf(); // note: already walking base-types; exclude base
  323. if (tmp != null && !list.Contains(tmp))
  324. {
  325. list.Add(tmp);
  326. CascadeDependents(list, tmp);
  327. }
  328. }
  329. }
  330. internal RuntimeTypeModel(bool isDefault)
  331. {
  332. #if FEAT_IKVM
  333. universe = new IKVM.Reflection.Universe();
  334. universe.EnableMissingMemberResolution(); // needed to avoid TypedReference issue on WinRT
  335. #endif
  336. AutoAddMissingTypes = true;
  337. UseImplicitZeroDefaults = true;
  338. SetOption(OPTIONS_IsDefaultModel, isDefault);
  339. #if FEAT_COMPILER && !FX11 && !DEBUG
  340. AutoCompile = true;
  341. #endif
  342. }
  343. #if FEAT_IKVM
  344. readonly IKVM.Reflection.Universe universe;
  345. /// <summary>
  346. /// Load an assembly into the model's universe
  347. /// </summary>
  348. public Assembly Load(string path)
  349. {
  350. return universe.LoadFile(path);
  351. }
  352. /// <summary>
  353. /// Gets the IKVM Universe that relates to this model
  354. /// </summary>
  355. public Universe Universe { get { return universe; } }
  356. /// <summary>
  357. /// Adds support for an additional type in this model, optionally
  358. /// applying inbuilt patterns. If the type is already known to the
  359. /// model, the existing type is returned **without** applying
  360. /// any additional behaviour.
  361. /// </summary>
  362. public MetaType Add(string assemblyQualifiedTypeName, bool applyDefaults)
  363. {
  364. Type type = universe.GetType(assemblyQualifiedTypeName, true);
  365. return Add(type, applyDefaults);
  366. }
  367. /// <summary>
  368. /// Adds support for an additional type in this model, optionally
  369. /// applying inbuilt patterns. If the type is already known to the
  370. /// model, the existing type is returned **without** applying
  371. /// any additional behaviour.
  372. /// </summary>
  373. public MetaType Add(System.Type type, bool applyDefaultBehaviour)
  374. {
  375. return Add(MapType(type), applyDefaultBehaviour);
  376. }
  377. /// <summary>
  378. /// Obtains the MetaType associated with a given Type for the current model,
  379. /// allowing additional configuration.
  380. /// </summary>
  381. public MetaType this[System.Type type] { get { return this[MapType(type)]; } }
  382. #endif
  383. /// <summary>
  384. /// Obtains the MetaType associated with a given Type for the current model,
  385. /// allowing additional configuration.
  386. /// </summary>
  387. public MetaType this[Type type] { get { return (MetaType)types[FindOrAddAuto(type, true, false, false)]; } }
  388. internal MetaType FindWithoutAdd(Type type)
  389. {
  390. // this list is thread-safe for reading
  391. foreach (MetaType metaType in types)
  392. {
  393. if (metaType.Type == type)
  394. {
  395. if (metaType.Pending) WaitOnLock(metaType);
  396. return metaType;
  397. }
  398. }
  399. // if that failed, check for a proxy
  400. Type underlyingType = ResolveProxies(type);
  401. return underlyingType == null ? null : FindWithoutAdd(underlyingType);
  402. }
  403. static readonly BasicList.MatchPredicate
  404. MetaTypeFinder = new BasicList.MatchPredicate(MetaTypeFinderImpl),
  405. BasicTypeFinder = new BasicList.MatchPredicate(BasicTypeFinderImpl);
  406. static bool MetaTypeFinderImpl(object value, object ctx)
  407. {
  408. return ((MetaType)value).Type == (Type)ctx;
  409. }
  410. static bool BasicTypeFinderImpl(object value, object ctx)
  411. {
  412. return ((BasicType)value).Type == (Type)ctx;
  413. }
  414. private void WaitOnLock(MetaType type)
  415. {
  416. int opaqueToken = 0;
  417. try
  418. {
  419. TakeLock(ref opaqueToken);
  420. }
  421. finally
  422. {
  423. ReleaseLock(opaqueToken);
  424. }
  425. }
  426. BasicList basicTypes = new BasicList();
  427. sealed class BasicType
  428. {
  429. private readonly Type type;
  430. public Type Type { get { return type; } }
  431. private readonly IProtoSerializer serializer;
  432. public IProtoSerializer Serializer { get { return serializer; } }
  433. public BasicType(Type type, IProtoSerializer serializer)
  434. {
  435. this.type = type;
  436. this.serializer = serializer;
  437. }
  438. }
  439. internal IProtoSerializer TryGetBasicTypeSerializer(Type type)
  440. {
  441. int idx = basicTypes.IndexOf(BasicTypeFinder, type);
  442. if (idx >= 0) return ((BasicType)basicTypes[idx]).Serializer;
  443. lock(basicTypes)
  444. { // don't need a full model lock for this
  445. // double-checked
  446. idx = basicTypes.IndexOf(BasicTypeFinder, type);
  447. if (idx >= 0) return ((BasicType)basicTypes[idx]).Serializer;
  448. WireType defaultWireType;
  449. MetaType.AttributeFamily family = MetaType.GetContractFamily(this, type, null);
  450. IProtoSerializer ser = family == MetaType.AttributeFamily.None
  451. ? ValueMember.TryGetCoreSerializer(this, DataFormat.Default, type, out defaultWireType, false, false, false, false)
  452. : null;
  453. if(ser != null) basicTypes.Add(new BasicType(type, ser));
  454. return ser;
  455. }
  456. }
  457. internal int FindOrAddAuto(Type type, bool demand, bool addWithContractOnly, bool addEvenIfAutoDisabled)
  458. {
  459. int key = types.IndexOf(MetaTypeFinder, type);
  460. MetaType metaType;
  461. // the fast happy path: meta-types we've already seen
  462. if (key >= 0)
  463. {
  464. metaType = (MetaType)types[key];
  465. if (metaType.Pending)
  466. {
  467. WaitOnLock(metaType);
  468. }
  469. return key;
  470. }
  471. // the fast fail path: types that will never have a meta-type
  472. bool shouldAdd = AutoAddMissingTypes || addEvenIfAutoDisabled;
  473. if (!Helpers.IsEnum(type) && TryGetBasicTypeSerializer(type) != null)
  474. {
  475. if (shouldAdd && !addWithContractOnly) throw MetaType.InbuiltType(type);
  476. return -1; // this will never be a meta-type
  477. }
  478. // otherwise: we don't yet know
  479. // check for proxy types
  480. Type underlyingType = ResolveProxies(type);
  481. if (underlyingType != null)
  482. {
  483. key = types.IndexOf(MetaTypeFinder, underlyingType);
  484. type = underlyingType; // if new added, make it reflect the underlying type
  485. }
  486. if (key < 0)
  487. {
  488. int opaqueToken = 0;
  489. try
  490. {
  491. TakeLock(ref opaqueToken);
  492. // try to recognise a few familiar patterns...
  493. if ((metaType = RecogniseCommonTypes(type)) == null)
  494. { // otherwise, check if it is a contract
  495. MetaType.AttributeFamily family = MetaType.GetContractFamily(this, type, null);
  496. if (family == MetaType.AttributeFamily.AutoTuple)
  497. {
  498. shouldAdd = addEvenIfAutoDisabled = true; // always add basic tuples, such as KeyValuePair
  499. }
  500. if (!shouldAdd || (
  501. !Helpers.IsEnum(type) && addWithContractOnly && family == MetaType.AttributeFamily.None)
  502. )
  503. {
  504. if (demand) ThrowUnexpectedType(type);
  505. return key;
  506. }
  507. metaType = Create(type);
  508. }
  509. metaType.Pending = true;
  510. bool weAdded = false;
  511. // double-checked
  512. int winner = types.IndexOf(MetaTypeFinder, type);
  513. if (winner < 0)
  514. {
  515. ThrowIfFrozen();
  516. key = types.Add(metaType);
  517. weAdded = true;
  518. }
  519. else
  520. {
  521. key = winner;
  522. }
  523. if (weAdded)
  524. {
  525. metaType.ApplyDefaultBehaviour();
  526. metaType.Pending = false;
  527. }
  528. }
  529. finally
  530. {
  531. ReleaseLock(opaqueToken);
  532. }
  533. }
  534. return key;
  535. }
  536. private MetaType RecogniseCommonTypes(Type type)
  537. {
  538. //#if !NO_GENERICS
  539. // if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>))
  540. // {
  541. // MetaType mt = new MetaType(this, type);
  542. // Type surrogate = typeof (KeyValuePairSurrogate<,>).MakeGenericType(type.GetGenericArguments());
  543. // mt.SetSurrogate(surrogate);
  544. // mt.IncludeSerializerMethod = false;
  545. // mt.Freeze();
  546. // MetaType surrogateMeta = (MetaType)types[FindOrAddAuto(surrogate, true, true, true)]; // this forcibly adds it if needed
  547. // if(surrogateMeta.IncludeSerializerMethod)
  548. // { // don't blindly set - it might be frozen
  549. // surrogateMeta.IncludeSerializerMethod = false;
  550. // }
  551. // surrogateMeta.Freeze();
  552. // return mt;
  553. // }
  554. //#endif
  555. return null;
  556. }
  557. private MetaType Create(Type type)
  558. {
  559. ThrowIfFrozen();
  560. return new MetaType(this, type, defaultFactory);
  561. }
  562. /// <summary>
  563. /// Adds support for an additional type in this model, optionally
  564. /// applying inbuilt patterns. If the type is already known to the
  565. /// model, the existing type is returned **without** applying
  566. /// any additional behaviour.
  567. /// </summary>
  568. /// <remarks>Inbuilt patterns include:
  569. /// [ProtoContract]/[ProtoMember(n)]
  570. /// [DataContract]/[DataMember(Order=n)]
  571. /// [XmlType]/[XmlElement(Order=n)]
  572. /// [On{Des|S}erializ{ing|ed}]
  573. /// ShouldSerialize*/*Specified
  574. /// </remarks>
  575. /// <param name="type">The type to be supported</param>
  576. /// <param name="applyDefaultBehaviour">Whether to apply the inbuilt configuration patterns (via attributes etc), or
  577. /// just add the type with no additional configuration (the type must then be manually configured).</param>
  578. /// <returns>The MetaType representing this type, allowing
  579. /// further configuration.</returns>
  580. public MetaType Add(Type type, bool applyDefaultBehaviour)
  581. {
  582. if (type == null) throw new ArgumentNullException("type");
  583. MetaType newType = FindWithoutAdd(type);
  584. if (newType != null) return newType; // return existing
  585. int opaqueToken = 0;
  586. #if WINRT || COREFX
  587. System.Reflection.TypeInfo typeInfo = System.Reflection.IntrospectionExtensions.GetTypeInfo(type);
  588. if (typeInfo.IsInterface && MetaType.ienumerable.IsAssignableFrom(typeInfo)
  589. #else
  590. if (type.IsInterface && MapType(MetaType.ienumerable).IsAssignableFrom(type)
  591. #endif
  592. && GetListItemType(this, type) == null)
  593. {
  594. throw new ArgumentException("IEnumerable[<T>] data cannot be used as a meta-type unless an Add method can be resolved");
  595. }
  596. try
  597. {
  598. newType = RecogniseCommonTypes(type);
  599. if(newType != null)
  600. {
  601. if(!applyDefaultBehaviour) {
  602. throw new ArgumentException(
  603. "Default behaviour must be observed for certain types with special handling; " + type.FullName,
  604. "applyDefaultBehaviour");
  605. }
  606. // we should assume that type is fully configured, though; no need to re-run:
  607. applyDefaultBehaviour = false;
  608. }
  609. if(newType == null) newType = Create(type);
  610. newType.Pending = true;
  611. TakeLock(ref opaqueToken);
  612. // double checked
  613. if (FindWithoutAdd(type) != null) throw new ArgumentException("Duplicate type", "type");
  614. ThrowIfFrozen();
  615. types.Add(newType);
  616. if (applyDefaultBehaviour) { newType.ApplyDefaultBehaviour(); }
  617. newType.Pending = false;
  618. }
  619. finally
  620. {
  621. ReleaseLock(opaqueToken);
  622. }
  623. return newType;
  624. }
  625. #if FEAT_COMPILER && !FX11
  626. /// <summary>
  627. /// Should serializers be compiled on demand? It may be useful
  628. /// to disable this for debugging purposes.
  629. /// </summary>
  630. public bool AutoCompile
  631. {
  632. get { return GetOption(OPTIONS_AutoCompile); }
  633. set { SetOption(OPTIONS_AutoCompile, value); }
  634. }
  635. #endif
  636. /// <summary>
  637. /// Should support for unexpected types be added automatically?
  638. /// If false, an exception is thrown when unexpected types
  639. /// are encountered.
  640. /// </summary>
  641. public bool AutoAddMissingTypes
  642. {
  643. get { return GetOption(OPTIONS_AutoAddMissingTypes); }
  644. set {
  645. if (!value && GetOption(OPTIONS_IsDefaultModel))
  646. {
  647. throw new InvalidOperationException("The default model must allow missing types");
  648. }
  649. ThrowIfFrozen();
  650. SetOption(OPTIONS_AutoAddMissingTypes, value);
  651. }
  652. }
  653. /// <summary>
  654. /// Verifies that the model is still open to changes; if not, an exception is thrown
  655. /// </summary>
  656. private void ThrowIfFrozen()
  657. {
  658. if (GetOption(OPTIONS_Frozen)) throw new InvalidOperationException("The model cannot be changed once frozen");
  659. }
  660. /// <summary>
  661. /// Prevents further changes to this model
  662. /// </summary>
  663. public void Freeze()
  664. {
  665. if (GetOption(OPTIONS_IsDefaultModel)) throw new InvalidOperationException("The default model cannot be frozen");
  666. SetOption(OPTIONS_Frozen, true);
  667. }
  668. private readonly BasicList types = new BasicList();
  669. /// <summary>
  670. /// Provides the key that represents a given type in the current model.
  671. /// </summary>
  672. protected override int GetKeyImpl(Type type)
  673. {
  674. return GetKey(type, false, true);
  675. }
  676. internal int GetKey(Type type, bool demand, bool getBaseKey)
  677. {
  678. Helpers.DebugAssert(type != null);
  679. try
  680. {
  681. int typeIndex = FindOrAddAuto(type, demand, true, false);
  682. if (typeIndex >= 0)
  683. {
  684. MetaType mt = (MetaType)types[typeIndex];
  685. if (getBaseKey)
  686. {
  687. mt = MetaType.GetRootType(mt);
  688. typeIndex = FindOrAddAuto(mt.Type, true, true, false);
  689. }
  690. }
  691. return typeIndex;
  692. }
  693. catch (NotSupportedException)
  694. {
  695. throw; // re-surface "as-is"
  696. }
  697. catch (Exception ex)
  698. {
  699. if (ex.Message.IndexOf(type.FullName) >= 0) throw; // already enough info
  700. throw new ProtoException(ex.Message + " (" + type.FullName + ")", ex);
  701. }
  702. }
  703. /// <summary>
  704. /// Writes a protocol-buffer representation of the given instance to the supplied stream.
  705. /// </summary>
  706. /// <param name="key">Represents the type (including inheritance) to consider.</param>
  707. /// <param name="value">The existing instance to be serialized (cannot be null).</param>
  708. /// <param name="dest">The destination stream to write to.</param>
  709. protected internal override void Serialize(int key, object value, ProtoWriter dest)
  710. {
  711. #if FEAT_IKVM
  712. throw new NotSupportedException();
  713. #else
  714. //Helpers.DebugWriteLine("Serialize", value);
  715. ((MetaType)types[key]).Serializer.Write(value, dest);
  716. #endif
  717. }
  718. /// <summary>
  719. /// Applies a protocol-buffer stream to an existing instance (which may be null).
  720. /// </summary>
  721. /// <param name="key">Represents the type (including inheritance) to consider.</param>
  722. /// <param name="value">The existing instance to be modified (can be null).</param>
  723. /// <param name="source">The binary stream to apply to the instance (cannot be null).</param>
  724. /// <returns>The updated instance; this may be different to the instance argument if
  725. /// either the original instance was null, or the stream defines a known sub-type of the
  726. /// original instance.</returns>
  727. protected internal override object Deserialize(int key, object value, ProtoReader source)
  728. {
  729. #if FEAT_IKVM
  730. throw new NotSupportedException();
  731. #else
  732. //Helpers.DebugWriteLine("Deserialize", value);
  733. IProtoSerializer ser = ((MetaType)types[key]).Serializer;
  734. if (value == null && Helpers.IsValueType(ser.ExpectedType)) {
  735. if(ser.RequiresOldValue) value = Activator.CreateInstance(ser.ExpectedType);
  736. return ser.Read(value, source);
  737. } else {
  738. return ser.Read(value, source);
  739. }
  740. #endif
  741. }
  742. #if FEAT_COMPILER
  743. // this is used by some unit-tests; do not remove
  744. internal Compiler.ProtoSerializer GetSerializer(IProtoSerializer serializer, bool compiled)
  745. {
  746. #if FEAT_IKVM
  747. throw new NotSupportedException();
  748. #else
  749. if (serializer == null) throw new ArgumentNullException("serializer");
  750. #if FEAT_COMPILER && !FX11
  751. if (compiled) return Compiler.CompilerContext.BuildSerializer(serializer, this);
  752. #endif
  753. return new Compiler.ProtoSerializer(serializer.Write);
  754. #endif
  755. }
  756. #if !FX11
  757. /// <summary>
  758. /// Compiles the serializers individually; this is *not* a full
  759. /// standalone compile, but can significantly boost performance
  760. /// while allowing additional types to be added.
  761. /// </summary>
  762. /// <remarks>An in-place compile can access non-public types / members</remarks>
  763. public void CompileInPlace()
  764. {
  765. foreach (MetaType type in types)
  766. {
  767. type.CompileInPlace();
  768. }
  769. }
  770. #endif
  771. #endif
  772. //internal override IProtoSerializer GetTypeSerializer(Type type)
  773. //{ // this list is thread-safe for reading
  774. // .Serializer;
  775. //}
  776. //internal override IProtoSerializer GetTypeSerializer(int key)
  777. //{ // this list is thread-safe for reading
  778. // MetaType type = (MetaType)types.TryGet(key);
  779. // if (type != null) return type.Serializer;
  780. // throw new KeyNotFoundException();
  781. //}
  782. #if FEAT_COMPILER
  783. private void BuildAllSerializers()
  784. {
  785. // note that types.Count may increase during this operation, as some serializers
  786. // bring other types into play
  787. for (int i = 0; i < types.Count; i++)
  788. {
  789. // the primary purpose of this is to force the creation of the Serializer
  790. MetaType mt = (MetaType)types[i];
  791. if (mt.Serializer == null)
  792. throw new InvalidOperationException("No serializer available for " + mt.Type.Name);
  793. }
  794. }
  795. #if !SILVERLIGHT
  796. internal sealed class SerializerPair : IComparable
  797. {
  798. int IComparable.CompareTo(object obj)
  799. {
  800. if (obj == null) throw new ArgumentException("obj");
  801. SerializerPair other = (SerializerPair)obj;
  802. // we want to bunch all the items with the same base-type together, but we need the items with a
  803. // different base **first**.
  804. if (this.BaseKey == this.MetaKey)
  805. {
  806. if (other.BaseKey == other.MetaKey)
  807. { // neither is a subclass
  808. return this.MetaKey.CompareTo(other.MetaKey);
  809. }
  810. else
  811. { // "other" (only) is involved in inheritance; "other" should be first
  812. return 1;
  813. }
  814. }
  815. else
  816. {
  817. if (other.BaseKey == other.MetaKey)
  818. { // "this" (only) is involved in inheritance; "this" should be first
  819. return -1;
  820. }
  821. else
  822. { // both are involved in inheritance
  823. int result = this.BaseKey.CompareTo(other.BaseKey);
  824. if (result == 0) result = this.MetaKey.CompareTo(other.MetaKey);
  825. return result;
  826. }
  827. }
  828. }
  829. public readonly int MetaKey, BaseKey;
  830. public readonly MetaType Type;
  831. public readonly MethodBuilder Serialize, Deserialize;
  832. public readonly ILGenerator SerializeBody, DeserializeBody;
  833. public SerializerPair(int metaKey, int baseKey, MetaType type, MethodBuilder serialize, MethodBuilder deserialize,
  834. ILGenerator serializeBody, ILGenerator deserializeBody)
  835. {
  836. this.MetaKey = metaKey;
  837. this.BaseKey = baseKey;
  838. this.Serialize = serialize;
  839. this.Deserialize = deserialize;
  840. this.SerializeBody = serializeBody;
  841. this.DeserializeBody = deserializeBody;
  842. this.Type = type;
  843. }
  844. }
  845. /// <summary>
  846. /// Fully compiles the current model into a static-compiled model instance
  847. /// </summary>
  848. /// <remarks>A full compilation is restricted to accessing public types / members</remarks>
  849. /// <returns>An instance of the newly created compiled type-model</returns>
  850. public TypeModel Compile()
  851. {
  852. CompilerOptions options = new CompilerOptions();
  853. return Compile(options);
  854. }
  855. static ILGenerator Override(TypeBuilder type, string name)
  856. {
  857. MethodInfo baseMethod = type.BaseType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance);
  858. ParameterInfo[] parameters = baseMethod.GetParameters();
  859. Type[] paramTypes = new Type[parameters.Length];
  860. for(int i = 0 ; i < paramTypes.Length ; i++) {
  861. paramTypes[i] = parameters[i].ParameterType;
  862. }
  863. MethodBuilder newMethod = type.DefineMethod(baseMethod.Name,
  864. (baseMethod.Attributes & ~MethodAttributes.Abstract) | MethodAttributes.Final, baseMethod.CallingConvention, baseMethod.ReturnType, paramTypes);
  865. ILGenerator il = newMethod.GetILGenerator();
  866. type.DefineMethodOverride(newMethod, baseMethod);
  867. return il;
  868. }
  869. #if FEAT_IKVM
  870. /// <summary>
  871. /// Inspect the model, and resolve all related types
  872. /// </summary>
  873. public void Cascade()
  874. {
  875. BuildAllSerializers();
  876. }
  877. /// <summary>
  878. /// Translate a System.Type into the universe's type representation
  879. /// </summary>
  880. protected internal override Type MapType(System.Type type, bool demand)
  881. {
  882. if (type == null) return null;
  883. #if DEBUG
  884. if (type.Assembly == typeof(IKVM.Reflection.Type).Assembly)
  885. {
  886. throw new InvalidOperationException(string.Format(
  887. "Somebody is passing me IKVM types! {0} should be fully-qualified at the call-site",
  888. type.Name));
  889. }
  890. #endif
  891. Type result = universe.GetType(type.AssemblyQualifiedName);
  892. if(result == null)
  893. {
  894. // things also tend to move around... *a lot* - especially in WinRT; search all as a fallback strategy
  895. foreach (Assembly a in universe.GetAssemblies())
  896. {
  897. result = a.GetType(type.FullName);
  898. if (result != null) break;
  899. }
  900. if (result == null && demand)
  901. {
  902. throw new InvalidOperationException("Unable to map type: " + type.AssemblyQualifiedName);
  903. }
  904. }
  905. return result;
  906. }
  907. #endif
  908. /// <summary>
  909. /// Represents configuration options for compiling a model to
  910. /// a standalone assembly.
  911. /// </summary>
  912. public sealed class CompilerOptions
  913. {
  914. /// <summary>
  915. /// Import framework options from an existing type
  916. /// </summary>
  917. public void SetFrameworkOptions(MetaType from)
  918. {
  919. if (from == null) throw new ArgumentNullException("from");
  920. AttributeMap[] attribs = AttributeMap.Create(from.Model, Helpers.GetAssembly(from.Type));
  921. foreach (AttributeMap attrib in attribs)
  922. {
  923. if (attrib.AttributeType.FullName == "System.Runtime.Versioning.TargetFrameworkAttribute")
  924. {
  925. object tmp;
  926. if (attrib.TryGet("FrameworkName", out tmp)) TargetFrameworkName = (string)tmp;
  927. if (attrib.TryGet("FrameworkDisplayName", out tmp)) TargetFrameworkDisplayName = (string)tmp;
  928. break;
  929. }
  930. }
  931. }
  932. private string targetFrameworkName, targetFrameworkDisplayName, typeName, outputPath, imageRuntimeVersion;
  933. private int metaDataVersion;
  934. /// <summary>
  935. /// The TargetFrameworkAttribute FrameworkName value to burn into the generated assembly
  936. /// </summary>
  937. public string TargetFrameworkName { get { return targetFrameworkName; } set { targetFrameworkName = value; } }
  938. /// <summary>
  939. /// The TargetFrameworkAttribute FrameworkDisplayName value to burn into the generated assembly
  940. /// </summary>
  941. public string TargetFrameworkDisplayName { get { return targetFrameworkDisplayName; } set { targetFrameworkDisplayName = value; } }
  942. /// <summary>
  943. /// The name of the TypeModel class to create
  944. /// </summary>
  945. public string TypeName { get { return typeName; } set { typeName = value; } }
  946. #if COREFX
  947. internal const string NoPersistence = "Assembly persistence not supported on this runtime";
  948. #endif
  949. /// <summary>
  950. /// The path for the new dll
  951. /// </summary>
  952. #if COREFX
  953. [Obsolete(NoPersistence)]
  954. #endif
  955. public string OutputPath { get { return outputPath; } set { outputPath = value; } }
  956. /// <summary>
  957. /// The runtime version for the generated assembly
  958. /// </summary>
  959. public string ImageRuntimeVersion { get { return imageRuntimeVersion; } set { imageRuntimeVersion = value; } }
  960. /// <summary>
  961. /// The runtime version for the generated assembly
  962. /// </summary>
  963. public int MetaDataVersion { get { return metaDataVersion; } set { metaDataVersion = value; } }
  964. private Accessibility accessibility = Accessibility.Public;
  965. /// <summary>
  966. /// The acecssibility of the generated serializer
  967. /// </summary>
  968. public Accessibility Accessibility { get { return accessibility; } set { accessibility = value; } }
  969. #if FEAT_IKVM
  970. /// <summary>
  971. /// The name of the container that holds the key pair.
  972. /// </summary>
  973. public string KeyContainer { get; set; }
  974. /// <summary>
  975. /// The path to a file that hold the key pair.
  976. /// </summary>
  977. public string KeyFile { get; set; }
  978. /// <summary>
  979. /// The public key to sign the file with.
  980. /// </summary>
  981. public string PublicKey { get; set; }
  982. #endif
  983. }
  984. /// <summary>
  985. /// Type accessibility
  986. /// </summary>
  987. public enum Accessibility
  988. {
  989. /// <summary>
  990. /// Available to all callers
  991. /// </summary>
  992. Public,
  993. /// <summary>
  994. /// Available to all callers in the same assembly, or assemblies specified via [InternalsVisibleTo(...)]
  995. /// </summary>
  996. Internal
  997. }
  998. #if !COREFX
  999. /// <summary>
  1000. /// Fully compiles the current model into a static-compiled serialization dll
  1001. /// (the serialization dll still requires protobuf-net for support services).
  1002. /// </summary>
  1003. /// <remarks>A full compilation is restricted to accessing public types / members</remarks>
  1004. /// <param name="name">The name of the TypeModel class to create</param>
  1005. /// <param name="path">The path for the new dll</param>
  1006. /// <returns>An instance of the newly created compiled type-model</returns>
  1007. public TypeModel Compile(string name, string path)
  1008. {
  1009. CompilerOptions options = new CompilerOptions();
  1010. options.TypeName = name;
  1011. options.OutputPath = path;
  1012. return Compile(options);
  1013. }
  1014. #endif
  1015. /// <summary>
  1016. /// Fully compiles the current model into a static-compiled serialization dll
  1017. /// (the serialization dll still requires protobuf-net for support services).
  1018. /// </summary>
  1019. /// <remarks>A full compilation is restricted to accessing public types / members</remarks>
  1020. /// <returns>An instance of the newly created compiled type-model</returns>
  1021. public TypeModel Compile(CompilerOptions options)
  1022. {
  1023. if (options == null) throw new ArgumentNullException("options");
  1024. string typeName = options.TypeName;
  1025. #pragma warning disable 0618
  1026. string path = options.OutputPath;
  1027. #pragma warning restore 0618
  1028. BuildAllSerializers();
  1029. Freeze();
  1030. bool save = !Helpers.IsNullOrEmpty(path);
  1031. if (Helpers.IsNullOrEmpty(typeName))
  1032. {
  1033. if (save) throw new ArgumentNullException("typeName");
  1034. typeName = Guid.NewGuid().ToString();
  1035. }
  1036. string assemblyName, moduleName;
  1037. if(path == null)
  1038. {
  1039. assemblyName = typeName;
  1040. moduleName = assemblyName + ".dll";
  1041. }
  1042. else
  1043. {
  1044. assemblyName = new System.IO.FileInfo(System.IO.Path.GetFileNameWithoutExtension(path)).Name;
  1045. moduleName = assemblyName + System.IO.Path.GetExtension(path);
  1046. }
  1047. #if FEAT_IKVM
  1048. IKVM.Reflection.AssemblyName an = new IKVM.Reflection.AssemblyName();
  1049. an.Name = assemblyName;
  1050. AssemblyBuilder asm = universe.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
  1051. if (!Helpers.IsNullOrEmpty(options.KeyFile))
  1052. {
  1053. asm.__SetAssemblyKeyPair(new StrongNameKeyPair(File.OpenRead(options.KeyFile)));
  1054. }
  1055. else if (!Helpers.IsNullOrEmpty(options.KeyContainer))
  1056. {
  1057. asm.__SetAssemblyKeyPair(new StrongNameKeyPair(options.KeyContainer));
  1058. }
  1059. else if (!Helpers.IsNullOrEmpty(options.PublicKey))
  1060. {
  1061. asm.__SetAssemblyPublicKey(FromHex(options.PublicKey));
  1062. }
  1063. if(!Helpers.IsNullOrEmpty(options.ImageRuntimeVersion) && options.MetaDataVersion != 0)
  1064. {
  1065. asm.__SetImageRuntimeVersion(options.ImageRuntimeVersion, options.MetaDataVersion);
  1066. }
  1067. ModuleBuilder module = asm.DefineDynamicModule(moduleName, path);
  1068. #elif COREFX
  1069. AssemblyName an = new AssemblyName();
  1070. an.Name = assemblyName;
  1071. AssemblyBuilder asm = AssemblyBuilder.DefineDynamicAssembly(an,
  1072. AssemblyBuilderAccess.Run);
  1073. ModuleBuilder module = asm.DefineDynamicModule(moduleName);
  1074. #else
  1075. AssemblyName an = new AssemblyName();
  1076. an.Name = assemblyName;
  1077. AssemblyBuilder asm = AppDomain.CurrentDomain.DefineDynamicAssembly(an,
  1078. (save ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run)
  1079. );
  1080. ModuleBuilder module = save ? asm.DefineDynamicModule(moduleName, path)
  1081. : asm.DefineDynamicModule(moduleName);
  1082. #endif
  1083. WriteAssemblyAttributes(options, assemblyName, asm);
  1084. TypeBuilder type = WriteBasicTypeModel(options, typeName, module);
  1085. int index;
  1086. bool hasInheritance;
  1087. SerializerPair[] methodPairs;
  1088. Compiler.CompilerContext.ILVersion ilVersion;
  1089. WriteSerializers(options, assemblyName, type, out index, out hasInheritance, out methodPairs, out ilVersion);
  1090. ILGenerator il;
  1091. int knownTypesCategory;
  1092. FieldBuilder knownTypes;
  1093. Type knownTypesLookupType;
  1094. WriteGetKeyImpl(type, hasInheritance, methodPairs, ilVersion, assemblyName, out il, out knownTypesCategory, out knownTypes, out knownTypesLookupType);
  1095. // trivial flags
  1096. il = Override(type, "SerializeDateTimeKind");
  1097. il.Emit(IncludeDateTimeKind ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
  1098. il.Emit(OpCodes.Ret);
  1099. // end: trivial flags
  1100. Compiler.CompilerContext ctx = WriteSerializeDeserialize(assemblyName, type, methodPairs, ilVersion, ref il);
  1101. WriteConstructors(type, ref index, methodPairs, ref il, knownTypesCategory, knownTypes, knownTypesLookupType, ctx);
  1102. #if COREFX
  1103. Type finalType = type.CreateTypeInfo().AsType();
  1104. #else
  1105. Type finalType = type.CreateType();
  1106. #endif
  1107. if (!Helpers.IsNullOrEmpty(path))
  1108. {
  1109. #if COREFX
  1110. throw new NotSupportedException(CompilerOptions.NoPersistence);
  1111. #else
  1112. asm.Save(path);
  1113. Helpers.DebugWriteLine("Wrote dll:" + path);
  1114. #endif
  1115. }
  1116. #if FEAT_IKVM
  1117. return null;
  1118. #else
  1119. return (TypeModel)Activator.CreateInstance(finalType);
  1120. #endif
  1121. }
  1122. #if FEAT_IKVM
  1123. private byte[] FromHex(string value)
  1124. {
  1125. if (Helpers.IsNullOrEmpty(value)) throw new ArgumentNullException("value");
  1126. int len = value.Length / 2;
  1127. byte[] result = new byte[len];
  1128. for(int i = 0 ; i < len ; i++)
  1129. {
  1130. result[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
  1131. }
  1132. return result;
  1133. }
  1134. #endif
  1135. private void WriteConstructors(TypeBuilder type, ref int index, SerializerPair[] methodPairs, ref ILGenerator il, int knownTypesCategory, FieldBuilder knownTypes, Type knownTypesLookupType, Compiler.CompilerContext ctx)
  1136. {
  1137. type.DefineDefaultConstructor(MethodAttributes.Public);
  1138. il = type.DefineTypeInitializer().GetILGenerator();
  1139. switch (knownTypesCategory)
  1140. {
  1141. case KnownTypes_Array:
  1142. {
  1143. Compiler.CompilerContext.LoadValue(il, types.Count);
  1144. il.Emit(OpCodes.Newarr, ctx.MapType(typeof(System.Type)));
  1145. index = 0;
  1146. foreach (SerializerPair pair in methodPairs)
  1147. {
  1148. il.Emit(OpCodes.Dup);
  1149. Compiler.CompilerContext.LoadValue(il, index);
  1150. il.Emit(OpCodes.Ldtoken, pair.Type.Type);
  1151. il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null);
  1152. il.Emit(OpCodes.Stelem_Ref);
  1153. index++;
  1154. }
  1155. il.Emit(OpCodes.Stsfld, knownTypes);
  1156. il.Emit(OpCodes.Ret);
  1157. }
  1158. break;
  1159. case KnownTypes_Dictionary:
  1160. {
  1161. Compiler.CompilerContext.LoadValue(il, types.Count);
  1162. //LocalBuilder loc = il.DeclareLocal(knownTypesLookupType);
  1163. il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[] { MapType(typeof(int)) }));
  1164. il.Emit(OpCodes.Stsfld, knownTypes);
  1165. int typeIndex = 0;
  1166. foreach (SerializerPair pair in methodPairs)
  1167. {
  1168. il.Emit(OpCodes.Ldsfld, knownTypes);
  1169. il.Emit(OpCodes.Ldtoken, pair.Type.Type);
  1170. il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null);
  1171. int keyIndex = typeIndex++, lastKey = pair.BaseKey;
  1172. if (lastKey != pair.MetaKey) // not a base-type; need to give the index of the base-type
  1173. {
  1174. keyIndex = -1; // assume epic fail
  1175. for (int j = 0; j < methodPairs.Length; j++)
  1176. {
  1177. if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
  1178. {
  1179. keyIndex = j;
  1180. break;
  1181. }
  1182. }
  1183. }
  1184. Compiler.CompilerContext.LoadValue(il, keyIndex);
  1185. il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[] { MapType(typeof(System.Type)), MapType(typeof(int)) }), null);
  1186. }
  1187. il.Emit(OpCodes.Ret);
  1188. }
  1189. break;
  1190. case KnownTypes_Hashtable:
  1191. {
  1192. Compiler.CompilerContext.LoadValue(il, types.Count);
  1193. il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[] { MapType(typeof(int)) }));
  1194. il.Emit(OpCodes.Stsfld, knownTypes);
  1195. int typeIndex = 0;
  1196. foreach (SerializerPair pair in methodPairs)
  1197. {
  1198. il.Emit(OpCodes.Ldsfld, knownTypes);
  1199. il.Emit(OpCodes.Ldtoken, pair.Type.Type);
  1200. il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null);
  1201. int keyIndex = typeIndex++, lastKey = pair.BaseKey;
  1202. if (lastKey != pair.MetaKey) // not a base-type; need to give the index of the base-type
  1203. {
  1204. keyIndex = -1; // assume epic fail
  1205. for (int j = 0; j < methodPairs.Length; j++)
  1206. {
  1207. if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
  1208. {
  1209. keyIndex = j;
  1210. break;
  1211. }
  1212. }
  1213. }
  1214. Compiler.CompilerContext.LoadValue(il, keyIndex);
  1215. il.Emit(OpCodes.Box, MapType(typeof(int)));
  1216. il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[] { MapType(typeof(object)), MapType(typeof(object)) }), null);
  1217. }
  1218. il.Emit(OpCodes.Ret);
  1219. }
  1220. break;
  1221. default:
  1222. throw new InvalidOperationException();
  1223. }
  1224. }
  1225. private Compiler.CompilerContext WriteSerializeDeserialize(string assemblyName, TypeBuilder type, SerializerPair[] methodPairs, Compiler.CompilerContext.ILVersion ilVersion, ref ILGenerator il)
  1226. {
  1227. il = Override(type, "Serialize");
  1228. Compiler.CompilerContext ctx = new Compiler.CompilerContext(il, false, true, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)), "Serialize " + type.Name);
  1229. // arg0 = this, arg1 = key, arg2=obj, arg3=dest
  1230. Compiler.CodeLabel[] jumpTable = new Compiler.CodeLabel[types.Count];
  1231. for (int i = 0; i < jumpTable.Length; i++)
  1232. {
  1233. jumpTable[i] = ctx.DefineLabel();
  1234. }
  1235. il.Emit(OpCodes.Ldarg_1);
  1236. ctx.Switch(jumpTable);
  1237. ctx.Return();
  1238. for (int i = 0; i < jumpTable.Length; i++)
  1239. {
  1240. SerializerPair pair = methodPairs[i];
  1241. ctx.MarkLabel(jumpTable[i]);
  1242. il.Emit(OpCodes.Ldarg_2);
  1243. ctx.CastFromObject(pair.Type.Type);
  1244. il.Emit(OpCodes.Ldarg_3);
  1245. il.EmitCall(OpCodes.Call, pair.Serialize, null);
  1246. ctx.Return();
  1247. }
  1248. il = Override(type, "Deserialize");
  1249. ctx = new Compiler.CompilerContext(il, false, false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(object)), "Deserialize " + type.Name);
  1250. // arg0 = this, arg1 = key, arg2=obj, arg3=source
  1251. for (int i = 0; i < jumpTable.Length; i++)
  1252. {
  1253. jumpTable[i] = ctx.DefineLabel();
  1254. }
  1255. il.Emit(OpCodes.Ldarg_1);
  1256. ctx.Switch(jumpTable);
  1257. ctx.LoadNullRef();
  1258. ctx.Return();
  1259. for (int i = 0; i < jumpTable.Length; i++)
  1260. {
  1261. SerializerPair pair = methodPairs[i];
  1262. ctx.MarkLabel(jumpTable[i]);
  1263. Type keyType = pair.Type.Type;
  1264. if (Helpers.IsValueType(keyType))
  1265. {
  1266. il.Emit(OpCodes.Ldarg_2);
  1267. il.Emit(OpCodes.Ldarg_3);
  1268. il.EmitCall(OpCodes.Call, EmitBoxedSerializer(type, i, keyType, methodPairs, this, ilVersion, assemblyName), null);
  1269. ctx.Return();
  1270. }
  1271. else
  1272. {
  1273. il.Emit(OpCodes.Ldarg_2);
  1274. ctx.CastFromObject(keyType);
  1275. il.Emit(OpCodes.Ldarg_3);
  1276. il.EmitCall(OpCodes.Call, pair.Deserialize, null);
  1277. ctx.Return();
  1278. }
  1279. }
  1280. return ctx;
  1281. }
  1282. private const int KnownTypes_Array = 1, KnownTypes_Dictionary = 2, KnownTypes_Hashtable = 3, KnownTypes_ArrayCutoff = 20;
  1283. private void WriteGetKeyImpl(TypeBuilder type, bool hasInheritance, SerializerPair[] methodPairs, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName, out ILGenerator il, out int knownTypesCategory, out FieldBuilder knownTypes, out Type knownTypesLookupType)
  1284. {
  1285. il = Override(type, "GetKeyImpl");
  1286. Compiler.CompilerContext ctx = new Compiler.CompilerContext(il, false, false, methodPairs, this, ilVersion, assemblyName, MapType(typeof(System.Type), true), "GetKeyImpl");
  1287. if (types.Count <= KnownTypes_ArrayCutoff)
  1288. {
  1289. knownTypesCategory = KnownTypes_Array;
  1290. knownTypesLookupType = MapType(typeof(System.Type[]), true);
  1291. }
  1292. else
  1293. {
  1294. #if NO_GENERICS
  1295. knownTypesLookupType = null;
  1296. #else
  1297. knownTypesLookupType = MapType(typeof(System.Collections.Generic.Dictionary<System.Type, int>), false);
  1298. #endif
  1299. #if !COREFX
  1300. if (knownTypesLookupType == null)
  1301. {
  1302. knownTypesLookupType = MapType(typeof(Hashtable), true);
  1303. knownTypesCategory = KnownTypes_Hashtable;
  1304. }
  1305. else
  1306. #endif
  1307. {
  1308. knownTypesCategory = KnownTypes_Dictionary;
  1309. }
  1310. }
  1311. knownTypes = type.DefineField("knownTypes", knownTypesLookupType, FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static);
  1312. switch (knownTypesCategory)
  1313. {
  1314. case KnownTypes_Array:
  1315. {
  1316. il.Emit(OpCodes.Ldsfld, knownTypes);
  1317. il.Emit(OpCodes.Ldarg_1);
  1318. // note that Array.IndexOf is not supported under CF
  1319. il.EmitCall(OpCodes.Callvirt, MapType(typeof(IList)).GetMethod(
  1320. "IndexOf", new Type[] { MapType(typeof(object)) }), null);
  1321. if (hasInheritance)
  1322. {
  1323. il.DeclareLocal(MapType(typeof(int))); // loc-0
  1324. il.Emit(OpCodes.Dup);
  1325. il.Emit(OpCodes.Stloc_0);
  1326. BasicList getKeyLabels = new BasicList();
  1327. int lastKey = -1;
  1328. for (int i = 0; i < methodPairs.Length; i++)
  1329. {
  1330. if (methodPairs[i].MetaKey == methodPairs[i].BaseKey) break;
  1331. if (lastKey == methodPairs[i].BaseKey)
  1332. { // add the last label again
  1333. getKeyLabels.Add(getKeyLabels[getKeyLabels.Count - 1]);
  1334. }
  1335. else
  1336. { // add a new unique label
  1337. getKeyLabels.Add(ctx.DefineLabel());
  1338. lastKey = methodPairs[i].BaseKey;
  1339. }
  1340. }
  1341. Compiler.CodeLabel[] subtypeLabels = new Compiler.CodeLabel[getKeyLabels.Count];
  1342. getKeyLabels.CopyTo(subtypeLabels, 0);
  1343. ctx.Switch(subtypeLabels);
  1344. il.Emit(OpCodes.Ldloc_0); // not a sub-type; use the original value
  1345. il.Emit(OpCodes.Ret);
  1346. lastKey = -1;
  1347. // now output the different branches per sub-type (not derived type)
  1348. for (int i = subtypeLabels.Length - 1; i >= 0; i--)
  1349. {
  1350. if (lastKey != methodPairs[i].BaseKey)
  1351. {
  1352. lastKey = methodPairs[i].BaseKey;
  1353. // find the actual base-index for this base-key (i.e. the index of
  1354. // the base-type)
  1355. int keyIndex = -1;
  1356. for (int j = subtypeLabels.Length; j < methodPairs.Length; j++)
  1357. {
  1358. if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey)
  1359. {
  1360. keyIndex = j;
  1361. break;
  1362. }
  1363. }
  1364. ctx.MarkLabel(subtypeLabels[i]);
  1365. Compiler.CompilerContext.LoadValue(il, keyIndex);
  1366. il.Emit(OpCodes.Ret);
  1367. }
  1368. }
  1369. }
  1370. else
  1371. {
  1372. il.Emit(OpCodes.Ret);
  1373. }
  1374. }
  1375. break;
  1376. case KnownTypes_Dictionary:
  1377. {
  1378. LocalBuilder result = il.DeclareLocal(MapType(typeof(int)));
  1379. Label otherwise = il.DefineLabel();
  1380. il.Emit(OpCodes.Ldsfld, knownTypes);
  1381. il.Emit(OpCodes.Ldarg_1);
  1382. il.Emit(OpCodes.Ldloca_S, result);
  1383. il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("TryGetValue", BindingFlags.Instance | BindingFlags.Public), null);
  1384. il.Emit(OpCodes.Brfalse_S, otherwise);
  1385. il.Emit(OpCodes.Ldloc_S, result);
  1386. il.Emit(OpCodes.Ret);
  1387. il.MarkLabel(otherwise);
  1388. il.Emit(OpCodes.Ldc_I4_M1);
  1389. il.Emit(OpCodes.Ret);
  1390. }
  1391. break;
  1392. case KnownTypes_Hashtable:
  1393. {
  1394. Label otherwise = il.DefineLabel();
  1395. il.Emit(OpCodes.Ldsfld, knownTypes);
  1396. il.Emit(OpCodes.Ldarg_1);
  1397. il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetProperty("Item").GetGetMethod(), null);
  1398. il.Emit(OpCodes.Dup);
  1399. il.Emit(OpCodes.Brfalse_S, otherwise);
  1400. #if FX11
  1401. il.Emit(OpCodes.Unbox, MapType(typeof(int)));
  1402. il.Emit(OpCodes.Ldobj, MapType(typeof(int)));
  1403. #else
  1404. if (ilVersion == Compiler.CompilerContext.ILVersion.Net1)
  1405. {
  1406. il.Emit(OpCodes.Unbox, MapType(typeof(int)));
  1407. il.Emit(OpCodes.Ldobj, MapType(typeof(int)));
  1408. }
  1409. else
  1410. {
  1411. il.Emit(OpCodes.Unbox_Any, MapType(typeof(int)));
  1412. }
  1413. #endif
  1414. il.Emit(OpCodes.Ret);
  1415. il.MarkLabel(otherwise);
  1416. il.Emit(OpCodes.Pop);
  1417. il.Emit(OpCodes.Ldc_I4_M1);
  1418. il.Emit(OpCodes.Ret);
  1419. }
  1420. break;
  1421. default:
  1422. throw new InvalidOperationException();
  1423. }
  1424. }
  1425. private void WriteSerializers(CompilerOptions options, string assemblyName, TypeBuilder type, out int index, out bool hasInheritance, out SerializerPair[] methodPairs, out Compiler.CompilerContext.ILVersion ilVersion)
  1426. {
  1427. Compiler.CompilerContext ctx;
  1428. index = 0;
  1429. hasInheritance = false;
  1430. methodPairs = new SerializerPair[types.Count];
  1431. foreach (MetaType metaType in types)
  1432. {
  1433. MethodBuilder writeMethod = type.DefineMethod("Write"
  1434. #if DEBUG
  1435. + metaType.Type.Name
  1436. #endif
  1437. ,
  1438. MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard,
  1439. MapType(typeof(void)), new Type[] { metaType.Type, MapType(typeof(ProtoWriter)) });
  1440. MethodBuilder readMethod = type.DefineMethod("Read"
  1441. #if DEBUG
  1442. + metaType.Type.Name
  1443. #endif
  1444. ,
  1445. MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard,
  1446. metaType.Type, new Type[] { metaType.Type, MapType(typeof(ProtoReader)) });
  1447. SerializerPair pair = new SerializerPair(
  1448. GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType,
  1449. writeMethod, readMethod, writeMethod.GetILGenerator(), readMethod.GetILGenerator());
  1450. methodPairs[index++] = pair;
  1451. if (pair.MetaKey != pair.BaseKey) hasInheritance = true;
  1452. }
  1453. if (hasInheritance)
  1454. {
  1455. Array.Sort(methodPairs);
  1456. }
  1457. ilVersion = Compiler.CompilerContext.ILVersion.Net2;
  1458. if (options.MetaDataVersion == 0x10000)
  1459. {
  1460. ilVersion = Compiler.CompilerContext.ILVersion.Net1; // old-school!
  1461. }
  1462. for (index = 0; index < methodPairs.Length; index++)
  1463. {
  1464. SerializerPair pair = methodPairs[index];
  1465. ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, methodPairs, this, ilVersion, assemblyName, pair.Type.Type, "SerializeImpl " + pair.Type.Type.Name);
  1466. ctx.CheckAccessibility(pair.Deserialize.ReturnType
  1467. #if COREFX
  1468. .GetTypeInfo()
  1469. #endif
  1470. );
  1471. pair.Type.Serializer.EmitWrite(ctx, ctx.InputValue);
  1472. ctx.Return();
  1473. ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, methodPairs, this, ilVersion, assemblyName, pair.Type.Type, "DeserializeImpl " + pair.Type.Type.Name);
  1474. pair.Type.Serializer.EmitRead(ctx, ctx.InputValue);
  1475. if (!pair.Type.Serializer.ReturnsValue)
  1476. {
  1477. ctx.LoadValue(ctx.InputValue);
  1478. }
  1479. ctx.Return();
  1480. }
  1481. }
  1482. private TypeBuilder WriteBasicTypeModel(CompilerOptions options, string typeName, ModuleBuilder module)
  1483. {
  1484. Type baseType = MapType(typeof(TypeModel));
  1485. #if COREFX
  1486. TypeAttributes typeAttributes = (baseType.GetTypeInfo().Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed;
  1487. #else
  1488. TypeAttributes typeAttributes = (baseType.Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed;
  1489. #endif
  1490. if (options.Accessibility == Accessibility.Internal)
  1491. {
  1492. typeAttributes &= ~TypeAttributes.Public;
  1493. }
  1494. TypeBuilder type = module.DefineType(typeName, typeAttributes, baseType);
  1495. return type;
  1496. }
  1497. private void WriteAssemblyAttributes(CompilerOptions options, string assemblyName, AssemblyBuilder asm)
  1498. {
  1499. if (!Helpers.IsNullOrEmpty(options.TargetFrameworkName))
  1500. {
  1501. // get [TargetFramework] from mscorlib/equivalent and burn into the new assembly
  1502. Type versionAttribType = null;
  1503. try
  1504. { // this is best-endeavours only
  1505. versionAttribType = GetType("System.Runtime.Versioning.TargetFrameworkAttribute", Helpers.GetAssembly(MapType(typeof(string))));
  1506. }
  1507. catch { /* don't stress */ }
  1508. if (versionAttribType != null)
  1509. {
  1510. PropertyInfo[] props;
  1511. object[] propValues;
  1512. if (Helpers.IsNullOrEmpty(options.TargetFrameworkDisplayName))
  1513. {
  1514. props = new PropertyInfo[0];
  1515. propValues = new object[0];
  1516. }
  1517. else
  1518. {
  1519. props = new PropertyInfo[1] { versionAttribType.GetProperty("FrameworkDisplayName") };
  1520. propValues = new object[1] { options.TargetFrameworkDisplayName };
  1521. }
  1522. CustomAttributeBuilder builder = new CustomAttributeBuilder(
  1523. versionAttribType.GetConstructor(new Type[] { MapType(typeof(string)) }),
  1524. new object[] { options.TargetFrameworkName },
  1525. props,
  1526. propValues);
  1527. asm.SetCustomAttribute(builder);
  1528. }
  1529. }
  1530. // copy assembly:InternalsVisibleTo
  1531. Type internalsVisibleToAttribType = null;
  1532. #if !FX11
  1533. try
  1534. {
  1535. internalsVisibleToAttribType = MapType(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute));
  1536. }
  1537. catch { /* best endeavors only */ }
  1538. #endif
  1539. if (internalsVisibleToAttribType != null)
  1540. {
  1541. BasicList internalAssemblies = new BasicList(), consideredAssemblies = new BasicList();
  1542. foreach (MetaType metaType in types)
  1543. {
  1544. Assembly assembly = Helpers.GetAssembly(metaType.Type);
  1545. if (consideredAssemblies.IndexOfReference(assembly) >= 0) continue;
  1546. consideredAssemblies.Add(assembly);
  1547. AttributeMap[] assemblyAttribsMap = AttributeMap.Create(this, assembly);
  1548. for (int i = 0; i < assemblyAttribsMap.Length; i++)
  1549. {
  1550. if (assemblyAttribsMap[i].AttributeType != internalsVisibleToAttribType) continue;
  1551. object privelegedAssemblyObj;
  1552. assemblyAttribsMap[i].TryGet("AssemblyName", out privelegedAssemblyObj);
  1553. string privelegedAssemblyName = privelegedAssemblyObj as string;
  1554. if (privelegedAssemblyName == assemblyName || Helpers.IsNullOrEmpty(privelegedAssemblyName)) continue; // ignore
  1555. if (internalAssemblies.IndexOfString(privelegedAssemblyName) >= 0) continue; // seen it before
  1556. internalAssemblies.Add(privelegedAssemblyName);
  1557. CustomAttributeBuilder builder = new CustomAttributeBuilder(
  1558. internalsVisibleToAttribType.GetConstructor(new Type[] { MapType(typeof(string)) }),
  1559. new object[] { privelegedAssemblyName });
  1560. asm.SetCustomAttribute(builder);
  1561. }
  1562. }
  1563. }
  1564. }
  1565. private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, TypeModel model, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName)
  1566. {
  1567. MethodInfo dedicated = methodPairs[i].Deserialize;
  1568. MethodBuilder boxedSerializer = type.DefineMethod("_" + i.ToString(), MethodAttributes.Static, CallingConventions.Standard,
  1569. model.MapType(typeof(object)), new Type[] { model.MapType(typeof(object)), model.MapType(typeof(ProtoReader)) });
  1570. Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object)), "BoxedSerializer " + valueType.Name);
  1571. ctx.LoadValue(ctx.InputValue);
  1572. Compiler.CodeLabel @null = ctx.DefineLabel();
  1573. ctx.BranchIfFalse(@null, true);
  1574. Type mappedValueType = valueType;
  1575. ctx.LoadValue(ctx.InputValue);
  1576. ctx.CastFromObject(mappedValueType);
  1577. ctx.LoadReaderWriter();
  1578. ctx.EmitCall(dedicated);
  1579. ctx.CastToObject(mappedValueType);
  1580. ctx.Return();
  1581. ctx.MarkLabel(@null);
  1582. using (Compiler.Local typedVal = new Compiler.Local(ctx, mappedValueType))
  1583. {
  1584. // create a new valueType
  1585. ctx.LoadAddress(typedVal, mappedValueType);
  1586. ctx.EmitCtor(mappedValueType);
  1587. ctx.LoadValue(typedVal);
  1588. ctx.LoadReaderWriter();
  1589. ctx.EmitCall(dedicated);
  1590. ctx.CastToObject(mappedValueType);
  1591. ctx.Return();
  1592. }
  1593. return boxedSerializer;
  1594. }
  1595. #endif
  1596. #endif
  1597. //internal bool IsDefined(Type type, int fieldNumber)
  1598. //{
  1599. // return FindWithoutAdd(type).IsDefined(fieldNumber);
  1600. //}
  1601. // note that this is used by some of the unit tests
  1602. internal bool IsPrepared(Type type)
  1603. {
  1604. MetaType meta = FindWithoutAdd(type);
  1605. return meta != null && meta.IsPrepared();
  1606. }
  1607. internal EnumSerializer.EnumPair[] GetEnumMap(Type type)
  1608. {
  1609. int index = FindOrAddAuto(type, false, false, false);
  1610. return index < 0 ? null : ((MetaType)types[index]).GetEnumMap();
  1611. }
  1612. private int metadataTimeoutMilliseconds = 5000;
  1613. /// <summary>
  1614. /// The amount of time to wait if there are concurrent metadata access operations
  1615. /// </summary>
  1616. public int MetadataTimeoutMilliseconds
  1617. {
  1618. get { return metadataTimeoutMilliseconds; }
  1619. set
  1620. {
  1621. if (value <= 0) throw new ArgumentOutOfRangeException("MetadataTimeoutMilliseconds");
  1622. metadataTimeoutMilliseconds = value;
  1623. }
  1624. }
  1625. #if DEBUG
  1626. int lockCount;
  1627. /// <summary>
  1628. /// Gets how many times a model lock was taken
  1629. /// </summary>
  1630. public int LockCount { get { return lockCount; } }
  1631. #endif
  1632. internal void TakeLock(ref int opaqueToken)
  1633. {
  1634. const string message = "Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection; please also see the LockContended event";
  1635. opaqueToken = 0;
  1636. #if PORTABLE
  1637. if(!Monitor.TryEnter(types, metadataTimeoutMilliseconds)) throw new TimeoutException(message);
  1638. opaqueToken = Interlocked.CompareExchange(ref contentionCounter, 0, 0); // just fetch current value (starts at 1)
  1639. #elif CF2 || CF35
  1640. int remaining = metadataTimeoutMilliseconds;
  1641. bool lockTaken;
  1642. do {
  1643. lockTaken = Monitor.TryEnter(types);
  1644. if(!lockTaken)
  1645. {
  1646. if(remaining <= 0) throw new TimeoutException(message);
  1647. remaining -= 50;
  1648. Thread.Sleep(50);
  1649. }
  1650. } while(!lockTaken);
  1651. opaqueToken = Interlocked.CompareExchange(ref contentionCounter, 0, 0); // just fetch current value (starts at 1)
  1652. #else
  1653. if (Monitor.TryEnter(types, metadataTimeoutMilliseconds))
  1654. {
  1655. opaqueToken = GetContention(); // just fetch current value (starts at 1)
  1656. }
  1657. else
  1658. {
  1659. AddContention();
  1660. #if FX11
  1661. throw new InvalidOperationException(message);
  1662. #else
  1663. throw new TimeoutException(message);
  1664. #endif
  1665. }
  1666. #endif
  1667. #if DEBUG // note that here, through all code-paths: we have the lock
  1668. lockCount++;
  1669. #endif
  1670. }
  1671. private int contentionCounter = 1;
  1672. #if PLAT_NO_INTERLOCKED
  1673. private readonly object contentionLock = new object();
  1674. #endif
  1675. private int GetContention()
  1676. {
  1677. #if PLAT_NO_INTERLOCKED
  1678. lock(contentionLock)
  1679. {
  1680. return contentionCounter;
  1681. }
  1682. #else
  1683. return Interlocked.CompareExchange(ref contentionCounter, 0, 0);
  1684. #endif
  1685. }
  1686. private void AddContention()
  1687. {
  1688. #if PLAT_NO_INTERLOCKED
  1689. lock(contentionLock)
  1690. {
  1691. contentionCounter++;
  1692. }
  1693. #else
  1694. Interlocked.Increment(ref contentionCounter);
  1695. #endif
  1696. }
  1697. internal void ReleaseLock(int opaqueToken)
  1698. {
  1699. if (opaqueToken != 0)
  1700. {
  1701. Monitor.Exit(types);
  1702. if(opaqueToken != GetContention()) // contention-count changes since we looked!
  1703. {
  1704. LockContentedEventHandler handler = LockContended;
  1705. if (handler != null)
  1706. {
  1707. // not hugely elegant, but this is such a far-corner-case that it doesn't need to be slick - I'll settle for cross-platform
  1708. string stackTrace;
  1709. try
  1710. {
  1711. throw new ProtoException();
  1712. }
  1713. catch(Exception ex)
  1714. {
  1715. stackTrace = ex.StackTrace;
  1716. }
  1717. handler(this, new LockContentedEventArgs(stackTrace));
  1718. }
  1719. }
  1720. }
  1721. }
  1722. /// <summary>
  1723. /// If a lock-contention is detected, this event signals the *owner* of the lock responsible for the blockage, indicating
  1724. /// what caused the problem; this is only raised if the lock-owning code successfully completes.
  1725. /// </summary>
  1726. public event LockContentedEventHandler LockContended;
  1727. internal void ResolveListTypes(Type type, ref Type itemType, ref Type defaultType)
  1728. {
  1729. if (type == null) return;
  1730. if(Helpers.GetTypeCode(type) != ProtoTypeCode.Unknown) return; // don't try this[type] for inbuilts
  1731. if(this[type].IgnoreListHandling) return;
  1732. // handle arrays
  1733. if (type.IsArray)
  1734. {
  1735. if (type.GetArrayRank() != 1)
  1736. {
  1737. throw new NotSupportedException("Multi-dimension arrays are supported");
  1738. }
  1739. itemType = type.GetElementType();
  1740. if (itemType == MapType(typeof(byte)))
  1741. {
  1742. defaultType = itemType = null;
  1743. }
  1744. else
  1745. {
  1746. defaultType = type;
  1747. }
  1748. }
  1749. // handle lists
  1750. if (itemType == null) { itemType = TypeModel.GetListItemType(this, type); }
  1751. // check for nested data (not allowed)
  1752. if (itemType != null)
  1753. {
  1754. Type nestedItemType = null, nestedDefaultType = null;
  1755. ResolveListTypes(itemType, ref nestedItemType, ref nestedDefaultType);
  1756. if (nestedItemType != null)
  1757. {
  1758. throw TypeModel.CreateNestedListsNotSupported();
  1759. }
  1760. }
  1761. if (itemType != null && defaultType == null)
  1762. {
  1763. #if WINRT || COREFX
  1764. System.Reflection.TypeInfo typeInfo = System.Reflection.IntrospectionExtensions.GetTypeInfo(type);
  1765. if (typeInfo.IsClass && !typeInfo.IsAbstract && Helpers.GetConstructor(typeInfo, Helpers.EmptyTypes, true) != null)
  1766. #else
  1767. if (type.IsClass && !type.IsAbstract && Helpers.GetConstructor(type, Helpers.EmptyTypes, true) != null)
  1768. #endif
  1769. {
  1770. defaultType = type;
  1771. }
  1772. if (defaultType == null)
  1773. {
  1774. #if WINRT || COREFX
  1775. if (typeInfo.IsInterface)
  1776. #else
  1777. if (type.IsInterface)
  1778. #endif
  1779. {
  1780. #if NO_GENERICS
  1781. defaultType = typeof(ArrayList);
  1782. #else
  1783. Type[] genArgs;
  1784. #if WINRT || COREFX
  1785. if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>)
  1786. && itemType == typeof(System.Collections.Generic.KeyValuePair<,>).MakeGenericType(genArgs = typeInfo.GenericTypeArguments))
  1787. #else
  1788. if (type.IsGenericType && type.GetGenericTypeDefinition() == MapType(typeof(System.Collections.Generic.IDictionary<,>))
  1789. && itemType == MapType(typeof(System.Collections.Generic.KeyValuePair<,>)).MakeGenericType(genArgs = type.GetGenericArguments()))
  1790. #endif
  1791. {
  1792. defaultType = MapType(typeof(System.Collections.Generic.Dictionary<,>)).MakeGenericType(genArgs);
  1793. }
  1794. else
  1795. {
  1796. defaultType = MapType(typeof(System.Collections.Generic.List<>)).MakeGenericType(itemType);
  1797. }
  1798. #endif
  1799. }
  1800. }
  1801. // verify that the default type is appropriate
  1802. if (defaultType != null && !Helpers.IsAssignableFrom(type, defaultType)) { defaultType = null; }
  1803. }
  1804. }
  1805. #if FEAT_IKVM
  1806. internal override Type GetType(string fullName, Assembly context)
  1807. {
  1808. if (context != null)
  1809. {
  1810. Type found = universe.GetType(context, fullName, false);
  1811. if (found != null) return found;
  1812. }
  1813. return universe.GetType(fullName, false);
  1814. }
  1815. #endif
  1816. internal string GetSchemaTypeName(Type effectiveType, DataFormat dataFormat, bool asReference, bool dynamicType, ref bool requiresBclImport)
  1817. {
  1818. Type tmp = Helpers.GetUnderlyingType(effectiveType);
  1819. if (tmp != null) effectiveType = tmp;
  1820. if (effectiveType == this.MapType(typeof(byte[]))) return "bytes";
  1821. WireType wireType;
  1822. IProtoSerializer ser = ValueMember.TryGetCoreSerializer(this, dataFormat, effectiveType, out wireType, false, false, false, false);
  1823. if (ser == null)
  1824. { // model type
  1825. if (asReference || dynamicType)
  1826. {
  1827. requiresBclImport = true;
  1828. return "bcl.NetObjectProxy";
  1829. }
  1830. return this[effectiveType].GetSurrogateOrBaseOrSelf(true).GetSchemaTypeName();
  1831. }
  1832. else
  1833. {
  1834. if (ser is ParseableSerializer)
  1835. {
  1836. if (asReference) requiresBclImport = true;
  1837. return asReference ? "bcl.NetObjectProxy" : "string";
  1838. }
  1839. switch (Helpers.GetTypeCode(effectiveType))
  1840. {
  1841. case ProtoTypeCode.Boolean: return "bool";
  1842. case ProtoTypeCode.Single: return "float";
  1843. case ProtoTypeCode.Double: return "double";
  1844. case ProtoTypeCode.String:
  1845. if (asReference) requiresBclImport = true;
  1846. return asReference ? "bcl.NetObjectProxy" : "string";
  1847. case ProtoTypeCode.Byte:
  1848. case ProtoTypeCode.Char:
  1849. case ProtoTypeCode.UInt16:
  1850. case ProtoTypeCode.UInt32:
  1851. switch (dataFormat)
  1852. {
  1853. case DataFormat.FixedSize: return "fixed32";
  1854. default: return "uint32";
  1855. }
  1856. case ProtoTypeCode.SByte:
  1857. case ProtoTypeCode.Int16:
  1858. case ProtoTypeCode.Int32:
  1859. switch (dataFormat)
  1860. {
  1861. case DataFormat.ZigZag: return "sint32";
  1862. case DataFormat.FixedSize: return "sfixed32";
  1863. default: return "int32";
  1864. }
  1865. case ProtoTypeCode.UInt64:
  1866. switch (dataFormat)
  1867. {
  1868. case DataFormat.FixedSize: return "fixed64";
  1869. default: return "uint64";
  1870. }
  1871. case ProtoTypeCode.Int64:
  1872. switch (dataFormat)
  1873. {
  1874. case DataFormat.ZigZag: return "sint64";
  1875. case DataFormat.FixedSize: return "sfixed64";
  1876. default: return "int64";
  1877. }
  1878. case ProtoTypeCode.DateTime: requiresBclImport = true; return "bcl.DateTime";
  1879. case ProtoTypeCode.TimeSpan: requiresBclImport = true; return "bcl.TimeSpan";
  1880. case ProtoTypeCode.Decimal: requiresBclImport = true; return "bcl.Decimal";
  1881. case ProtoTypeCode.Guid: requiresBclImport = true; return "bcl.Guid";
  1882. default: throw new NotSupportedException("No .proto map found for: " + effectiveType.FullName);
  1883. }
  1884. }
  1885. }
  1886. /// <summary>
  1887. /// Designate a factory-method to use to create instances of any type; note that this only affect types seen by the serializer *after* setting the factory.
  1888. /// </summary>
  1889. public void SetDefaultFactory(MethodInfo methodInfo)
  1890. {
  1891. VerifyFactory(methodInfo, null);
  1892. defaultFactory = methodInfo;
  1893. }
  1894. private MethodInfo defaultFactory;
  1895. internal void VerifyFactory(MethodInfo factory, Type type)
  1896. {
  1897. if (factory != null)
  1898. {
  1899. if (type != null && Helpers.IsValueType(type)) throw new InvalidOperationException();
  1900. if (!factory.IsStatic) throw new ArgumentException("A factory-method must be static", "factory");
  1901. if ((type != null && factory.ReturnType != type) && factory.ReturnType != MapType(typeof(object))) throw new ArgumentException("The factory-method must return object" + (type == null ? "" : (" or " + type.FullName)), "factory");
  1902. if (!CallbackSet.CheckCallbackParameters(this, factory)) throw new ArgumentException("Invalid factory signature in " + factory.DeclaringType.FullName + "." + factory.Name, "factory");
  1903. }
  1904. }
  1905. }
  1906. /// <summary>
  1907. /// Contains the stack-trace of the owning code when a lock-contention scenario is detected
  1908. /// </summary>
  1909. public sealed class LockContentedEventArgs : EventArgs
  1910. {
  1911. private readonly string ownerStackTrace;
  1912. internal LockContentedEventArgs(string ownerStackTrace)
  1913. {
  1914. this.ownerStackTrace = ownerStackTrace;
  1915. }
  1916. /// <summary>
  1917. /// The stack-trace of the code that owned the lock when a lock-contention scenario occurred
  1918. /// </summary>
  1919. public string OwnerStackTrace { get { return ownerStackTrace; } }
  1920. }
  1921. /// <summary>
  1922. /// Event-type that is raised when a lock-contention scenario is detected
  1923. /// </summary>
  1924. public delegate void LockContentedEventHandler(object sender, LockContentedEventArgs args);
  1925. }
  1926. #endif