PageRenderTime 63ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/referencesource/System.Data.Linq/SqlClient/Reader/ObjectReaderCompiler.cs

http://github.com/mono/mono
C# | 3005 lines | 2536 code | 304 blank | 165 comment | 535 complexity | 6d4fc14b897c24fa9b7101285428e674 MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Linq.Expressions;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Reflection.Emit;
  9. using System.Data;
  10. using System.Data.Common;
  11. using System.Data.Linq;
  12. using System.Data.Linq.Mapping;
  13. using System.Data.Linq.Provider;
  14. using System.Runtime.CompilerServices;
  15. using System.Runtime.Versioning;
  16. using System.Security;
  17. using System.Security.Permissions;
  18. using System.Threading;
  19. namespace System.Data.Linq.SqlClient {
  20. using System.Data.Linq.SqlClient.Implementation;
  21. using System.Diagnostics.CodeAnalysis;
  22. using System.Diagnostics;
  23. #if ILGEN || DEBUG
  24. namespace Implementation {
  25. /// <summary>
  26. /// Internal interface type defining the operations dynamic materialization functions need to perform when
  27. /// materializing objects, without reflecting/invoking privates.
  28. /// <remarks>This interface is required because our anonymously hosted materialization delegates
  29. /// run under partial trust and cannot access non-public members of types in the fully trusted
  30. /// framework assemblies.</remarks>
  31. /// </summary>
  32. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Materializer", Justification = "Spelling is correct.")]
  33. [SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors", Justification = "Unknown reason.")]
  34. public abstract class ObjectMaterializer<TDataReader> where TDataReader : DbDataReader {
  35. // These are public fields rather than properties for access speed
  36. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  37. public int[] Ordinals;
  38. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Globals", Justification = "Spelling is correct.")]
  39. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  40. public object[] Globals;
  41. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  42. public object[] Locals;
  43. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  44. public object[] Arguments;
  45. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  46. public TDataReader DataReader;
  47. [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
  48. public DbDataReader BufferReader;
  49. public ObjectMaterializer() {
  50. DataReader = default(TDataReader);
  51. }
  52. public abstract object InsertLookup(int globalMetaType, object instance);
  53. public abstract void SendEntityMaterialized(int globalMetaType, object instance);
  54. public abstract IEnumerable ExecuteSubQuery(int iSubQuery, object[] args);
  55. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
  56. public abstract IEnumerable<T> GetLinkSource<T>(int globalLink, int localFactory, object[] keyValues);
  57. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
  58. public abstract IEnumerable<T> GetNestedLinkSource<T>(int globalLink, int localFactory, object instance);
  59. public abstract bool Read();
  60. public abstract bool CanDeferLoad { get; }
  61. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  62. [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
  63. public static IEnumerable<TOutput> Convert<TOutput>(IEnumerable source) {
  64. foreach (object value in source) {
  65. yield return DBConvert.ChangeType<TOutput>(value);
  66. }
  67. }
  68. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  69. public static IGrouping<TKey, TElement> CreateGroup<TKey, TElement>(TKey key, IEnumerable<TElement> items) {
  70. return new ObjectReaderCompiler.Group<TKey, TElement>(key, items);
  71. }
  72. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  73. public static IOrderedEnumerable<TElement> CreateOrderedEnumerable<TElement>(IEnumerable<TElement> items) {
  74. return new ObjectReaderCompiler.OrderedResults<TElement>(items);
  75. }
  76. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes", Justification = "xiaoruda: The method has to be static because it's used in our generated code and there is no instance of the type.")]
  77. public static Exception ErrorAssignmentToNull(Type type) {
  78. return Error.CannotAssignNull(type);
  79. }
  80. }
  81. }
  82. internal class ObjectReaderCompiler : IObjectReaderCompiler {
  83. Type dataReaderType;
  84. IDataServices services;
  85. MethodInfo miDRisDBNull;
  86. MethodInfo miBRisDBNull;
  87. FieldInfo readerField;
  88. FieldInfo bufferReaderField;
  89. FieldInfo ordinalsField;
  90. FieldInfo globalsField;
  91. FieldInfo argsField;
  92. #if DEBUG
  93. static AssemblyBuilder captureAssembly;
  94. static ModuleBuilder captureModule;
  95. static string captureAssemblyFilename;
  96. static int iCaptureId;
  97. internal static int GetNextId() {
  98. return iCaptureId++;
  99. }
  100. internal static ModuleBuilder CaptureModule {
  101. get { return captureModule; }
  102. }
  103. [ResourceExposure(ResourceScope.Machine)] // filename parameter later used by other methods.
  104. internal static void StartCaptureToFile(string filename) {
  105. if (captureAssembly == null) {
  106. string dir = System.IO.Path.GetDirectoryName(filename);
  107. if (dir.Length == 0) dir = null;
  108. string name = System.IO.Path.GetFileName(filename);
  109. AssemblyName assemblyName = new AssemblyName(System.IO.Path.GetFileNameWithoutExtension(name));
  110. captureAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, dir);
  111. captureModule = captureAssembly.DefineDynamicModule(name);
  112. captureAssemblyFilename = filename;
  113. }
  114. }
  115. [ResourceExposure(ResourceScope.None)] // Exposure is via StartCaptureToFile method.
  116. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // Assembly.Save method call.
  117. internal static void StopCapture() {
  118. if (captureAssembly != null) {
  119. captureAssembly.Save(captureAssemblyFilename);
  120. captureAssembly = null;
  121. }
  122. }
  123. internal static void SetMaxReaderCacheSize(int size) {
  124. if (size <= 1) {
  125. throw Error.ArgumentOutOfRange("size");
  126. }
  127. maxReaderCacheSize = size;
  128. }
  129. #endif
  130. static LocalDataStoreSlot cacheSlot = Thread.AllocateDataSlot();
  131. static int maxReaderCacheSize = 10;
  132. static ObjectReaderCompiler() {
  133. }
  134. internal ObjectReaderCompiler(Type dataReaderType, IDataServices services) {
  135. this.dataReaderType = dataReaderType;
  136. this.services = services;
  137. this.miDRisDBNull = dataReaderType.GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  138. this.miBRisDBNull = typeof(DbDataReader).GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  139. Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
  140. this.ordinalsField = orbType.GetField("Ordinals", BindingFlags.Instance | BindingFlags.Public);
  141. this.globalsField = orbType.GetField("Globals", BindingFlags.Instance | BindingFlags.Public);
  142. this.argsField = orbType.GetField("Arguments", BindingFlags.Instance | BindingFlags.Public);
  143. this.readerField = orbType.GetField("DataReader", BindingFlags.Instance | BindingFlags.Public);
  144. this.bufferReaderField = orbType.GetField("BufferReader", BindingFlags.Instance | BindingFlags.Public);
  145. System.Diagnostics.Debug.Assert(
  146. this.miDRisDBNull != null &&
  147. this.miBRisDBNull != null &&
  148. this.readerField != null &&
  149. this.bufferReaderField != null &&
  150. this.ordinalsField != null &&
  151. this.globalsField != null &&
  152. this.argsField != null
  153. );
  154. }
  155. [ResourceExposure(ResourceScope.None)] // Consumed by Thread.AllocateDataSource result being unique.
  156. [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)] // Thread.GetData method call.
  157. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  158. public IObjectReaderFactory Compile(SqlExpression expression, Type elementType) {
  159. object mapping = this.services.Context.Mapping.Identity;
  160. DataLoadOptions options = this.services.Context.LoadOptions;
  161. IObjectReaderFactory factory = null;
  162. ReaderFactoryCache cache = null;
  163. bool canBeCompared = SqlProjectionComparer.CanBeCompared(expression);
  164. if (canBeCompared) {
  165. cache = (ReaderFactoryCache)Thread.GetData(cacheSlot);
  166. if (cache == null) {
  167. cache = new ReaderFactoryCache(maxReaderCacheSize);
  168. Thread.SetData(cacheSlot, cache);
  169. }
  170. factory = cache.GetFactory(elementType, this.dataReaderType, mapping, options, expression);
  171. }
  172. if (factory == null) {
  173. Generator gen = new Generator(this, elementType);
  174. #if DEBUG
  175. if (ObjectReaderCompiler.CaptureModule != null) {
  176. this.CompileCapturedMethod(gen, expression, elementType);
  177. }
  178. #endif
  179. DynamicMethod dm = this.CompileDynamicMethod(gen, expression, elementType);
  180. Type fnMatType = typeof(Func<,>).MakeGenericType(typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType), elementType);
  181. var fnMaterialize = (Delegate)dm.CreateDelegate(fnMatType);
  182. Type factoryType = typeof(ObjectReaderFactory<,>).MakeGenericType(this.dataReaderType, elementType);
  183. factory = (IObjectReaderFactory)Activator.CreateInstance(
  184. factoryType, BindingFlags.Instance | BindingFlags.NonPublic, null,
  185. new object[] { fnMaterialize, gen.NamedColumns, gen.Globals, gen.Locals }, null
  186. );
  187. if (canBeCompared) {
  188. expression = new SourceExpressionRemover().VisitExpression(expression);
  189. cache.AddFactory(elementType, this.dataReaderType, mapping, options, expression, factory);
  190. }
  191. }
  192. return factory;
  193. }
  194. private class SourceExpressionRemover : SqlDuplicator.DuplicatingVisitor {
  195. internal SourceExpressionRemover()
  196. : base(true) {
  197. }
  198. internal override SqlNode Visit(SqlNode node) {
  199. node = base.Visit(node);
  200. if (node != null) {
  201. node.ClearSourceExpression();
  202. }
  203. return node;
  204. }
  205. internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
  206. SqlExpression result = base.VisitColumnRef(cref);
  207. if (result != null && result == cref) {
  208. // reference to outer scope, don't propogate references to expressions or aliases
  209. SqlColumn col = cref.Column;
  210. SqlColumn newcol = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression);
  211. newcol.Ordinal = col.Ordinal;
  212. result = new SqlColumnRef(newcol);
  213. newcol.ClearSourceExpression();
  214. }
  215. return result;
  216. }
  217. internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
  218. SqlExpression result = base.VisitAliasRef(aref);
  219. if (result != null && result == aref) {
  220. // reference to outer scope, don't propogate references to expressions or aliases
  221. SqlAlias alias = aref.Alias;
  222. SqlAlias newalias = new SqlAlias(new SqlNop(aref.ClrType, aref.SqlType, null));
  223. return new SqlAliasRef(newalias);
  224. }
  225. return result;
  226. }
  227. }
  228. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  229. public IObjectReaderSession CreateSession(DbDataReader reader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
  230. Type sessionType = typeof(ObjectReaderSession<>).MakeGenericType(this.dataReaderType);
  231. return (IObjectReaderSession)Activator.CreateInstance(sessionType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
  232. new object[] { reader, provider, parentArgs, userArgs, subQueries }, null);
  233. }
  234. #if DEBUG
  235. private void CompileCapturedMethod(Generator gen, SqlExpression expression, Type elementType) {
  236. TypeBuilder tb = ObjectReaderCompiler.CaptureModule.DefineType("reader_type_" + ObjectReaderCompiler.GetNextId());
  237. MethodBuilder mb = tb.DefineMethod(
  238. "Read_" + elementType.Name,
  239. MethodAttributes.Static | MethodAttributes.Public,
  240. CallingConventions.Standard,
  241. elementType,
  242. new Type[] { typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType) }
  243. );
  244. gen.GenerateBody(mb.GetILGenerator(), (SqlExpression)SqlDuplicator.Copy(expression));
  245. tb.CreateType();
  246. }
  247. #endif
  248. private DynamicMethod CompileDynamicMethod(Generator gen, SqlExpression expression, Type elementType) {
  249. Type objectReaderType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
  250. DynamicMethod dm = new DynamicMethod(
  251. "Read_" + elementType.Name,
  252. elementType,
  253. new Type[] { objectReaderType },
  254. true
  255. );
  256. gen.GenerateBody(dm.GetILGenerator(), expression);
  257. return dm;
  258. }
  259. class ReaderFactoryCache {
  260. int maxCacheSize;
  261. LinkedList<CacheInfo> list;
  262. class CacheInfo {
  263. internal Type elementType;
  264. internal Type dataReaderType;
  265. internal object mapping;
  266. internal DataLoadOptions options;
  267. internal SqlExpression projection;
  268. internal IObjectReaderFactory factory;
  269. public CacheInfo(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
  270. this.elementType = elementType;
  271. this.dataReaderType = dataReaderType;
  272. this.options = options;
  273. this.mapping = mapping;
  274. this.projection = projection;
  275. this.factory = factory;
  276. }
  277. }
  278. internal ReaderFactoryCache(int maxCacheSize) {
  279. this.maxCacheSize = maxCacheSize;
  280. this.list = new LinkedList<CacheInfo>();
  281. }
  282. internal IObjectReaderFactory GetFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection) {
  283. for (LinkedListNode<CacheInfo> info = this.list.First; info != null; info = info.Next) {
  284. if (elementType == info.Value.elementType &&
  285. dataReaderType == info.Value.dataReaderType &&
  286. mapping == info.Value.mapping &&
  287. DataLoadOptions.ShapesAreEquivalent(options, info.Value.options) &&
  288. SqlProjectionComparer.AreSimilar(projection, info.Value.projection)
  289. ) {
  290. // move matching item to head of list to reset its lifetime
  291. this.list.Remove(info);
  292. this.list.AddFirst(info);
  293. return info.Value.factory;
  294. }
  295. }
  296. return null;
  297. }
  298. internal void AddFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
  299. this.list.AddFirst(new LinkedListNode<CacheInfo>(new CacheInfo(elementType, dataReaderType, mapping, options, projection, factory)));
  300. if (this.list.Count > this.maxCacheSize) {
  301. this.list.RemoveLast();
  302. }
  303. }
  304. }
  305. internal class SqlProjectionComparer {
  306. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  307. internal static bool CanBeCompared(SqlExpression node) {
  308. if (node == null) {
  309. return true;
  310. }
  311. switch (node.NodeType) {
  312. case SqlNodeType.New: {
  313. SqlNew new1 = (SqlNew)node;
  314. for (int i = 0, n = new1.Args.Count; i < n; i++) {
  315. if (!CanBeCompared(new1.Args[i])) {
  316. return false;
  317. }
  318. }
  319. for (int i = 0, n = new1.Members.Count; i < n; i++) {
  320. if (!CanBeCompared(new1.Members[i].Expression)) {
  321. return false;
  322. }
  323. }
  324. return true;
  325. }
  326. case SqlNodeType.ColumnRef:
  327. case SqlNodeType.Value:
  328. case SqlNodeType.UserColumn:
  329. return true;
  330. case SqlNodeType.Link: {
  331. SqlLink l1 = (SqlLink)node;
  332. for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
  333. if (!CanBeCompared(l1.KeyExpressions[i])) {
  334. return false;
  335. }
  336. }
  337. return true;
  338. }
  339. case SqlNodeType.OptionalValue:
  340. return CanBeCompared(((SqlOptionalValue)node).Value);
  341. case SqlNodeType.ValueOf:
  342. case SqlNodeType.OuterJoinedValue:
  343. return CanBeCompared(((SqlUnary)node).Operand);
  344. case SqlNodeType.Lift:
  345. return CanBeCompared(((SqlLift)node).Expression);
  346. case SqlNodeType.Grouping: {
  347. SqlGrouping g1 = (SqlGrouping)node;
  348. return CanBeCompared(g1.Key) && CanBeCompared(g1.Group);
  349. }
  350. case SqlNodeType.ClientArray: {
  351. if (node.SourceExpression.NodeType != ExpressionType.NewArrayInit &&
  352. node.SourceExpression.NodeType != ExpressionType.NewArrayBounds) {
  353. return false;
  354. }
  355. SqlClientArray a1 = (SqlClientArray)node;
  356. for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
  357. if (!CanBeCompared(a1.Expressions[i])) {
  358. return false;
  359. }
  360. }
  361. return true;
  362. }
  363. case SqlNodeType.ClientCase: {
  364. SqlClientCase c1 = (SqlClientCase)node;
  365. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  366. if (!CanBeCompared(c1.Whens[i].Match) ||
  367. !CanBeCompared(c1.Whens[i].Value)) {
  368. return false;
  369. }
  370. }
  371. return true;
  372. }
  373. case SqlNodeType.SearchedCase: {
  374. SqlSearchedCase c1 = (SqlSearchedCase)node;
  375. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  376. if (!CanBeCompared(c1.Whens[i].Match) ||
  377. !CanBeCompared(c1.Whens[i].Value)) {
  378. return false;
  379. }
  380. }
  381. return CanBeCompared(c1.Else);
  382. }
  383. case SqlNodeType.TypeCase: {
  384. SqlTypeCase c1 = (SqlTypeCase)node;
  385. if (!CanBeCompared(c1.Discriminator)) {
  386. return false;
  387. }
  388. for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
  389. if (!CanBeCompared(c1.Whens[i].Match)) {
  390. return false;
  391. }
  392. if (!CanBeCompared(c1.Whens[i].TypeBinding)) {
  393. return false;
  394. }
  395. }
  396. return true;
  397. }
  398. case SqlNodeType.DiscriminatedType:
  399. return CanBeCompared(((SqlDiscriminatedType)node).Discriminator);
  400. case SqlNodeType.JoinedCollection: {
  401. SqlJoinedCollection j1 = (SqlJoinedCollection)node;
  402. return CanBeCompared(j1.Count) && CanBeCompared(j1.Expression);
  403. }
  404. case SqlNodeType.Member:
  405. return CanBeCompared(((SqlMember)node).Expression);
  406. case SqlNodeType.MethodCall: {
  407. SqlMethodCall mc = (SqlMethodCall)node;
  408. if (mc.Object != null && !CanBeCompared(mc.Object)) {
  409. return false;
  410. }
  411. for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
  412. if (!CanBeCompared(mc.Arguments[0])) {
  413. return false;
  414. }
  415. }
  416. return true;
  417. }
  418. case SqlNodeType.ClientQuery:
  419. return true;
  420. case SqlNodeType.ClientParameter:
  421. default:
  422. return false;
  423. }
  424. }
  425. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  426. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  427. internal static bool AreSimilar(SqlExpression node1, SqlExpression node2) {
  428. if (node1 == node2) {
  429. return true;
  430. }
  431. if (node1 == null || node2 == null) {
  432. return false;
  433. }
  434. if (node1.NodeType != node2.NodeType ||
  435. node1.ClrType != node2.ClrType ||
  436. node1.SqlType != node2.SqlType) {
  437. return false;
  438. }
  439. switch (node1.NodeType) {
  440. case SqlNodeType.New: {
  441. SqlNew new1 = (SqlNew)node1;
  442. SqlNew new2 = (SqlNew)node2;
  443. if (new1.Args.Count != new2.Args.Count ||
  444. new1.Members.Count != new2.Members.Count) {
  445. return false;
  446. }
  447. for (int i = 0, n = new1.Args.Count; i < n; i++) {
  448. if (!AreSimilar(new1.Args[i], new2.Args[i])) {
  449. return false;
  450. }
  451. }
  452. for (int i = 0, n = new1.Members.Count; i < n; i++) {
  453. if (!MetaPosition.AreSameMember(new1.Members[i].Member, new2.Members[i].Member) ||
  454. !AreSimilar(new1.Members[i].Expression, new2.Members[i].Expression)) {
  455. return false;
  456. }
  457. }
  458. return true;
  459. }
  460. case SqlNodeType.ColumnRef: {
  461. SqlColumnRef cref1 = (SqlColumnRef)node1;
  462. SqlColumnRef cref2 = (SqlColumnRef)node2;
  463. return cref1.Column.Ordinal == cref2.Column.Ordinal;
  464. }
  465. case SqlNodeType.Link: {
  466. SqlLink l1 = (SqlLink)node1;
  467. SqlLink l2 = (SqlLink)node2;
  468. if (!MetaPosition.AreSameMember(l1.Member.Member, l2.Member.Member)) {
  469. return false;
  470. }
  471. if (l1.KeyExpressions.Count != l2.KeyExpressions.Count) {
  472. return false;
  473. }
  474. for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
  475. if (!AreSimilar(l1.KeyExpressions[i], l2.KeyExpressions[i])) {
  476. return false;
  477. }
  478. }
  479. return true;
  480. }
  481. case SqlNodeType.Value:
  482. return Object.Equals(((SqlValue)node1).Value, ((SqlValue)node2).Value);
  483. case SqlNodeType.OptionalValue: {
  484. SqlOptionalValue ov1 = (SqlOptionalValue)node1;
  485. SqlOptionalValue ov2 = (SqlOptionalValue)node2;
  486. return AreSimilar(ov1.Value, ov2.Value);
  487. }
  488. case SqlNodeType.ValueOf:
  489. case SqlNodeType.OuterJoinedValue:
  490. return AreSimilar(((SqlUnary)node1).Operand, ((SqlUnary)node2).Operand);
  491. case SqlNodeType.Lift:
  492. return AreSimilar(((SqlLift)node1).Expression, ((SqlLift)node2).Expression);
  493. case SqlNodeType.Grouping: {
  494. SqlGrouping g1 = (SqlGrouping)node1;
  495. SqlGrouping g2 = (SqlGrouping)node2;
  496. return AreSimilar(g1.Key, g2.Key) && AreSimilar(g1.Group, g2.Group);
  497. }
  498. case SqlNodeType.ClientArray: {
  499. SqlClientArray a1 = (SqlClientArray)node1;
  500. SqlClientArray a2 = (SqlClientArray)node2;
  501. if (a1.Expressions.Count != a2.Expressions.Count) {
  502. return false;
  503. }
  504. for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
  505. if (!AreSimilar(a1.Expressions[i], a2.Expressions[i])) {
  506. return false;
  507. }
  508. }
  509. return true;
  510. }
  511. case SqlNodeType.UserColumn:
  512. return ((SqlUserColumn)node1).Name == ((SqlUserColumn)node2).Name;
  513. case SqlNodeType.ClientCase: {
  514. SqlClientCase c1 = (SqlClientCase)node1;
  515. SqlClientCase c2 = (SqlClientCase)node2;
  516. if (c1.Whens.Count != c2.Whens.Count) {
  517. return false;
  518. }
  519. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  520. if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
  521. !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value)) {
  522. return false;
  523. }
  524. }
  525. return true;
  526. }
  527. case SqlNodeType.SearchedCase: {
  528. SqlSearchedCase c1 = (SqlSearchedCase)node1;
  529. SqlSearchedCase c2 = (SqlSearchedCase)node2;
  530. if (c1.Whens.Count != c2.Whens.Count) {
  531. return false;
  532. }
  533. for (int i = 0, n = c1.Whens.Count; i < n; i++) {
  534. if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
  535. !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value))
  536. return false;
  537. }
  538. return AreSimilar(c1.Else, c2.Else);
  539. }
  540. case SqlNodeType.TypeCase: {
  541. SqlTypeCase c1 = (SqlTypeCase)node1;
  542. SqlTypeCase c2 = (SqlTypeCase)node2;
  543. if (!AreSimilar(c1.Discriminator, c2.Discriminator)) {
  544. return false;
  545. }
  546. if (c1.Whens.Count != c2.Whens.Count) {
  547. return false;
  548. }
  549. for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
  550. if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match)) {
  551. return false;
  552. }
  553. if (!AreSimilar(c1.Whens[i].TypeBinding, c2.Whens[i].TypeBinding)) {
  554. return false;
  555. }
  556. }
  557. return true;
  558. }
  559. case SqlNodeType.DiscriminatedType: {
  560. SqlDiscriminatedType dt1 = (SqlDiscriminatedType)node1;
  561. SqlDiscriminatedType dt2 = (SqlDiscriminatedType)node2;
  562. return AreSimilar(dt1.Discriminator, dt2.Discriminator);
  563. }
  564. case SqlNodeType.JoinedCollection: {
  565. SqlJoinedCollection j1 = (SqlJoinedCollection)node1;
  566. SqlJoinedCollection j2 = (SqlJoinedCollection)node2;
  567. return AreSimilar(j1.Count, j2.Count) && AreSimilar(j1.Expression, j2.Expression);
  568. }
  569. case SqlNodeType.Member: {
  570. SqlMember m1 = (SqlMember)node1;
  571. SqlMember m2 = (SqlMember)node2;
  572. return m1.Member == m2.Member && AreSimilar(m1.Expression, m2.Expression);
  573. }
  574. case SqlNodeType.ClientQuery: {
  575. SqlClientQuery cq1 = (SqlClientQuery)node1;
  576. SqlClientQuery cq2 = (SqlClientQuery)node2;
  577. if (cq1.Arguments.Count != cq2.Arguments.Count) {
  578. return false;
  579. }
  580. for (int i = 0, n = cq1.Arguments.Count; i < n; i++) {
  581. if (!AreSimilar(cq1.Arguments[i], cq2.Arguments[i])) {
  582. return false;
  583. }
  584. }
  585. return true;
  586. }
  587. case SqlNodeType.MethodCall: {
  588. SqlMethodCall mc1 = (SqlMethodCall)node1;
  589. SqlMethodCall mc2 = (SqlMethodCall)node2;
  590. if (mc1.Method != mc2.Method || !AreSimilar(mc1.Object, mc2.Object)) {
  591. return false;
  592. }
  593. if (mc1.Arguments.Count != mc2.Arguments.Count) {
  594. return false;
  595. }
  596. for (int i = 0, n = mc1.Arguments.Count; i < n; i++) {
  597. if (!AreSimilar(mc1.Arguments[i], mc2.Arguments[i])) {
  598. return false;
  599. }
  600. }
  601. return true;
  602. }
  603. case SqlNodeType.ClientParameter:
  604. default:
  605. return false;
  606. }
  607. }
  608. }
  609. class SideEffectChecker : SqlVisitor {
  610. bool hasSideEffect;
  611. internal bool HasSideEffect(SqlNode node) {
  612. this.hasSideEffect = false;
  613. this.Visit(node);
  614. return this.hasSideEffect;
  615. }
  616. internal override SqlExpression VisitJoinedCollection(SqlJoinedCollection jc) {
  617. this.hasSideEffect = true;
  618. return jc;
  619. }
  620. internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
  621. return cq;
  622. }
  623. }
  624. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unknown reason.")]
  625. class Generator {
  626. ObjectReaderCompiler compiler;
  627. ILGenerator gen;
  628. List<object> globals;
  629. List<NamedColumn> namedColumns;
  630. LocalBuilder locDataReader;
  631. Type elementType;
  632. int nLocals;
  633. Dictionary<MetaAssociation, int> associationSubQueries;
  634. SideEffectChecker sideEffectChecker = new SideEffectChecker();
  635. internal Generator(ObjectReaderCompiler compiler, Type elementType) {
  636. this.compiler = compiler;
  637. this.elementType = elementType;
  638. this.associationSubQueries = new Dictionary<MetaAssociation,int>();
  639. }
  640. internal void GenerateBody(ILGenerator generator, SqlExpression expression) {
  641. this.gen = generator;
  642. this.globals = new List<object>();
  643. this.namedColumns = new List<NamedColumn>();
  644. // prepare locDataReader
  645. this.locDataReader = generator.DeclareLocal(this.compiler.dataReaderType);
  646. generator.Emit(OpCodes.Ldarg_0);
  647. generator.Emit(OpCodes.Ldfld, this.compiler.readerField);
  648. generator.Emit(OpCodes.Stloc, this.locDataReader);
  649. this.GenerateExpressionForType(expression, this.elementType);
  650. generator.Emit(OpCodes.Ret);
  651. }
  652. internal object[] Globals {
  653. get { return this.globals.ToArray(); }
  654. }
  655. internal NamedColumn[] NamedColumns {
  656. get { return this.namedColumns.ToArray(); }
  657. }
  658. internal int Locals {
  659. get { return this.nLocals; }
  660. }
  661. #if DEBUG
  662. private int stackDepth;
  663. #endif
  664. private Type Generate(SqlNode node) {
  665. return this.Generate(node, null);
  666. }
  667. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
  668. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  669. private Type Generate(SqlNode node, LocalBuilder locInstance) {
  670. #if DEBUG
  671. try {
  672. stackDepth++;
  673. System.Diagnostics.Debug.Assert(stackDepth < 500);
  674. #endif
  675. switch (node.NodeType) {
  676. case SqlNodeType.New:
  677. return this.GenerateNew((SqlNew)node);
  678. case SqlNodeType.ColumnRef:
  679. return this.GenerateColumnReference((SqlColumnRef)node);
  680. case SqlNodeType.ClientQuery:
  681. return this.GenerateClientQuery((SqlClientQuery)node, locInstance);
  682. case SqlNodeType.JoinedCollection:
  683. return this.GenerateJoinedCollection((SqlJoinedCollection)node);
  684. case SqlNodeType.Link:
  685. return this.GenerateLink((SqlLink)node, locInstance);
  686. case SqlNodeType.Value:
  687. return this.GenerateValue((SqlValue)node);
  688. case SqlNodeType.ClientParameter:
  689. return this.GenerateClientParameter((SqlClientParameter)node);
  690. case SqlNodeType.ValueOf:
  691. return this.GenerateValueOf((SqlUnary)node);
  692. case SqlNodeType.OptionalValue:
  693. return this.GenerateOptionalValue((SqlOptionalValue)node);
  694. case SqlNodeType.OuterJoinedValue:
  695. return this.Generate(((SqlUnary)node).Operand);
  696. case SqlNodeType.Lift:
  697. return this.GenerateLift((SqlLift)node);
  698. case SqlNodeType.Grouping:
  699. return this.GenerateGrouping((SqlGrouping)node);
  700. case SqlNodeType.ClientArray:
  701. return this.GenerateClientArray((SqlClientArray)node);
  702. case SqlNodeType.UserColumn:
  703. return this.GenerateUserColumn((SqlUserColumn)node);
  704. case SqlNodeType.ClientCase:
  705. return this.GenerateClientCase((SqlClientCase)node, false, locInstance);
  706. case SqlNodeType.SearchedCase:
  707. return this.GenerateSearchedCase((SqlSearchedCase)node);
  708. case SqlNodeType.TypeCase:
  709. return this.GenerateTypeCase((SqlTypeCase)node);
  710. case SqlNodeType.DiscriminatedType:
  711. return this.GenerateDiscriminatedType((SqlDiscriminatedType)node);
  712. case SqlNodeType.Member:
  713. return this.GenerateMember((SqlMember)node);
  714. case SqlNodeType.MethodCall:
  715. return this.GenerateMethodCall((SqlMethodCall)node);
  716. default:
  717. throw Error.CouldNotTranslateExpressionForReading(node.SourceExpression);
  718. }
  719. #if DEBUG
  720. }
  721. finally {
  722. stackDepth--;
  723. }
  724. #endif
  725. }
  726. private void GenerateAccessBufferReader() {
  727. gen.Emit(OpCodes.Ldarg_0);
  728. gen.Emit(OpCodes.Ldfld, this.compiler.bufferReaderField);
  729. }
  730. private void GenerateAccessDataReader() {
  731. gen.Emit(OpCodes.Ldloc, this.locDataReader);
  732. }
  733. private void GenerateAccessOrdinals() {
  734. gen.Emit(OpCodes.Ldarg_0);
  735. gen.Emit(OpCodes.Ldfld, this.compiler.ordinalsField);
  736. }
  737. private void GenerateAccessGlobals() {
  738. gen.Emit(OpCodes.Ldarg_0);
  739. gen.Emit(OpCodes.Ldfld, this.compiler.globalsField);
  740. }
  741. private void GenerateAccessArguments() {
  742. gen.Emit(OpCodes.Ldarg_0);
  743. gen.Emit(OpCodes.Ldfld, this.compiler.argsField);
  744. }
  745. private Type GenerateValue(SqlValue value) {
  746. return this.GenerateConstant(value.ClrType, value.Value);
  747. }
  748. private Type GenerateClientParameter(SqlClientParameter cp) {
  749. Delegate d = cp.Accessor.Compile();
  750. int iGlobal = this.AddGlobal(d.GetType(), d);
  751. this.GenerateGlobalAccess(iGlobal, d.GetType());
  752. this.GenerateAccessArguments();
  753. MethodInfo miInvoke = d.GetType().GetMethod(
  754. "Invoke",
  755. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
  756. null,
  757. new Type[] { typeof(object[]) },
  758. null
  759. );
  760. System.Diagnostics.Debug.Assert(miInvoke != null);
  761. gen.Emit(GetMethodCallOpCode(miInvoke), miInvoke);
  762. return d.Method.ReturnType;
  763. }
  764. private Type GenerateValueOf(SqlUnary u) {
  765. System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(u.Operand.ClrType));
  766. this.GenerateExpressionForType(u.Operand, u.Operand.ClrType);
  767. LocalBuilder loc = gen.DeclareLocal(u.Operand.ClrType);
  768. gen.Emit(OpCodes.Stloc, loc);
  769. gen.Emit(OpCodes.Ldloca, loc);
  770. this.GenerateGetValue(u.Operand.ClrType);
  771. return u.ClrType;
  772. }
  773. private Type GenerateOptionalValue(SqlOptionalValue opt) {
  774. System.Diagnostics.Debug.Assert(opt.HasValue.ClrType == typeof(int?));
  775. Label labIsNull = gen.DefineLabel();
  776. Label labExit = gen.DefineLabel();
  777. Type actualType = this.Generate(opt.HasValue);
  778. System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(actualType));
  779. LocalBuilder loc = gen.DeclareLocal(actualType);
  780. gen.Emit(OpCodes.Stloc, loc);
  781. gen.Emit(OpCodes.Ldloca, loc);
  782. this.GenerateHasValue(actualType);
  783. gen.Emit(OpCodes.Brfalse, labIsNull);
  784. this.GenerateExpressionForType(opt.Value, opt.ClrType);
  785. gen.Emit(OpCodes.Br_S, labExit);
  786. gen.MarkLabel(labIsNull);
  787. this.GenerateConstant(opt.ClrType, null);
  788. gen.MarkLabel(labExit);
  789. return opt.ClrType;
  790. }
  791. private Type GenerateLift(SqlLift lift) {
  792. return this.GenerateExpressionForType(lift.Expression, lift.ClrType);
  793. }
  794. private Type GenerateClientArray(SqlClientArray ca) {
  795. if (!ca.ClrType.IsArray) {
  796. throw Error.CannotMaterializeList(ca.ClrType);
  797. }
  798. Type elemType = TypeSystem.GetElementType(ca.ClrType);
  799. this.GenerateConstInt(ca.Expressions.Count);
  800. gen.Emit(OpCodes.Newarr, elemType);
  801. for (int i = 0, n = ca.Expressions.Count; i < n; i++) {
  802. gen.Emit(OpCodes.Dup);
  803. this.GenerateConstInt(i);
  804. this.GenerateExpressionForType(ca.Expressions[i], elemType);
  805. this.GenerateArrayAssign(elemType);
  806. }
  807. return ca.ClrType;
  808. }
  809. private Type GenerateMember(SqlMember m) {
  810. FieldInfo fi = m.Member as FieldInfo;
  811. if (fi != null) {
  812. this.GenerateExpressionForType(m.Expression, m.Expression.ClrType);
  813. gen.Emit(OpCodes.Ldfld, fi);
  814. return fi.FieldType;
  815. }
  816. else {
  817. PropertyInfo pi = (PropertyInfo)m.Member;
  818. return this.GenerateMethodCall(new SqlMethodCall(m.ClrType, m.SqlType, pi.GetGetMethod(), m.Expression, null, m.SourceExpression));
  819. }
  820. }
  821. private Type GenerateMethodCall(SqlMethodCall mc) {
  822. ParameterInfo[] pis = mc.Method.GetParameters();
  823. if (mc.Object != null) {
  824. Type actualType = this.GenerateExpressionForType(mc.Object, mc.Object.ClrType);
  825. if (actualType.IsValueType) {
  826. LocalBuilder loc = gen.DeclareLocal(actualType);
  827. gen.Emit(OpCodes.Stloc, loc);
  828. gen.Emit(OpCodes.Ldloca, loc);
  829. }
  830. }
  831. for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
  832. ParameterInfo pi = pis[i];
  833. Type pType = pi.ParameterType;
  834. if (pType.IsByRef) {
  835. pType = pType.GetElementType();
  836. this.GenerateExpressionForType(mc.Arguments[i], pType);
  837. LocalBuilder loc = gen.DeclareLocal(pType);
  838. gen.Emit(OpCodes.Stloc, loc);
  839. gen.Emit(OpCodes.Ldloca, loc);
  840. }
  841. else {
  842. this.GenerateExpressionForType(mc.Arguments[i], pType);
  843. }
  844. }
  845. OpCode callOpCode = GetMethodCallOpCode(mc.Method);
  846. if (mc.Object != null && TypeSystem.IsNullableType(mc.Object.ClrType) && callOpCode == OpCodes.Callvirt){
  847. gen.Emit(OpCodes.Constrained, mc.Object.ClrType);
  848. }
  849. gen.Emit(callOpCode, mc.Method);
  850. return mc.Method.ReturnType;
  851. }
  852. /// <summary>
  853. /// Cannot use Call for virtual methods - it results in unverifiable code. Ensure we're using the correct op code.
  854. /// </summary>
  855. private static OpCode GetMethodCallOpCode(MethodInfo mi) {
  856. return (mi.IsStatic || mi.DeclaringType.IsValueType) ? OpCodes.Call : OpCodes.Callvirt;
  857. }
  858. private Type GenerateNew(SqlNew sn) {
  859. LocalBuilder locInstance = gen.DeclareLocal(sn.ClrType);
  860. LocalBuilder locStoreInMember = null;
  861. Label labNewExit = gen.DefineLabel();
  862. Label labAlreadyCached = gen.DefineLabel();
  863. // read all arg values
  864. if (sn.Args.Count > 0) {
  865. ParameterInfo[] pis = sn.Constructor.GetParameters();
  866. for (int i = 0, n = sn.Args.Count; i < n; i++) {
  867. this.GenerateExpressionForType(sn.Args[i], pis[i].ParameterType);
  868. }
  869. }
  870. // construct the new instance
  871. if (sn.Constructor != null) {
  872. gen.Emit(OpCodes.Newobj, sn.Constructor);
  873. gen.Emit(OpCodes.Stloc, locInstance);
  874. }
  875. else if (sn.ClrType.IsValueType) {
  876. gen.Emit(OpCodes.Ldloca, locInstance);
  877. gen.Emit(OpCodes.Initobj, sn.ClrType);
  878. }
  879. else {
  880. ConstructorInfo ci = sn.ClrType.GetConst

Large files files are truncated, but you can click here to view the full file