CompilerContext.cs 55 KB

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