CompilerContext.cs 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451
  1. #if FEAT_COMPILER
  2. //#define DEBUG_COMPILE
  3. using System;
  4. using System.Threading;
  5. using ProtoBuf.Meta;
  6. using ProtoBuf.Serializers;
  7. #if FEAT_IKVM
  8. using Type = IKVM.Reflection.Type;
  9. using IKVM.Reflection;
  10. using IKVM.Reflection.Emit;
  11. #else
  12. using System.Reflection;
  13. using System.Reflection.Emit;
  14. #endif
  15. namespace ProtoBuf.Compiler
  16. {
  17. internal struct CodeLabel
  18. {
  19. public readonly Label Value;
  20. public readonly int Index;
  21. public CodeLabel(Label value, int index)
  22. {
  23. this.Value = value;
  24. this.Index = index;
  25. }
  26. }
  27. internal sealed class CompilerContext
  28. {
  29. public TypeModel Model { get { return model; } }
  30. #if !(FX11 || FEAT_IKVM)
  31. readonly DynamicMethod method;
  32. static int next;
  33. #endif
  34. internal CodeLabel DefineLabel()
  35. {
  36. CodeLabel result = new CodeLabel(il.DefineLabel(), nextLabel++);
  37. return result;
  38. }
  39. internal void MarkLabel(CodeLabel label)
  40. {
  41. il.MarkLabel(label.Value);
  42. #if DEBUG_COMPILE
  43. Helpers.DebugWriteLine("#: " + label.Index);
  44. #endif
  45. }
  46. #if !(FX11 || FEAT_IKVM)
  47. public static ProtoSerializer BuildSerializer(IProtoSerializer head, TypeModel model)
  48. {
  49. Type type = head.ExpectedType;
  50. try
  51. {
  52. CompilerContext ctx = new CompilerContext(type, true, true, model, typeof(object));
  53. ctx.LoadValue(ctx.InputValue);
  54. ctx.CastFromObject(type);
  55. ctx.WriteNullCheckedTail(type, head, null);
  56. ctx.Emit(OpCodes.Ret);
  57. return (ProtoSerializer)ctx.method.CreateDelegate(
  58. typeof(ProtoSerializer));
  59. }
  60. catch (Exception ex)
  61. {
  62. string name = type.FullName;
  63. if(string.IsNullOrEmpty(name)) name = type.Name;
  64. throw new InvalidOperationException("It was not possible to prepare a serializer for: " + name, ex);
  65. }
  66. }
  67. /*public static ProtoCallback BuildCallback(IProtoTypeSerializer head)
  68. {
  69. Type type = head.ExpectedType;
  70. CompilerContext ctx = new CompilerContext(type, true, true);
  71. using (Local typedVal = new Local(ctx, type))
  72. {
  73. ctx.LoadValue(Local.InputValue);
  74. ctx.CastFromObject(type);
  75. ctx.StoreValue(typedVal);
  76. CodeLabel[] jumpTable = new CodeLabel[4];
  77. for(int i = 0 ; i < jumpTable.Length ; i++) {
  78. jumpTable[i] = ctx.DefineLabel();
  79. }
  80. ctx.LoadReaderWriter();
  81. ctx.Switch(jumpTable);
  82. ctx.Return();
  83. for(int i = 0 ; i < jumpTable.Length ; i++) {
  84. ctx.MarkLabel(jumpTable[i]);
  85. if (head.HasCallbacks((TypeModel.CallbackType)i))
  86. {
  87. head.EmitCallback(ctx, typedVal, (TypeModel.CallbackType)i);
  88. }
  89. ctx.Return();
  90. }
  91. }
  92. ctx.Emit(OpCodes.Ret);
  93. return (ProtoCallback)ctx.method.CreateDelegate(
  94. typeof(ProtoCallback));
  95. }*/
  96. public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model)
  97. {
  98. Type type = head.ExpectedType;
  99. CompilerContext ctx = new CompilerContext(type, false, true, model, typeof(object));
  100. using (Local typedVal = new Local(ctx, type))
  101. {
  102. if (!type.IsValueType)
  103. {
  104. ctx.LoadValue(ctx.InputValue);
  105. ctx.CastFromObject(type);
  106. ctx.StoreValue(typedVal);
  107. }
  108. else
  109. {
  110. ctx.LoadValue(ctx.InputValue);
  111. CodeLabel notNull = ctx.DefineLabel(), endNull = ctx.DefineLabel();
  112. ctx.BranchIfTrue(notNull, true);
  113. ctx.LoadAddress(typedVal, type);
  114. ctx.EmitCtor(type);
  115. ctx.Branch(endNull, true);
  116. ctx.MarkLabel(notNull);
  117. ctx.LoadValue(ctx.InputValue);
  118. ctx.CastFromObject(type);
  119. ctx.StoreValue(typedVal);
  120. ctx.MarkLabel(endNull);
  121. }
  122. head.EmitRead(ctx, typedVal);
  123. if (head.ReturnsValue) {
  124. ctx.StoreValue(typedVal);
  125. }
  126. ctx.LoadValue(typedVal);
  127. ctx.CastToObject(type);
  128. }
  129. ctx.Emit(OpCodes.Ret);
  130. return (ProtoDeserializer)ctx.method.CreateDelegate(
  131. typeof(ProtoDeserializer));
  132. }
  133. #endif
  134. internal void Return()
  135. {
  136. Emit(OpCodes.Ret);
  137. }
  138. static bool IsObject(Type type)
  139. {
  140. #if FEAT_IKVM
  141. return type.FullName == "System.Object";
  142. #else
  143. return type == typeof(object);
  144. #endif
  145. }
  146. internal void CastToObject(Type type)
  147. {
  148. if(IsObject(type))
  149. { }
  150. else if (type.IsValueType)
  151. {
  152. il.Emit(OpCodes.Box, type);
  153. #if DEBUG_COMPILE
  154. Helpers.DebugWriteLine(OpCodes.Box + ": " + type);
  155. #endif
  156. }
  157. else
  158. {
  159. il.Emit(OpCodes.Castclass, MapType(typeof(object)));
  160. #if DEBUG_COMPILE
  161. Helpers.DebugWriteLine(OpCodes.Castclass + ": " + type);
  162. #endif
  163. }
  164. }
  165. internal void CastFromObject(Type type)
  166. {
  167. if (IsObject(type))
  168. { }
  169. else if (type.IsValueType)
  170. {
  171. switch (MetadataVersion)
  172. {
  173. case ILVersion.Net1:
  174. il.Emit(OpCodes.Unbox, type);
  175. il.Emit(OpCodes.Ldobj, type);
  176. #if DEBUG_COMPILE
  177. Helpers.DebugWriteLine(OpCodes.Unbox + ": " + type);
  178. Helpers.DebugWriteLine(OpCodes.Ldobj + ": " + type);
  179. #endif
  180. break;
  181. default:
  182. #if FX11
  183. throw new NotSupportedException();
  184. #else
  185. il.Emit(OpCodes.Unbox_Any, type);
  186. #if DEBUG_COMPILE
  187. Helpers.DebugWriteLine(OpCodes.Unbox_Any + ": " + type);
  188. #endif
  189. break;
  190. #endif
  191. }
  192. }
  193. else
  194. {
  195. il.Emit(OpCodes.Castclass, type);
  196. #if DEBUG_COMPILE
  197. Helpers.DebugWriteLine(OpCodes.Castclass + ": " + type);
  198. #endif
  199. }
  200. }
  201. private readonly bool isStatic;
  202. #if !SILVERLIGHT
  203. private readonly RuntimeTypeModel.SerializerPair[] methodPairs;
  204. internal MethodBuilder GetDedicatedMethod(int metaKey, bool read)
  205. {
  206. if (methodPairs == null) return null;
  207. // but if we *do* have pairs, we demand that we find a match...
  208. for (int i = 0; i < methodPairs.Length; i++ )
  209. {
  210. if (methodPairs[i].MetaKey == metaKey) { return read ? methodPairs[i].Deserialize : methodPairs[i].Serialize; }
  211. }
  212. throw new ArgumentException("Meta-key not found", "metaKey");
  213. }
  214. internal int MapMetaKeyToCompiledKey(int metaKey)
  215. {
  216. if (metaKey < 0 || methodPairs == null) return metaKey; // all meta, or a dummy/wildcard key
  217. for (int i = 0; i < methodPairs.Length; i++)
  218. {
  219. if (methodPairs[i].MetaKey == metaKey) return i;
  220. }
  221. throw new ArgumentException("Key could not be mapped: " + metaKey.ToString(), "metaKey");
  222. }
  223. #else
  224. internal int MapMetaKeyToCompiledKey(int metaKey)
  225. {
  226. return metaKey;
  227. }
  228. #endif
  229. private readonly bool isWriter;
  230. #if FX11 || FEAT_IKVM
  231. internal bool NonPublic { get { return false; } }
  232. #else
  233. private readonly bool nonPublic;
  234. internal bool NonPublic { get { return nonPublic; } }
  235. #endif
  236. private readonly Local inputValue;
  237. public Local InputValue { get { return inputValue; } }
  238. #if !(SILVERLIGHT || PHONE8)
  239. private readonly string assemblyName;
  240. internal CompilerContext(ILGenerator il, bool isStatic, bool isWriter, RuntimeTypeModel.SerializerPair[] methodPairs, TypeModel model, ILVersion metadataVersion, string assemblyName, Type inputType)
  241. {
  242. if (il == null) throw new ArgumentNullException("il");
  243. if (methodPairs == null) throw new ArgumentNullException("methodPairs");
  244. if (model == null) throw new ArgumentNullException("model");
  245. if (Helpers.IsNullOrEmpty(assemblyName)) throw new ArgumentNullException("assemblyName");
  246. this.assemblyName = assemblyName;
  247. this.isStatic = isStatic;
  248. this.methodPairs = methodPairs;
  249. this.il = il;
  250. // nonPublic = false; <== implicit
  251. this.isWriter = isWriter;
  252. this.model = model;
  253. this.metadataVersion = metadataVersion;
  254. if (inputType != null) this.inputValue = new Local(null, inputType);
  255. }
  256. #endif
  257. #if !(FX11 || FEAT_IKVM)
  258. private CompilerContext(Type associatedType, bool isWriter, bool isStatic, TypeModel model, Type inputType)
  259. {
  260. if (model == null) throw new ArgumentNullException("model");
  261. #if FX11
  262. metadataVersion = ILVersion.Net1;
  263. #else
  264. metadataVersion = ILVersion.Net2;
  265. #endif
  266. this.isStatic = isStatic;
  267. this.isWriter = isWriter;
  268. this.model = model;
  269. nonPublic = true;
  270. Type[] paramTypes;
  271. Type returnType;
  272. if (isWriter)
  273. {
  274. returnType = typeof(void);
  275. paramTypes = new Type[] { typeof(object), typeof(ProtoWriter) };
  276. }
  277. else
  278. {
  279. returnType = typeof(object);
  280. paramTypes = new Type[] { typeof(object), typeof(ProtoReader) };
  281. }
  282. int uniqueIdentifier;
  283. #if PLAT_NO_INTERLOCKED
  284. uniqueIdentifier = ++next;
  285. #else
  286. uniqueIdentifier = Interlocked.Increment(ref next);
  287. #endif
  288. method = new DynamicMethod("proto_" + uniqueIdentifier.ToString(), returnType, paramTypes, associatedType.IsInterface ? typeof(object) : associatedType, true);
  289. this.il = method.GetILGenerator();
  290. if (inputType != null) this.inputValue = new Local(null, inputType);
  291. }
  292. #endif
  293. private readonly ILGenerator il;
  294. private void Emit(OpCode opcode)
  295. {
  296. il.Emit(opcode);
  297. #if DEBUG_COMPILE
  298. Helpers.DebugWriteLine(opcode.ToString());
  299. #endif
  300. }
  301. public void LoadValue(string value)
  302. {
  303. if (value == null)
  304. {
  305. LoadNullRef();
  306. }
  307. else
  308. {
  309. il.Emit(OpCodes.Ldstr, value);
  310. #if DEBUG_COMPILE
  311. Helpers.DebugWriteLine(OpCodes.Ldstr + ": " + value);
  312. #endif
  313. }
  314. }
  315. public void LoadValue(float value)
  316. {
  317. il.Emit(OpCodes.Ldc_R4, value);
  318. #if DEBUG_COMPILE
  319. Helpers.DebugWriteLine(OpCodes.Ldc_R4 + ": " + value);
  320. #endif
  321. }
  322. public void LoadValue(double value)
  323. {
  324. il.Emit(OpCodes.Ldc_R8, value);
  325. #if DEBUG_COMPILE
  326. Helpers.DebugWriteLine(OpCodes.Ldc_R8 + ": " + value);
  327. #endif
  328. }
  329. public void LoadValue(long value)
  330. {
  331. il.Emit(OpCodes.Ldc_I8, value);
  332. #if DEBUG_COMPILE
  333. Helpers.DebugWriteLine(OpCodes.Ldc_I8 + ": " + value);
  334. #endif
  335. }
  336. public void LoadValue(int value)
  337. {
  338. switch (value)
  339. {
  340. case 0: Emit(OpCodes.Ldc_I4_0); break;
  341. case 1: Emit(OpCodes.Ldc_I4_1); break;
  342. case 2: Emit(OpCodes.Ldc_I4_2); break;
  343. case 3: Emit(OpCodes.Ldc_I4_3); break;
  344. case 4: Emit(OpCodes.Ldc_I4_4); break;
  345. case 5: Emit(OpCodes.Ldc_I4_5); break;
  346. case 6: Emit(OpCodes.Ldc_I4_6); break;
  347. case 7: Emit(OpCodes.Ldc_I4_7); break;
  348. case 8: Emit(OpCodes.Ldc_I4_8); break;
  349. case -1: Emit(OpCodes.Ldc_I4_M1); break;
  350. default:
  351. if (value >= -128 && value <= 127)
  352. {
  353. il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
  354. #if DEBUG_COMPILE
  355. Helpers.DebugWriteLine(OpCodes.Ldc_I4_S + ": " + value);
  356. #endif
  357. }
  358. else
  359. {
  360. il.Emit(OpCodes.Ldc_I4, value);
  361. #if DEBUG_COMPILE
  362. Helpers.DebugWriteLine(OpCodes.Ldc_I4 + ": " + value);
  363. #endif
  364. }
  365. break;
  366. }
  367. }
  368. MutableList locals = new MutableList();
  369. internal LocalBuilder GetFromPool(Type type)
  370. {
  371. int count = locals.Count;
  372. for (int i = 0; i < count; i++)
  373. {
  374. LocalBuilder item = (LocalBuilder)locals[i];
  375. if (item != null && item.LocalType == type)
  376. {
  377. locals[i] = null; // remove from pool
  378. return item;
  379. }
  380. }
  381. LocalBuilder result = il.DeclareLocal(type);
  382. #if DEBUG_COMPILE
  383. Helpers.DebugWriteLine("$ " + result + ": " + type);
  384. #endif
  385. return result;
  386. }
  387. //
  388. internal void ReleaseToPool(LocalBuilder value)
  389. {
  390. int count = locals.Count;
  391. for (int i = 0; i < count; i++)
  392. {
  393. if (locals[i] == null)
  394. {
  395. locals[i] = value; // released into existing slot
  396. return;
  397. }
  398. }
  399. locals.Add(value); // create a new slot
  400. }
  401. public void LoadReaderWriter()
  402. {
  403. Emit(isStatic ? OpCodes.Ldarg_1 : OpCodes.Ldarg_2);
  404. }
  405. public void StoreValue(Local local)
  406. {
  407. if (local == this.InputValue)
  408. {
  409. byte b = isStatic ? (byte) 0 : (byte)1;
  410. il.Emit(OpCodes.Starg_S, b);
  411. #if DEBUG_COMPILE
  412. Helpers.DebugWriteLine(OpCodes.Starg_S + ": $" + b);
  413. #endif
  414. }
  415. else
  416. {
  417. #if !FX11
  418. switch (local.Value.LocalIndex)
  419. {
  420. case 0: Emit(OpCodes.Stloc_0); break;
  421. case 1: Emit(OpCodes.Stloc_1); break;
  422. case 2: Emit(OpCodes.Stloc_2); break;
  423. case 3: Emit(OpCodes.Stloc_3); break;
  424. default:
  425. #endif
  426. OpCode code = UseShortForm(local) ? OpCodes.Stloc_S : OpCodes.Stloc;
  427. il.Emit(code, local.Value);
  428. #if DEBUG_COMPILE
  429. Helpers.DebugWriteLine(code + ": $" + local.Value);
  430. #endif
  431. #if !FX11
  432. break;
  433. }
  434. #endif
  435. }
  436. }
  437. public void LoadValue(Local local)
  438. {
  439. if (local == null) { /* nothing to do; top of stack */}
  440. else if (local == this.InputValue)
  441. {
  442. Emit(isStatic ? OpCodes.Ldarg_0 : OpCodes.Ldarg_1);
  443. }
  444. else
  445. {
  446. #if !FX11
  447. switch (local.Value.LocalIndex)
  448. {
  449. case 0: Emit(OpCodes.Ldloc_0); break;
  450. case 1: Emit(OpCodes.Ldloc_1); break;
  451. case 2: Emit(OpCodes.Ldloc_2); break;
  452. case 3: Emit(OpCodes.Ldloc_3); break;
  453. default:
  454. #endif
  455. OpCode code = UseShortForm(local) ? OpCodes.Ldloc_S : OpCodes.Ldloc;
  456. il.Emit(code, local.Value);
  457. #if DEBUG_COMPILE
  458. Helpers.DebugWriteLine(code + ": $" + local.Value);
  459. #endif
  460. #if !FX11
  461. break;
  462. }
  463. #endif
  464. }
  465. }
  466. public Local GetLocalWithValue(Type type, Compiler.Local fromValue)
  467. {
  468. if (fromValue != null)
  469. {
  470. if (fromValue.Type == type) return fromValue.AsCopy();
  471. // otherwise, load onto the stack and let the default handling (below) deal with it
  472. LoadValue(fromValue);
  473. if (!type.IsValueType && (fromValue.Type == null || !type.IsAssignableFrom(fromValue.Type)))
  474. { // need to cast
  475. Cast(type);
  476. }
  477. }
  478. // need to store the value from the stack
  479. Local result = new Local(this, type);
  480. StoreValue(result);
  481. return result;
  482. }
  483. internal void EmitBasicRead(string methodName, Type expectedType)
  484. {
  485. MethodInfo method = MapType(typeof(ProtoReader)).GetMethod(
  486. methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  487. if (method == null || method.ReturnType != expectedType
  488. || method.GetParameters().Length != 0) throw new ArgumentException("methodName");
  489. LoadReaderWriter();
  490. EmitCall(method);
  491. }
  492. internal void EmitBasicRead(Type helperType, string methodName, Type expectedType)
  493. {
  494. MethodInfo method = helperType.GetMethod(
  495. methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
  496. if (method == null || method.ReturnType != expectedType
  497. || method.GetParameters().Length != 1) throw new ArgumentException("methodName");
  498. LoadReaderWriter();
  499. EmitCall(method);
  500. }
  501. internal void EmitBasicWrite(string methodName, Compiler.Local fromValue)
  502. {
  503. if (Helpers.IsNullOrEmpty(methodName)) throw new ArgumentNullException("methodName");
  504. LoadValue(fromValue);
  505. LoadReaderWriter();
  506. EmitCall(GetWriterMethod(methodName));
  507. }
  508. private MethodInfo GetWriterMethod(string methodName)
  509. {
  510. Type writerType = MapType(typeof(ProtoWriter));
  511. MethodInfo[] methods = writerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
  512. foreach (MethodInfo method in methods)
  513. {
  514. if(method.Name != methodName) continue;
  515. ParameterInfo[] pis = method.GetParameters();
  516. if (pis.Length == 2 && pis[1].ParameterType == writerType) return method;
  517. }
  518. throw new ArgumentException("No suitable method found for: " + methodName, "methodName");
  519. }
  520. internal void EmitWrite(Type helperType, string methodName, Compiler.Local valueFrom)
  521. {
  522. if (Helpers.IsNullOrEmpty(methodName)) throw new ArgumentNullException("methodName");
  523. MethodInfo method = helperType.GetMethod(
  524. methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
  525. if (method == null || method.ReturnType != MapType(typeof(void))) throw new ArgumentException("methodName");
  526. LoadValue(valueFrom);
  527. LoadReaderWriter();
  528. EmitCall(method);
  529. }
  530. public void EmitCall(MethodInfo method)
  531. {
  532. Helpers.DebugAssert(method != null);
  533. CheckAccessibility(method);
  534. OpCode opcode = (method.IsStatic || method.DeclaringType.IsValueType) ? OpCodes.Call : OpCodes.Callvirt;
  535. il.EmitCall(opcode, method, null);
  536. #if DEBUG_COMPILE
  537. Helpers.DebugWriteLine(opcode + ": " + method + " on " + method.DeclaringType);
  538. #endif
  539. }
  540. /// <summary>
  541. /// Pushes a null reference onto the stack. Note that this should only
  542. /// be used to return a null (or set a variable to null); for null-tests
  543. /// use BranchIfTrue / BranchIfFalse.
  544. /// </summary>
  545. public void LoadNullRef()
  546. {
  547. Emit(OpCodes.Ldnull);
  548. }
  549. private int nextLabel;
  550. internal void WriteNullCheckedTail(Type type, IProtoSerializer tail, Compiler.Local valueFrom)
  551. {
  552. if (type.IsValueType)
  553. {
  554. Type underlyingType = null;
  555. #if !FX11
  556. underlyingType = Helpers.GetUnderlyingType(type);
  557. #endif
  558. if (underlyingType == null)
  559. { // not a nullable T; can invoke directly
  560. tail.EmitWrite(this, valueFrom);
  561. }
  562. else
  563. { // nullable T; check HasValue
  564. using (Compiler.Local valOrNull = GetLocalWithValue(type, valueFrom))
  565. {
  566. LoadAddress(valOrNull, type);
  567. LoadValue(type.GetProperty("HasValue"));
  568. CodeLabel @end = DefineLabel();
  569. BranchIfFalse(@end, false);
  570. LoadAddress(valOrNull, type);
  571. EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
  572. tail.EmitWrite(this, null);
  573. MarkLabel(@end);
  574. }
  575. }
  576. }
  577. else
  578. { // ref-type; do a null-check
  579. LoadValue(valueFrom);
  580. CopyValue();
  581. CodeLabel hasVal = DefineLabel(), @end = DefineLabel();
  582. BranchIfTrue(hasVal, true);
  583. DiscardValue();
  584. Branch(@end, false);
  585. MarkLabel(hasVal);
  586. tail.EmitWrite(this, null);
  587. MarkLabel(@end);
  588. }
  589. }
  590. internal void ReadNullCheckedTail(Type type, IProtoSerializer tail, Compiler.Local valueFrom)
  591. {
  592. #if !FX11
  593. Type underlyingType;
  594. if (type.IsValueType && (underlyingType = Helpers.GetUnderlyingType(type)) != null)
  595. {
  596. if(tail.RequiresOldValue)
  597. {
  598. // we expect the input value to be in valueFrom; need to unpack it from T?
  599. using (Local loc = GetLocalWithValue(type, valueFrom))
  600. {
  601. LoadAddress(loc, type);
  602. EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
  603. }
  604. }
  605. else
  606. {
  607. Helpers.DebugAssert(valueFrom == null); // not expecting a valueFrom in this case
  608. }
  609. tail.EmitRead(this, null); // either unwrapped on the stack or not provided
  610. if (tail.ReturnsValue)
  611. {
  612. // now re-wrap the value
  613. EmitCtor(type, underlyingType);
  614. }
  615. return;
  616. }
  617. #endif
  618. // either a ref-type of a non-nullable struct; treat "as is", even if null
  619. // (the type-serializer will handle the null case; it needs to allow null
  620. // inputs to perform the correct type of subclass creation)
  621. tail.EmitRead(this, valueFrom);
  622. }
  623. public void EmitCtor(Type type)
  624. {
  625. EmitCtor(type, Helpers.EmptyTypes);
  626. }
  627. public void EmitCtor(ConstructorInfo ctor)
  628. {
  629. if (ctor == null) throw new ArgumentNullException("ctor");
  630. CheckAccessibility(ctor);
  631. il.Emit(OpCodes.Newobj, ctor);
  632. #if DEBUG_COMPILE
  633. Helpers.DebugWriteLine(OpCodes.Newobj + ": " + ctor.DeclaringType);
  634. #endif
  635. }
  636. public void EmitCtor(Type type, params Type[] parameterTypes)
  637. {
  638. Helpers.DebugAssert(type != null);
  639. Helpers.DebugAssert(parameterTypes != null);
  640. if (type.IsValueType && parameterTypes.Length == 0)
  641. {
  642. il.Emit(OpCodes.Initobj, type);
  643. #if DEBUG_COMPILE
  644. Helpers.DebugWriteLine(OpCodes.Initobj + ": " + type);
  645. #endif
  646. }
  647. else
  648. {
  649. ConstructorInfo ctor = Helpers.GetConstructor(type, parameterTypes, true);
  650. if (ctor == null) throw new InvalidOperationException("No suitable constructor found for " + type.FullName);
  651. EmitCtor(ctor);
  652. }
  653. }
  654. #if !(PHONE8 || SILVERLIGHT || FX11)
  655. BasicList knownTrustedAssemblies, knownUntrustedAssemblies;
  656. #endif
  657. bool InternalsVisible(Assembly assembly)
  658. {
  659. #if PHONE8 || SILVERLIGHT || FX11
  660. return false;
  661. #else
  662. if (Helpers.IsNullOrEmpty(assemblyName)) return false;
  663. if (knownTrustedAssemblies != null)
  664. {
  665. if (knownTrustedAssemblies.IndexOfReference(assembly) >= 0)
  666. {
  667. return true;
  668. }
  669. }
  670. if (knownUntrustedAssemblies != null)
  671. {
  672. if (knownUntrustedAssemblies.IndexOfReference(assembly) >= 0)
  673. {
  674. return false;
  675. }
  676. }
  677. bool isTrusted = false;
  678. Type attributeType = MapType(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute));
  679. if(attributeType == null) return false;
  680. #if FEAT_IKVM
  681. foreach (CustomAttributeData attrib in assembly.__GetCustomAttributes(attributeType, false))
  682. {
  683. if (attrib.ConstructorArguments.Count == 1)
  684. {
  685. string privelegedAssembly = attrib.ConstructorArguments[0].Value as string;
  686. if (privelegedAssembly == assemblyName || privelegedAssembly.StartsWith(assemblyName + ","))
  687. {
  688. isTrusted = true;
  689. break;
  690. }
  691. }
  692. }
  693. #else
  694. foreach (System.Runtime.CompilerServices.InternalsVisibleToAttribute attrib in assembly.GetCustomAttributes(attributeType, false))
  695. {
  696. if (attrib.AssemblyName == assemblyName || attrib.AssemblyName.StartsWith(assemblyName + ","))
  697. {
  698. isTrusted = true;
  699. break;
  700. }
  701. }
  702. #endif
  703. if (isTrusted)
  704. {
  705. if (knownTrustedAssemblies == null) knownTrustedAssemblies = new BasicList();
  706. knownTrustedAssemblies.Add(assembly);
  707. }
  708. else
  709. {
  710. if (knownUntrustedAssemblies == null) knownUntrustedAssemblies = new BasicList();
  711. knownUntrustedAssemblies.Add(assembly);
  712. }
  713. return isTrusted;
  714. #endif
  715. }
  716. internal void CheckAccessibility(MemberInfo member)
  717. {
  718. if (member == null)
  719. {
  720. throw new ArgumentNullException("member");
  721. }
  722. MemberTypes memberType = member.MemberType;
  723. Type type;
  724. if (!NonPublic)
  725. {
  726. bool isPublic;
  727. switch (memberType)
  728. {
  729. case MemberTypes.TypeInfo:
  730. // top-level type
  731. type = (Type)member;
  732. isPublic = type.IsPublic || InternalsVisible(type.Assembly);
  733. break;
  734. case MemberTypes.NestedType:
  735. type = (Type)member;
  736. do
  737. {
  738. isPublic = type.IsNestedPublic || type.IsPublic || ((type.DeclaringType == null || type.IsNestedAssembly || type.IsNestedFamORAssem) && InternalsVisible(type.Assembly));
  739. } while (isPublic && (type = type.DeclaringType) != null); // ^^^ !type.IsNested, but not all runtimes have that
  740. break;
  741. case MemberTypes.Field:
  742. FieldInfo field = ((FieldInfo)member);
  743. isPublic = field.IsPublic || ((field.IsAssembly || field.IsFamilyOrAssembly) && InternalsVisible(field.DeclaringType.Assembly));
  744. break;
  745. case MemberTypes.Constructor:
  746. ConstructorInfo ctor = ((ConstructorInfo)member);
  747. isPublic = ctor.IsPublic || ((ctor.IsAssembly || ctor.IsFamilyOrAssembly) && InternalsVisible(ctor.DeclaringType.Assembly));
  748. break;
  749. case MemberTypes.Method:
  750. MethodInfo method = ((MethodInfo)member);
  751. isPublic = method.IsPublic || ((method.IsAssembly || method.IsFamilyOrAssembly) && InternalsVisible(method.DeclaringType.Assembly));
  752. if (!isPublic)
  753. {
  754. // allow calls to TypeModel protected methods, and methods we are in the process of creating
  755. if(
  756. #if !SILVERLIGHT
  757. member is MethodBuilder ||
  758. #endif
  759. member.DeclaringType == MapType(typeof(TypeModel))) isPublic = true;
  760. }
  761. break;
  762. case MemberTypes.Property:
  763. isPublic = true; // defer to get/set
  764. break;
  765. default:
  766. throw new NotSupportedException(memberType.ToString());
  767. }
  768. if (!isPublic)
  769. {
  770. switch (memberType)
  771. {
  772. case MemberTypes.TypeInfo:
  773. case MemberTypes.NestedType:
  774. throw new InvalidOperationException("Non-public type cannot be used with full dll compilation: " +
  775. ((Type)member).FullName);
  776. default:
  777. throw new InvalidOperationException("Non-public member cannot be used with full dll compilation: " +
  778. member.DeclaringType.FullName + "." + member.Name);
  779. }
  780. }
  781. }
  782. }
  783. public void LoadValue(FieldInfo field)
  784. {
  785. CheckAccessibility(field);
  786. OpCode code = field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld;
  787. il.Emit(code, field);
  788. #if DEBUG_COMPILE
  789. Helpers.DebugWriteLine(code + ": " + field + " on " + field.DeclaringType);
  790. #endif
  791. }
  792. #if FEAT_IKVM
  793. public void StoreValue(System.Reflection.FieldInfo field)
  794. {
  795. StoreValue(MapType(field.DeclaringType).GetField(field.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance));
  796. }
  797. public void StoreValue(System.Reflection.PropertyInfo property)
  798. {
  799. StoreValue(MapType(property.DeclaringType).GetProperty(property.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance));
  800. }
  801. public void LoadValue(System.Reflection.FieldInfo field)
  802. {
  803. LoadValue(MapType(field.DeclaringType).GetField(field.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance));
  804. }
  805. public void LoadValue(System.Reflection.PropertyInfo property)
  806. {
  807. LoadValue(MapType(property.DeclaringType).GetProperty(property.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance));
  808. }
  809. #endif
  810. public void StoreValue(FieldInfo field)
  811. {
  812. CheckAccessibility(field);
  813. OpCode code = field.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld;
  814. il.Emit(code, field);
  815. #if DEBUG_COMPILE
  816. Helpers.DebugWriteLine(code + ": " + field + " on " + field.DeclaringType);
  817. #endif
  818. }
  819. public void LoadValue(PropertyInfo property)
  820. {
  821. CheckAccessibility(property);
  822. EmitCall(Helpers.GetGetMethod(property, true, true));
  823. }
  824. public void StoreValue(PropertyInfo property)
  825. {
  826. CheckAccessibility(property);
  827. EmitCall(Helpers.GetSetMethod(property, true, true));
  828. }
  829. //internal void EmitInstance()
  830. //{
  831. // if (isStatic) throw new InvalidOperationException();
  832. // Emit(OpCodes.Ldarg_0);
  833. //}
  834. internal static void LoadValue(ILGenerator il, int value)
  835. {
  836. switch (value)
  837. {
  838. case 0: il.Emit(OpCodes.Ldc_I4_0); break;
  839. case 1: il.Emit(OpCodes.Ldc_I4_1); break;
  840. case 2: il.Emit(OpCodes.Ldc_I4_2); break;
  841. case 3: il.Emit(OpCodes.Ldc_I4_3); break;
  842. case 4: il.Emit(OpCodes.Ldc_I4_4); break;
  843. case 5: il.Emit(OpCodes.Ldc_I4_5); break;
  844. case 6: il.Emit(OpCodes.Ldc_I4_6); break;
  845. case 7: il.Emit(OpCodes.Ldc_I4_7); break;
  846. case 8: il.Emit(OpCodes.Ldc_I4_8); break;
  847. case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
  848. default: il.Emit(OpCodes.Ldc_I4, value); break;
  849. }
  850. }
  851. private bool UseShortForm(Local local)
  852. {
  853. #if FX11
  854. return locals.Count < 256;
  855. #else
  856. return local.Value.LocalIndex < 256;
  857. #endif
  858. }
  859. #if FEAT_IKVM
  860. internal void LoadAddress(Local local, System.Type type)
  861. {
  862. LoadAddress(local, MapType(type));
  863. }
  864. #endif
  865. internal void LoadAddress(Local local, Type type)
  866. {
  867. if (type.IsValueType)
  868. {
  869. if (local == null)
  870. {
  871. throw new InvalidOperationException("Cannot load the address of a struct at the head of the stack");
  872. }
  873. if (local == this.InputValue)
  874. {
  875. il.Emit(OpCodes.Ldarga_S, (isStatic ? (byte)0 : (byte)1));
  876. #if DEBUG_COMPILE
  877. Helpers.DebugWriteLine(OpCodes.Ldarga_S + ": $" + (isStatic ? 0 : 1));
  878. #endif
  879. }
  880. else
  881. {
  882. OpCode code = UseShortForm(local) ? OpCodes.Ldloca_S : OpCodes.Ldloca;
  883. il.Emit(code, local.Value);
  884. #if DEBUG_COMPILE
  885. Helpers.DebugWriteLine(code + ": $" + local.Value);
  886. #endif
  887. }
  888. }
  889. else
  890. { // reference-type; already *is* the address; just load it
  891. LoadValue(local);
  892. }
  893. }
  894. internal void Branch(CodeLabel label, bool @short)
  895. {
  896. OpCode code = @short ? OpCodes.Br_S : OpCodes.Br;
  897. il.Emit(code, label.Value);
  898. #if DEBUG_COMPILE
  899. Helpers.DebugWriteLine(code + ": " + label.Index);
  900. #endif
  901. }
  902. internal void BranchIfFalse(CodeLabel label, bool @short)
  903. {
  904. OpCode code = @short ? OpCodes.Brfalse_S : OpCodes.Brfalse;
  905. il.Emit(code, label.Value);
  906. #if DEBUG_COMPILE
  907. Helpers.DebugWriteLine(code + ": " + label.Index);
  908. #endif
  909. }
  910. internal void BranchIfTrue(CodeLabel label, bool @short)
  911. {
  912. OpCode code = @short ? OpCodes.Brtrue_S : OpCodes.Brtrue;
  913. il.Emit(code, label.Value);
  914. #if DEBUG_COMPILE
  915. Helpers.DebugWriteLine(code + ": " + label.Index);
  916. #endif
  917. }
  918. internal void BranchIfEqual(CodeLabel label, bool @short)
  919. {
  920. OpCode code = @short ? OpCodes.Beq_S : OpCodes.Beq;
  921. il.Emit(code, label.Value);
  922. #if DEBUG_COMPILE
  923. Helpers.DebugWriteLine(code + ": " + label.Index);
  924. #endif
  925. }
  926. //internal void TestEqual()
  927. //{
  928. // Emit(OpCodes.Ceq);
  929. //}
  930. internal void CopyValue()
  931. {
  932. Emit(OpCodes.Dup);
  933. }
  934. internal void BranchIfGreater(CodeLabel label, bool @short)
  935. {
  936. OpCode code = @short ? OpCodes.Bgt_S : OpCodes.Bgt;
  937. il.Emit(code, label.Value);
  938. #if DEBUG_COMPILE
  939. Helpers.DebugWriteLine(code + ": " + label.Index);
  940. #endif
  941. }
  942. internal void BranchIfLess(CodeLabel label, bool @short)
  943. {
  944. OpCode code = @short ? OpCodes.Blt_S : OpCodes.Blt;
  945. il.Emit(code, label.Value);
  946. #if DEBUG_COMPILE
  947. Helpers.DebugWriteLine(code + ": " + label.Index);
  948. #endif
  949. }
  950. internal void DiscardValue()
  951. {
  952. Emit(OpCodes.Pop);
  953. }
  954. public void Subtract()
  955. {
  956. Emit(OpCodes.Sub);
  957. }
  958. public void Switch(CodeLabel[] jumpTable)
  959. {
  960. const int MAX_JUMPS = 128;
  961. if (jumpTable.Length <= MAX_JUMPS)
  962. {
  963. // simple case
  964. Label[] labels = new Label[jumpTable.Length];
  965. for (int i = 0; i < labels.Length; i++)
  966. {
  967. labels[i] = jumpTable[i].Value;
  968. }
  969. #if DEBUG_COMPILE
  970. Helpers.DebugWriteLine(OpCodes.Switch.ToString());
  971. #endif
  972. il.Emit(OpCodes.Switch, labels);
  973. }
  974. else
  975. {
  976. // too many to jump easily (especially on Android) - need to split up (note: uses a local pulled from the stack)
  977. using (Local val = GetLocalWithValue(MapType(typeof(int)), null))
  978. {
  979. int count = jumpTable.Length, offset = 0;
  980. int blockCount = count / MAX_JUMPS;
  981. if ((count % MAX_JUMPS) != 0) blockCount++;
  982. Label[] blockLabels = new Label[blockCount];
  983. for (int i = 0; i < blockCount; i++)
  984. {
  985. blockLabels[i] = il.DefineLabel();
  986. }
  987. CodeLabel endOfSwitch = DefineLabel();
  988. LoadValue(val);
  989. LoadValue(MAX_JUMPS);
  990. Emit(OpCodes.Div);
  991. #if DEBUG_COMPILE
  992. Helpers.DebugWriteLine(OpCodes.Switch.ToString());
  993. #endif
  994. il.Emit(OpCodes.Switch, blockLabels);
  995. Branch(endOfSwitch, false);
  996. Label[] innerLabels = new Label[MAX_JUMPS];
  997. for (int blockIndex = 0; blockIndex < blockCount; blockIndex++)
  998. {
  999. il.MarkLabel(blockLabels[blockIndex]);
  1000. int itemsThisBlock = Math.Min(MAX_JUMPS, count);
  1001. count -= itemsThisBlock;
  1002. if (innerLabels.Length != itemsThisBlock) innerLabels = new Label[itemsThisBlock];
  1003. int subtract = offset;
  1004. for (int j = 0; j < itemsThisBlock; j++)
  1005. {
  1006. innerLabels[j] = jumpTable[offset++].Value;
  1007. }
  1008. LoadValue(val);
  1009. if (subtract != 0) // switches are always zero-based
  1010. {
  1011. LoadValue(subtract);
  1012. Emit(OpCodes.Sub);
  1013. }
  1014. #if DEBUG_COMPILE
  1015. Helpers.DebugWriteLine(OpCodes.Switch.ToString());
  1016. #endif
  1017. il.Emit(OpCodes.Switch, innerLabels);
  1018. if (count != 0)
  1019. { // force default to the very bottom
  1020. Branch(endOfSwitch, false);
  1021. }
  1022. }
  1023. Helpers.DebugAssert(count == 0, "Should use exactly all switch items");
  1024. MarkLabel(endOfSwitch);
  1025. }
  1026. }
  1027. }
  1028. internal void EndFinally()
  1029. {
  1030. il.EndExceptionBlock();
  1031. #if DEBUG_COMPILE
  1032. Helpers.DebugWriteLine("EndExceptionBlock");
  1033. #endif
  1034. }
  1035. internal void BeginFinally()
  1036. {
  1037. il.BeginFinallyBlock();
  1038. #if DEBUG_COMPILE
  1039. Helpers.DebugWriteLine("BeginFinallyBlock");
  1040. #endif
  1041. }
  1042. internal void EndTry(CodeLabel label, bool @short)
  1043. {
  1044. OpCode code = @short ? OpCodes.Leave_S : OpCodes.Leave;
  1045. il.Emit(code, label.Value);
  1046. #if DEBUG_COMPILE
  1047. Helpers.DebugWriteLine(code + ": " + label.Index);
  1048. #endif
  1049. }
  1050. internal CodeLabel BeginTry()
  1051. {
  1052. CodeLabel label = new CodeLabel(il.BeginExceptionBlock(), nextLabel++);
  1053. #if DEBUG_COMPILE
  1054. Helpers.DebugWriteLine("BeginExceptionBlock: " + label.Index);
  1055. #endif
  1056. return label;
  1057. }
  1058. #if !FX11
  1059. internal void Constrain(Type type)
  1060. {
  1061. il.Emit(OpCodes.Constrained, type);
  1062. #if DEBUG_COMPILE
  1063. Helpers.DebugWriteLine(OpCodes.Constrained + ": " + type);
  1064. #endif
  1065. }
  1066. #endif
  1067. internal void TryCast(Type type)
  1068. {
  1069. il.Emit(OpCodes.Isinst, type);
  1070. #if DEBUG_COMPILE
  1071. Helpers.DebugWriteLine(OpCodes.Isinst + ": " + type);
  1072. #endif
  1073. }
  1074. internal void Cast(Type type)
  1075. {
  1076. il.Emit(OpCodes.Castclass, type);
  1077. #if DEBUG_COMPILE
  1078. Helpers.DebugWriteLine(OpCodes.Castclass + ": " + type);
  1079. #endif
  1080. }
  1081. public IDisposable Using(Local local)
  1082. {
  1083. return new UsingBlock(this, local);
  1084. }
  1085. private sealed class UsingBlock : IDisposable{
  1086. private Local local;
  1087. CompilerContext ctx;
  1088. CodeLabel label;
  1089. /// <summary>
  1090. /// Creates a new "using" block (equivalent) around a variable;
  1091. /// the variable must exist, and note that (unlike in C#) it is
  1092. /// the variables *final* value that gets disposed. If you need
  1093. /// *original* disposal, copy your variable first.
  1094. ///
  1095. /// It is the callers responsibility to ensure that the variable's
  1096. /// scope fully-encapsulates the "using"; if not, the variable
  1097. /// may be re-used (and thus re-assigned) unexpectedly.
  1098. /// </summary>
  1099. public UsingBlock(CompilerContext ctx, Local local)
  1100. {
  1101. if (ctx == null) throw new ArgumentNullException("ctx");
  1102. if (local == null) throw new ArgumentNullException("local");
  1103. Type type = local.Type;
  1104. // check if **never** disposable
  1105. if ((type.IsValueType || type.IsSealed) &&
  1106. !ctx.MapType(typeof(IDisposable)).IsAssignableFrom(type))
  1107. {
  1108. return; // nothing to do! easiest "using" block ever
  1109. // (note that C# wouldn't allow this as a "using" block,
  1110. // but we'll be generous and simply not do anything)
  1111. }
  1112. this.local = local;
  1113. this.ctx = ctx;
  1114. label = ctx.BeginTry();
  1115. }
  1116. public void Dispose()
  1117. {
  1118. if (local == null || ctx == null) return;
  1119. ctx.EndTry(label, false);
  1120. ctx.BeginFinally();
  1121. Type disposableType = ctx.MapType(typeof (IDisposable));
  1122. MethodInfo dispose = disposableType.GetMethod("Dispose");
  1123. Type type = local.Type;
  1124. // remember that we've already (in the .ctor) excluded the case
  1125. // where it *cannot* be disposable
  1126. if (type.IsValueType)
  1127. {
  1128. ctx.LoadAddress(local, type);
  1129. switch (ctx.MetadataVersion)
  1130. {
  1131. case ILVersion.Net1:
  1132. ctx.LoadValue(local);
  1133. ctx.CastToObject(type);
  1134. break;
  1135. default:
  1136. #if FX11
  1137. throw new NotSupportedException();
  1138. #else
  1139. ctx.Constrain(type);
  1140. break;
  1141. #endif
  1142. }
  1143. ctx.EmitCall(dispose);
  1144. }
  1145. else
  1146. {
  1147. Compiler.CodeLabel @null = ctx.DefineLabel();
  1148. if (disposableType.IsAssignableFrom(type))
  1149. { // *known* to be IDisposable; just needs a null-check
  1150. ctx.LoadValue(local);
  1151. ctx.BranchIfFalse(@null, true);
  1152. ctx.LoadAddress(local, type);
  1153. }
  1154. else
  1155. { // *could* be IDisposable; test via "as"
  1156. using (Compiler.Local disp = new Compiler.Local(ctx, disposableType))
  1157. {
  1158. ctx.LoadValue(local);
  1159. ctx.TryCast(disposableType);
  1160. ctx.CopyValue();
  1161. ctx.StoreValue(disp);
  1162. ctx.BranchIfFalse(@null, true);
  1163. ctx.LoadAddress(disp, disposableType);
  1164. }
  1165. }
  1166. ctx.EmitCall(dispose);
  1167. ctx.MarkLabel(@null);
  1168. }
  1169. ctx.EndFinally();
  1170. this.local = null;
  1171. this.ctx = null;
  1172. label = new CodeLabel(); // default
  1173. }
  1174. }
  1175. internal void Add()
  1176. {
  1177. Emit(OpCodes.Add);
  1178. }
  1179. internal void LoadLength(Local arr, bool zeroIfNull)
  1180. {
  1181. Helpers.DebugAssert(arr.Type.IsArray && arr.Type.GetArrayRank() == 1);
  1182. if (zeroIfNull)
  1183. {
  1184. Compiler.CodeLabel notNull = DefineLabel(), done = DefineLabel();
  1185. LoadValue(arr);
  1186. CopyValue(); // optimised for non-null case
  1187. BranchIfTrue(notNull, true);
  1188. DiscardValue();
  1189. LoadValue(0);
  1190. Branch(done, true);
  1191. MarkLabel(notNull);
  1192. Emit(OpCodes.Ldlen);
  1193. Emit(OpCodes.Conv_I4);
  1194. MarkLabel(done);
  1195. }
  1196. else
  1197. {
  1198. LoadValue(arr);
  1199. Emit(OpCodes.Ldlen);
  1200. Emit(OpCodes.Conv_I4);
  1201. }
  1202. }
  1203. internal void CreateArray(Type elementType, Local length)
  1204. {
  1205. LoadValue(length);
  1206. il.Emit(OpCodes.Newarr, elementType);
  1207. #if DEBUG_COMPILE
  1208. Helpers.DebugWriteLine(OpCodes.Newarr + ": " + elementType);
  1209. #endif
  1210. }
  1211. internal void LoadArrayValue(Local arr, Local i)
  1212. {
  1213. Type type = arr.Type;
  1214. Helpers.DebugAssert(type.IsArray && arr.Type.GetArrayRank() == 1);
  1215. type = type.GetElementType();
  1216. Helpers.DebugAssert(type != null, "Not an array: " + arr.Type.FullName);
  1217. LoadValue(arr);
  1218. LoadValue(i);
  1219. switch(Helpers.GetTypeCode(type)) {
  1220. case ProtoTypeCode.SByte: Emit(OpCodes.Ldelem_I1); break;
  1221. case ProtoTypeCode.Int16: Emit(OpCodes.Ldelem_I2); break;
  1222. case ProtoTypeCode.Int32: Emit(OpCodes.Ldelem_I4); break;
  1223. case ProtoTypeCode.Int64: Emit(OpCodes.Ldelem_I8); break;
  1224. case ProtoTypeCode.Byte: Emit(OpCodes.Ldelem_U1); break;
  1225. case ProtoTypeCode.UInt16: Emit(OpCodes.Ldelem_U2); break;
  1226. case ProtoTypeCode.UInt32: Emit(OpCodes.Ldelem_U4); break;
  1227. case ProtoTypeCode.UInt64: Emit(OpCodes.Ldelem_I8); break; // odd, but this is what C# does...
  1228. case ProtoTypeCode.Single: Emit(OpCodes.Ldelem_R4); break;
  1229. case ProtoTypeCode.Double: Emit(OpCodes.Ldelem_R8); break;
  1230. default:
  1231. if (type.IsValueType)
  1232. {
  1233. il.Emit(OpCodes.Ldelema, type);
  1234. il.Emit(OpCodes.Ldobj, type);
  1235. #if DEBUG_COMPILE
  1236. Helpers.DebugWriteLine(OpCodes.Ldelema + ": " + type);
  1237. Helpers.DebugWriteLine(OpCodes.Ldobj + ": " + type);
  1238. #endif
  1239. }
  1240. else
  1241. {
  1242. Emit(OpCodes.Ldelem_Ref);
  1243. }
  1244. break;
  1245. }
  1246. }
  1247. internal void LoadValue(Type type)
  1248. {
  1249. il.Emit(OpCodes.Ldtoken, type);
  1250. #if DEBUG_COMPILE
  1251. Helpers.DebugWriteLine(OpCodes.Ldtoken + ": " + type);
  1252. #endif
  1253. EmitCall(MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"));
  1254. }
  1255. internal void ConvertToInt32(ProtoTypeCode typeCode, bool uint32Overflow)
  1256. {
  1257. switch (typeCode)
  1258. {
  1259. case ProtoTypeCode.Byte:
  1260. case ProtoTypeCode.SByte:
  1261. case ProtoTypeCode.Int16:
  1262. case ProtoTypeCode.UInt16:
  1263. Emit(OpCodes.Conv_I4);
  1264. break;
  1265. case ProtoTypeCode.Int32:
  1266. break;
  1267. case ProtoTypeCode.Int64:
  1268. Emit(OpCodes.Conv_Ovf_I4);
  1269. break;
  1270. case ProtoTypeCode.UInt32:
  1271. Emit(uint32Overflow ? OpCodes.Conv_Ovf_I4_Un : OpCodes.Conv_Ovf_I4);
  1272. break;
  1273. case ProtoTypeCode.UInt64:
  1274. Emit(OpCodes.Conv_Ovf_I4_Un);
  1275. break;
  1276. default:
  1277. throw new InvalidOperationException("ConvertToInt32 not implemented for: " + typeCode.ToString());
  1278. }
  1279. }
  1280. internal void ConvertFromInt32(ProtoTypeCode typeCode, bool uint32Overflow)
  1281. {
  1282. switch (typeCode)
  1283. {
  1284. case ProtoTypeCode.SByte: Emit(OpCodes.Conv_Ovf_I1); break;
  1285. case ProtoTypeCode.Byte: Emit(OpCodes.Conv_Ovf_U1); break;
  1286. case ProtoTypeCode.Int16: Emit(OpCodes.Conv_Ovf_I2); break;
  1287. case ProtoTypeCode.UInt16: Emit(OpCodes.Conv_Ovf_U2); break;
  1288. case ProtoTypeCode.Int32: break;
  1289. case ProtoTypeCode.UInt32: Emit(uint32Overflow ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4); break;
  1290. case ProtoTypeCode.Int64: Emit(OpCodes.Conv_I8); break;
  1291. case ProtoTypeCode.UInt64: Emit(OpCodes.Conv_U8); break;
  1292. default: throw new InvalidOperationException();
  1293. }
  1294. }
  1295. internal void LoadValue(decimal value)
  1296. {
  1297. if (value == 0M)
  1298. {
  1299. LoadValue(typeof(decimal).GetField("Zero"));
  1300. }
  1301. else
  1302. {
  1303. int[] bits = decimal.GetBits(value);
  1304. LoadValue(bits[0]); // lo
  1305. LoadValue(bits[1]); // mid
  1306. LoadValue(bits[2]); // hi
  1307. LoadValue((int)(((uint)bits[3]) >> 31)); // isNegative (bool, but int for CLI purposes)
  1308. LoadValue((bits[3] >> 16) & 0xFF); // scale (byte, but int for CLI purposes)
  1309. EmitCtor(MapType(typeof(decimal)), new Type[] { MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(int)), MapType(typeof(bool)), MapType(typeof(byte)) });
  1310. }
  1311. }
  1312. internal void LoadValue(Guid value)
  1313. {
  1314. if (value == Guid.Empty)
  1315. {
  1316. LoadValue(typeof(Guid).GetField("Empty"));
  1317. }
  1318. else
  1319. { // note we're adding lots of shorts/bytes here - but at the IL level they are I4, not I1/I2 (which barely exist)
  1320. byte[] bytes = value.ToByteArray();
  1321. int i = (bytes[0]) | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
  1322. LoadValue(i);
  1323. short s = (short)((bytes[4]) | (bytes[5] << 8));
  1324. LoadValue(s);
  1325. s = (short)((bytes[6]) | (bytes[7] << 8));
  1326. LoadValue(s);
  1327. for (i = 8; i <= 15; i++)
  1328. {
  1329. LoadValue(bytes[i]);
  1330. }
  1331. EmitCtor(MapType(typeof(Guid)), new Type[] { MapType(typeof(int)), MapType(typeof(short)), MapType(typeof(short)),
  1332. MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)), MapType(typeof(byte)) });
  1333. }
  1334. }
  1335. //internal void LoadValue(bool value)
  1336. //{
  1337. // Emit(value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
  1338. //}
  1339. internal void LoadSerializationContext()
  1340. {
  1341. LoadReaderWriter();
  1342. LoadValue((isWriter ? typeof(ProtoWriter) : typeof(ProtoReader)).GetProperty("Context"));
  1343. }
  1344. private readonly TypeModel model;
  1345. internal Type MapType(System.Type type)
  1346. {
  1347. return model.MapType(type);
  1348. }
  1349. private readonly ILVersion metadataVersion;
  1350. public ILVersion MetadataVersion { get { return metadataVersion; } }
  1351. public enum ILVersion
  1352. {
  1353. Net1, Net2
  1354. }
  1355. internal bool AllowInternal(PropertyInfo property)
  1356. {
  1357. return NonPublic ? true : InternalsVisible(property.DeclaringType.Assembly);
  1358. }
  1359. }
  1360. }
  1361. #endif