/mcs/class/referencesource/System.Data.Linq/SqlClient/Reader/ObjectReaderCompiler.cs
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
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq.Expressions;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Data;
- using System.Data.Common;
- using System.Data.Linq;
- using System.Data.Linq.Mapping;
- using System.Data.Linq.Provider;
- using System.Runtime.CompilerServices;
- using System.Runtime.Versioning;
- using System.Security;
- using System.Security.Permissions;
- using System.Threading;
- namespace System.Data.Linq.SqlClient {
- using System.Data.Linq.SqlClient.Implementation;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics;
- #if ILGEN || DEBUG
- namespace Implementation {
- /// <summary>
- /// Internal interface type defining the operations dynamic materialization functions need to perform when
- /// materializing objects, without reflecting/invoking privates.
- /// <remarks>This interface is required because our anonymously hosted materialization delegates
- /// run under partial trust and cannot access non-public members of types in the fully trusted
- /// framework assemblies.</remarks>
- /// </summary>
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Materializer", Justification = "Spelling is correct.")]
- [SuppressMessage("Microsoft.Design", "CA1012:AbstractTypesShouldNotHaveConstructors", Justification = "Unknown reason.")]
- public abstract class ObjectMaterializer<TDataReader> where TDataReader : DbDataReader {
- // These are public fields rather than properties for access speed
- [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
- public int[] Ordinals;
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Globals", Justification = "Spelling is correct.")]
- [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
- public object[] Globals;
- [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
- public object[] Locals;
- [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
- public object[] Arguments;
- [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
- public TDataReader DataReader;
- [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Microsoft: This is a public type that is not intended for public use.")]
- public DbDataReader BufferReader;
- public ObjectMaterializer() {
- DataReader = default(TDataReader);
- }
- public abstract object InsertLookup(int globalMetaType, object instance);
- public abstract void SendEntityMaterialized(int globalMetaType, object instance);
- public abstract IEnumerable ExecuteSubQuery(int iSubQuery, object[] args);
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
- public abstract IEnumerable<T> GetLinkSource<T>(int globalLink, int localFactory, object[] keyValues);
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
- public abstract IEnumerable<T> GetNestedLinkSource<T>(int globalLink, int localFactory, object instance);
- public abstract bool Read();
- public abstract bool CanDeferLoad { get; }
- [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.")]
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "Microsoft: Generic parameters are required for strong-typing of the return type.")]
- public static IEnumerable<TOutput> Convert<TOutput>(IEnumerable source) {
- foreach (object value in source) {
- yield return DBConvert.ChangeType<TOutput>(value);
- }
- }
- [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.")]
- public static IGrouping<TKey, TElement> CreateGroup<TKey, TElement>(TKey key, IEnumerable<TElement> items) {
- return new ObjectReaderCompiler.Group<TKey, TElement>(key, items);
- }
- [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.")]
- public static IOrderedEnumerable<TElement> CreateOrderedEnumerable<TElement>(IEnumerable<TElement> items) {
- return new ObjectReaderCompiler.OrderedResults<TElement>(items);
- }
- [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.")]
- public static Exception ErrorAssignmentToNull(Type type) {
- return Error.CannotAssignNull(type);
- }
- }
- }
- internal class ObjectReaderCompiler : IObjectReaderCompiler {
- Type dataReaderType;
- IDataServices services;
- MethodInfo miDRisDBNull;
- MethodInfo miBRisDBNull;
- FieldInfo readerField;
- FieldInfo bufferReaderField;
- FieldInfo ordinalsField;
- FieldInfo globalsField;
- FieldInfo argsField;
- #if DEBUG
- static AssemblyBuilder captureAssembly;
- static ModuleBuilder captureModule;
- static string captureAssemblyFilename;
- static int iCaptureId;
- internal static int GetNextId() {
- return iCaptureId++;
- }
- internal static ModuleBuilder CaptureModule {
- get { return captureModule; }
- }
- [ResourceExposure(ResourceScope.Machine)] // filename parameter later used by other methods.
- internal static void StartCaptureToFile(string filename) {
- if (captureAssembly == null) {
- string dir = System.IO.Path.GetDirectoryName(filename);
- if (dir.Length == 0) dir = null;
- string name = System.IO.Path.GetFileName(filename);
- AssemblyName assemblyName = new AssemblyName(System.IO.Path.GetFileNameWithoutExtension(name));
- captureAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, dir);
- captureModule = captureAssembly.DefineDynamicModule(name);
- captureAssemblyFilename = filename;
- }
- }
- [ResourceExposure(ResourceScope.None)] // Exposure is via StartCaptureToFile method.
- [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // Assembly.Save method call.
- internal static void StopCapture() {
- if (captureAssembly != null) {
- captureAssembly.Save(captureAssemblyFilename);
- captureAssembly = null;
- }
- }
- internal static void SetMaxReaderCacheSize(int size) {
- if (size <= 1) {
- throw Error.ArgumentOutOfRange("size");
- }
- maxReaderCacheSize = size;
- }
- #endif
- static LocalDataStoreSlot cacheSlot = Thread.AllocateDataSlot();
- static int maxReaderCacheSize = 10;
- static ObjectReaderCompiler() {
- }
- internal ObjectReaderCompiler(Type dataReaderType, IDataServices services) {
- this.dataReaderType = dataReaderType;
- this.services = services;
- this.miDRisDBNull = dataReaderType.GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- this.miBRisDBNull = typeof(DbDataReader).GetMethod("IsDBNull", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
- Type orbType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
- this.ordinalsField = orbType.GetField("Ordinals", BindingFlags.Instance | BindingFlags.Public);
- this.globalsField = orbType.GetField("Globals", BindingFlags.Instance | BindingFlags.Public);
- this.argsField = orbType.GetField("Arguments", BindingFlags.Instance | BindingFlags.Public);
- this.readerField = orbType.GetField("DataReader", BindingFlags.Instance | BindingFlags.Public);
- this.bufferReaderField = orbType.GetField("BufferReader", BindingFlags.Instance | BindingFlags.Public);
- System.Diagnostics.Debug.Assert(
- this.miDRisDBNull != null &&
- this.miBRisDBNull != null &&
- this.readerField != null &&
- this.bufferReaderField != null &&
- this.ordinalsField != null &&
- this.globalsField != null &&
- this.argsField != null
- );
- }
- [ResourceExposure(ResourceScope.None)] // Consumed by Thread.AllocateDataSource result being unique.
- [ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)] // Thread.GetData method call.
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- public IObjectReaderFactory Compile(SqlExpression expression, Type elementType) {
- object mapping = this.services.Context.Mapping.Identity;
- DataLoadOptions options = this.services.Context.LoadOptions;
- IObjectReaderFactory factory = null;
- ReaderFactoryCache cache = null;
- bool canBeCompared = SqlProjectionComparer.CanBeCompared(expression);
- if (canBeCompared) {
- cache = (ReaderFactoryCache)Thread.GetData(cacheSlot);
- if (cache == null) {
- cache = new ReaderFactoryCache(maxReaderCacheSize);
- Thread.SetData(cacheSlot, cache);
- }
- factory = cache.GetFactory(elementType, this.dataReaderType, mapping, options, expression);
- }
- if (factory == null) {
- Generator gen = new Generator(this, elementType);
- #if DEBUG
- if (ObjectReaderCompiler.CaptureModule != null) {
- this.CompileCapturedMethod(gen, expression, elementType);
- }
- #endif
- DynamicMethod dm = this.CompileDynamicMethod(gen, expression, elementType);
- Type fnMatType = typeof(Func<,>).MakeGenericType(typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType), elementType);
- var fnMaterialize = (Delegate)dm.CreateDelegate(fnMatType);
- Type factoryType = typeof(ObjectReaderFactory<,>).MakeGenericType(this.dataReaderType, elementType);
- factory = (IObjectReaderFactory)Activator.CreateInstance(
- factoryType, BindingFlags.Instance | BindingFlags.NonPublic, null,
- new object[] { fnMaterialize, gen.NamedColumns, gen.Globals, gen.Locals }, null
- );
- if (canBeCompared) {
- expression = new SourceExpressionRemover().VisitExpression(expression);
- cache.AddFactory(elementType, this.dataReaderType, mapping, options, expression, factory);
- }
- }
- return factory;
- }
- private class SourceExpressionRemover : SqlDuplicator.DuplicatingVisitor {
- internal SourceExpressionRemover()
- : base(true) {
- }
- internal override SqlNode Visit(SqlNode node) {
- node = base.Visit(node);
- if (node != null) {
- node.ClearSourceExpression();
- }
- return node;
- }
- internal override SqlExpression VisitColumnRef(SqlColumnRef cref) {
- SqlExpression result = base.VisitColumnRef(cref);
- if (result != null && result == cref) {
- // reference to outer scope, don't propogate references to expressions or aliases
- SqlColumn col = cref.Column;
- SqlColumn newcol = new SqlColumn(col.ClrType, col.SqlType, col.Name, col.MetaMember, null, col.SourceExpression);
- newcol.Ordinal = col.Ordinal;
- result = new SqlColumnRef(newcol);
- newcol.ClearSourceExpression();
- }
- return result;
- }
- internal override SqlExpression VisitAliasRef(SqlAliasRef aref) {
- SqlExpression result = base.VisitAliasRef(aref);
- if (result != null && result == aref) {
- // reference to outer scope, don't propogate references to expressions or aliases
- SqlAlias alias = aref.Alias;
- SqlAlias newalias = new SqlAlias(new SqlNop(aref.ClrType, aref.SqlType, null));
- return new SqlAliasRef(newalias);
- }
- return result;
- }
- }
- [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- public IObjectReaderSession CreateSession(DbDataReader reader, IReaderProvider provider, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries) {
- Type sessionType = typeof(ObjectReaderSession<>).MakeGenericType(this.dataReaderType);
- return (IObjectReaderSession)Activator.CreateInstance(sessionType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
- new object[] { reader, provider, parentArgs, userArgs, subQueries }, null);
- }
- #if DEBUG
- private void CompileCapturedMethod(Generator gen, SqlExpression expression, Type elementType) {
- TypeBuilder tb = ObjectReaderCompiler.CaptureModule.DefineType("reader_type_" + ObjectReaderCompiler.GetNextId());
- MethodBuilder mb = tb.DefineMethod(
- "Read_" + elementType.Name,
- MethodAttributes.Static | MethodAttributes.Public,
- CallingConventions.Standard,
- elementType,
- new Type[] { typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType) }
- );
- gen.GenerateBody(mb.GetILGenerator(), (SqlExpression)SqlDuplicator.Copy(expression));
- tb.CreateType();
- }
- #endif
- private DynamicMethod CompileDynamicMethod(Generator gen, SqlExpression expression, Type elementType) {
- Type objectReaderType = typeof(ObjectMaterializer<>).MakeGenericType(this.dataReaderType);
- DynamicMethod dm = new DynamicMethod(
- "Read_" + elementType.Name,
- elementType,
- new Type[] { objectReaderType },
- true
- );
- gen.GenerateBody(dm.GetILGenerator(), expression);
- return dm;
- }
- class ReaderFactoryCache {
- int maxCacheSize;
- LinkedList<CacheInfo> list;
- class CacheInfo {
- internal Type elementType;
- internal Type dataReaderType;
- internal object mapping;
- internal DataLoadOptions options;
- internal SqlExpression projection;
- internal IObjectReaderFactory factory;
- public CacheInfo(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
- this.elementType = elementType;
- this.dataReaderType = dataReaderType;
- this.options = options;
- this.mapping = mapping;
- this.projection = projection;
- this.factory = factory;
- }
- }
- internal ReaderFactoryCache(int maxCacheSize) {
- this.maxCacheSize = maxCacheSize;
- this.list = new LinkedList<CacheInfo>();
- }
- internal IObjectReaderFactory GetFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection) {
- for (LinkedListNode<CacheInfo> info = this.list.First; info != null; info = info.Next) {
- if (elementType == info.Value.elementType &&
- dataReaderType == info.Value.dataReaderType &&
- mapping == info.Value.mapping &&
- DataLoadOptions.ShapesAreEquivalent(options, info.Value.options) &&
- SqlProjectionComparer.AreSimilar(projection, info.Value.projection)
- ) {
- // move matching item to head of list to reset its lifetime
- this.list.Remove(info);
- this.list.AddFirst(info);
- return info.Value.factory;
- }
- }
- return null;
- }
- internal void AddFactory(Type elementType, Type dataReaderType, object mapping, DataLoadOptions options, SqlExpression projection, IObjectReaderFactory factory) {
- this.list.AddFirst(new LinkedListNode<CacheInfo>(new CacheInfo(elementType, dataReaderType, mapping, options, projection, factory)));
- if (this.list.Count > this.maxCacheSize) {
- this.list.RemoveLast();
- }
- }
- }
- internal class SqlProjectionComparer {
- [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.")]
- internal static bool CanBeCompared(SqlExpression node) {
- if (node == null) {
- return true;
- }
- switch (node.NodeType) {
- case SqlNodeType.New: {
- SqlNew new1 = (SqlNew)node;
- for (int i = 0, n = new1.Args.Count; i < n; i++) {
- if (!CanBeCompared(new1.Args[i])) {
- return false;
- }
- }
- for (int i = 0, n = new1.Members.Count; i < n; i++) {
- if (!CanBeCompared(new1.Members[i].Expression)) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.ColumnRef:
- case SqlNodeType.Value:
- case SqlNodeType.UserColumn:
- return true;
- case SqlNodeType.Link: {
- SqlLink l1 = (SqlLink)node;
- for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
- if (!CanBeCompared(l1.KeyExpressions[i])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.OptionalValue:
- return CanBeCompared(((SqlOptionalValue)node).Value);
- case SqlNodeType.ValueOf:
- case SqlNodeType.OuterJoinedValue:
- return CanBeCompared(((SqlUnary)node).Operand);
- case SqlNodeType.Lift:
- return CanBeCompared(((SqlLift)node).Expression);
- case SqlNodeType.Grouping: {
- SqlGrouping g1 = (SqlGrouping)node;
- return CanBeCompared(g1.Key) && CanBeCompared(g1.Group);
- }
- case SqlNodeType.ClientArray: {
- if (node.SourceExpression.NodeType != ExpressionType.NewArrayInit &&
- node.SourceExpression.NodeType != ExpressionType.NewArrayBounds) {
- return false;
- }
- SqlClientArray a1 = (SqlClientArray)node;
- for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
- if (!CanBeCompared(a1.Expressions[i])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.ClientCase: {
- SqlClientCase c1 = (SqlClientCase)node;
- for (int i = 0, n = c1.Whens.Count; i < n; i++) {
- if (!CanBeCompared(c1.Whens[i].Match) ||
- !CanBeCompared(c1.Whens[i].Value)) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.SearchedCase: {
- SqlSearchedCase c1 = (SqlSearchedCase)node;
- for (int i = 0, n = c1.Whens.Count; i < n; i++) {
- if (!CanBeCompared(c1.Whens[i].Match) ||
- !CanBeCompared(c1.Whens[i].Value)) {
- return false;
- }
- }
- return CanBeCompared(c1.Else);
- }
- case SqlNodeType.TypeCase: {
- SqlTypeCase c1 = (SqlTypeCase)node;
- if (!CanBeCompared(c1.Discriminator)) {
- return false;
- }
- for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
- if (!CanBeCompared(c1.Whens[i].Match)) {
- return false;
- }
- if (!CanBeCompared(c1.Whens[i].TypeBinding)) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.DiscriminatedType:
- return CanBeCompared(((SqlDiscriminatedType)node).Discriminator);
- case SqlNodeType.JoinedCollection: {
- SqlJoinedCollection j1 = (SqlJoinedCollection)node;
- return CanBeCompared(j1.Count) && CanBeCompared(j1.Expression);
- }
- case SqlNodeType.Member:
- return CanBeCompared(((SqlMember)node).Expression);
- case SqlNodeType.MethodCall: {
- SqlMethodCall mc = (SqlMethodCall)node;
- if (mc.Object != null && !CanBeCompared(mc.Object)) {
- return false;
- }
- for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
- if (!CanBeCompared(mc.Arguments[0])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.ClientQuery:
- return true;
- case SqlNodeType.ClientParameter:
- default:
- return false;
- }
- }
- [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.")]
- [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.")]
- internal static bool AreSimilar(SqlExpression node1, SqlExpression node2) {
- if (node1 == node2) {
- return true;
- }
- if (node1 == null || node2 == null) {
- return false;
- }
- if (node1.NodeType != node2.NodeType ||
- node1.ClrType != node2.ClrType ||
- node1.SqlType != node2.SqlType) {
- return false;
- }
- switch (node1.NodeType) {
- case SqlNodeType.New: {
- SqlNew new1 = (SqlNew)node1;
- SqlNew new2 = (SqlNew)node2;
- if (new1.Args.Count != new2.Args.Count ||
- new1.Members.Count != new2.Members.Count) {
- return false;
- }
- for (int i = 0, n = new1.Args.Count; i < n; i++) {
- if (!AreSimilar(new1.Args[i], new2.Args[i])) {
- return false;
- }
- }
- for (int i = 0, n = new1.Members.Count; i < n; i++) {
- if (!MetaPosition.AreSameMember(new1.Members[i].Member, new2.Members[i].Member) ||
- !AreSimilar(new1.Members[i].Expression, new2.Members[i].Expression)) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.ColumnRef: {
- SqlColumnRef cref1 = (SqlColumnRef)node1;
- SqlColumnRef cref2 = (SqlColumnRef)node2;
- return cref1.Column.Ordinal == cref2.Column.Ordinal;
- }
- case SqlNodeType.Link: {
- SqlLink l1 = (SqlLink)node1;
- SqlLink l2 = (SqlLink)node2;
- if (!MetaPosition.AreSameMember(l1.Member.Member, l2.Member.Member)) {
- return false;
- }
- if (l1.KeyExpressions.Count != l2.KeyExpressions.Count) {
- return false;
- }
- for (int i = 0, c = l1.KeyExpressions.Count; i < c; ++i) {
- if (!AreSimilar(l1.KeyExpressions[i], l2.KeyExpressions[i])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.Value:
- return Object.Equals(((SqlValue)node1).Value, ((SqlValue)node2).Value);
- case SqlNodeType.OptionalValue: {
- SqlOptionalValue ov1 = (SqlOptionalValue)node1;
- SqlOptionalValue ov2 = (SqlOptionalValue)node2;
- return AreSimilar(ov1.Value, ov2.Value);
- }
- case SqlNodeType.ValueOf:
- case SqlNodeType.OuterJoinedValue:
- return AreSimilar(((SqlUnary)node1).Operand, ((SqlUnary)node2).Operand);
- case SqlNodeType.Lift:
- return AreSimilar(((SqlLift)node1).Expression, ((SqlLift)node2).Expression);
- case SqlNodeType.Grouping: {
- SqlGrouping g1 = (SqlGrouping)node1;
- SqlGrouping g2 = (SqlGrouping)node2;
- return AreSimilar(g1.Key, g2.Key) && AreSimilar(g1.Group, g2.Group);
- }
- case SqlNodeType.ClientArray: {
- SqlClientArray a1 = (SqlClientArray)node1;
- SqlClientArray a2 = (SqlClientArray)node2;
- if (a1.Expressions.Count != a2.Expressions.Count) {
- return false;
- }
- for (int i = 0, n = a1.Expressions.Count; i < n; i++) {
- if (!AreSimilar(a1.Expressions[i], a2.Expressions[i])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.UserColumn:
- return ((SqlUserColumn)node1).Name == ((SqlUserColumn)node2).Name;
- case SqlNodeType.ClientCase: {
- SqlClientCase c1 = (SqlClientCase)node1;
- SqlClientCase c2 = (SqlClientCase)node2;
- if (c1.Whens.Count != c2.Whens.Count) {
- return false;
- }
- for (int i = 0, n = c1.Whens.Count; i < n; i++) {
- if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
- !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value)) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.SearchedCase: {
- SqlSearchedCase c1 = (SqlSearchedCase)node1;
- SqlSearchedCase c2 = (SqlSearchedCase)node2;
- if (c1.Whens.Count != c2.Whens.Count) {
- return false;
- }
- for (int i = 0, n = c1.Whens.Count; i < n; i++) {
- if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match) ||
- !AreSimilar(c1.Whens[i].Value, c2.Whens[i].Value))
- return false;
- }
- return AreSimilar(c1.Else, c2.Else);
- }
- case SqlNodeType.TypeCase: {
- SqlTypeCase c1 = (SqlTypeCase)node1;
- SqlTypeCase c2 = (SqlTypeCase)node2;
- if (!AreSimilar(c1.Discriminator, c2.Discriminator)) {
- return false;
- }
- if (c1.Whens.Count != c2.Whens.Count) {
- return false;
- }
- for (int i = 0, c = c1.Whens.Count; i < c; ++i) {
- if (!AreSimilar(c1.Whens[i].Match, c2.Whens[i].Match)) {
- return false;
- }
- if (!AreSimilar(c1.Whens[i].TypeBinding, c2.Whens[i].TypeBinding)) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.DiscriminatedType: {
- SqlDiscriminatedType dt1 = (SqlDiscriminatedType)node1;
- SqlDiscriminatedType dt2 = (SqlDiscriminatedType)node2;
- return AreSimilar(dt1.Discriminator, dt2.Discriminator);
- }
- case SqlNodeType.JoinedCollection: {
- SqlJoinedCollection j1 = (SqlJoinedCollection)node1;
- SqlJoinedCollection j2 = (SqlJoinedCollection)node2;
- return AreSimilar(j1.Count, j2.Count) && AreSimilar(j1.Expression, j2.Expression);
- }
- case SqlNodeType.Member: {
- SqlMember m1 = (SqlMember)node1;
- SqlMember m2 = (SqlMember)node2;
- return m1.Member == m2.Member && AreSimilar(m1.Expression, m2.Expression);
- }
- case SqlNodeType.ClientQuery: {
- SqlClientQuery cq1 = (SqlClientQuery)node1;
- SqlClientQuery cq2 = (SqlClientQuery)node2;
- if (cq1.Arguments.Count != cq2.Arguments.Count) {
- return false;
- }
- for (int i = 0, n = cq1.Arguments.Count; i < n; i++) {
- if (!AreSimilar(cq1.Arguments[i], cq2.Arguments[i])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.MethodCall: {
- SqlMethodCall mc1 = (SqlMethodCall)node1;
- SqlMethodCall mc2 = (SqlMethodCall)node2;
- if (mc1.Method != mc2.Method || !AreSimilar(mc1.Object, mc2.Object)) {
- return false;
- }
- if (mc1.Arguments.Count != mc2.Arguments.Count) {
- return false;
- }
- for (int i = 0, n = mc1.Arguments.Count; i < n; i++) {
- if (!AreSimilar(mc1.Arguments[i], mc2.Arguments[i])) {
- return false;
- }
- }
- return true;
- }
- case SqlNodeType.ClientParameter:
- default:
- return false;
- }
- }
- }
- class SideEffectChecker : SqlVisitor {
- bool hasSideEffect;
- internal bool HasSideEffect(SqlNode node) {
- this.hasSideEffect = false;
- this.Visit(node);
- return this.hasSideEffect;
- }
- internal override SqlExpression VisitJoinedCollection(SqlJoinedCollection jc) {
- this.hasSideEffect = true;
- return jc;
- }
- internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
- return cq;
- }
- }
- [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unknown reason.")]
- class Generator {
- ObjectReaderCompiler compiler;
- ILGenerator gen;
- List<object> globals;
- List<NamedColumn> namedColumns;
- LocalBuilder locDataReader;
- Type elementType;
- int nLocals;
- Dictionary<MetaAssociation, int> associationSubQueries;
- SideEffectChecker sideEffectChecker = new SideEffectChecker();
- internal Generator(ObjectReaderCompiler compiler, Type elementType) {
- this.compiler = compiler;
- this.elementType = elementType;
- this.associationSubQueries = new Dictionary<MetaAssociation,int>();
- }
- internal void GenerateBody(ILGenerator generator, SqlExpression expression) {
- this.gen = generator;
- this.globals = new List<object>();
- this.namedColumns = new List<NamedColumn>();
- // prepare locDataReader
- this.locDataReader = generator.DeclareLocal(this.compiler.dataReaderType);
- generator.Emit(OpCodes.Ldarg_0);
- generator.Emit(OpCodes.Ldfld, this.compiler.readerField);
- generator.Emit(OpCodes.Stloc, this.locDataReader);
- this.GenerateExpressionForType(expression, this.elementType);
- generator.Emit(OpCodes.Ret);
- }
- internal object[] Globals {
- get { return this.globals.ToArray(); }
- }
- internal NamedColumn[] NamedColumns {
- get { return this.namedColumns.ToArray(); }
- }
- internal int Locals {
- get { return this.nLocals; }
- }
- #if DEBUG
- private int stackDepth;
- #endif
- private Type Generate(SqlNode node) {
- return this.Generate(node, null);
- }
- [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Microsoft: Cast is dependent on node type and casts do not happen unecessarily in a single code path.")]
- [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.")]
- private Type Generate(SqlNode node, LocalBuilder locInstance) {
- #if DEBUG
- try {
- stackDepth++;
- System.Diagnostics.Debug.Assert(stackDepth < 500);
- #endif
- switch (node.NodeType) {
- case SqlNodeType.New:
- return this.GenerateNew((SqlNew)node);
- case SqlNodeType.ColumnRef:
- return this.GenerateColumnReference((SqlColumnRef)node);
- case SqlNodeType.ClientQuery:
- return this.GenerateClientQuery((SqlClientQuery)node, locInstance);
- case SqlNodeType.JoinedCollection:
- return this.GenerateJoinedCollection((SqlJoinedCollection)node);
- case SqlNodeType.Link:
- return this.GenerateLink((SqlLink)node, locInstance);
- case SqlNodeType.Value:
- return this.GenerateValue((SqlValue)node);
- case SqlNodeType.ClientParameter:
- return this.GenerateClientParameter((SqlClientParameter)node);
- case SqlNodeType.ValueOf:
- return this.GenerateValueOf((SqlUnary)node);
- case SqlNodeType.OptionalValue:
- return this.GenerateOptionalValue((SqlOptionalValue)node);
- case SqlNodeType.OuterJoinedValue:
- return this.Generate(((SqlUnary)node).Operand);
- case SqlNodeType.Lift:
- return this.GenerateLift((SqlLift)node);
- case SqlNodeType.Grouping:
- return this.GenerateGrouping((SqlGrouping)node);
- case SqlNodeType.ClientArray:
- return this.GenerateClientArray((SqlClientArray)node);
- case SqlNodeType.UserColumn:
- return this.GenerateUserColumn((SqlUserColumn)node);
- case SqlNodeType.ClientCase:
- return this.GenerateClientCase((SqlClientCase)node, false, locInstance);
- case SqlNodeType.SearchedCase:
- return this.GenerateSearchedCase((SqlSearchedCase)node);
- case SqlNodeType.TypeCase:
- return this.GenerateTypeCase((SqlTypeCase)node);
- case SqlNodeType.DiscriminatedType:
- return this.GenerateDiscriminatedType((SqlDiscriminatedType)node);
- case SqlNodeType.Member:
- return this.GenerateMember((SqlMember)node);
- case SqlNodeType.MethodCall:
- return this.GenerateMethodCall((SqlMethodCall)node);
- default:
- throw Error.CouldNotTranslateExpressionForReading(node.SourceExpression);
- }
- #if DEBUG
- }
- finally {
- stackDepth--;
- }
- #endif
- }
- private void GenerateAccessBufferReader() {
- gen.Emit(OpCodes.Ldarg_0);
- gen.Emit(OpCodes.Ldfld, this.compiler.bufferReaderField);
- }
- private void GenerateAccessDataReader() {
- gen.Emit(OpCodes.Ldloc, this.locDataReader);
- }
- private void GenerateAccessOrdinals() {
- gen.Emit(OpCodes.Ldarg_0);
- gen.Emit(OpCodes.Ldfld, this.compiler.ordinalsField);
- }
- private void GenerateAccessGlobals() {
- gen.Emit(OpCodes.Ldarg_0);
- gen.Emit(OpCodes.Ldfld, this.compiler.globalsField);
- }
- private void GenerateAccessArguments() {
- gen.Emit(OpCodes.Ldarg_0);
- gen.Emit(OpCodes.Ldfld, this.compiler.argsField);
- }
- private Type GenerateValue(SqlValue value) {
- return this.GenerateConstant(value.ClrType, value.Value);
- }
- private Type GenerateClientParameter(SqlClientParameter cp) {
- Delegate d = cp.Accessor.Compile();
- int iGlobal = this.AddGlobal(d.GetType(), d);
- this.GenerateGlobalAccess(iGlobal, d.GetType());
- this.GenerateAccessArguments();
- MethodInfo miInvoke = d.GetType().GetMethod(
- "Invoke",
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
- null,
- new Type[] { typeof(object[]) },
- null
- );
- System.Diagnostics.Debug.Assert(miInvoke != null);
- gen.Emit(GetMethodCallOpCode(miInvoke), miInvoke);
- return d.Method.ReturnType;
- }
- private Type GenerateValueOf(SqlUnary u) {
- System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(u.Operand.ClrType));
- this.GenerateExpressionForType(u.Operand, u.Operand.ClrType);
- LocalBuilder loc = gen.DeclareLocal(u.Operand.ClrType);
- gen.Emit(OpCodes.Stloc, loc);
- gen.Emit(OpCodes.Ldloca, loc);
- this.GenerateGetValue(u.Operand.ClrType);
- return u.ClrType;
- }
- private Type GenerateOptionalValue(SqlOptionalValue opt) {
- System.Diagnostics.Debug.Assert(opt.HasValue.ClrType == typeof(int?));
- Label labIsNull = gen.DefineLabel();
- Label labExit = gen.DefineLabel();
- Type actualType = this.Generate(opt.HasValue);
- System.Diagnostics.Debug.Assert(TypeSystem.IsNullableType(actualType));
- LocalBuilder loc = gen.DeclareLocal(actualType);
- gen.Emit(OpCodes.Stloc, loc);
- gen.Emit(OpCodes.Ldloca, loc);
- this.GenerateHasValue(actualType);
- gen.Emit(OpCodes.Brfalse, labIsNull);
- this.GenerateExpressionForType(opt.Value, opt.ClrType);
- gen.Emit(OpCodes.Br_S, labExit);
- gen.MarkLabel(labIsNull);
- this.GenerateConstant(opt.ClrType, null);
- gen.MarkLabel(labExit);
- return opt.ClrType;
- }
- private Type GenerateLift(SqlLift lift) {
- return this.GenerateExpressionForType(lift.Expression, lift.ClrType);
- }
- private Type GenerateClientArray(SqlClientArray ca) {
- if (!ca.ClrType.IsArray) {
- throw Error.CannotMaterializeList(ca.ClrType);
- }
- Type elemType = TypeSystem.GetElementType(ca.ClrType);
- this.GenerateConstInt(ca.Expressions.Count);
- gen.Emit(OpCodes.Newarr, elemType);
- for (int i = 0, n = ca.Expressions.Count; i < n; i++) {
- gen.Emit(OpCodes.Dup);
- this.GenerateConstInt(i);
- this.GenerateExpressionForType(ca.Expressions[i], elemType);
- this.GenerateArrayAssign(elemType);
- }
- return ca.ClrType;
- }
- private Type GenerateMember(SqlMember m) {
- FieldInfo fi = m.Member as FieldInfo;
- if (fi != null) {
- this.GenerateExpressionForType(m.Expression, m.Expression.ClrType);
- gen.Emit(OpCodes.Ldfld, fi);
- return fi.FieldType;
- }
- else {
- PropertyInfo pi = (PropertyInfo)m.Member;
- return this.GenerateMethodCall(new SqlMethodCall(m.ClrType, m.SqlType, pi.GetGetMethod(), m.Expression, null, m.SourceExpression));
- }
- }
- private Type GenerateMethodCall(SqlMethodCall mc) {
- ParameterInfo[] pis = mc.Method.GetParameters();
- if (mc.Object != null) {
- Type actualType = this.GenerateExpressionForType(mc.Object, mc.Object.ClrType);
- if (actualType.IsValueType) {
- LocalBuilder loc = gen.DeclareLocal(actualType);
- gen.Emit(OpCodes.Stloc, loc);
- gen.Emit(OpCodes.Ldloca, loc);
- }
- }
- for (int i = 0, n = mc.Arguments.Count; i < n; i++) {
- ParameterInfo pi = pis[i];
- Type pType = pi.ParameterType;
- if (pType.IsByRef) {
- pType = pType.GetElementType();
- this.GenerateExpressionForType(mc.Arguments[i], pType);
- LocalBuilder loc = gen.DeclareLocal(pType);
- gen.Emit(OpCodes.Stloc, loc);
- gen.Emit(OpCodes.Ldloca, loc);
- }
- else {
- this.GenerateExpressionForType(mc.Arguments[i], pType);
- }
- }
- OpCode callOpCode = GetMethodCallOpCode(mc.Method);
- if (mc.Object != null && TypeSystem.IsNullableType(mc.Object.ClrType) && callOpCode == OpCodes.Callvirt){
- gen.Emit(OpCodes.Constrained, mc.Object.ClrType);
- }
- gen.Emit(callOpCode, mc.Method);
- return mc.Method.ReturnType;
- }
- /// <summary>
- /// Cannot use Call for virtual methods - it results in unverifiable code. Ensure we're using the correct op code.
- /// </summary>
- private static OpCode GetMethodCallOpCode(MethodInfo mi) {
- return (mi.IsStatic || mi.DeclaringType.IsValueType) ? OpCodes.Call : OpCodes.Callvirt;
- }
- private Type GenerateNew(SqlNew sn) {
- LocalBuilder locInstance = gen.DeclareLocal(sn.ClrType);
- LocalBuilder locStoreInMember = null;
- Label labNewExit = gen.DefineLabel();
- Label labAlreadyCached = gen.DefineLabel();
- // read all arg values
- if (sn.Args.Count > 0) {
- ParameterInfo[] pis = sn.Constructor.GetParameters();
- for (int i = 0, n = sn.Args.Count; i < n; i++) {
- this.GenerateExpressionForType(sn.Args[i], pis[i].ParameterType);
- }
- }
- // construct the new instance
- if (sn.Constructor != null) {
- gen.Emit(OpCodes.Newobj, sn.Constructor);
- gen.Emit(OpCodes.Stloc, locInstance);
- }
- else if (sn.ClrType.IsValueType) {
- gen.Emit(OpCodes.Ldloca, locInstance);
- gen.Emit(OpCodes.Initobj, sn.ClrType);
- }
- else {
- ConstructorInfo ci = sn.ClrType.GetConst…
Large files files are truncated, but you can click here to view the full file